Test Codex MCP images through app-server boundary

This commit is contained in:
Mohamed Boudra
2026-07-03 01:16:57 +02:00
parent 4ca89f21f9
commit 6781521c8e
@@ -2359,12 +2359,41 @@ describe("Codex app-server provider", () => {
rmSync(source, { force: true }); rmSync(source, { force: true });
}); });
test("emits mcpToolCall image content as a completed tool call plus assistant markdown image", () => { test("mcpToolCall image content emits a completed tool call plus assistant markdown image", async () => {
const session = createSession(); const appServer = createFakeCodexAppServer();
const session = new CodexAppServerAgentSession(
createConfig({ cwd: "/workspace/project" }),
null,
createTestLogger(),
async () => appServer.child,
);
const events: AgentStreamEvent[] = []; const events: AgentStreamEvent[] = [];
session.subscribe((event) => events.push(event)); const timelineEvents: Array<Extract<AgentStreamEvent, { type: "timeline" }>> = [];
const timelineItemsReceived = new Promise<void>((resolve, reject) => {
const timeout = setTimeout(() => {
unsubscribe();
reject(new Error("Timed out waiting for MCP image timeline events"));
}, 1000);
const unsubscribe = session.subscribe((event) => {
events.push(event);
if (event.type !== "timeline") {
return;
}
timelineEvents.push(event);
if (timelineEvents.length === 2) {
clearTimeout(timeout);
unsubscribe();
resolve();
}
});
});
asInternals(session).handleNotification("item/completed", { try {
const { turnId } = await session.startTurn("capture a browser screenshot");
appServer.child.stdout.write(
`${JSON.stringify({
method: "item/completed",
params: {
item: { item: {
id: "mcp-browser-screenshot", id: "mcp-browser-screenshot",
type: "mcpToolCall", type: "mcpToolCall",
@@ -2389,13 +2418,17 @@ describe("Codex app-server provider", () => {
}, },
}, },
}, },
}); },
})}\n`,
);
expect(events.length).toBe(2); await timelineItemsReceived;
expect(events[0]).toEqual({
expect(timelineEvents).toEqual([
{
type: "timeline", type: "timeline",
provider: "codex", provider: "codex",
turnId: "test-turn", turnId,
item: { item: {
type: "tool_call", type: "tool_call",
callId: "mcp-browser-screenshot", callId: "mcp-browser-screenshot",
@@ -2423,15 +2456,16 @@ describe("Codex app-server provider", () => {
}, },
}, },
}, },
}); },
expect(events[1]).toMatchObject({ {
type: "timeline", type: "timeline",
provider: "codex", provider: "codex",
turnId: "test-turn", turnId,
item: { type: "assistant_message" }, item: expect.objectContaining({ type: "assistant_message" }),
}); },
const imageEvent = events[1]; ]);
if (imageEvent?.type !== "timeline" || imageEvent.item.type !== "assistant_message") { const imageEvent = timelineEvents[1];
if (imageEvent.item.type !== "assistant_message") {
throw new Error("Expected assistant image timeline event"); throw new Error("Expected assistant image timeline event");
} }
expect(JSON.stringify(events)).not.toContain(ONE_BY_ONE_PNG_BASE64); expect(JSON.stringify(events)).not.toContain(ONE_BY_ONE_PNG_BASE64);
@@ -2439,6 +2473,10 @@ describe("Codex app-server provider", () => {
expect(source).toMatch(/paseo-attachments[\\/].+\.png$/); expect(source).toMatch(/paseo-attachments[\\/].+\.png$/);
expect(existsSync(source)).toBe(true); expect(existsSync(source)).toBe(true);
rmSync(source, { force: true }); rmSync(source, { force: true });
appServer.assertNoErrors();
} finally {
await session.close();
}
}); });
test("ignores incomplete imageGeneration thread items without failing the turn", () => { test("ignores incomplete imageGeneration thread items without failing the turn", () => {