From 38dc9d246b941aeadcbe55bbb13bcd945e47c05b Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Tue, 10 Nov 2026 16:15:13 -0500 Subject: [PATCH] test: own and bound Electron hook fixture processes --- .../browser/electronActiveHook.test.ts | 38 ++++++++----------- .../process/processFixtureLifecycle.test.ts | 28 ++++++++++++++ 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/tests/boundary/browser/electronActiveHook.test.ts b/tests/boundary/browser/electronActiveHook.test.ts index 3b6fb39728..222099cd0c 100644 --- a/tests/boundary/browser/electronActiveHook.test.ts +++ b/tests/boundary/browser/electronActiveHook.test.ts @@ -1,10 +1,11 @@ +import { withVerifierCleanup } from "../../../scripts/lib/verifier-cleanup.mjs"; +import { createTestProcessRegistry } from "../../support/process/processFixture.js"; import { copyFile, mkdir, readFile, rm, writeFile } from "node:fs/promises"; import { dirname, join } from "node:path"; -import { spawn } from "node:child_process"; import { createRequire } from "node:module"; import { fileURLToPath } from "node:url"; -import { afterEach, expect, it } from "vitest"; +import { afterEach, expect, it, onTestFinished } from "vitest"; import { electronActiveTimelineEventSchema } from "../../../src/domain/javascript/electronActiveObservation.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; @@ -3675,28 +3676,19 @@ const runNode = async ( readonly stdout: string; readonly stderr: string; }> => { - const hook = await stageHook(root, sourceHook); - return new Promise((resolve, reject) => { - const child = spawn( + const processes = createTestProcessRegistry(); + onTestFinished(() => processes.shutdown()); + return withVerifierCleanup(async () => { + const hook = await stageHook(root, sourceHook); + const result = await processes.run( process.execPath, [...nodeArguments, ...(preloadHook ? ["-r", hook] : []), script], - { - cwd: root, - env: process.env, - stdio: ["ignore", "pipe", "pipe"], - }, + { cwd: root, env: process.env, timeoutMs: 10_000 }, ); - const stdout: Buffer[] = []; - const stderr: Buffer[] = []; - child.stdout.on("data", (chunk: Buffer) => stdout.push(chunk)); - child.stderr.on("data", (chunk: Buffer) => stderr.push(chunk)); - child.once("error", reject); - child.once("close", (code) => - resolve({ - code, - stdout: Buffer.concat(stdout).toString("utf8"), - stderr: Buffer.concat(stderr).toString("utf8"), - }), - ); - }); + return { + code: result.exitCode, + stdout: result.stdout, + stderr: result.stderr, + }; + }, [() => processes.shutdown()]); }; diff --git a/tests/boundary/process/processFixtureLifecycle.test.ts b/tests/boundary/process/processFixtureLifecycle.test.ts index 6e07a4c0e4..3cc810a942 100644 --- a/tests/boundary/process/processFixtureLifecycle.test.ts +++ b/tests/boundary/process/processFixtureLifecycle.test.ts @@ -249,3 +249,31 @@ it("times out when a real descendant holds the exited leader's output open", asy } } }, 17_000); + +it("terminates a stalled real child at its deadline while retaining captured output", async () => { + const processes = createTestProcessRegistry(); + try { + const result = await processes + .run( + process.execPath, + [ + "-e", + 'process.stdout.write("ready\\n"); setInterval(() => {}, 1000);', + ], + { timeoutMs: 1000 }, + ) + .catch((cause: unknown) => cause); + expect(result).toMatchObject({ + message: "Test process exceeded its 1000ms deadline", + cause: { + exitCode: null, + signal: expect.stringMatching(/^SIG(?:TERM|KILL)$/u), + stdout: "ready\n", + stderr: "", + }, + }); + await expect(processes.shutdown()).resolves.toBeUndefined(); + } finally { + await processes.shutdown(); + } +});