rea/tests/fixtures/processFinalization.mjs

58 lines
2.0 KiB
JavaScript

import { spawn } from "node:child_process";
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { fileURLToPath } from "node:url";
// Writes a periodic report, then behaves by mode:
// - "cooperative": stays alive; SIGTERM prints a line, writes a final report
// and exits cleanly.
// - "ignoring": stays alive and swallows SIGTERM and SIGINT, so only SIGKILL
// ends the process.
// - "delayed-ignoring": installs those handlers after a slow startup.
// - "exits": exits on its own at once.
// - "descendant": cooperates while leaving a SIGTERM-ignoring child for owned cleanup.
// An optional third argument is the exit code a cooperative finalizer uses.
const [mode, directory, finalExitCode = "0"] = process.argv.slice(2);
// Make the cancellation case exercise startup beyond its old 300 ms capture
// window deterministically, rather than relying on a busy CI runner.
if (mode === "delayed-ignoring") await delay(500);
writeFileSync(join(directory, "pid.txt"), String(process.pid));
writeFileSync(
join(directory, "periodic.json"),
JSON.stringify({ phase: "periodic" }),
);
if (mode === "exits") process.exit(0);
if (mode === "descendant") {
const childDirectory = join(directory, "descendant");
mkdirSync(childDirectory);
spawn(
process.execPath,
[fileURLToPath(import.meta.url), "ignoring", childDirectory],
{ detached: true, stdio: "ignore" },
);
}
if (mode === "ignoring" || mode === "delayed-ignoring") {
process.on("SIGTERM", () => {
writeFileSync(join(directory, "sigterm-received"), "received");
});
process.on("SIGINT", () => undefined);
} else {
process.on("SIGTERM", () => {
writeFileSync(join(directory, "sigterm-received"), "received");
process.stdout.write("finalized\n");
writeFileSync(
join(directory, "final.json"),
JSON.stringify({ phase: "final" }),
);
process.exit(Number(finalExitCode));
});
}
setInterval(() => undefined, 1_000);