From df6fafa76b0eec167fa1fa18a40632c2e95181ff Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 24 Sep 2026 17:58:08 +0200 Subject: fix(node): Peers tab answered 500 after the webrtc split /api/peers imported _get_remote_ip, inside the function, from webrtc_server, which no longer has it. A test now resolves every meshbay import in the node's source, function bodies included. Co-Authored-By: Claude Opus 5.5 --- packages/meshbay-node/src/meshbay_node/ui/app.py | 2 +- packages/meshbay-node/tests/test_api_peers.py | 31 ++++++++++++++++++ .../meshbay-node/tests/test_imports_resolve.py | 38 ++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 packages/meshbay-node/tests/test_api_peers.py create mode 100644 packages/meshbay-node/tests/test_imports_resolve.py (limited to 'packages') diff --git a/packages/meshbay-node/src/meshbay_node/ui/app.py b/packages/meshbay-node/src/meshbay_node/ui/app.py index ff28e53..eabe97a 100644 --- a/packages/meshbay-node/src/meshbay_node/ui/app.py +++ b/packages/meshbay-node/src/meshbay_node/ui/app.py @@ -249,7 +249,7 @@ def create_ui_app(state: dict) -> FastAPI: users, groups = await _display_names(state) peers = [] for pid, session in list(webrtc._sessions.items()): - from meshbay_node.transport.webrtc_server import _get_remote_ip + from meshbay_node.transport.webrtc.channel import _get_remote_ip uid = session._user_id or "" gid = session._group_id or "" peers.append({ diff --git a/packages/meshbay-node/tests/test_api_peers.py b/packages/meshbay-node/tests/test_api_peers.py new file mode 100644 index 0000000..420ccba --- /dev/null +++ b/packages/meshbay-node/tests/test_api_peers.py @@ -0,0 +1,31 @@ +""" +The Node page's Peers tab: one row per live session, from the loopback API. + +`_get_remote_ip` moved out of `webrtc_server.py` and the endpoint's import, +inside the function, went on naming the old place. Nothing imports a function +body until it runs, so the tab answered 500 and nothing else noticed. +""" + +from types import SimpleNamespace + +from fastapi.testclient import TestClient +from meshbay_node.ui.app import create_ui_app + + +def test_the_peers_tab_lists_a_live_session(): + session = SimpleNamespace( + _user_id="u-bob", _group_id="g" * 32, _username="bob", + # No address learnt at the handshake: the endpoint asks the ICE + # transport, which is the line that broke. + _remote_ip="", + _pc=SimpleNamespace(sctp=None, connectionState="connected")) + state = {"status": "running", + "webrtc": SimpleNamespace(_sessions={"p1": session})} + + resp = TestClient(create_ui_app(state)).get("/api/peers") + + assert resp.status_code == 200, resp.text + assert resp.json() == {"peers": [{ + "peer_id": "p1", "user_id": "u-bob", "username": "bob", + "group_id": "g" * 32, "group_name": "", "remote_ip": "", + "state": "connected"}]} diff --git a/packages/meshbay-node/tests/test_imports_resolve.py b/packages/meshbay-node/tests/test_imports_resolve.py new file mode 100644 index 0000000..2d6610b --- /dev/null +++ b/packages/meshbay-node/tests/test_imports_resolve.py @@ -0,0 +1,38 @@ +""" +Every `from meshbay_… import NAME` in the node's source names something that +exists — including the imports inside function bodies, which nothing loads +until that function runs. + +When code moves between modules, an import at the top of a file breaks at +once, and loudly. One inside a function breaks the first time somebody clicks +the button that calls it: the Peers tab answered 500 that way after +`_get_remote_ip` left `webrtc_server.py`. +""" + +import ast +import importlib +from pathlib import Path + +SRC = Path(__file__).resolve().parents[1] / "src" + + +def test_every_import_in_the_node_names_something_that_exists(): + missing, checked = [], 0 + for path in sorted(SRC.rglob("*.py")): + tree = ast.parse(path.read_text(encoding="utf-8")) + for node in ast.walk(tree): + if not (isinstance(node, ast.ImportFrom) and node.level == 0 + and node.module and node.module.startswith("meshbay_")): + continue + module = importlib.import_module(node.module) + for alias in node.names: + checked += 1 + if alias.name == "*" or hasattr(module, alias.name): + continue + try: + importlib.import_module(f"{node.module}.{alias.name}") + except ImportError: + missing.append(f"{path.relative_to(SRC)}:{node.lineno} " + f"{node.module}.{alias.name}") + assert checked > 300, "the walk found almost nothing to check" + assert not missing, "imports of names that do not exist:\n" + "\n".join(missing) -- cgit v1.2.3