Files
mesh-lab/test/integration/genesis-single.test.ts
T
jschoubben 5d6e8fbe7a Rename mesh-control -> mesh-controller, substrate -> foundation
One name per thing, per the HQ glossary: the module/container/image/binary/repo
becomes mesh-controller, the seat the-controller, and the store+broker pair the
foundation (embedded base bundles, default template and example lock renamed with
their go:embed directives). No behaviour change — a pure vocabulary rename.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
2026-09-16 18:40:40 +02:00

116 lines
5.0 KiB
TypeScript

/**
* A MESH OF ONE, raised by the installer and nothing else.
*
* The four-node bed proves genesis too, but it proves it entangled with three machines joining
* afterwards across a gateway. This bed is genesis alone: one machine, the installer, and the
* question "is this a working mesh?" asked of the machine.
*
* It shares its genesis with the four-node bed (`./genesis.ts`) rather than describing installing
* a second time — a second description kept in step with the first is the arrangement that put
* the whole procedure inside a fixture in the first place (novox/hq ADR 0067).
*
* MESH_LAB_INCUS='sudo -n incus'
* MESH_LAB_HOST_BINARY=.../mesh-host/mesh-host
* MESH_LAB_BOOTSTRAP_BINARY=.../mesh-host/mesh-bootstrap
* MESH_LAB_BUNDLE=.../mesh-host/examples/foundation-first-node.lock
* MESH_LAB_CATALOG=.../mesh-catalog/modules
* MESH_LAB_KEEP=1 to leave it standing afterwards
*/
import { test, before, after } from "node:test";
import assert from "node:assert/strict";
import { existsSync } from "node:fs";
import { loadScenario } from "../../src/declaration/parse.ts";
import { raise } from "../../src/lifecycle/raise.ts";
import { destroy } from "../../src/lifecycle/operate.ts";
import { bootstrapBinaryPath, hostBinaryPath } from "../../src/lifecycle/place.ts";
import { labIsUsable, destroyAll, foundationBundle } from "./harness.ts";
import { genesis, type GenesisResult } from "./genesis.ts";
const SCENARIO = "genesis-single";
const NODE = "anchor";
const capability = await labIsUsable();
const binary = hostBinaryPath();
const installer = bootstrapBinaryPath();
const bundle = process.env["MESH_LAB_BUNDLE"] ?? "";
const catalogDir = process.env["MESH_LAB_CATALOG"] ?? "";
// What the installer is told to build. It carries a builder rather than a finished control plane
// (novox/hq ADR 0073), so genesis needs a repository and a commit — and a commit rather than a
// branch, because what is cloned is the trust anchor for everything this mesh will ever run.
const source = process.env["MESH_LAB_SOURCE"] ?? "";
const sourceRef = process.env["MESH_LAB_SOURCE_REF"] ?? "";
const KEEP = !!process.env["MESH_LAB_KEEP"];
const FIXED_ID = process.env["MESH_LAB_INSTANCE_ID"] ?? (KEEP ? "genesis-single-live" : undefined);
const skip =
!capability.usable ? capability.why :
!binary ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" :
!installer ? "MESH_LAB_BOOTSTRAP_BINARY is not set to a built mesh-bootstrap (mesh-host `make " +
"bootstrap IMAGE=mesh-builder:development`)" :
!source ? "MESH_LAB_SOURCE is not set to the repository the control plane is built from" :
!sourceRef ? "MESH_LAB_SOURCE_REF is not set to the commit to build" :
!bundle || !existsSync(bundle) ? "MESH_LAB_BUNDLE is not set to a foundation template" :
!catalogDir || !existsSync(catalogDir) ? "MESH_LAB_CATALOG is not set to mesh-catalog/modules" :
false;
let instanceId = "";
let result: GenesisResult;
before(async () => {
if (skip) return;
const raised = await raise(loadScenario(`scenarios/${SCENARIO}.yml`), {
onProgress: (m) => console.log(`raise: ${m}`),
...(FIXED_ID ? { instanceId: FIXED_ID } : {}),
});
instanceId = raised.instanceId;
console.log(`INSTANCE ${instanceId}${KEEP ? " (KEEP — will be left standing)" : ""}`);
// Caught rather than allowed to propagate, so a failure BEFORE the installer ran is reported in
// the same shape as one the installer itself named, instead of a bare stack trace.
try {
result = await genesis({
instanceId,
node: NODE,
installer: installer as string,
catalogDir,
// The template, not a bundle. The store and broker become the references mesh-catalog pins,
// and the machine pulls them over its uplink like any first node. mesh-controller is left
// naming a registry that does not exist — the installer overwrites it, and leaving it proves
// that it does.
bundleTemplate: foundationBundle(bundle, []),
source,
sourceRef,
// Phase two builds from the same forge; the repositories sit beside the control plane's.
toolsSource: source.replace(/[^/]+\.git$/, "mesh-tools.git"),
catalogSource: source.replace(/[^/]+\.git$/, "mesh-catalog.git"),
log: (m) => console.log(m),
});
} catch (err) {
result = {
ok: false,
step: "getting the machine ready to be bootstrapped — the installer never ran",
why: (err as Error).message,
report: [],
};
}
console.log(result.report.join("\n"));
}, { timeout: 3_600_000 });
after(async () => {
if (KEEP) {
console.log(`\nLEFT STANDING: ${instanceId} — not destroyed (MESH_LAB_KEEP).`);
return;
}
if (instanceId) await destroy(instanceId);
await destroyAll(`${SCENARIO}-`);
}, { timeout: 900_000 });
test("one machine becomes a working mesh of one, raised by the installer", {
skip, timeout: 3_600_000,
}, () => {
assert.ok(result.ok,
`GENESIS FAILED — ${result.step || "no step named"}.\n\n${result.why}\n\n` +
result.report.join("\n"));
});