From 51c5605820cafe80f9bb3e125b9443ad882b297b Mon Sep 17 00:00:00 2001 From: naiba Date: Mon, 12 Oct 2026 02:39:22 +0000 Subject: [PATCH] test: close real app-server fixtures after each test Co-authored-by: naiba/CloudCode --- src/__tests__/acp-test-utils.test.ts | 15 ++++++++++++++- src/__tests__/acp-test-utils.ts | 23 ++++++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/__tests__/acp-test-utils.test.ts b/src/__tests__/acp-test-utils.test.ts index 7ee8e2f1f..afadf6c59 100644 --- a/src/__tests__/acp-test-utils.test.ts +++ b/src/__tests__/acp-test-utils.test.ts @@ -1,5 +1,5 @@ import {describe, expect, it} from "vitest"; -import {createObjectDump} from "./acp-test-utils"; +import {createTestFixture, createObjectDump} from "./acp-test-utils"; describe("createObjectDump anonymizer", () => { it("anonymizes both direct keys and dotted paths", () => { @@ -33,3 +33,16 @@ describe("createObjectDump anonymizer", () => { expect(parsed.items[1].secret).toBe("second-item"); }); }); + + +describe("native fixture cleanup", () => { + let previous: ReturnType; + it("owns a running app-server for the current test", async () => { + previous = createTestFixture(); + await previous.getCodexAcpAgent().initialize({protocolVersion: 1}); + expect(previous.getCodexAcpAgent()["getExitCode"]()).toBeNull(); + }); + it("does not leave the preceding test app-server alive", () => { + expect(previous.getCodexAcpAgent()["getExitCode"]()).not.toBeNull(); + }); +}); diff --git a/src/__tests__/acp-test-utils.ts b/src/__tests__/acp-test-utils.ts index df6bcb74d..29f753515 100644 --- a/src/__tests__/acp-test-utils.ts +++ b/src/__tests__/acp-test-utils.ts @@ -13,7 +13,7 @@ import fs from "node:fs"; import os from "node:os"; import {AgentMode} from "../AgentMode"; import {DEFAULT_COLLABORATION_MODE} from "../CollaborationModeConfig"; -import {expect, vi} from "vitest"; +import {afterEach, expect, vi} from "vitest"; import type {Model, ReasoningEffortOption} from "../app-server/v2"; import {CodexSubagentEventRouter} from "../subagents/CodexSubagentEventRouter"; import {CodexEventHandler} from "../CodexEventHandler"; @@ -189,6 +189,13 @@ export function createBaseTestFixture(config: ConnectionConfig): TestFixture { * Creates a test fixture with a real Codex connection. * Use for integration tests that need to interact with the actual Codex binary. */ +const nativeFixtureCleanup = new Set<() => Promise>(); +afterEach(async () => { + const pending = [...nativeFixtureCleanup]; + nativeFixtureCleanup.clear(); + await Promise.all(pending.map(cleanup => cleanup())); +}); + export function createTestFixture(): TestFixture { const pathToCodex = path.resolve(process.cwd(), "node_modules", ".bin", process.platform === 'win32' ? "codex.cmd" : "codex"); if (!fs.existsSync(pathToCodex)) { @@ -200,6 +207,20 @@ export function createTestFixture(): TestFixture { ...process.env, CODEX_HOME: codexHome, }); + // Real fixtures previously accumulated app-servers until the worker exited, + // starving later initialize requests and making unrelated commands time out. + nativeFixtureCleanup.add(async () => { + if (codexConnection.process.exitCode !== null || codexConnection.process.signalCode !== null) return; + const exited = new Promise(resolve => codexConnection.process.once("exit", () => resolve())); + const deadline = setTimeout(() => codexConnection.process.kill("SIGKILL"), 2000); + try { + codexConnection.process.stdin.end(); + await exited; + } finally { + clearTimeout(deadline); + codexConnection.connection.dispose(); + } + }); codexConnection.process.on("exit", () => { removeDirectoryWithRetry(codexHome); });