/** * Spawn-based integration suite: the runner as the provisioner (story 06) * will actually run it — a real child process, config via env only, port * discovered from stdout, real WS from the outside. */ import { afterEach, describe, expect, it } from "vitest"; import { spawnRunner, type SpawnedRunner } from "./spawn-runner"; import { connectProtocolClient, expectNamedError, handshake, type ProtocolClient, } from "./protocol-client"; const TOKEN = "spawned-runner-token"; let runner: SpawnedRunner | undefined; let clients: ProtocolClient[] = []; afterEach(async () => { for (const client of clients) client.close(); clients = []; await runner?.stop(); runner = undefined; }); describe("spawned runner process", () => { it("boots from env config, announces its port, and completes the handshake over real WS", async () => { runner = await spawnRunner({ token: TOKEN }); expect(runner.port).toBeGreaterThan(0); const client = await connectProtocolClient(runner.port, TOKEN); clients.push(client); const ready = await handshake(client); expect(ready).toEqual({ type: "ready" }); }); it("rejects a bad workspace token from a real client and closes the socket", async () => { runner = await spawnRunner({ token: TOKEN }); const client = await connectProtocolClient(runner.port, "not-the-token"); clients.push(client); client.send({ type: "hello" }); await expectNamedError(client, "unauthorized"); await expect(client.closed).resolves.toBe(1008); // The runner never writes the workspace token to its logs. expect(runner.output()).not.toContain(TOKEN); }); });