import { test } from "node:test"; import assert from "node:assert/strict"; import { existsSync, readdirSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; import { catalogueIsPresent, catalogueDir } from "./integration/harness.ts"; /** * A bed installs a catalogue module by reading the catalogue, never by carrying a copy. * * The beds used to build the manifests they install inline, as literals taken from the catalogue * when each bed was written. The copies did not move when the catalogue did: six modules were * converted to file-delivered secrets and not one bed ran the converted shape, because every bed * ran its own copy (novox/hq 04-ISSUES/073). "Proven in the lab" then meant "the copy was proven". * * So: a manifest literal in a bed that names a catalogue module is refused, unless the bed is * listed below with the reason it still carries one. The list is the debt, and it only shrinks. * * What this reads: `module: ""` and `"module": ""` with a `version` close by, either * order, in test/integration/*.test.ts, against the catalogue's directory names. Skipped aloud * where MESH_LAB_CATALOG is unset — a skip is reported, never silent. A bed that hid the name * behind a computed string would pass — this is a fence, not a proof, and the reviewer of a bed * that builds a manifest inline is the proof. */ /** * Beds that still carry an inline copy of a catalogue module's manifest, and why. Three reasons * recur, and each names the work that removes the entry: * * BESIDE the catalogue's module CLAIMS the foundation's container (postgres claims mesh-store, * lavinmq mesh-broker) and adopts it in place; the bed raises a second one beside the * foundation's instead. Reading the catalogue changes what the bed raises — it would * adopt — and the bed's assertions with it. * WEARING the bed proves a mesh mechanism (a grant, a credential, a restart, a route) with a * module cut down to the shape the mechanism needs — no upstream server, a secret in the * environment, a requirement edge removed — and gives it a catalogue name. It is a mesh * test wearing a catalogue module's name. It should carry a name of its own, or read the * catalogue and meet the module's real requirements. * DIFFERS a module bed whose copy differs from the catalogue in more than the lab may rewrite * (an image, a port, an address). Reading the catalogue is the fix and needs a run. */ const STILL_CARRIED: Record = { "assigned-catalogue-apps.test.ts": { modules: ["postgres", "mongodb", "unifi", "marrytts"], why: "BESIDE (postgres); DIFFERS (unifi takes its credentials from the environment, mongodb and marrytts drop listens)" }, "assigned-catalogue-media.test.ts": { modules: ["sonarr", "radarr"], why: "DIFFERS: both drop the route requirement the catalogue declares, and take their API keys from the environment" }, "assigned-catalogue-small.test.ts": { modules: ["postgres", "minio", "redis", "plex"], why: "BESIDE (postgres); DIFFERS (minio's root password by env-file, redis minting its own secret instead of the vault's, plex without its server)" }, "assigned-model-usage.test.ts": { modules: ["postgres"], why: "BESIDE" }, "assigned-two-node-db.test.ts": { modules: ["redis", "baserow", "letta"], why: "DIFFERS: redis mints its own secret, baserow drops its route requirement, letta drops its ports" }, "lavinmq-bed.test.ts": { modules: ["lavinmq", "amqp-ping"], why: "BESIDE (lavinmq, with a bootstrap step and a data directory the catalogue has not got); DIFFERS (amqp-ping names its entrypoint)" }, "assigned-grafana.test.ts": { modules: ["grafana"], why: "WEARING: the sidecar alone, no Grafana, no route" }, "assigned-plex.test.ts": { modules: ["plex"], why: "WEARING: the sidecar alone, no Plex, the token in the environment" }, "assigned-redis.test.ts": { modules: ["redis"], why: "WEARING: its own secret, a lab seal key in the environment" }, "assigned-sonarr.test.ts": { modules: ["sonarr"], why: "WEARING: the sidecar alone against a forged config.xml" }, "mesh-grant-end-to-end.test.ts": { modules: ["redis"], why: "WEARING: a grant mechanism test" }, "minio-grant-end-to-end.test.ts": { modules: ["minio"], why: "WEARING: a grant mechanism test, the root password by env-file" }, "postgres-grant-end-to-end.test.ts": { modules: ["postgres"], why: "WEARING: a grant mechanism test, the superuser by env-file" }, "provider-on-backend-network.test.ts": { modules: ["redis"], why: "WEARING: a network mechanism test" }, "provider-uses-mesh-credential.test.ts": { modules: ["redis"], why: "WEARING: a credential mechanism test" }, "runtime-restart-on-config.test.ts": { modules: ["grafana"], why: "WEARING: a restart mechanism test" }, "route-forwarding.test.ts": { modules: ["route-proxy", "hello-web"], why: "WEARING: route-proxy without its certificate authority, hello-web with the route shape ADR 0066 replaced" }, "mesh.test.ts": { modules: ["postgres", "builder", "umami"], why: "WEARING: a postgres with no resources, a builder that builds itself, an umami that is another module of that name" }, }; const beds = resolve(import.meta.dirname, "integration"); test("a bed that installs a catalogue module reads the catalogue", (t) => { const absent = catalogueIsPresent(); if (absent) { // Said, not silent: a check that cannot see the catalogue has checked nothing. t.skip(`cannot check — ${absent}`); return; } const names = new Set(readdirSync(catalogueDir(), { withFileTypes: true }) .filter((d) => d.isDirectory() && existsSync(resolve(catalogueDir(), d.name, "module.json"))) .map((d) => d.name)); const offences: string[] = []; for (const file of readdirSync(beds).filter((f) => f.endsWith(".test.ts")).sort()) { const text = readFileSync(resolve(beds, file), "utf8"); const found = new Set(); // A manifest literal: the module's name with its version close behind it. A `module:` key // elsewhere (a table of what to register, a grant entry) has no version and is not one. for (const m of text.matchAll(/(?:^|[\s{,])(?:"module"|module)\s*:\s*"([a-z0-9.-]+)"[^}]{0,160}?(?:"version"|version)\s*:/g)) { if (names.has(m[1]!)) found.add(m[1]!); } // And the other order — a literal that names its version first. for (const m of text.matchAll(/(?:^|[\s{,])(?:"version"|version)\s*:\s*"[^"]*"[^}]{0,160}?(?:"module"|module)\s*:\s*"([a-z0-9.-]+)"/g)) { if (names.has(m[1]!)) found.add(m[1]!); } const declared = STILL_CARRIED[file]; for (const name of [...found].sort()) { if (declared?.modules.includes(name)) continue; offences.push(`${file}: an inline manifest for the catalogue's '${name}'`); } for (const name of declared?.modules ?? []) { if (!found.has(name)) offences.push(`${file}: declared as still carrying '${name}', and it does not — remove the declaration`); } } assert.deepEqual(offences, [], `a bed carries a copy of a catalogue manifest; read it with catalogueModule() from the harness:\n ${offences.join("\n ")}`); });