mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-15 07:04:01 -06:00
998f0ed27d
* fix(e2e): keep phase log tails UTF-8 safe The phase runner keeps a bounded in-memory log tail per phase and writes it to stderr when a phase fails. The byte-based cut can land inside a multi-byte UTF-8 character, and decoding from that offset emits U+FFFD replacement characters at the start of the diagnostics tail. Reuse the decodeUtf8Tail helper from scripts/e2e/lib/text-file-utils.mjs (#109669) so the retained window starts on a character boundary. Co-Authored-By: Claude <noreply@anthropic.com> * fix(e2e): declare decodeUtf8Tail in text-file-utils types phase-runner.ts imports decodeUtf8Tail from text-file-utils.mjs; the adjacent declaration file must export it or check-test-types fails with TS2305. Co-Authored-By: Claude <noreply@anthropic.com> * refactor(e2e): reuse tailText wrapper for phase log tails Co-Authored-By: Claude <noreply@anthropic.com> * test(e2e): focus phase tail regression Co-authored-by: wangmiao0668000666 <wang.miao86@xydigit.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
44 lines
1.4 KiB
TypeScript
44 lines
1.4 KiB
TypeScript
// Parallels Phase Runner tests cover bounded in-memory phase log tails.
|
|
import { afterEach, 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("");
|
|
}
|
|
|
|
it("keeps the truncated phase 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));
|
|
});
|