import { test } from "node:test"; import assert from "node:assert/strict"; import { parseScenario } from "../src/declaration/parse.ts"; import { loadScenario } from "../src/declaration/parse.ts"; /** Every rejection below is a fault that would otherwise be silent at runtime. */ function refuses(yaml: string, pattern: RegExp): void { assert.throws(() => parseScenario(yaml), (err: Error) => { assert.match(err.message, pattern); return true; }); } test("the shipped scenarios are valid", () => { for (const file of ["scenarios/bootstrap-single.yml", "scenarios/the-ordinary-shape.yml"]) { assert.doesNotThrow(() => loadScenario(file)); } }); test("a public segment on a private range is refused — the mesh would silently never form", () => { refuses( `scenario: x segments: { net: { kind: public, cidr: [192.168.1.0/24] } } machines: { a: { at: { segment: net, address: [192.168.1.1] } } }`, /not documentation space/, ); }); test("a public segment on a real routable range is refused", () => { refuses( `scenario: x segments: { net: { kind: public, cidr: [8.8.8.0/24] } } machines: { a: { at: { segment: net, address: [8.8.8.8] } } }`, /not documentation space/, ); }); test("a private segment may use any range, including someone else's RFC 1918", () => { assert.doesNotThrow(() => parseScenario(`scenario: x segments: pub: { kind: public, cidr: [192.0.2.0/24] } cafe: { kind: private, cidr: [10.50.0.0/16], gateway: { to: pub, address: [192.0.2.5], nat: [v4], forwardable: false } } machines: { a: { at: { segment: cafe, address: [10.50.0.9] } } }`), ); }); test("publishing through an unforwardable gateway is refused — that is the constraint", () => { refuses( `scenario: x segments: pub: { kind: public, cidr: [192.0.2.0/24] } cafe: { kind: private, cidr: [10.50.0.0/16], gateway: { to: pub, address: [192.0.2.5], nat: [v4], forwardable: false } } machines: a: at: { segment: cafe, address: [10.50.0.9] } published: [{ port: 443, on: cafe }]`, /not forwardable/, ); }); test("a gateway address must be on the PARENT segment, not the one behind it", () => { refuses( `scenario: x segments: pub: { kind: public, cidr: [192.0.2.0/24] } home: { kind: private, cidr: [192.168.1.0/24], gateway: { to: pub, address: [192.168.1.1], nat: [v4] } } machines: { a: { at: { segment: home, address: [192.168.1.9] } } }`, /is not within 'pub'/, ); }); test("a machine address outside its segment is refused", () => { refuses( `scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: net, address: [203.0.113.9] } } }`, /is not within segment 'net'/, ); }); test("an unknown segment reference is refused", () => { refuses( `scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: nope, address: [192.0.2.1] } } }`, /unknown segment 'nope'/, ); }); test("a gateway loop is refused rather than raised forever", () => { refuses( `scenario: x segments: a: { kind: private, cidr: [10.0.0.0/24], gateway: { to: b, address: [10.0.1.1], nat: [] } } b: { kind: private, cidr: [10.0.1.0/24], gateway: { to: a, address: [10.0.0.1], nat: [] } } machines: { m: { at: { segment: a, address: [10.0.0.9] } } }`, /loops through/, ); }); test("a detached machine cannot publish", () => { refuses( `scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: detached, published: [{ port: 443, on: net }] } }`, /detached but declares published/, ); }); test("publishing on a segment the machine is not attached to is refused", () => { refuses( `scenario: x segments: pub: { kind: public, cidr: [192.0.2.0/24] } home: { kind: private, cidr: [192.168.1.0/24], gateway: { to: pub, address: [192.0.2.5], nat: [v4] } } machines: a: at: { segment: pub, address: [192.0.2.10] } published: [{ port: 443, on: home }]`, /not attached to it/, ); }); test("two addresses of one family on one attachment is refused", () => { refuses( `scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: net, address: [192.0.2.1, 192.0.2.2] } } }`, /two v4 addresses/, ); }); test("place naming a machine that does not exist is refused", () => { refuses( `scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: net, address: [192.0.2.1] } } } place: { ghost: [host] }`, /'ghost' is not a machine/, ); }); test("every problem is reported, not just the first", () => { try { parseScenario(`scenario: x segments: { net: { kind: public, cidr: [192.168.0.0/24] } } machines: { a: { at: { segment: nope, address: [1.2.3.4] } } } place: { ghost: [host] }`); assert.fail("should have thrown"); } catch (err) { const problems = (err as { problems: string[] }).problems; assert.ok(problems.length >= 3, `expected several problems, got ${problems.length}`); } }); test("a detached machine is valid", () => { assert.doesNotThrow(() => parseScenario(`scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: a: { at: { segment: net, address: [192.0.2.1] } } roamer: { at: detached }`), ); }); test("a multi-homed machine is valid", () => { assert.doesNotThrow(() => parseScenario(`scenario: x segments: pub: { kind: public, cidr: [192.0.2.0/24] } home: { kind: private, cidr: [192.168.1.0/24], gateway: { to: pub, address: [192.0.2.5], nat: [v4] } } machines: border: at: - { segment: pub, address: [192.0.2.60] } - { segment: home, address: [192.168.1.2] }`), ); }); test("an isolated private segment with no gateway is valid — a site with no internet", () => { assert.doesNotThrow(() => parseScenario(`scenario: x segments: { island: { kind: private, cidr: [10.9.0.0/24] } } machines: { a: { at: { segment: island, address: [10.9.0.1] } } }`), ); });