diff --git a/test/integration/built-store-cross-node.test.ts b/test/integration/built-store-cross-node.test.ts index c8b9542..b89a9e4 100644 --- a/test/integration/built-store-cross-node.test.ts +++ b/test/integration/built-store-cross-node.test.ts @@ -330,5 +330,28 @@ test("a joined node's consumers open the store and broker the mesh built and ado `a runtime waits for its broker in-process (hq issue 058):\n` + (await on(NODE, `docker logs amqp-ping 2>&1 | head -20`)).out); + // The broker survives a restart as a reachable thing, not just a running one (hq issue 063). + // The foundation's ports are opened from anywhere on input, but the broker is a published + // container port — so a cross-node dial is forwarded, and until 063 the forward chain carried + // no foundation rule: the joined node reached the broker only until its first connection's + // conntrack entry dropped. Restarting the broker here drops it deliberately; the node must + // reconnect and the vhost the provisioner holds must still be listed, proving the forward rule + // and not a surviving conntrack entry is what carries the connection. + await must(CONTROL, `docker restart mesh-broker`, 120_000); + { + const deadline = Date.now() + 180_000; + let reachable = ""; + while (Date.now() < deadline) { + reachable = (await on(NODE, + `timeout 5 bash -c 'cat < /dev/null > /dev/tcp/${ANCHOR}/5671' 2>&1 && echo REACHED`)).out; + if (/REACHED/.test(reachable)) break; + await new Promise((r) => setTimeout(r, 5_000)); + } + assert.match(reachable, /REACHED/, + `after the broker restarted, ${NODE} cannot reach it at ${ANCHOR}:5671 — the foundation's ` + + `forward rule is missing (hq issue 063):\n` + + (await on(CONTROL, `nft list ruleset 2>/dev/null | sed -n '/chain forward/,/}/p'`)).out); + } + console.log(`amqp: ${amqpBound.trim().slice(0, 160)}`); });