



























11// Run Additional Boundary Checks tests cover run additional boundary checks script behavior.
2+import fs from "node:fs";
3+import os from "node:os";
4+import path from "node:path";
25import { describe, expect, it } from "vitest";
36import {
47BOUNDARY_CHECKS,
@@ -26,6 +29,43 @@ function createOutputBuffer() {
2629};
2730}
283132+function isProcessAlive(pid: number): boolean {
33+try {
34+process.kill(pid, 0);
35+return true;
36+} catch {
37+return false;
38+}
39+}
40+41+async function sleep(ms: number): Promise<void> {
42+await new Promise((resolve) => {
43+setTimeout(resolve, ms);
44+});
45+}
46+47+async function waitForFile(filePath: string, timeoutMs: number): Promise<void> {
48+const deadlineAt = Date.now() + timeoutMs;
49+while (Date.now() < deadlineAt) {
50+if (fs.existsSync(filePath)) {
51+return;
52+}
53+await sleep(25);
54+}
55+throw new Error(`timeout waiting for ${filePath}`);
56+}
57+58+async function waitForDead(pid: number, timeoutMs: number): Promise<void> {
59+const deadlineAt = Date.now() + timeoutMs;
60+while (Date.now() < deadlineAt) {
61+if (!isProcessAlive(pid)) {
62+return;
63+}
64+await sleep(25);
65+}
66+throw new Error(`process still alive: ${pid}`);
67+}
68+2969describe("run-additional-boundary-checks", () => {
3070it("runs prompt snapshot drift checks in CI", () => {
3171expect(BOUNDARY_CHECKS[0]).toEqual({
@@ -158,4 +198,53 @@ describe("run-additional-boundary-checks", () => {
158198expect(result.timedOut).toBe(true);
159199expect(result.output).toContain("timed out after 50ms");
160200});
201+202+it.skipIf(process.platform === "win32")(
203+"waits for timed-out process groups after the wrapper exits",
204+async () => {
205+const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-boundary-timeout-"));
206+const childPidPath = path.join(tempDir, "child.pid");
207+let childPid = 0;
208+try {
209+const childScript = [
210+"process.on('SIGTERM', () => {});",
211+"setInterval(() => {}, 1000);",
212+].join("");
213+const parentScript = [
214+"const { spawn } = require('node:child_process');",
215+"const fs = require('node:fs');",
216+`const child = spawn(process.execPath, ['-e', ${JSON.stringify(childScript)}], { stdio: 'ignore' });`,
217+"fs.writeFileSync(process.env.OPENCLAW_TEST_CHILD_PID, String(child.pid));",
218+"setInterval(() => {}, 1000);",
219+].join("");
220+221+const resultPromise = runSingleCheck(
222+{
223+label: "wrapper-exits",
224+command: process.execPath,
225+args: ["-e", parentScript],
226+},
227+{
228+checkTimeoutMs: 100,
229+cwd: process.cwd(),
230+env: { ...process.env, OPENCLAW_TEST_CHILD_PID: childPidPath },
231+outputMaxBytes: 4096,
232+},
233+);
234+235+await waitForFile(childPidPath, 2000);
236+childPid = Number(fs.readFileSync(childPidPath, "utf8"));
237+const result = await resultPromise;
238+239+expect(result.code).toBe(1);
240+expect(result.timedOut).toBe(true);
241+await waitForDead(childPid, 2000);
242+} finally {
243+if (childPid && isProcessAlive(childPid)) {
244+process.kill(childPid, "SIGKILL");
245+}
246+fs.rmSync(tempDir, { force: true, recursive: true });
247+}
248+},
249+);
161250});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。