import { test } from "node:test"; import assert from "node:assert/strict"; import { parseScenario } from "../src/declaration/parse.ts"; import { assertSupported, UnsupportedError } from "../src/lifecycle/supported.ts"; /** * A declaration the runtime silently ignores is the fault this lab exists to catch — * novox/hq 04-ISSUES/003, where a firewall key is declared in five manifests and read by no * code. These tests exist so the lab never commits it, and they move as the runtime catches * up with the model. */ const withGateway = `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: home, address: [192.168.1.9] } } }`; test("a plain scenario is raisable", () => { const scenario = parseScenario(`scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: net, address: [192.0.2.1] } } }`); assert.doesNotThrow(() => assertSupported(scenario)); }); test("gateways are implemented — a router is materialised for them", () => { assert.doesNotThrow(() => assertSupported(parseScenario(withGateway))); }); test("published ports and policy are implemented", () => { const scenario = 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] } } iot: { kind: private, cidr: [192.168.30.0/24], gateway: { to: pub, address: [192.0.2.5], nat: [v4] } } policy: [{ from: iot, to: home, allow: false }] machines: a: at: { segment: home, address: [192.168.1.9] } published: [{ port: 443, on: home }]`); assert.doesNotThrow(() => assertSupported(scenario)); }); test("inbound: deny is implemented — a host firewall is applied and read back", () => { const scenario = parseScenario(`scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: net, address: [192.0.2.1] }, inbound: deny } }`); assert.doesNotThrow(() => assertSupported(scenario)); }); test("the host is placeable — it used to be refused, and tier 0 now exists", () => { // These two tests failed the moment placement worked, which is what they were for. They // defended "there is nothing to place yet" while that was true; the decision changed, so // they change with it rather than being deleted (novox/hq ADR 0017). const scenario = parseScenario(`scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: net, address: [192.0.2.1] } } } place: { all: [host] }`); assert.doesNotThrow(() => assertSupported(scenario)); }); test("a tier above 0 is still refused, and named", () => { // The refusal narrowed rather than disappearing. A scenario placing a host AND a substrate // must be told which half is missing — not that `place:` is unsupported, when half of it // now works. const scenario = parseScenario(`scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: net, address: [192.0.2.1] } } } place: { all: [host, substrate] }`); try { assertSupported(scenario); assert.fail("should have refused"); } catch (err) { assert.ok(err instanceof UnsupportedError); assert.equal(err.missing.length, 1, `expected only the substrate: ${err.missing.join(", ")}`); assert.match(err.missing[0] ?? "", /substrate/); assert.match(err instanceof Error ? err.message : "", /silently lacks them/); } }); test("inbound: allow is not a gap — only deny needs enforcing", () => { const scenario = parseScenario(`scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: net, address: [192.0.2.1] }, inbound: allow } }`); assert.doesNotThrow(() => assertSupported(scenario)); }); test("egress is parsed, and off unless asked for", () => { const scenario = parseScenario(`scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: a: { at: { segment: net, address: [192.0.2.1] }, egress: true } b: { at: { segment: net, address: [192.0.2.2] } }`); assert.equal(scenario.machines["a"]?.egress, true); assert.equal(scenario.machines["b"]?.egress, undefined); });