fix(test): hard kill boundary prep timeouts

This commit is contained in:
Vincent Koc
2026-05-28 14:40:52 +02:00
parent 3fb67467fa
commit a854331c4c
2 changed files with 41 additions and 3 deletions

View File

@@ -195,10 +195,11 @@ function abortSiblingSteps(abortController) {
}
}
function runNodeStep(label, args, timeoutMs, params = {}) {
export function runNodeStep(label, args, timeoutMs, params = {}) {
const abortController = params.abortController;
const spawnImpl = params.spawnImpl ?? spawn;
return new Promise((resolvePromise, rejectPromise) => {
const child = spawn(process.execPath, args, {
const child = spawnImpl(process.execPath, args, {
cwd: repoRoot,
env: params.env ? { ...process.env, ...params.env } : process.env,
signal: abortController?.signal,
@@ -212,8 +213,8 @@ function runNodeStep(label, args, timeoutMs, params = {}) {
if (settled) {
return;
}
child.kill("SIGTERM");
settled = true;
child.kill("SIGKILL");
stdoutWriter.flush();
stderrWriter.flush();
abortSiblingSteps(abortController);

View File

@@ -1,3 +1,4 @@
import { EventEmitter } from "node:events";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
@@ -6,12 +7,21 @@ import {
createPrefixedOutputWriter,
isArtifactSetFresh,
parseMode,
runNodeStep,
runNodeSteps,
runNodeStepsInParallel,
} from "../../scripts/prepare-extension-package-boundary-artifacts.mjs";
const tempRoots = new Set<string>();
function createMockPipe() {
const pipe = new EventEmitter() as EventEmitter & {
setEncoding: (encoding: string) => void;
};
pipe.setEncoding = () => {};
return pipe;
}
afterEach(() => {
for (const rootDir of tempRoots) {
fs.rmSync(rootDir, { force: true, recursive: true });
@@ -58,6 +68,33 @@ describe("prepare-extension-package-boundary-artifacts", () => {
expect(Date.now() - startedAt).toBeLessThan(abortBudgetMs);
}, 45_000);
it("hard-kills timed out prep steps", async () => {
const signals: Array<NodeJS.Signals | number | undefined> = [];
const child = new EventEmitter() as EventEmitter & {
kill: (signal?: NodeJS.Signals | number) => boolean;
stderr: ReturnType<typeof createMockPipe>;
stdout: ReturnType<typeof createMockPipe>;
};
child.stdout = createMockPipe();
child.stderr = createMockPipe();
child.kill = (signal) => {
signals.push(signal);
return true;
};
await expect(
runNodeStep("hung-prep", ["--eval", "setTimeout(() => {}, 60_000)"], 5, {
spawnImpl(command: string, args: string[]) {
expect(command).toBe(process.execPath);
expect(args).toEqual(["--eval", "setTimeout(() => {}, 60_000)"]);
return child;
},
}),
).rejects.toThrow("hung-prep timed out after 5ms");
expect(signals).toEqual(["SIGKILL"]);
});
it("runs boundary prep steps serially for local checks", async () => {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-boundary-serial-"));
tempRoots.add(rootDir);