aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_transport_contracts.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests/test_transport_contracts.py')
-rw-r--r--packages/meshbay-hub/tests/test_transport_contracts.py54
1 files changed, 54 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_transport_contracts.py b/packages/meshbay-hub/tests/test_transport_contracts.py
index f2f4372..c506ff1 100644
--- a/packages/meshbay-hub/tests/test_transport_contracts.py
+++ b/packages/meshbay-hub/tests/test_transport_contracts.py
@@ -452,3 +452,57 @@ def test_the_index_is_never_reported_from_a_failed_decrypt(transport):
assert "catch" not in body, (
"_applyIndexMessage swallows its own failure instead of letting "
"_queueIndexMessage end the session")
+
+
+# ── One node refusing must not take a group down (2026-09-11) ────────────────
+#
+# `/v1/groups/{id}/nodes` returns every node registered for the group, in hub
+# registration order. GroupPage took `nodesData.nodes[0]` and stopped there, so
+# a node that could not serve the group — refusing the handshake with "Group
+# not hosted on this node" — made the group unopenable while the node that
+# *did* host it sat second in the same list.
+#
+# These strip comments first. The lesson this repeats otherwise is the CSP read
+# out of the comment above the meta tag, and the packaging unit whose test
+# matched the comment explaining why `User=` was absent: a source-level check
+# that can match prose is not a check.
+
+def _code_only(src: str) -> str:
+ """Source with // and /* */ comments removed. Crude, and enough here: no
+ string literal in these files carries a comment marker."""
+ src = re.sub(r"/\*.*?\*/", "", src, flags=re.S)
+ return re.sub(r"^\s*//.*$", "", src, flags=re.M)
+
+
+def test_the_group_page_tries_every_node_the_hub_offers(group_page):
+ code = _code_only(group_page)
+ assert "for (const n of nodesData.nodes)" in code, (
+ "the connect effect must walk the list, not index into it")
+ assert "nodesData.nodes[0]" not in code, (
+ "taking the head and stopping is the defect — one wrongly registered "
+ "node captured the whole group's traffic")
+
+
+def test_a_not_hosted_refusal_moves_on_to_the_next_node(group_page):
+ code = _code_only(group_page)
+ body = code[code.index("for (const n of nodesData.nodes)"):]
+ body = body[:body.index("if (!transport)")]
+ assert "not_hosted" in body, (
+ "without the code, a refusal this browser cannot act on is "
+ "indistinguishable from one it must stop for")
+ assert "throw e" in body, (
+ "a refusal naming a state of this browser — a pairing code, a "
+ "passphrase, a device — is the same from every node and must stop here")
+
+
+def test_the_last_refusal_is_what_the_reader_is_told(group_page):
+ code = _code_only(group_page)
+ assert "if (!transport) throw (lastErr" in code, (
+ "exhausting the list must report why, not fall through silently")
+
+
+def test_the_refusal_the_loop_keys_on_has_a_message(transport):
+ """A code the page routes on, with nothing to show, is a blank error."""
+ refusals = transport[transport.index("const HANDSHAKE_REFUSALS"):]
+ refusals = refusals[:refusals.index("};")]
+ assert "not_hosted:" in refusals