mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-14 11:12:17 +03:00
111 lines
3.7 KiB
TypeScript
111 lines
3.7 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
// #9156: macOS launchd autostart fails because the supervisor spawns the child
|
|
// with bare "node", but launchd's PATH cannot resolve it. process.execPath is
|
|
// always the absolute path to the running Node.js binary and is always resolvable.
|
|
//
|
|
// We verify the fix via:
|
|
// 1. Static source analysis — the spawn() call must use process.execPath
|
|
// unconditionally (no fallback to bare "node"). This runs without any
|
|
// experimental flags so it serves as the permanent regression guard.
|
|
// 2. Runtime test via mock.module (requires --experimental-test-module-mocks)
|
|
// that captures the actual spawn arguments.
|
|
|
|
const __filename = new URL(import.meta.url).pathname;
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
const SUPERVISOR_PATH = path.resolve(
|
|
__dirname,
|
|
"../../bin/cli/runtime/processSupervisor.mjs"
|
|
);
|
|
const supervisorSrc = fs.readFileSync(SUPERVISOR_PATH, "utf8");
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 1. Source-level verification (no experimental flag required)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
test("spawn() uses process.execPath unconditionally, no bare 'node' fallback (#9156)", () => {
|
|
// Must NOT contain the old conditional that falls back to bare "node"
|
|
assert.ok(
|
|
!supervisorSrc.includes('process.versions.bun ? process.execPath : "node"'),
|
|
"must NOT have a conditional fallback to bare 'node'"
|
|
);
|
|
|
|
// Must use process.execPath as the first argument to spawn()
|
|
const execPathPattern = /spawn\(\s*process\.execPath\s*,/;
|
|
assert.ok(
|
|
execPathPattern.test(supervisorSrc),
|
|
"spawn() must receive process.execPath as first argument"
|
|
);
|
|
});
|
|
|
|
test("process.execPath is an absolute path to the running Node.js binary", () => {
|
|
assert.ok(
|
|
path.isAbsolute(process.execPath),
|
|
`process.execPath must be absolute, got: ${process.execPath}`
|
|
);
|
|
assert.ok(
|
|
fs.existsSync(process.execPath),
|
|
`process.execPath must exist: ${process.execPath}`
|
|
);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 2. Runtime test via mock.module (requires --experimental-test-module-mocks)
|
|
// ---------------------------------------------------------------------------
|
|
//
|
|
// Run manually: node --experimental-test-module-mocks --import tsx/esm --test tests/unit/repro-9156.test.ts
|
|
|
|
import { mock } from "node:test";
|
|
|
|
if (typeof mock.module === "function") {
|
|
test("(runtime) ServerSupervisor.start() spawns with process.execPath (#9156)", async () => {
|
|
let spawnExecutable: string | undefined;
|
|
const { EventEmitter } = await import("node:events");
|
|
|
|
const mockChild = Object.assign(new EventEmitter(), {
|
|
pid: 12345,
|
|
stdout: null,
|
|
stderr: null,
|
|
kill: () => {},
|
|
});
|
|
|
|
mock.module("node:child_process", {
|
|
exports: {
|
|
spawn: (...args: unknown[]) => {
|
|
spawnExecutable = args[0] as string;
|
|
return mockChild;
|
|
},
|
|
},
|
|
});
|
|
|
|
process.env.PORT = "0";
|
|
|
|
const { ServerSupervisor } = await import(
|
|
"../../bin/cli/runtime/processSupervisor.mjs"
|
|
);
|
|
|
|
const supervisor = new ServerSupervisor({
|
|
serverPath: "/fake/server.js",
|
|
env: {},
|
|
maxRestarts: 0,
|
|
});
|
|
|
|
spawnExecutable = undefined;
|
|
supervisor.start();
|
|
|
|
assert.ok(spawnExecutable, "spawn() must have been called");
|
|
assert.equal(
|
|
spawnExecutable,
|
|
process.execPath,
|
|
`expected process.execPath, got: ${spawnExecutable}`
|
|
);
|
|
assert.notEqual(spawnExecutable, "node", "must not be bare 'node'");
|
|
|
|
mockChild.removeAllListeners();
|
|
delete process.env.PORT;
|
|
});
|
|
} |