fix(test): use API-key auth for Codex live Docker lanes

This commit is contained in:
Vincent Koc
2026-06-04 12:34:54 -07:00
parent 57ec0b236f
commit 40661e9d19
3 changed files with 78 additions and 24 deletions

View File

@@ -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,

View File

@@ -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);

View File

@@ -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"] });