""" What the node claims to host, on the wire. 2026-09-11: the node omitted `group_ids` from its WS auth frame whenever it hosted nothing (`if gids:`), and the hub read an absent claim as "every group this account belongs to". An unconfigured node was therefore registered as a source for other people's groups, could serve none of them, and — being listed first — took them down for every member. The hub no longer widens an absent claim. This pins the node's half: the empty set is stated, not left to be inferred from a missing field. """ import asyncio import json import pytest from meshbay_node.hub_client import HubClient, HubConfig, HubSession class _FakeWS: """Just enough of a websockets connection for one auth round trip.""" def __init__(self, sent: list, authed: asyncio.Event): self._sent = sent self._authed = authed async def __aenter__(self): return self async def __aexit__(self, *_): return False async def send(self, raw: str) -> None: self._sent.append(json.loads(raw)) async def recv(self) -> str: return json.dumps({"type": "auth_ok", "node_id": "node-1"}) def __aiter__(self): return self async def __anext__(self): # Registered; nothing more to drive. Hold here rather than closing, so # the reconnect path does not run and muddy what was sent. self._authed.set() await asyncio.sleep(3600) raise StopAsyncIteration async def _auth_frame(monkeypatch, group_ids) -> dict: import websockets sent: list = [] authed = asyncio.Event() monkeypatch.setattr( websockets, "connect", lambda *a, **kw: _FakeWS(sent, authed)) client = HubClient(HubConfig(hub_url="https://hub.test", username="u"), keys=None) client._session = HubSession( hub_url="https://hub.test", username="u", user_id="user-1", access_token="tok", refresh_token="ref", hub_pk_pem=b"", node_id="node-1") task = asyncio.create_task(client.maintain_ws(group_ids=group_ids)) try: await asyncio.wait_for(authed.wait(), timeout=5) finally: task.cancel() try: await task except asyncio.CancelledError: pass await client.close() assert sent, "no auth frame was sent" return sent[0] @pytest.mark.asyncio async def test_a_node_hosting_nothing_says_so(monkeypatch): """The empty set on the wire, rather than a key the hub has to interpret.""" frame = await _auth_frame(monkeypatch, lambda: []) assert "group_ids" in frame, ( "an absent claim is what the hub used to read as 'all of them'") assert frame["group_ids"] == [] @pytest.mark.asyncio async def test_a_node_declares_the_groups_it_hosts(monkeypatch): frame = await _auth_frame(monkeypatch, lambda: ["g-1", "g-2"]) assert frame["group_ids"] == ["g-1", "g-2"] @pytest.mark.asyncio async def test_a_caller_with_nothing_to_declare_sends_no_key(monkeypatch): """`None` is not the same as `[]`: it is "I am not answering that question".""" frame = await _auth_frame(monkeypatch, None) assert "group_ids" not in frame