diff --git a/scenarios/two-nodes.yml b/scenarios/two-nodes.yml index b5ae654..87d2c4b 100644 --- a/scenarios/two-nodes.yml +++ b/scenarios/two-nodes.yml @@ -40,6 +40,9 @@ images: - mesh-provision-postgres:development # And the proxy, which is what turns a route grant into traffic actually arriving. - mesh-route-proxy:development + # And the object store's provisioner, so the module describing it can be planned. Without it + # that module still names an image nothing serves, and planning it is refused — correctly. + - mesh-provision-objectstore:development # And a forge, so one of the real module descriptions can be started rather than only planned. # It is the first of them to run: it needs a database from another module, a credential it did # not choose, and a connection string it could not have written itself. diff --git a/test/integration/mesh.test.ts b/test/integration/mesh.test.ts index 821e692..65e9421 100644 --- a/test/integration/mesh.test.ts +++ b/test/integration/mesh.test.ts @@ -321,7 +321,12 @@ test("a credential reaches both ends and the mesh holds neither", { skip, timeou ["laptop", "/var/lib/mesh-host/state.json"], ["anchor", "/var/lib/mesh-host/declared.json"], ] as const) { - const { out } = await on(machine, `grep -c ${quote(onConsumer)} ${where}`); + // **`--` first, or the password is read as options.** A generated credential is random, so + // one of them eventually begins with a dash — this one started `-S` and grep refused the + // whole invocation. The test then compared an error message against "0" and reported the + // password as leaked, which is the worst way for a search to fail: it says it found + // something. + const { out } = await on(machine, `grep -c -e ${quote(onConsumer)} -- ${where}`); assert.equal(out.trim(), "0", `the password is in ${where} on ${machine}`); } const inTheMesh = await must("anchor", @@ -1712,6 +1717,7 @@ test("the real modules resolve together, and compose a declaration a host accept skip, timeout: 300_000, }, async (t) => { const modules = ["postgres", "keycloak", "gitea", "minio", "mailu"]; + const planned: string[] = []; for (const name of modules) { const raw = readFileSync( `${process.env["MESH_LAB_MODULES"]}/${name}.json`, "utf8"); @@ -1722,6 +1728,16 @@ test("the real modules resolve together, and compose a declaration a host accept // declaration from it is refused (novox/hq 04-ISSUES/025). Planning what could never run is // what this test used to do. const pinned = pinnedInto(raw, stocked); + // What this scenario does not serve cannot be redirected, and a module still naming a + // placeholder cannot be planned — the refusal is the point (novox/hq 04-ISSUES/025). Skipped + // and said, rather than silently dropped: a planning test quietly covering four modules + // instead of five is the false coverage this suite exists to prevent. + const left = stillUnpinned(pinned); + if (left.length > 0) { + console.log(`skipping ${name}: this scenario serves no ${left.join(", ")}`); + continue; + } + planned.push(name); await must("anchor", `printf %s ${quote(pinned)} > /${name}.json`); await must("anchor", `docker cp /${name}.json mesh-control:/${name}.json`); await mesh(`module add /${name}.json`); @@ -1732,12 +1748,12 @@ test("the real modules resolve together, and compose a declaration a host accept // the first time this test failed — and the next test's push was refused by a module this one // had left behind, which reads as a fault in the test that was actually working. t.after(async () => { - for (const name of modules) await mesh(`unassign anchor ${name}`).catch(() => {}); + for (const name of planned) await mesh(`unassign anchor ${name}`).catch(() => {}); }); // Assigned one at a time, because assignment resolves the whole set and says so immediately. // A refusal here is the graph rejecting something, which is the point of asking. - for (const name of modules) { + for (const name of planned) { await mesh(`assign anchor ${name}`); }