test(codex): clean goal fixtures after setup failures

This commit is contained in:
paseo-bot[bot]
2026-10-13 11:03:17 +02:00
parent da5e8fbce3
commit 060d521340
2 changed files with 90 additions and 69 deletions
@@ -5,7 +5,7 @@ import path from "node:path";
import { expect, test } from "vitest";
import { createTestLogger } from "../../../test-utils/test-logger.js";
import { CodexAppServerAgentClient } from "./codex-app-server-agent.js";
import type { AgentStreamEvent } from "../agent-sdk-types.js";
import type { AgentSession, AgentStreamEvent } from "../agent-sdk-types.js";
// Native Codex owns goals; the fixture supplies inference responses only.
test("records all four /goal commands as user edits in the next model request", async () => {
@@ -45,13 +45,15 @@ test("records all four /goal commands as user edits in the next model request",
});
}
const server = createServer(handleRequest);
server.listen(0, "127.0.0.1");
await once(server, "listening");
const address = server.address();
if (!address || typeof address === "string") throw new Error("missing fixture address");
writeFileSync(
path.join(root, "config.toml"),
`
let session: AgentSession | null = null;
try {
server.listen(0, "127.0.0.1");
await once(server, "listening");
const address = server.address();
if (!address || typeof address === "string") throw new Error("missing fixture address");
writeFileSync(
path.join(root, "config.toml"),
`
model = "gpt-6.1-sol"
model_reasoning_effort = "low"
model_provider = "fixture"
@@ -68,61 +70,75 @@ supports_websockets = false
request_max_retries = 0
stream_max_retries = 0
`,
);
const client = new CodexAppServerAgentClient(createTestLogger(), { env: { CODEX_HOME: root } });
const session = await client.createSession({
provider: "codex",
cwd: root,
model: "gpt-6.1-sol",
thinkingOptionId: "low",
});
let active = false;
const unsubscribe = session.subscribe((event) => {
if (event.type === "turn_started") active = true;
if (["turn_completed", "turn_canceled", "turn_failed"].includes(event.type)) active = false;
});
try {
expect((await session.listCommands?.())?.some((command) => command.name === "goal")).toBe(true);
for (const command of [
"/goal AUDIT_GOAL: summarize local evidence",
"/goal pause",
"/goal resume",
"/goal clear",
]) {
const handler = session.tryHandleOutOfBand?.(command);
expect(handler).toBeTruthy();
const events: AgentStreamEvent[] = [];
await handler!.run({ emit: (event) => events.push(event) });
expect(events).not.toEqual(
expect.arrayContaining([
expect.objectContaining({
item: expect.objectContaining({
text: expect.stringContaining("Failed to update goal"),
}),
}),
]),
);
const client = new CodexAppServerAgentClient(createTestLogger(), { env: { CODEX_HOME: root } });
session = await client.createSession({
provider: "codex",
cwd: root,
model: "gpt-6.1-sol",
thinkingOptionId: "low",
});
let active = false;
const unsubscribe = session.subscribe((event) => {
if (event.type === "turn_started") active = true;
if (["turn_completed", "turn_canceled", "turn_failed"].includes(event.type)) active = false;
});
try {
expect((await session.listCommands?.())?.some((command) => command.name === "goal")).toBe(
true,
);
for (const command of [
"/goal AUDIT_GOAL: summarize local evidence",
"/goal pause",
"/goal resume",
"/goal clear",
]) {
const handler = session.tryHandleOutOfBand?.(command);
expect(handler).toBeTruthy();
const events: AgentStreamEvent[] = [];
await handler!.run({ emit: (event) => events.push(event) });
expect(events).not.toEqual(
expect.arrayContaining([
expect.objectContaining({
item: expect.objectContaining({
text: expect.stringContaining("Failed to update goal"),
}),
}),
]),
);
}
await expect.poll(() => active, { timeout: 30_000 }).toBe(false);
expect((await session.run("PROBE_AFTER_GOAL_CLEAR")).finalText).toBe(
"LOCAL_FIXTURE_RESPONSE",
);
const body = JSON.parse(requests.at(-1)!);
const goalContext = body.input
.filter((item: { role: string }) => item.role === "user")
.flatMap((item: { content: Array<{ text?: string }> }) =>
item.content.map((part) => part.text ?? ""),
)
.filter((text: string) => text.includes('<codex_internal_context source="user_goal">'))
.join("\n");
expect(goalContext).toContain('User set the goal: "AUDIT_GOAL: summarize local evidence"');
expect(goalContext).toContain('User set goal status: "paused".');
expect(goalContext).toContain('User set goal status: "active".');
expect(goalContext).toContain("User cleared the goal.");
} finally {
unsubscribe();
}
await expect.poll(() => active, { timeout: 30_000 }).toBe(false);
expect((await session.run("PROBE_AFTER_GOAL_CLEAR")).finalText).toBe("LOCAL_FIXTURE_RESPONSE");
const body = JSON.parse(requests.at(-1)!);
const goalContext = body.input
.filter((item: { role: string }) => item.role === "user")
.flatMap((item: { content: Array<{ text?: string }> }) =>
item.content.map((part) => part.text ?? ""),
)
.filter((text: string) => text.includes('<codex_internal_context source="user_goal">'))
.join("\n");
expect(goalContext).toContain('User set the goal: "AUDIT_GOAL: summarize local evidence"');
expect(goalContext).toContain('User set goal status: "paused".');
expect(goalContext).toContain('User set goal status: "active".');
expect(goalContext).toContain("User cleared the goal.");
} finally {
unsubscribe();
await session.close();
server.closeAllConnections();
server.close();
await once(server, "close");
rmSync(root, { recursive: true, force: true });
try {
await session?.close();
} finally {
try {
server.closeAllConnections();
if (server.listening) {
server.close();
await once(server, "close");
}
} finally {
rmSync(root, { recursive: true, force: true });
}
}
}
}, 60_000);
@@ -4,6 +4,7 @@ import { expect, test } from "vitest";
import { createTestLogger } from "../../../test-utils/test-logger.js";
import { CodexAppServerAgentClient } from "./codex-app-server-agent.js";
import type { AgentSession } from "../agent-sdk-types.js";
// A protocol peer exercises the public session interface without a Codex install.
test("marks set, pause, resume and clear as user goal mutations", async () => {
@@ -35,13 +36,14 @@ readline.createInterface({ input: process.stdin }).on("line", (line) => {
command: { mode: "replace", argv: [process.execPath, script] },
env: { CODEX_HOME: root, CAPTURE: capture },
});
const session = await client.createSession({
provider: "codex",
cwd: root,
model: "test-model",
thinkingOptionId: "low",
});
let session: AgentSession | null = null;
try {
session = await client.createSession({
provider: "codex",
cwd: root,
model: "test-model",
thinkingOptionId: "low",
});
for (const command of ["/goal ship feature", "/goal pause", "/goal resume", "/goal clear"]) {
const handler = session.tryHandleOutOfBand?.(command);
if (!handler) throw new Error(`Missing handler for ${command}`);
@@ -73,7 +75,10 @@ readline.createInterface({ input: process.stdin }).on("line", (line) => {
{ method: "thread/goal/clear", params: { threadId: "goal-thread", origin: "user" } },
]);
} finally {
await session.close();
rmSync(root, { recursive: true, force: true });
try {
await session?.close();
} finally {
rmSync(root, { recursive: true, force: true });
}
}
});