diff --git a/scripts/e2e/lib/parallels-package/log-progress-extract.mjs b/scripts/e2e/lib/parallels-package/log-progress-extract.mjs index c5ebff378bf8..6849ca78ecb2 100644 --- a/scripts/e2e/lib/parallels-package/log-progress-extract.mjs +++ b/scripts/e2e/lib/parallels-package/log-progress-extract.mjs @@ -1,4 +1,7 @@ import fs from "node:fs"; +import { readTextFileTail } from "../text-file-utils.mjs"; + +const LOG_PROGRESS_TAIL_BYTES = 256 * 1024; const [logPath] = process.argv.slice(2); if (!logPath || !fs.existsSync(logPath)) { @@ -6,7 +9,7 @@ if (!logPath || !fs.existsSync(logPath)) { process.exit(0); } -const text = fs.readFileSync(logPath, "utf8"); +const text = readTextFileTail(logPath, LOG_PROGRESS_TAIL_BYTES); const lines = text .split(/\r?\n/) .map((line) => line.trim()) diff --git a/test/scripts/parallels-package-log-progress-extract.test.ts b/test/scripts/parallels-package-log-progress-extract.test.ts new file mode 100644 index 000000000000..ea258f5ca70b --- /dev/null +++ b/test/scripts/parallels-package-log-progress-extract.test.ts @@ -0,0 +1,58 @@ +import { spawnSync } from "node:child_process"; +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { afterEach, describe, expect, it } from "vitest"; + +const SCRIPT_PATH = "scripts/e2e/lib/parallels-package/log-progress-extract.mjs"; +const tempRoots: string[] = []; + +function makeTempRoot(): string { + const root = mkdtempSync(path.join(tmpdir(), "openclaw-parallels-progress-")); + tempRoots.push(root); + return root; +} + +function runExtract(logPath?: string) { + return spawnSync(process.execPath, [SCRIPT_PATH, ...(logPath ? [logPath] : [])], { + encoding: "utf8", + }); +} + +afterEach(() => { + for (const root of tempRoots.splice(0)) { + rmSync(root, { force: true, recursive: true }); + } +}); + +describe("parallels package log progress extractor", () => { + it("prints a blank status when the log is absent", () => { + const result = runExtract(path.join(makeTempRoot(), "missing.log")); + + expect(result.status).toBe(0); + expect(result.stdout).toBe("\n"); + }); + + it("extracts the latest progress line from recent log output", () => { + const logPath = path.join(makeTempRoot(), "phase.log"); + writeFileSync(logPath, "==> Build package\nwarn: transient\n==> Copy artifact\n"); + + const result = runExtract(logPath); + + expect(result.status).toBe(0); + expect(result.stdout).toBe("Copy artifact\n"); + }); + + it("does not let stale progress hide recent warnings in long logs", () => { + const logPath = path.join(makeTempRoot(), "phase.log"); + writeFileSync( + logPath, + `==> Stale build step\n${"ordinary output\n".repeat(24 * 1024)}warn: recent package issue\n`, + ); + + const result = runExtract(logPath); + + expect(result.status).toBe(0); + expect(result.stdout).toBe("warn: recent package issue\n"); + }); +});