import { test } from "node:test"; import assert from "node:assert/strict"; import { parseScenario } from "../src/declaration/parse.ts"; import { planPlacements, PLACEABLE } from "../src/lifecycle/place.ts"; import { assertSupported, UnsupportedError } from "../src/lifecycle/supported.ts"; /** * `place:` is the seam where the lab stops being infrastructure with no consumer. Each test * names what it defends, per novox/hq ADR 0034. */ function scenario(place: string): ReturnType { return parseScenario(` scenario: placing segments: hosting: kind: public cidr: [192.0.2.0/24] machines: anchor: at: { segment: hosting, address: [192.0.2.10] } peer: at: { segment: hosting, address: [192.0.2.20] } ${place} `); } test("`all:` reaches every machine", () => { const placements = planPlacements(scenario("place:\n all: [host]")); assert.deepEqual( placements.map((p) => p.machine).sort(), ["anchor", "peer"], ); for (const p of placements) assert.deepEqual(p.artifacts, ["host"]); }); test("a per-machine entry OVERRIDES `all:`, it does not add to it", () => { // Worth being exact about: a scenario naming one artifact for one machine gets that // artifact, not that artifact plus everything in `all:`. The opposite reading would place // things nobody asked for, which is the shape of fault this lab exists to catch. const placements = planPlacements(scenario("place:\n all: [host]\n anchor: [substrate]")); const byMachine = new Map(placements.map((p) => [p.machine, p.artifacts])); assert.deepEqual(byMachine.get("anchor"), ["substrate"], "anchor should have ONLY substrate"); assert.deepEqual(byMachine.get("peer"), ["host"]); }); test("a machine placed with nothing is not a placement", () => { const placements = planPlacements(scenario("place:\n all: [host]\n anchor: []")); assert.deepEqual(placements.map((p) => p.machine), ["peer"]); }); test("no `place:` at all is no placements, not an error", () => { assert.deepEqual(planPlacements(scenario("")), []); }); test("placing the host is supported", () => { // The whole point of stage 1: this used to be refused. assert.doesNotThrow(() => assertSupported(scenario("place:\n all: [host]"))); }); test("a tier that does not exist is refused BY NAME", () => { // Named individually rather than refused as a whole, so a scenario placing a host and a // substrate is told exactly which half the lab cannot do — rather than being told `place:` // is unsupported when half of it now works. try { assertSupported(scenario("place:\n all: [host, substrate]\n peer: [control]")); assert.fail("expected a refusal"); } catch (err) { assert.ok(err instanceof UnsupportedError); const missing = err.missing.join("\n"); assert.match(missing, /substrate/, "the substrate was not named"); assert.match(missing, /control/, "the control plane was not named"); assert.doesNotMatch(missing, /place: host/, "the host is placeable and was refused anyway"); } }); test("the refusal says what CAN be placed", () => { // A refusal that does not say what is possible sends someone to the source to find out. try { assertSupported(scenario("place:\n all: [forge]")); assert.fail("expected a refusal"); } catch (err) { assert.ok(err instanceof UnsupportedError); for (const placeable of PLACEABLE) { assert.match(err.missing.join("\n"), new RegExp(placeable)); } } });