test(electron): isolate active-hook fixture resolution (#3565)

Stage the real active hook and boundary helper next to a fixture-owned CommonJS Electron package so preload and exercise resolve the same module. Remove NODE_PATH reliance, retain capacity/preload/fatal-exit coverage, and prove isolation against a conflicting ancestor package.

Fixes #3473

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-10-15 00:44:48 +08:00
1 parent 569ef4159d
commit 1579b6865e
1 file changed
+86 -22
@@ -1,6 +1,8 @@
import { rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { copyFile, mkdir, 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";
@@ -131,11 +133,10 @@ afterEach(async () => {
it("retains active hook evidence within its event-retention budget", async () => {
const root = await createTestTempDirectory("rea-electron-hook-");
temporary.push(root);
await writeFile(join(root, "electron.js"), electronModuleSource);
await writeElectronFixture(root);
const script = join(root, "exercise.cjs");
await writeFile(script, activeCaptureSource);
const hook = join(process.cwd(), "scripts/electron-active-hook.cjs");
const execution = await runNode(hook, script, root, root);
const execution = await runNode(script, root);
expect(execution.code, execution.stderr).toBe(0);
const result = JSON.parse(
execution.stdout.trim().split("\n").at(-1) ?? "null",
@@ -223,7 +224,7 @@ it("classifies a dropped browser preload as preload configuration at capture", a
"rea-electron-hook-browser-preload-",
);
temporary.push(root);
await writeFile(join(root, "electron.js"), electronModuleSource);
await writeElectronFixture(root);
const script = join(root, "browser-preload.cjs");
await writeFile(
script,
@@ -234,8 +235,7 @@ const snapshot = globalThis.__reaElectronActiveSnapshot();
process.stdout.write(JSON.stringify(snapshot) + "\\n");
`,
);
const hook = join(process.cwd(), "scripts/electron-active-hook.cjs");
const execution = await runNode(hook, script, root, root);
const execution = await runNode(script, root);
expect(execution.code, execution.stderr).toBe(0);
const snapshot = JSON.parse(
execution.stdout.trim().split("\n").at(-1) ?? "null",
@@ -259,35 +259,98 @@ process.stdout.write(JSON.stringify(snapshot) + "\\n");
});
});
it("isolates hook and exercise from ancestor Electron packages", async () => {
const root = await createTestTempDirectory("rea-electron-hook-ancestor-");
temporary.push(root);
await writeElectronFixture(root, 'module.exports = "/ambient/Electron";');
const ambientHook = await stageHook(join(root, "producer", "scripts"));
expect(createRequire(ambientHook)("electron")).toBe("/ambient/Electron");
const fixture = join(root, "fixture");
await writeElectronFixture(fixture);
const script = join(fixture, "exercise.cjs");
await writeFile(
script,
`const { BrowserWindow } = require("electron");
const window = new BrowserWindow({ webPreferences: {} });
process.stdout.write(JSON.stringify({
hook_error: globalThis.__reaElectronActiveSnapshot().hook_error,
window_open_handler: typeof window.webContents.windowOpenHandler,
}) + "\\n");
`,
);
const result = await runNode(script, fixture, ambientHook);
expect(result.code, result.stderr).toBe(0);
expect(JSON.parse(result.stdout)).toEqual({
hook_error: false,
window_open_handler: "function",
});
});
it("preserves fatal process termination after recording uncaught exceptions", async () => {
const root = await createTestTempDirectory("rea-electron-hook-fatal-");
temporary.push(root);
await writeElectronFixture(
root,
'throw new Error("controlled Electron unavailable");',
);
const script = join(root, "fatal.cjs");
await writeFile(script, 'throw new Error("fatal-hook-secret");\n');
const hook = join(process.cwd(), "scripts/electron-active-hook.cjs");
const result = await runNode(hook, script, root, root);
await writeFile(
script,
'process.stdout.write(String(globalThis.__reaElectronActiveSnapshot().hook_error) + "\\n");\nthrow new Error("fatal-hook-secret");\n',
);
const result = await runNode(script, root);
expect(result.code).not.toBe(0);
expect(result.stdout.trim()).toBe("true");
expect(result.stderr).toContain("fatal-hook-secret");
});
const runNode = (
hook: string,
const writeElectronFixture = async (
root: string,
source = electronModuleSource,
) => {
const directory = join(root, "node_modules", "electron");
await mkdir(directory, { recursive: true });
await writeFile(
join(directory, "package.json"),
JSON.stringify({ name: "electron", main: "index.cjs" }),
);
await writeFile(join(directory, "index.cjs"), source);
};
const stageHook = async (
root: string,
source = fileURLToPath(
new URL("../../../scripts/electron-active-hook.cjs", import.meta.url),
),
) => {
await mkdir(root, { recursive: true });
const hook = join(root, "electron-active-hook.cjs");
await Promise.all([
copyFile(source, hook),
copyFile(
join(dirname(source), "electron-active-hook-boundaries.cjs"),
join(root, "electron-active-hook-boundaries.cjs"),
),
]);
return hook;
};
const runNode = async (
script: string,
moduleRoot: string,
cwd: string,
root: string,
sourceHook?: string,
): Promise<{
readonly code: number | null;
readonly stdout: string;
readonly stderr: string;
}> =>
new Promise((resolve, reject) => {
}> => {
const hook = await stageHook(root, sourceHook);
return new Promise((resolve, reject) => {
const child = spawn(process.execPath, ["-r", hook, script], {
cwd,
env: {
...process.env,
NODE_PATH: moduleRoot,
},
cwd: root,
env: process.env,
stdio: ["ignore", "pipe", "pipe"],
});
const stdout: Buffer[] = [];
@@ -303,3 +366,4 @@ const runNode = (
}),
);
});
};