mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-08 11:02:26 +00:00
fix(test): use API-key auth for Codex live Docker lanes
This commit is contained in:
@@ -15,6 +15,7 @@ const rootManagedVpsUpgradeCommand =
|
||||
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:root-managed-vps-upgrade";
|
||||
const updateRestartAuthCommand =
|
||||
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:update-restart-auth";
|
||||
const CODEX_HARNESS_API_KEY_ENV = "OPENCLAW_LIVE_CODEX_HARNESS_AUTH=api-key";
|
||||
|
||||
const LIVE_RETRY_PATTERNS = [
|
||||
/529\b/i,
|
||||
@@ -513,13 +514,17 @@ export const tailLanes = [
|
||||
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openai-web-search-minimal",
|
||||
{ stateScenario: "empty", timeoutMs: 8 * 60 * 1000 },
|
||||
),
|
||||
liveLane("live-codex-harness", liveDockerScriptCommand("test-live-codex-harness-docker.sh"), {
|
||||
cacheKey: "codex-harness",
|
||||
provider: "codex-cli",
|
||||
resources: ["npm"],
|
||||
timeoutMs: LIVE_ACP_TIMEOUT_MS,
|
||||
weight: 3,
|
||||
}),
|
||||
liveLane(
|
||||
"live-codex-harness",
|
||||
liveDockerScriptCommand("test-live-codex-harness-docker.sh", CODEX_HARNESS_API_KEY_ENV),
|
||||
{
|
||||
cacheKey: "codex-harness",
|
||||
provider: "openai",
|
||||
resources: ["npm"],
|
||||
timeoutMs: LIVE_ACP_TIMEOUT_MS,
|
||||
weight: 3,
|
||||
},
|
||||
),
|
||||
liveLane(
|
||||
"live-codex-media-path",
|
||||
liveDockerScriptCommand(
|
||||
@@ -549,11 +554,11 @@ export const tailLanes = [
|
||||
"live-codex-bind",
|
||||
liveDockerScriptCommand(
|
||||
"test-live-codex-harness-docker.sh",
|
||||
"OPENCLAW_LIVE_CODEX_BIND=1 OPENCLAW_LIVE_CODEX_TEST_FILES=src/gateway/gateway-codex-bind.live.test.ts",
|
||||
`${CODEX_HARNESS_API_KEY_ENV} OPENCLAW_LIVE_CODEX_BIND=1 OPENCLAW_LIVE_CODEX_TEST_FILES=src/gateway/gateway-codex-bind.live.test.ts`,
|
||||
),
|
||||
{
|
||||
cacheKey: "codex-harness",
|
||||
provider: "codex-cli",
|
||||
provider: "openai",
|
||||
resources: ["npm"],
|
||||
timeoutMs: LIVE_ACP_TIMEOUT_MS,
|
||||
weight: 3,
|
||||
|
||||
@@ -31,8 +31,14 @@ import { startGatewayServer } from "./server.js";
|
||||
const LIVE = isLiveTestEnabled();
|
||||
const CODEX_BIND_LIVE = isTruthyEnvValue(process.env.OPENCLAW_LIVE_CODEX_BIND);
|
||||
const describeLive = LIVE && CODEX_BIND_LIVE ? describe : describe.skip;
|
||||
const CODEX_BIND_TIMEOUT_MS = 10 * 60_000;
|
||||
const CODEX_BIND_REQUEST_TIMEOUT_MS = 180_000;
|
||||
const CODEX_BIND_TIMEOUT_MS = resolveLiveTimeoutMs(
|
||||
process.env.OPENCLAW_LIVE_CODEX_BIND_TIMEOUT_MS,
|
||||
900_000,
|
||||
);
|
||||
const CODEX_BIND_REQUEST_TIMEOUT_MS = resolveLiveTimeoutMs(
|
||||
process.env.OPENCLAW_LIVE_CODEX_BIND_REQUEST_TIMEOUT_MS,
|
||||
300_000,
|
||||
);
|
||||
const DEFAULT_CODEX_BIND_MODEL = "gpt-5.5";
|
||||
|
||||
type CapturedOutboundReply = {
|
||||
@@ -42,6 +48,15 @@ type CapturedOutboundReply = {
|
||||
to: string;
|
||||
};
|
||||
|
||||
function resolveLiveTimeoutMs(raw: string | undefined, fallback: number): number {
|
||||
const parsed = raw ? Number(raw) : Number.NaN;
|
||||
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;
|
||||
}
|
||||
|
||||
function logCodexBindStep(message: string): void {
|
||||
console.info(`[live-codex-bind] ${message}`);
|
||||
}
|
||||
|
||||
function createSlackCurrentConversationBindingRegistry(outboundReplies: CapturedOutboundReply[]) {
|
||||
return createTestRegistry([
|
||||
{
|
||||
@@ -164,14 +179,24 @@ function restoreEnvVar(name: string, value: string | undefined): void {
|
||||
process.env[name] = value;
|
||||
}
|
||||
|
||||
async function waitForAgentRunOk(client: GatewayClient, runId: string): Promise<void> {
|
||||
const result: { status?: string } = await client.request(
|
||||
"agent.wait",
|
||||
{ runId, timeoutMs: CODEX_BIND_REQUEST_TIMEOUT_MS },
|
||||
{ timeoutMs: CODEX_BIND_REQUEST_TIMEOUT_MS + 5_000 },
|
||||
);
|
||||
async function waitForAgentRunOk(
|
||||
client: GatewayClient,
|
||||
runId: string,
|
||||
context: string,
|
||||
): Promise<void> {
|
||||
let result: { status?: string };
|
||||
try {
|
||||
result = await client.request(
|
||||
"agent.wait",
|
||||
{ runId, timeoutMs: CODEX_BIND_REQUEST_TIMEOUT_MS },
|
||||
{ timeoutMs: CODEX_BIND_REQUEST_TIMEOUT_MS + 5_000 },
|
||||
);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(`${context}: agent.wait error for ${runId}: ${message}`, { cause: error });
|
||||
}
|
||||
if (result?.status !== "ok") {
|
||||
throw new Error(`agent.wait failed for ${runId}: status=${String(result?.status)}`);
|
||||
throw new Error(`${context}: agent.wait failed for ${runId}: status=${String(result?.status)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +204,7 @@ async function sendChatAndWait(params: {
|
||||
client: GatewayClient;
|
||||
sessionKey: string;
|
||||
idempotencyKey: string;
|
||||
context: string;
|
||||
message: string;
|
||||
originatingChannel: string;
|
||||
originatingTo: string;
|
||||
@@ -201,9 +227,13 @@ async function sendChatAndWait(params: {
|
||||
attachments: params.attachments,
|
||||
});
|
||||
if (started?.status !== "started" || typeof started.runId !== "string") {
|
||||
throw new Error(`chat.send did not start correctly: ${JSON.stringify(started)}`);
|
||||
throw new Error(
|
||||
`${params.context}: chat.send did not start correctly: ${JSON.stringify(started)}`,
|
||||
);
|
||||
}
|
||||
await waitForAgentRunOk(params.client, started.runId);
|
||||
logCodexBindStep(`${params.context} started (${started.runId})`);
|
||||
await waitForAgentRunOk(params.client, started.runId, params.context);
|
||||
logCodexBindStep(`${params.context} completed`);
|
||||
}
|
||||
|
||||
async function waitForAssistantText(params: {
|
||||
@@ -344,8 +374,10 @@ async function writeGatewayConfig(params: {
|
||||
agents: {
|
||||
defaults: {
|
||||
workspace: params.workspace,
|
||||
agentRuntime: { id: "codex" },
|
||||
model: { primary: `${modelProvider}/${params.model}` },
|
||||
models: {
|
||||
[`${modelProvider}/${params.model}`]: { agentRuntime: { id: "codex" } },
|
||||
},
|
||||
skipBootstrap: true,
|
||||
heartbeat: { every: "0m" },
|
||||
sandbox: { mode: "off" },
|
||||
@@ -462,6 +494,7 @@ describeLive("gateway live (native Codex conversation binding)", () => {
|
||||
client,
|
||||
sessionKey,
|
||||
idempotencyKey: `idem-codex-bind-${randomUUID()}`,
|
||||
context: "bind command",
|
||||
message: `/codex bind --cwd ${workspace} --model ${bindModel}${
|
||||
bindProvider ? ` --provider ${bindProvider}` : ""
|
||||
}`,
|
||||
@@ -481,6 +514,7 @@ describeLive("gateway live (native Codex conversation binding)", () => {
|
||||
accountId,
|
||||
conversationId,
|
||||
});
|
||||
logCodexBindStep(`binding resolved to ${boundSessionKey}`);
|
||||
let commandReplyCount = bindReply.outboundTexts.length;
|
||||
|
||||
const sendCodexCommand = async (message: string, contains: string, timeoutMs = 60_000) => {
|
||||
@@ -488,6 +522,7 @@ describeLive("gateway live (native Codex conversation binding)", () => {
|
||||
client,
|
||||
sessionKey,
|
||||
idempotencyKey: `idem-codex-command-${randomUUID()}`,
|
||||
context: message,
|
||||
message,
|
||||
originatingChannel: "slack",
|
||||
originatingTo: conversationId,
|
||||
@@ -530,6 +565,7 @@ describeLive("gateway live (native Codex conversation binding)", () => {
|
||||
client,
|
||||
sessionKey,
|
||||
idempotencyKey: `idem-codex-bound-text-${randomUUID()}`,
|
||||
context: "bound text turn",
|
||||
message: `Reply with exactly this token and nothing else: ${textToken}`,
|
||||
originatingChannel: "slack",
|
||||
originatingTo: conversationId,
|
||||
@@ -547,6 +583,7 @@ describeLive("gateway live (native Codex conversation binding)", () => {
|
||||
client,
|
||||
sessionKey,
|
||||
idempotencyKey: `idem-codex-bound-image-${randomUUID()}`,
|
||||
context: "bound image turn",
|
||||
message:
|
||||
"What animal is drawn in the attached image? Reply with only the lowercase animal name.",
|
||||
originatingChannel: "slack",
|
||||
@@ -578,7 +615,7 @@ describeLive("gateway live (native Codex conversation binding)", () => {
|
||||
clearRuntimeConfigSnapshot();
|
||||
await client.stopAndWait({ timeoutMs: 2_000 }).catch(() => {});
|
||||
await server.close();
|
||||
await fs.rm(tempRoot, { recursive: true, force: true });
|
||||
await fs.rm(tempRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
restoreEnvVar("CODEX_HOME", previous.codexHome);
|
||||
restoreEnvVar("OPENCLAW_CONFIG_PATH", previous.configPath);
|
||||
restoreEnvVar("OPENCLAW_GATEWAY_TOKEN", previous.gatewayToken);
|
||||
|
||||
@@ -677,11 +677,11 @@ describe("scripts/lib/docker-e2e-plan", () => {
|
||||
{ credentials: ["anthropic", "gemini"], name: "live-gateway" },
|
||||
{ credentials: ["anthropic"], name: "live-cli-backend-claude" },
|
||||
{ credentials: ["gemini"], name: "live-cli-backend-gemini" },
|
||||
{ credentials: ["codex"], name: "live-codex-harness" },
|
||||
{ credentials: ["openai"], name: "live-codex-harness" },
|
||||
{ credentials: ["openai"], name: "live-codex-media-path" },
|
||||
{ credentials: ["openai"], name: "live-mcp-code-mode-gateway" },
|
||||
{ credentials: ["openai"], name: "live-subagent-announce" },
|
||||
{ credentials: ["codex"], name: "live-codex-bind" },
|
||||
{ credentials: ["openai"], name: "live-codex-bind" },
|
||||
{ credentials: ["anthropic"], name: "live-acp-bind-claude" },
|
||||
{ credentials: ["codex", "openai"], name: "live-acp-bind-codex" },
|
||||
{ credentials: ["factory"], name: "live-acp-bind-droid" },
|
||||
@@ -694,6 +694,18 @@ describe("scripts/lib/docker-e2e-plan", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("plans Codex harness Docker-all lanes for API-key Testbox auth", () => {
|
||||
for (const name of ["live-codex-harness", "live-codex-bind"]) {
|
||||
const plan = planFor({ selectedLaneNames: [name] });
|
||||
const lane = requireFirstLane(plan);
|
||||
|
||||
expect(plan.credentials, name).toEqual(["openai"]);
|
||||
expect(lane.command, name).toContain("OPENCLAW_LIVE_CODEX_HARNESS_AUTH=api-key");
|
||||
expect(lane.resources, name).toContain("live:openai");
|
||||
expect(lane.resources, name).not.toContain("live:codex");
|
||||
}
|
||||
});
|
||||
|
||||
it("plans the Codex npm plugin live lane as package-backed OpenAI proof", () => {
|
||||
const plan = planFor({ selectedLaneNames: ["live-codex-npm-plugin"] });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user