mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 20:32:20 +03:00
104 lines
3.5 KiB
TypeScript
104 lines
3.5 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
// #8091: when the child process itself fails to spawn (ENOENT/EACCES/etc.), the
|
|
// 'error' listener passes `err` into handleExit(-1, err) but handleExit only ever
|
|
// declared a single `code` parameter — the real reason (err.code/err.path/err.message)
|
|
// was silently dropped. The user only ever saw "Server exited (code=-1)" plus a
|
|
// permanently empty crash log, with no way to diagnose the underlying spawn failure.
|
|
//
|
|
// This test spawns a server that does not exist on disk, which forces a deterministic,
|
|
// cross-platform ENOENT 'error' event (no Windows/Bun needed to reproduce the bug class),
|
|
// and asserts the real error surfaces both via console.error and via crashLog/dumpCrashLog().
|
|
|
|
process.env.PORT = "0";
|
|
|
|
test("ServerSupervisor surfaces the real spawn error (ENOENT) instead of swallowing it (#8091)", async () => {
|
|
const { ServerSupervisor } = await import("../../bin/cli/runtime/processSupervisor.mjs");
|
|
|
|
const logs: string[] = [];
|
|
const origErr = console.error.bind(console);
|
|
console.error = (...args: unknown[]) => {
|
|
logs.push(args.map((a) => String(a)).join(" "));
|
|
};
|
|
|
|
const origExit = process.exit.bind(process);
|
|
// @ts-ignore
|
|
process.exit = () => {};
|
|
|
|
const supervisor = new ServerSupervisor({
|
|
serverPath: "/definitely/does/not/exist/server.js",
|
|
env: {},
|
|
maxRestarts: 0,
|
|
});
|
|
|
|
// Real spawn+'error' path the supervisor uses in production; ENOENT fires async.
|
|
supervisor.start();
|
|
|
|
await new Promise<void>((resolve) => {
|
|
const check = setInterval(() => {
|
|
if (logs.length || supervisor.crashLog.length) {
|
|
clearInterval(check);
|
|
resolve();
|
|
}
|
|
}, 10);
|
|
setTimeout(() => {
|
|
clearInterval(check);
|
|
resolve();
|
|
}, 2000);
|
|
});
|
|
|
|
// @ts-ignore
|
|
process.exit = origExit;
|
|
console.error = origErr;
|
|
|
|
const printed = logs.join("\n");
|
|
const inCrashLog = supervisor.crashLog.join("\n");
|
|
|
|
assert.ok(
|
|
printed.includes("ENOENT") || inCrashLog.includes("ENOENT"),
|
|
`expected the real spawn error (ENOENT) to be surfaced via console.error or crashLog, got:\nconsole.error: ${printed}\ncrashLog: ${inCrashLog}`
|
|
);
|
|
});
|
|
|
|
test("ServerSupervisor.handleExit(code, err) logs err.code/err.path/err.message and pushes into crashLog", async () => {
|
|
const { ServerSupervisor } = await import("../../bin/cli/runtime/processSupervisor.mjs");
|
|
|
|
const logs: string[] = [];
|
|
const origErr = console.error.bind(console);
|
|
console.error = (...args: unknown[]) => {
|
|
logs.push(args.map((a) => String(a)).join(" "));
|
|
};
|
|
|
|
const supervisor = new ServerSupervisor({
|
|
serverPath: "/fake/server.js",
|
|
env: {},
|
|
maxRestarts: 5,
|
|
});
|
|
// @ts-ignore — stub start() so the scheduled restart never spawns a real process
|
|
supervisor.start = () => null;
|
|
supervisor.startedAt = Date.now() - 100;
|
|
|
|
const fakeErr = Object.assign(new Error("spawn /fake/server.js ENOENT"), {
|
|
code: "ENOENT",
|
|
path: "/fake/server.js",
|
|
syscall: "spawn",
|
|
});
|
|
supervisor.handleExit(-1, fakeErr);
|
|
|
|
console.error = origErr;
|
|
|
|
const printed = logs.join("\n");
|
|
assert.ok(printed.includes("ENOENT"), `expected err.code (ENOENT) to be printed, got: ${printed}`);
|
|
assert.ok(
|
|
printed.includes("/fake/server.js"),
|
|
`expected err.path to be printed, got: ${printed}`
|
|
);
|
|
assert.ok(
|
|
supervisor.crashLog.some((l: string) => l.includes("ENOENT")),
|
|
`expected the error to be pushed into crashLog, got: ${JSON.stringify(supervisor.crashLog)}`
|
|
);
|
|
|
|
await new Promise((r) => setTimeout(r, 1100)); // drain the scheduled restart timer
|
|
});
|