diff --git a/test/integration/fresh-mesh.test.ts b/test/integration/fresh-mesh.test.ts index 2c7198b..b5b524b 100644 --- a/test/integration/fresh-mesh.test.ts +++ b/test/integration/fresh-mesh.test.ts @@ -138,6 +138,22 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi async function mesh(command: string, timeoutMs?: number): Promise { return must(CONTROL, `docker exec mesh-control /mesh-control ${command}`, timeoutMs); } + +/** + * Register a module from a manifest on this workstation. + * + * **The control plane runs in a container, so a file on the machine is not a file it can open.** + * Pushing the manifest to the machine and naming that path got `no such file or directory` from + * inside mesh-control, which is correct and was briefly mistaken for a missing manifest. It is + * copied the last step of the way with `docker cp`. + */ +async function registerModule(module: string, manifest: string): Promise { + assert.ok(existsSync(manifest), `no manifest for ${module} at ${manifest}`); + const onMachine = `/tmp/${module}.json`; + await push(instanceId, CONTROL, manifest, onMachine); + await must(CONTROL, `docker cp ${onMachine} mesh-control:${onMachine}`); + return mesh(`module add ${onMachine}`); +} function tokenFrom(said: string): string { const found = said.split("\n").map((l) => l.trim()).find((l) => l.length > 100 && !l.includes(" ")); assert.ok(found, `no token in:\n${said}`); @@ -250,12 +266,15 @@ before(async () => { // machine, published into the mesh's own registry. await step("the mesh builds the shared base from source", "three machines join it across the household gateway", async () => { - assert.ok(existsSync(baseManifest), - `no manifest for the shared base at ${baseManifest} — set MESH_LAB_BASE_MANIFEST`); - await push(instanceId, CONTROL, baseManifest, `/tmp/${BASE.module}.json`); // Registered from the manifest the builder will also read, so what the mesh holds and what it // builds are the same description of the same module. - await mesh(`module add /tmp/${BASE.module}.json`).catch(() => {}); + // + // **Not swallowed.** This call used to end in `.catch(() => {})`, on the reasoning that the + // base might already be known. It hid a real failure — the manifest was being named at a path + // inside a container that had never seen it — and the step passed anyway, because a base with + // nothing to stand on builds whether or not the mesh has a record of it. The next step, which + // needs that record, is where it surfaced. + await registerModule(BASE.module, baseManifest); const built = await mesh( `build ${forgeUrl(BASE.repo)} --ref ${buildRef} --wait 1200s`, 1_500_000); assert.doesNotMatch(built, /failed/i, built); @@ -265,10 +284,7 @@ before(async () => { // ---- 4. AND A MODULE STANDING ON IT --------------------------------------------------------- await step("the mesh builds a module standing on that base", "the mesh builds the shared base from source", async () => { - const manifest = resolve(catalogDir, MODULE.module, "module.json"); - assert.ok(existsSync(manifest), `no manifest at ${manifest}`); - await push(instanceId, CONTROL, manifest, `/tmp/${MODULE.module}.json`); - await mesh(`module add /tmp/${MODULE.module}.json`); + await registerModule(MODULE.module, resolve(catalogDir, MODULE.module, "module.json")); const built = await mesh( `build ${forgeUrl(MODULE.repo)} --path ${MODULE.path} --ref ${buildRef} --wait 1200s`, 1_500_000);