






















@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
44import { delimiter, join, win32 } from "node:path";
55import { setTimeout as delay } from "node:timers/promises";
66import { pathToFileURL } from "node:url";
7-import { beforeAll, describe, expect, it } from "vitest";
7+import { beforeAll, describe, expect, it, vi } from "vitest";
88import {
99modelProviderConfigBatchJson,
1010readPositiveIntEnv,
@@ -21,6 +21,7 @@ import { testing as hostServerTesting } from "../../scripts/e2e/parallels/host-s
2121import { parseArgs as parseLinuxSmokeArgs } from "../../scripts/e2e/parallels/linux-smoke.ts";
2222import { parseArgs as parseMacosSmokeArgs } from "../../scripts/e2e/parallels/macos-smoke.ts";
2323import { parseArgs as parseNpmUpdateSmokeArgs } from "../../scripts/e2e/parallels/npm-update-smoke.ts";
24+import { PhaseRunner } from "../../scripts/e2e/parallels/phase-runner.ts";
2425import { parseArgs as parseWindowsSmokeArgs } from "../../scripts/e2e/parallels/windows-smoke.ts";
2526import { spawnNodeEvalSync } from "../../src/test-utils/node-process.js";
2627@@ -615,6 +616,37 @@ if (isPrlctl) {
615616}
616617});
617618619+it("streams full phase logs to disk while bounding the failure tail", async () => {
620+const runDir = mkdtempSync(join(tmpdir(), "openclaw-parallels-phase-"));
621+const phaseRunner = new PhaseRunner(runDir, 128);
622+const writes: string[] = [];
623+const stderrWrite = vi.spyOn(process.stderr, "write").mockImplementation((chunk) => {
624+writes.push(String(chunk));
625+return true;
626+});
627+628+try {
629+await expect(
630+phaseRunner.phase("noisy", 30, () => {
631+phaseRunner.append(`old-${"x".repeat(256)}`);
632+phaseRunner.append("recent failure");
633+throw new Error("phase failed");
634+}),
635+).rejects.toThrow("phase failed");
636+637+const logText = readFileSync(join(runDir, "noisy.log"), "utf8");
638+expect(logText).toContain("old-");
639+expect(logText).toContain("recent failure");
640+const stderr = writes.join("");
641+expect(stderr).toContain("phase log tail truncated");
642+expect(stderr).toContain("recent failure");
643+expect(stderr).not.toContain(`old-${"x".repeat(200)}`);
644+} finally {
645+stderrWrite.mockRestore();
646+rmSync(runDir, { force: true, recursive: true });
647+}
648+});
649+618650it("runs POSIX guest shell scripts with a normal install umask", () => {
619651const guestTransports = readFileSync(TS_PATHS.guestTransports, "utf8");
620652@@ -775,30 +807,24 @@ if (isPrlctl) {
775807expect(Date.now() - startedAt).toBeLessThan(500);
776808});
777809778-it.runIf(process.platform !== "win32")(
779-"throws checked timed host command timeouts",
780-() => {
781-expect(() =>
782-run(process.execPath, ["-e", "setInterval(() => {}, 1000);"], {
783-quiet: true,
784-timeoutMs: 50,
785-}),
786-).toThrow(/timed out after 50ms/u);
787-},
788-);
789-790-it.runIf(process.platform !== "win32")(
791-"preserves child exit 124 in timed host commands",
792-() => {
793-const result = run(process.execPath, ["-e", "process.exit(124)"], {
794-check: false,
810+it.runIf(process.platform !== "win32")("throws checked timed host command timeouts", () => {
811+expect(() =>
812+run(process.execPath, ["-e", "setInterval(() => {}, 1000);"], {
795813quiet: true,
796-timeoutMs: 1_000,
797-});
814+timeoutMs: 50,
815+}),
816+).toThrow(/timed out after 50ms/u);
817+});
798818799-expect(result.status).toBe(124);
800-},
801-);
819+it.runIf(process.platform !== "win32")("preserves child exit 124 in timed host commands", () => {
820+const result = run(process.execPath, ["-e", "process.exit(124)"], {
821+check: false,
822+quiet: true,
823+timeoutMs: 1_000,
824+});
825+826+expect(result.status).toBe(124);
827+});
802828803829it.runIf(process.platform !== "win32")(
804830"kills timed-out host command process groups",
@@ -843,18 +869,15 @@ setInterval(() => {}, 1000);
843869},
844870);
845871846-it.runIf(process.platform !== "win32")(
847-"preserves timed host command spawn errors",
848-() => {
849-expect(() =>
850-run("openclaw-definitely-missing-host-command", [], {
851-check: false,
852-quiet: true,
853-timeoutMs: 50,
854-}),
855-).toThrow(/ENOENT/u);
856-},
857-);
872+it.runIf(process.platform !== "win32")("preserves timed host command spawn errors", () => {
873+expect(() =>
874+run("openclaw-definitely-missing-host-command", [], {
875+check: false,
876+quiet: true,
877+timeoutMs: 50,
878+}),
879+).toThrow(/ENOENT/u);
880+});
858881859882it.runIf(process.platform !== "win32")(
860883"does not treat timed command stderr as wrapper control data",
@@ -873,20 +896,17 @@ setInterval(() => {}, 1000);
873896},
874897);
875898876-it.runIf(process.platform !== "win32")(
877-"preserves timed host command output capture",
878-() => {
879-const expected = "x".repeat(256 * 1024);
880-const result = run(process.execPath, ["-e", "process.stdout.write('x'.repeat(256 * 1024))"], {
881-check: false,
882-quiet: true,
883-timeoutMs: 1_000,
884-});
899+it.runIf(process.platform !== "win32")("preserves timed host command output capture", () => {
900+const expected = "x".repeat(256 * 1024);
901+const result = run(process.execPath, ["-e", "process.stdout.write('x'.repeat(256 * 1024))"], {
902+check: false,
903+quiet: true,
904+timeoutMs: 1_000,
905+});
885906886-expect(result.status).toBe(0);
887-expect(result.stdout).toBe(expected);
888-},
889-);
907+expect(result.status).toBe(0);
908+expect(result.stdout).toBe(expected);
909+});
890910891911it.runIf(process.platform !== "win32")(
892912"ignores broken stdin pipes from timed host commands that exit early",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。