Files
agentic-dev/apps/runner/tests/runner-process.integration.test.ts
Danijel Martinek b090e26701 feat(runner): WS protocol server with handshake
apps/runner scaffold (app-tier, walking-skeleton story 04): WS server
speaking @repo/core-runner-protocol. Every inbound/outbound frame is
envelope-wrapped and zod-parsed; hello/ready handshake gates on the
workspace-scoped token (constant-time compare, redacted token on
rejection replies); named error events for version/schema/auth
rejections. Config via env only (token never argv); port announced on
stdout for the story-06 provisioner. Runtime deps: ws (the standard
Node WS server; ADR-022 traces do not apply to app-tier) and zod.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
2026-07-12 22:50:41 +02:00

49 lines
1.6 KiB
TypeScript

/**
* 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);
});
});