diff --git a/extensions/codex/src/app-server/client.test.ts b/extensions/codex/src/app-server/client.test.ts index 8f30b5da360..b2545f64eb1 100644 --- a/extensions/codex/src/app-server/client.test.ts +++ b/extensions/codex/src/app-server/client.test.ts @@ -1,5 +1,5 @@ import { EventEmitter } from "node:events"; -import { PassThrough, Writable } from "node:stream"; +import { PassThrough } from "node:stream"; import { afterEach, describe, expect, it, vi } from "vitest"; import { __testing, @@ -9,42 +9,23 @@ import { readCodexVersionFromUserAgent, } from "./client.js"; import { resetSharedCodexAppServerClientForTests } from "./shared-client.js"; - -function createClientHarness() { - const stdout = new PassThrough(); - const stderr = new PassThrough(); - const writes: string[] = []; - const stdin = new Writable({ - write(chunk, _encoding, callback) { - writes.push(chunk.toString()); - callback(); - }, - }); - const process = Object.assign(new EventEmitter(), { - stdin, - stdout, - stderr, - killed: false, - kill: vi.fn(() => { - process.killed = true; - }), - }); - // fromTransportForTests speaks the same newline-delimited JSON-RPC as the - // spawned app-server, but keeps the process lifecycle fully observable. - const client = CodexAppServerClient.fromTransportForTests(process); - return { - client, - process, - writes, - send(message: unknown) { - stdout.write(`${JSON.stringify(message)}\n`); - }, - }; -} +import { createClientHarness } from "./test-support.js"; describe("CodexAppServerClient", () => { const clients: CodexAppServerClient[] = []; + function startInitialize() { + const harness = createClientHarness(); + clients.push(harness.client); + const initializing = harness.client.initialize(); + const outbound = JSON.parse(harness.writes[0] ?? "{}") as { + id?: number; + method?: string; + params?: { clientInfo?: { name?: string; title?: string; version?: string } }; + }; + return { harness, initializing, outbound }; + } + afterEach(() => { resetSharedCodexAppServerClientForTests(); vi.useRealTimers(); @@ -115,15 +96,7 @@ describe("CodexAppServerClient", () => { }); it("initializes with the required client version", async () => { - const harness = createClientHarness(); - clients.push(harness.client); - - const initializing = harness.client.initialize(); - const outbound = JSON.parse(harness.writes[0] ?? "{}") as { - id?: number; - method?: string; - params?: { clientInfo?: { name?: string; title?: string; version?: string } }; - }; + const { harness, initializing, outbound } = startInitialize(); harness.send({ id: outbound.id, result: { userAgent: "openclaw/0.118.0 (macOS; test)" }, @@ -145,11 +118,7 @@ describe("CodexAppServerClient", () => { }); it("blocks unsupported app-server versions during initialize", async () => { - const harness = createClientHarness(); - clients.push(harness.client); - - const initializing = harness.client.initialize(); - const outbound = JSON.parse(harness.writes[0] ?? "{}") as { id?: number }; + const { harness, initializing, outbound } = startInitialize(); harness.send({ id: outbound.id, result: { userAgent: "openclaw/0.117.9 (macOS; test)" }, @@ -162,11 +131,7 @@ describe("CodexAppServerClient", () => { }); it("blocks app-server initialize responses without a version", async () => { - const harness = createClientHarness(); - clients.push(harness.client); - - const initializing = harness.client.initialize(); - const outbound = JSON.parse(harness.writes[0] ?? "{}") as { id?: number }; + const { harness, initializing, outbound } = startInitialize(); harness.send({ id: outbound.id, result: {} }); await expect(initializing).rejects.toThrow( diff --git a/extensions/codex/src/app-server/transcript-mirror.test.ts b/extensions/codex/src/app-server/transcript-mirror.test.ts index adaddf578d4..5574d1eca8c 100644 --- a/extensions/codex/src/app-server/transcript-mirror.test.ts +++ b/extensions/codex/src/app-server/transcript-mirror.test.ts @@ -1,11 +1,32 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import type { AgentMessage } from "@mariozechner/pi-agent-core"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { mirrorCodexAppServerTranscript } from "./transcript-mirror.js"; let tempDir: string; +function assistantMessage(text: string, timestamp: number): AgentMessage { + return { + role: "assistant", + content: [{ type: "text", text }], + api: "openai-codex-responses", + provider: "openai-codex", + model: "gpt-5.4-codex", + usage: { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + totalTokens: 0, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, + }, + stopReason: "stop", + timestamp, + }; +} + describe("mirrorCodexAppServerTranscript", () => { beforeEach(async () => { tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-transcript-")); @@ -23,40 +44,8 @@ describe("mirrorCodexAppServerTranscript", () => { sessionKey: "agent:main:session-1", messages: [ { role: "user", content: "hello", timestamp: 1 }, - { - role: "assistant", - content: [{ type: "text", text: "Codex plan:\ninspect" }], - api: "openai-codex-responses", - provider: "openai-codex", - model: "gpt-5.4-codex", - usage: { - input: 0, - output: 0, - cacheRead: 0, - cacheWrite: 0, - totalTokens: 0, - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, - }, - stopReason: "stop", - timestamp: 2, - }, - { - role: "assistant", - content: [{ type: "text", text: "hi" }], - api: "openai-codex-responses", - provider: "openai-codex", - model: "gpt-5.4-codex", - usage: { - input: 0, - output: 0, - cacheRead: 0, - cacheWrite: 0, - totalTokens: 0, - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, - }, - stopReason: "stop", - timestamp: 3, - }, + assistantMessage("Codex plan:\ninspect", 2), + assistantMessage("hi", 3), ], }); @@ -76,23 +65,7 @@ describe("mirrorCodexAppServerTranscript", () => { const sessionFile = path.join(tempDir, "session.jsonl"); const messages = [ { role: "user" as const, content: "hello", timestamp: 1 }, - { - role: "assistant" as const, - content: [{ type: "text" as const, text: "hi" }], - api: "openai-codex-responses", - provider: "openai-codex", - model: "gpt-5.4-codex", - usage: { - input: 0, - output: 0, - cacheRead: 0, - cacheWrite: 0, - totalTokens: 0, - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, - }, - stopReason: "stop" as const, - timestamp: 2, - }, + assistantMessage("hi", 2), ]; await mirrorCodexAppServerTranscript({