fix(test): hard kill boundary step timeouts

This commit is contained in:
Vincent Koc
2026-05-28 14:34:06 +02:00
parent 51e57d46cf
commit 3fb67467fa
2 changed files with 52 additions and 2 deletions

View File

@@ -337,9 +337,10 @@ function abortSiblingSteps(abortController) {
export function runNodeStepAsync(label, args, timeoutMs, params = {}) {
const abortController = params.abortController;
const onFailure = params.onFailure;
const spawnImpl = params.spawnImpl ?? spawn;
const startedAt = Date.now();
return new Promise((resolvePromise, rejectPromise) => {
const child = spawn(process.execPath, args, {
const child = spawnImpl(process.execPath, args, {
cwd: repoRoot,
env: process.env,
signal: abortController?.signal,
@@ -353,8 +354,8 @@ export function runNodeStepAsync(label, args, timeoutMs, params = {}) {
if (settled) {
return;
}
child.kill("SIGTERM");
settled = true;
child.kill("SIGKILL");
const error = attachStepFailureMetadata(
new Error(
formatStepFailure(label, {

View File

@@ -35,6 +35,14 @@ function writeCanaryArtifacts(rootDir: string, extensionId = "demo") {
return { canaryPath, tsconfigPath };
}
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 });
@@ -348,6 +356,47 @@ describe("check-extension-package-tsc-boundary", () => {
expect(elapsedMs).toBeGreaterThanOrEqual(0);
}, 30_000);
it("hard-kills timed out async node 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;
};
const failure = await runNodeStepAsync(
"hung-plugin",
["--eval", "setTimeout(() => {}, 60_000)"],
5,
{
spawnImpl(command: string, args: string[]) {
expect(command).toBe(process.execPath);
expect(args).toEqual(["--eval", "setTimeout(() => {}, 60_000)"]);
return child;
},
},
).then(
() => {
throw new Error("expected hung-plugin step to time out");
},
(error: unknown) => error,
);
expect(signals).toEqual(["SIGKILL"]);
expect(failure).toBeInstanceOf(Error);
if (!(failure instanceof Error)) {
throw new Error("expected timeout failure to reject with an Error");
}
expect(failure.message).toContain("hung-plugin timed out after 5ms");
expect((failure as { kind?: unknown }).kind).toBe("timeout");
});
it("aborts concurrent sibling steps after the first failure", async () => {
const startedAt = Date.now();
const slowStepTimeoutMs = 60_000;