From 28f25c6d18725c77f47c899e66dd5944f370fbe5 Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Fri, 25 Sep 2026 20:33:02 +0800 Subject: [PATCH 1/8] test: stabilize isolated suite fixtures and compaction replay --- tests/clients/remote-workspace-command-runner.test.ts | 6 +++--- tests/responses/responses-compaction-routing.test.ts | 2 +- tests/service/service-claim.test.ts | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/clients/remote-workspace-command-runner.test.ts b/tests/clients/remote-workspace-command-runner.test.ts index fc8170c41ac..73c93273f5c 100644 --- a/tests/clients/remote-workspace-command-runner.test.ts +++ b/tests/clients/remote-workspace-command-runner.test.ts @@ -32,9 +32,9 @@ function fixture() { } function privateBubblewrapFixture(): string { - // The production guard checks every ancestor, so tmpdir's shared /tmp parent - // is deliberately ineligible. Own a disposable sibling under the trusted - // interpreter directory without chmod'ing the interpreter or shared parents. + // These argv tests never execute bubblewrap. On Unix, use a system executable + // whose ancestors are trusted even when the test's Bun binary lives under /tmp. + if (process.platform !== "win32") return "/usr/bin/env"; const root = mkdtempSync(join(dirname(realpathSync(process.execPath)), "ocx-bwrap-fixture-")); roots.push(root); const path = join(root, "bwrap"); diff --git a/tests/responses/responses-compaction-routing.test.ts b/tests/responses/responses-compaction-routing.test.ts index bf2a4555991..a201a1b133d 100644 --- a/tests/responses/responses-compaction-routing.test.ts +++ b/tests/responses/responses-compaction-routing.test.ts @@ -1122,7 +1122,7 @@ describe("compact alternate-account attempt (#913)", () => { expect(JSON.stringify(calls.at(-1)!.body.input)).toContain(OPAQUE_COMPACTION_NOTE); expect(calls).toHaveLength(4); }); - }); + }, 20_000); test("native compact headers followed by a stalled body return 504 without retry and release account cleanup", async () => { await withPoolEnv("ocx-compact-body-deadline-", async config => { diff --git a/tests/service/service-claim.test.ts b/tests/service/service-claim.test.ts index d886631522d..43e0536e53e 100644 --- a/tests/service/service-claim.test.ts +++ b/tests/service/service-claim.test.ts @@ -147,7 +147,8 @@ describe("runServiceClaim", () => { const previousUserProfile = process.env.USERPROFILE; if (process.platform === "win32") process.env.USERPROFILE = home.root; try { - expect(serviceStatePaths().every(path => path.startsWith(home.root))).toBe(true); + expect(serviceStatePath().startsWith(home.root)).toBe(true); + expect(serviceStatePaths()).toContain(serviceStatePath()); mkdirSync(serviceStatePath()); const lines: string[] = []; const code = await runServiceClaim([...VALID, "--json"], { From 3b9947d1bbc6dbc82e88f8bbc1c6b95bf8b41258 Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Fri, 25 Sep 2026 21:27:05 +0800 Subject: [PATCH 2/8] test: isolate shared-state suites and bound retry probes --- scripts/test.ts | 3 +++ tests/codex-integration/codex-shim.test.ts | 2 +- tests/responses/chat-conversation-affinity.test.ts | 4 ++-- tests/server/server-auth.test.ts | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/test.ts b/scripts/test.ts index 6db9075224a..51cf3f1f7ec 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -386,6 +386,9 @@ export const SERIAL_FULL_SUITE_FILES = [ "service/service-ownership-state.test.ts", "service/service-sqlite-home.test.ts", "service/service.test.ts", + "service/service-claim.test.ts", + "service/service-wsl-home-ownership.test.ts", + "codex-integration/native-codex-toggle.test.ts", "codex-integration/native-grok-toggle.test.ts", ] as const; diff --git a/tests/codex-integration/codex-shim.test.ts b/tests/codex-integration/codex-shim.test.ts index efc971030b4..4c4c8fc728c 100644 --- a/tests/codex-integration/codex-shim.test.ts +++ b/tests/codex-integration/codex-shim.test.ts @@ -641,7 +641,7 @@ os._exit(0) try { process.env.PATH = prependPath(binDir, oldPath); process.env.OPENCODEX_HOME = home; - setCodexShimProbeObservationMsForTests(1_500); + setCodexShimProbeObservationMsForTests(4_000); writeFileSync(codexPath, original, "utf8"); chmodSync(codexPath, 0o755); diff --git a/tests/responses/chat-conversation-affinity.test.ts b/tests/responses/chat-conversation-affinity.test.ts index 3254c709042..98cef3e41ac 100644 --- a/tests/responses/chat-conversation-affinity.test.ts +++ b/tests/responses/chat-conversation-affinity.test.ts @@ -90,7 +90,7 @@ describe("Chat conversation identity at canonical Responses outbound boundary", } expect(JSON.stringify(seen[1]!.body.input).length).toBeGreaterThan(JSON.stringify(seen[0]!.body.input).length); expect(seen[0]!.body.input).toEqual(seen[2]!.body.input); - }); + }, 20_000); } test(`identity absent, key=${keyPresent}: no session is synthesized`, async () => { @@ -101,6 +101,6 @@ describe("Chat conversation identity at canonical Responses outbound boundary", for (const name of identityHeaders) expect(wire.headers.has(name)).toBe(false); expect(wire.body.prompt_cache_key).toBe(keyPresent ? "shared-cache-cohort" : undefined); } - }); + }, 20_000); } }); diff --git a/tests/server/server-auth.test.ts b/tests/server/server-auth.test.ts index 8bccda10382..feddaf526a2 100644 --- a/tests/server/server-auth.test.ts +++ b/tests/server/server-auth.test.ts @@ -1515,7 +1515,7 @@ describe("server local API auth", () => { url.protocol = "ws:"; const ws = new WebSocket(url, { headers: { "x-opencodex-api-key": "local-secret", ...(headers ?? {}) } } as unknown as string[]); return new Promise((resolve, reject) => { - const timer = setTimeout(() => reject(new Error("tier websocket timeout")), watchdogMs(5_000)); + const timer = setTimeout(() => reject(new Error("tier websocket timeout")), watchdogMs(20_000)); ws.addEventListener("open", () => { ws.send(JSON.stringify({ type: "response.create", model, input: "hello" })); }, { once: true }); From 3cc162e13987348aa68ea5aec422700d091947ec Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Fri, 25 Sep 2026 21:38:11 +0800 Subject: [PATCH 3/8] test: wait for launcher config injection before shutdown assertion --- tests/service/shutdown-launcher.test.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/service/shutdown-launcher.test.ts b/tests/service/shutdown-launcher.test.ts index c8cf5b2d98b..9f7dca0b8c2 100644 --- a/tests/service/shutdown-launcher.test.ts +++ b/tests/service/shutdown-launcher.test.ts @@ -142,12 +142,18 @@ describe.skipIf(!runnable)("ocx launcher graceful shutdown", () => { child.stderr?.on("data", chunk => { output += String(chunk); }); // 1. Proxy comes up + injected the Codex config (Design B root override on loopback). - const up = await waitUntil(() => healthy(port), STARTUP_BUDGET_MS); + // The health listener may answer before the launcher completes injection. + let healthSeen = false; + const up = await waitUntil(async () => { + if (!(await healthy(port))) return false; + healthSeen = true; + return readFileSync(codexConfig, "utf8").includes(OCX_ROUTING_MARKER_LINE); + }, STARTUP_BUDGET_MS); if (!up) { // Name what actually went wrong instead of asserting a bare boolean. const died = exited ? ` The launcher EXITED (code ${exitCode}, signal ${exitSignal}).` : " The launcher was still running."; throw new Error( - `The proxy never answered /healthz on port ${port} within ${STARTUP_BUDGET_MS}ms.${died}` + `The proxy ${healthSeen ? "answered /healthz but did not inject Codex config" : "never answered /healthz"} on port ${port} within ${STARTUP_BUDGET_MS}ms.${died}` + ` Launcher output:\n${output.trim() || "(none)"}`, ); } From 6e69d87e8807c17f5fb61bb811187c2f7db03450 Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Fri, 25 Sep 2026 22:06:47 +0800 Subject: [PATCH 4/8] test: report caller-main retry context on failure --- tests/server/server-auth.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/server/server-auth.test.ts b/tests/server/server-auth.test.ts index feddaf526a2..55680463391 100644 --- a/tests/server/server-auth.test.ts +++ b/tests/server/server-auth.test.ts @@ -3035,7 +3035,9 @@ describe("server local API auth", () => { model, headers: { "chatgpt-account-id": "acct-caller-main" }, }); - expect(response.status).toBe(200); + if (response.status !== 200) { + throw new Error(`caller-main retry returned ${response.status}: ${await response.text()}; dispatches=${JSON.stringify(harness.dispatches)}; observed=${JSON.stringify(observed)}`); + } expect((await response.json() as { id: string }).id).toBe("caller-main-success"); expect(observed).toEqual([ { authorization: "Bearer pool-a-token", accountId: "acct-pool-a" }, From f400166cb763091a284965f924ec8eeef0f5554b Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Fri, 25 Sep 2026 22:20:42 +0800 Subject: [PATCH 5/8] test: bound forward replay integration case under load --- tests/codex-integration/issue-702-expired-replay-state.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codex-integration/issue-702-expired-replay-state.test.ts b/tests/codex-integration/issue-702-expired-replay-state.test.ts index 59657cfd870..a682fa6db88 100644 --- a/tests/codex-integration/issue-702-expired-replay-state.test.ts +++ b/tests/codex-integration/issue-702-expired-replay-state.test.ts @@ -787,7 +787,7 @@ describe("Issue #702 expired forward replay state", () => { expect(serialized).toContain(HISTORICAL_USER_SENTINEL); expect(serialized).toContain(HISTORICAL_ASSISTANT_SENTINEL); expect(serialized).toContain(CURRENT_USER_SENTINEL); - }); + }, SERVER_BUDGET_MS); test("a task-scope mismatch refuses the delta before ordinary HTTP upstream I/O", async () => { const scenario = await runForwardScenario("fresh", { "x-codex-parent-thread-id": "other-task" }); From d77851ebbbbe5faecdaff04f7795bb35ac634bcf Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Fri, 25 Sep 2026 22:38:37 +0800 Subject: [PATCH 6/8] test: isolate roster retention from long-lived suite pool --- scripts/test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/test.ts b/scripts/test.ts index 51cf3f1f7ec..1b06865dc72 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -371,6 +371,9 @@ export const SERIAL_FULL_SUITE_FILES = [ // Synchronous injection subprocesses can wedge the long-lived macOS isolate // parent while reaping a history Worker; contain them in a fresh bounded lane. "codex-integration/codex-inject-write-lock.test.ts", + // Its management API import stalled the long-lived macOS isolate pool before + // any case ran; the complete file finishes in under a second in a fresh process. + "routing/subagent-roster-retention.test.ts", "update/update-stop-first.test.ts", // Relays a 50 MiB WebSocket frame end to end against a 15s deadline, so its result is a // measurement of the whole process, not of the relay. On a healthy 3-CPU macOS runner the From 107e3bd8ef08316f1c75ad7ee22afa95110322ee Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Sat, 26 Sep 2026 11:44:11 +0800 Subject: [PATCH 7/8] test: retain protected-home and executable trust assertions --- .../remote-workspace-command-runner.test.ts | 14 ++++++++++++-- tests/service/service-claim.test.ts | 11 +++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/clients/remote-workspace-command-runner.test.ts b/tests/clients/remote-workspace-command-runner.test.ts index 73c93273f5c..e4d8304ac55 100644 --- a/tests/clients/remote-workspace-command-runner.test.ts +++ b/tests/clients/remote-workspace-command-runner.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, test } from "bun:test"; import { randomUUID } from "node:crypto"; -import { chmodSync, existsSync, linkSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, symlinkSync, writeFileSync } from "node:fs"; +import { chmodSync, existsSync, linkSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, statSync, symlinkSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { @@ -34,7 +34,17 @@ function fixture() { function privateBubblewrapFixture(): string { // These argv tests never execute bubblewrap. On Unix, use a system executable // whose ancestors are trusted even when the test's Bun binary lives under /tmp. - if (process.platform !== "win32") return "/usr/bin/env"; + // Minimal images may hard-link env, so choose a single-link executable. + if (process.platform !== "win32") { + for (const candidate of ["/usr/bin/env", "/bin/true", "/usr/bin/true", "/bin/cat", "/bin/ls"]) { + try { + const canonical = realpathSync(candidate); + const file = statSync(canonical); + if (file.isFile() && file.nlink === 1) return canonical; + } catch { /* Candidate is absent on this image. */ } + } + throw new Error("no single-link system executable for the bubblewrap argv fixture"); + } const root = mkdtempSync(join(dirname(realpathSync(process.execPath)), "ocx-bwrap-fixture-")); roots.push(root); const path = join(root, "bwrap"); diff --git a/tests/service/service-claim.test.ts b/tests/service/service-claim.test.ts index 43e0536e53e..10296af18f0 100644 --- a/tests/service/service-claim.test.ts +++ b/tests/service/service-claim.test.ts @@ -1,5 +1,8 @@ import { describe, expect, test } from "bun:test"; import { mkdirSync, statSync } from "node:fs"; +import { homedir } from "node:os"; +import { dirname, join } from "node:path"; +import { isProtectedHomeUnderTest } from "../../src/lib/test-home-guard"; import { parseClaimArgs, runServiceClaim, CLAIM_SCHEMA } from "../../src/service/claim"; import { ServiceOwnershipSubjectMismatchError, serviceStatePath, serviceStatePaths } from "../../src/service/state"; import type { ServiceOwnershipSubject } from "../../src/service/state"; @@ -147,8 +150,12 @@ describe("runServiceClaim", () => { const previousUserProfile = process.env.USERPROFILE; if (process.platform === "win32") process.env.USERPROFILE = home.root; try { - expect(serviceStatePath().startsWith(home.root)).toBe(true); - expect(serviceStatePaths()).toContain(serviceStatePath()); + const primary = serviceStatePath(); + const legacy = join(homedir(), ".opencodex", "service-state.json"); + expect(primary.startsWith(home.root)).toBe(true); + expect(serviceStatePaths()).toContain(primary); + expect(serviceStatePaths().every(path => path === primary + || (path === legacy && !isProtectedHomeUnderTest(dirname(path))))).toBe(true); mkdirSync(serviceStatePath()); const lines: string[] = []; const code = await runServiceClaim([...VALID, "--json"], { From 866406bc8ec04aef4067e756587d1e55ba8ae138 Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Sat, 26 Sep 2026 11:56:36 +0800 Subject: [PATCH 8/8] test: bound native combo recall cases beyond their internal timer --- tests/responses/responses-compaction-routing.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/responses/responses-compaction-routing.test.ts b/tests/responses/responses-compaction-routing.test.ts index a201a1b133d..52540f1dd4a 100644 --- a/tests/responses/responses-compaction-routing.test.ts +++ b/tests/responses/responses-compaction-routing.test.ts @@ -44,6 +44,7 @@ import { captureConfigGeneration } from "../../src/lib/state-store-sweeper"; import { removeTreeWithRetry } from "../helpers/remove-tree"; import { baseCompactionBody, compactionRequest, completedPayload, jsonResponse, keyProviderConfig, nativePoolConfig, sseResponse, twoAccountPoolConfig } from "../helpers/compaction-routing-fixtures"; import { acquireOwnedSpendHome } from "../helpers/owned-spend-home"; +import { SERVER_BUDGET_MS } from "../helpers/test-budget"; const originalFetch = globalThis.fetch; @@ -981,7 +982,7 @@ describe("compact alternate-account attempt (#913)", () => { clearComboTargetCooldowns(); } }); - }); + }, SERVER_BUDGET_MS); } for (const [model, account] of [["gpt-5.5", "pool-a"], ["side/gpt-5.5", "pool-b"]] as const) {