-
-
Notifications
You must be signed in to change notification settings - Fork 80.8k
Expand file tree
/
Copy pathparallels-phase-runner.test.ts
More file actions
63 lines (53 loc) · 2.12 KB
/
Copy pathparallels-phase-runner.test.ts
File metadata and controls
63 lines (53 loc) · 2.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// Parallels Phase Runner tests cover bounded in-memory phase log tails.
import { afterEach, describe, expect, it, vi } from "vitest";
import { PhaseRunner } from "../../scripts/e2e/parallels/phase-runner.ts";
import { cleanupTempDirs, makeTempDir } from "../helpers/temp-dir.js";
const tempRoots: string[] = [];
function makeTempRoot() {
return makeTempDir(tempRoots, "openclaw-parallels-phase-runner-");
}
afterEach(() => {
cleanupTempDirs(tempRoots);
vi.restoreAllMocks();
});
async function captureFailedPhaseTail(runner: PhaseRunner, text: string): Promise<string> {
const written: string[] = [];
vi.spyOn(process.stderr, "write").mockImplementation((chunk) => {
written.push(String(chunk));
return true;
});
await expect(
runner.phase("utf8-tail", 60, () => {
runner.append(text);
throw new Error("boom");
}),
).rejects.toThrow("boom");
return written.join("");
}
describe("PhaseRunner log tail", () => {
it("keeps the truncated tail UTF-8 safe when the byte cut splits a character", async () => {
// 134 bytes total; the retained window starts one byte into the 4-byte
// emoji, so a byte-naive decode would emit replacement characters.
const tail = await captureFailedPhaseTail(
new PhaseRunner(makeTempRoot(), 128),
`${"x".repeat(50)}😀${"y".repeat(79)}`,
);
expect(tail).toContain("[phase log tail truncated to last 128 bytes]");
expect(tail).not.toContain("�");
expect(tail).toContain("y".repeat(79));
});
it("keeps the full tail when the byte cut lands on a character boundary", async () => {
// 129 bytes total; the retained window starts exactly on the emoji.
const tail = await captureFailedPhaseTail(
new PhaseRunner(makeTempRoot(), 128),
`${"x".repeat(46)}😀${"y".repeat(78)}`,
);
expect(tail).toContain(`😀${"y".repeat(78)}`);
expect(tail).not.toContain("�");
});
it("keeps short logs untruncated", async () => {
const tail = await captureFailedPhaseTail(new PhaseRunner(makeTempRoot(), 128), "short log");
expect(tail).toContain("short log");
expect(tail).not.toContain("truncated");
});
});