diff --git a/extensions/codex/src/app-server/run-attempt.test.ts b/extensions/codex/src/app-server/run-attempt.test.ts index 3da9b17ee38d..28c3d1442f4c 100644 --- a/extensions/codex/src/app-server/run-attempt.test.ts +++ b/extensions/codex/src/app-server/run-attempt.test.ts @@ -96,6 +96,11 @@ import { let tempDir: string; let codexAppServerClientFactoryForTest: CodexAppServerClientFactory | undefined; const fastWait = { interval: 1, timeout: 5_000 } as const; +const appServerHarnessWait = { interval: 1, timeout: 120_000 } as const; +const activeAppServerAttemptsForTest = new Set<{ + abortController?: AbortController; + promise: Promise; +}>(); type RunCodexAppServerAttemptOptions = NonNullable< Parameters[1] @@ -150,10 +155,38 @@ function runCodexAppServerAttempt( options: RunCodexAppServerAttemptOptions = {}, ) { const clientFactory = options.clientFactory ?? codexAppServerClientFactoryForTest; - return runCodexAppServerAttemptImpl( - params, + const abortController = params.abortSignal ? undefined : new AbortController(); + const trackedParams = abortController + ? ({ ...params, abortSignal: abortController.signal } as EmbeddedRunAttemptParams) + : params; + const entry = { + abortController, + promise: undefined as unknown as Promise, + }; + const promise = runCodexAppServerAttemptImpl( + trackedParams, clientFactory ? { ...options, clientFactory } : options, - ); + ).finally(() => { + activeAppServerAttemptsForTest.delete(entry); + }); + entry.promise = promise; + activeAppServerAttemptsForTest.add(entry); + promise.catch(() => undefined); + return promise; +} + +async function drainActiveAppServerAttemptsForTest(): Promise { + const attempts = [...activeAppServerAttemptsForTest]; + if (attempts.length === 0) { + return; + } + for (const attempt of attempts) { + attempt.abortController?.abort("test_cleanup"); + } + await Promise.race([ + Promise.allSettled(attempts.map((attempt) => attempt.promise)), + new Promise((resolve) => setTimeout(resolve, 5_000)), + ]); } function createParams(sessionFile: string, workspaceDir: string): EmbeddedRunAttemptParams { @@ -367,7 +400,7 @@ function createAppServerHarness( const waitForServerRequestHandler = async () => { await vi.waitFor(() => expect(handleServerRequest).toBeTypeOf("function"), { interval: 1, - timeout: 30_000, + timeout: appServerHarnessWait.timeout, }); return handleServerRequest!; }; @@ -375,7 +408,7 @@ function createAppServerHarness( return { request, requests, - async waitForMethod(method: string, timeoutMs = 30_000) { + async waitForMethod(method: string, timeoutMs = appServerHarnessWait.timeout) { await vi.waitFor( () => { if (!requests.some((entry) => entry.method === method)) { @@ -837,6 +870,8 @@ describe("runCodexAppServerAttempt", () => { }); afterEach(async () => { + await drainActiveAppServerAttemptsForTest(); + await closeCodexSandboxExecServersForTests(); resetCodexAppServerClientFactoryForTest(); testing.resetOpenClawCodingToolsFactoryForTests(); resetCodexRateLimitCacheForTests();