

















@@ -0,0 +1,127 @@
1+import { EventEmitter } from "node:events";
2+import { appendFileSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3+import { tmpdir } from "node:os";
4+import path from "node:path";
5+import { afterEach, describe, expect, it } from "vitest";
6+import { createJsonlRequestTailer } from "../../scripts/e2e/lib/codex-media-path/jsonl-request-tail.mjs";
7+import { waitForWebSocketOpen } from "../../scripts/e2e/lib/codex-media-path/open-websocket.mjs";
8+9+const tempRoots: string[] = [];
10+11+function makeTempRoot(): string {
12+const root = mkdtempSync(path.join(tmpdir(), "openclaw-codex-media-path-"));
13+tempRoots.push(root);
14+return root;
15+}
16+17+function jsonl(value: unknown): string {
18+return `${JSON.stringify(value)}\n`;
19+}
20+21+class FakeWebSocket extends EventEmitter {
22+terminated = false;
23+closed = false;
24+25+terminate(): void {
26+this.terminated = true;
27+queueMicrotask(() => {
28+this.emit("error", new Error("socket abort after terminate"));
29+this.emit("close");
30+});
31+}
32+33+close(): void {
34+this.closed = true;
35+}
36+}
37+38+afterEach(() => {
39+for (const root of tempRoots.splice(0)) {
40+rmSync(root, { recursive: true, force: true });
41+}
42+});
43+44+describe("codex media path JSONL tailer", () => {
45+it("keeps parsed app-server requests and reads only appended lines", () => {
46+const logPath = path.join(makeTempRoot(), "app-server.jsonl");
47+const tailer = createJsonlRequestTailer(logPath, { maxReadBytes: 1024, historyLimit: 10 });
48+49+expect(tailer.read()).toEqual([]);
50+51+writeFileSync(logPath, jsonl({ method: "initialize" }));
52+expect(tailer.read()).toEqual([{ method: "initialize" }]);
53+54+appendFileSync(logPath, JSON.stringify({ method: "turn/start" }));
55+expect(tailer.read()).toEqual([{ method: "initialize" }]);
56+57+appendFileSync(logPath, "\n");
58+expect(tailer.read()).toEqual([{ method: "initialize" }, { method: "turn/start" }]);
59+});
60+61+it("starts from a bounded tail of oversized logs", () => {
62+const logPath = path.join(makeTempRoot(), "app-server.jsonl");
63+const lastLine = jsonl({ method: "turn/start" });
64+writeFileSync(logPath, `${"x".repeat(256)}\n${jsonl({ method: "old" })}${lastLine}`);
65+66+const tailer = createJsonlRequestTailer(logPath, {
67+maxReadBytes: lastLine.length + 2,
68+historyLimit: 10,
69+});
70+71+expect(tailer.read()).toEqual([{ method: "turn/start" }]);
72+});
73+74+it("keeps a complete line when the bounded tail starts on its boundary", () => {
75+const logPath = path.join(makeTempRoot(), "app-server.jsonl");
76+const lastLine = jsonl({ method: "turn/start" });
77+writeFileSync(logPath, `${"x".repeat(256)}\n${lastLine}`);
78+79+const tailer = createJsonlRequestTailer(logPath, {
80+maxReadBytes: lastLine.length,
81+historyLimit: 10,
82+});
83+84+expect(tailer.read()).toEqual([{ method: "turn/start" }]);
85+});
86+87+it("resets request history when the app-server log is truncated", () => {
88+const logPath = path.join(makeTempRoot(), "app-server.jsonl");
89+const tailer = createJsonlRequestTailer(logPath, { maxReadBytes: 1024, historyLimit: 10 });
90+91+writeFileSync(logPath, jsonl({ method: "initialize", payload: "long enough to rotate" }));
92+expect(tailer.read()).toEqual([{ method: "initialize", payload: "long enough to rotate" }]);
93+94+writeFileSync(logPath, jsonl({ method: "turn/start" }));
95+expect(tailer.read()).toEqual([{ method: "turn/start" }]);
96+});
97+});
98+99+describe("codex media path WebSocket open guard", () => {
100+it("terminates sockets that never open", async () => {
101+const ws = new FakeWebSocket();
102+const keepAlive = setTimeout(() => {}, 100);
103+104+try {
105+await expect(waitForWebSocketOpen(ws, 1)).rejects.toThrow("gateway ws open timeout");
106+} finally {
107+clearTimeout(keepAlive);
108+}
109+110+expect(ws.terminated).toBe(true);
111+await new Promise((resolve) => setImmediate(resolve));
112+expect(ws.listenerCount("open")).toBe(0);
113+expect(ws.listenerCount("error")).toBe(0);
114+});
115+116+it("cleans listeners after successful opens", async () => {
117+const ws = new FakeWebSocket();
118+const opened = waitForWebSocketOpen(ws, 100);
119+120+ws.emit("open");
121+122+await expect(opened).resolves.toBeUndefined();
123+expect(ws.terminated).toBe(false);
124+expect(ws.listenerCount("open")).toBe(0);
125+expect(ws.listenerCount("error")).toBe(0);
126+});
127+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。