mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:40:44 +00:00
test: share codex app-server client fixtures
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user