From a46181f1680bfd51061b08e9fde0ebd41281facf Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 3 Jun 2026 03:47:43 -0700 Subject: [PATCH] test: stabilize timing-sensitive ARM suites --- .../src/app-server/attempt-startup.test.ts | 48 +++++++++++++------ .../codex/src/app-server/test-support.ts | 4 +- src/agents/code-mode.test.ts | 2 +- src/tui/tui-pty-harness.e2e.test.ts | 4 +- 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/extensions/codex/src/app-server/attempt-startup.test.ts b/extensions/codex/src/app-server/attempt-startup.test.ts index 6f9967b66fa..8245f74a58b 100644 --- a/extensions/codex/src/app-server/attempt-startup.test.ts +++ b/extensions/codex/src/app-server/attempt-startup.test.ts @@ -48,6 +48,8 @@ const bundleMcpThreadConfig = { fingerprint: undefined, } satisfies CodexBundleMcpThreadConfig; +const HARNESS_REQUEST_TIMEOUT_MS = 15_000; + function readHarnessMessages(writes: string[]): Array<{ id?: number; method?: string }> { return writes.map((write) => JSON.parse(write) as { id?: number; method?: string }); } @@ -105,7 +107,7 @@ function startThreadWithHarness( async function answerInitialize(harness: ClientHarness): Promise { await vi.waitFor(() => expect(harness.writes.length).toBeGreaterThanOrEqual(1), { interval: 1, - timeout: 5_000, + timeout: HARNESS_REQUEST_TIMEOUT_MS, }); const initialize = JSON.parse(harness.writes[0] ?? "{}") as { id?: number }; harness.send({ id: initialize.id, result: { userAgent: "openclaw/0.125.0 (macOS; test)" } }); @@ -120,7 +122,7 @@ async function waitForRequest( expect(readHarnessMessages(harness.writes).some((write) => write.method === method)).toBe( true, ), - { interval: 1, timeout: 5_000 }, + { interval: 1, timeout: HARNESS_REQUEST_TIMEOUT_MS }, ); const request = readHarnessMessages(harness.writes).find((write) => write.method === method); if (!request) { @@ -238,9 +240,10 @@ describe("startCodexAttemptThread", () => { harness: retained, skipStartSpy: true, }); + const rejected = expect(run).rejects.toThrow("codex app-server startup timed out"); const threadStart = await waitForThreadStart(retained); - await expect(run).rejects.toThrow("codex app-server startup timed out"); + await rejected; expect(retained.process.stdin.destroyed).toBe(false); retained.send({ id: threadStart.id, result: { threadId: "late-thread" } }); @@ -249,11 +252,18 @@ describe("startCodexAttemptThread", () => { }); it("closes the shared app-server when startup times out during initialize", async () => { - const { harness, run } = startThreadWithHarness(100); + const { harness, run } = startThreadWithHarness(2_000); + const runError = run.then( + () => undefined, + (error: unknown) => error, + ); - await vi.waitFor(() => expect(harness.writes.length).toBeGreaterThanOrEqual(1)); + const initialize = await waitForRequest(harness, "initialize"); + expect(initialize.id).toBeDefined(); - await expect(run).rejects.toThrow("codex app-server startup timed out"); + const error = await runError; + expect(error).toBeInstanceOf(Error); + expect((error as Error).message).toBe("codex app-server startup timed out"); await vi.waitFor(() => expect(harness.stdinDestroyed).toBe(true), { interval: 1, timeout: 2_000, @@ -270,19 +280,29 @@ describe("startCodexAttemptThread", () => { abandonSignal?: AbortSignal; } | undefined; + let resolveFactoryDone: () => void = () => undefined; + const factoryDone = new Promise((resolve) => { + resolveFactoryDone = resolve; + }); const { harness, run } = startThreadWithHarness(100, new AbortController().signal, { attemptClientFactory: (factoryHarness) => async (_startOptions, _authProfileId, _agentDir, _config, options) => { - observedFactoryOptions = options; - await new Promise((resolve) => { - setTimeout(resolve, 250); - }); - options?.onStartedClient?.(factoryHarness.client); - return factoryHarness.client; + try { + observedFactoryOptions = options; + await new Promise((resolve) => { + setTimeout(resolve, 250); + }); + options?.onStartedClient?.(factoryHarness.client); + return factoryHarness.client; + } finally { + resolveFactoryDone(); + } }, }); + const rejected = expect(run).rejects.toThrow("codex app-server startup timed out"); - await expect(run).rejects.toThrow("codex app-server startup timed out"); + await rejected; + await factoryDone; await vi.waitFor(() => expect(harness.stdinDestroyed).toBe(true), { interval: 1, timeout: 2_000, @@ -296,7 +316,7 @@ describe("startCodexAttemptThread", () => { it("clears the shared app-server when cancellation abandons an in-flight thread request", async () => { const abortController = new AbortController(); - const { harness, run } = startThreadWithHarness(5_000, abortController.signal); + const { harness, run } = startThreadWithHarness(30_000, abortController.signal); const runError = run.then( () => undefined, (error: unknown) => error, diff --git a/extensions/codex/src/app-server/test-support.ts b/extensions/codex/src/app-server/test-support.ts index b57e95f416b..3dbd0070359 100644 --- a/extensions/codex/src/app-server/test-support.ts +++ b/extensions/codex/src/app-server/test-support.ts @@ -43,7 +43,9 @@ export function createClientHarness() { const result = destroyStdin(error); if (!exitEmitted) { exitEmitted = true; - queueMicrotask(emitProcessExit); + // Let stdin surface pipe errors before the harness emits the fake child exit. + // Otherwise close-reason tests can race EPIPE against a synthetic clean exit. + setImmediate(emitProcessExit); } return result; }) as typeof stdin.destroy; diff --git a/src/agents/code-mode.test.ts b/src/agents/code-mode.test.ts index a88dffc6014..1416ee41c96 100644 --- a/src/agents/code-mode.test.ts +++ b/src/agents/code-mode.test.ts @@ -1384,7 +1384,7 @@ describe("Code Mode", () => { tools: { codeMode: { enabled: true, - timeoutMs: 100, + timeoutMs: 500, }, }, } as never; diff --git a/src/tui/tui-pty-harness.e2e.test.ts b/src/tui/tui-pty-harness.e2e.test.ts index 27a5f39c8ae..2e70130c9d1 100644 --- a/src/tui/tui-pty-harness.e2e.test.ts +++ b/src/tui/tui-pty-harness.e2e.test.ts @@ -11,11 +11,11 @@ type FixtureLogEntry = { }; const activeRuns: PtyRun[] = []; -const STARTUP_TIMEOUT_MS = 10_000; +const STARTUP_TIMEOUT_MS = 20_000; const OUTPUT_TIMEOUT_MS = 2_000; const EXIT_TIMEOUT_MS = 4_000; const TEST_TIMEOUT_MS = 5_000; -const STARTUP_TEST_TIMEOUT_MS = 10_000; +const STARTUP_TEST_TIMEOUT_MS = 25_000; async function readFixtureLog(logPath: string): Promise { try {