pi-mono/packages/chord/test/next-node/workers/process.test.ts
Mario Zechner 8959ad6e68 feat(chord): Chord v2 slice 10, HTTP and workers
chord/next/http: a Web fetch handler built from the registry Http (routes, wraps,
sockets, hosts, relays, CSP sources) and Authenticators, responses as descriptions,
a security baseline on by default, Chord's own Node listener as a host-lifetime
object, nodeRequest for apps mounting it, links and plugin sockets.
chord/next/workers: a forked worker per address, started on demand, stopped when
idle, never restarted by itself, plugins built by the server and shipped.
Plugins: kinds with platform and host packages, createPluginRuntime, scripts(kind),
rollout results per host, imports of other plugins by name. Kernel: RouteHandle
(retry, suspend), no route backoff, anonymous host.root, origins only from relays.
ws is an optional peer dependency.
2026-10-31 20:40:36 +00:00

46 lines
1.8 KiB
TypeScript

// Tests of worker processes (slice 10 §3): forked with their output piped (the stderr tail kept), the link made at the
// fork (an early frame queued), control messages told, and the whole tree killed.
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import type { FromWorker } from "../../../src/next-node/workers/control.ts";
import { WorkerProcess } from "../../../src/next-node/workers/process.ts";
import { until } from "./support.ts";
const CHILD = fileURLToPath(new URL("../fixtures/workers/child.ts", import.meta.url));
/** Whether process `pid` lives. */
function alive(pid: number): boolean {
try {
process.kill(pid, 0);
return true;
} catch {
return false;
}
}
describe("a worker process", () => {
it("pipes its output, keeps its stderr tail, queues an early frame on its link, tells control messages", async () => {
const output: string[] = [];
const control: FromWorker[] = [];
const child = new WorkerProcess(
{
entry: CHILD,
execArgv: ["--conditions=source", "--experimental-strip-types", "--no-warnings"],
onOutput: (stream, chunk) => output.push(`${stream}: ${Buffer.from(chunk).toString().trim()}`),
},
{ control: (message) => control.push(message), linkLost: () => {}, linkClosed: () => {}, exited: () => {} },
);
await until(() => control.length > 0 && output.length >= 2);
const frames: number[][] = [];
child.link.onMessage((message) => frames.push([...message]));
expect(frames).toEqual([[1, 2, 3]]);
expect(control).toEqual([{ kind: "started" }]);
expect(child.stderrTail).toBe("an error line\n");
const grandchild = Number(/grandchild (\d+)/.exec(output.join("\n"))![1]);
expect(alive(grandchild)).toBe(true);
child.kill();
expect(await child.exited).toEqual({ code: null, signal: "SIGKILL" });
await until(() => !alive(grandchild), 2_000);
});
});