diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-23 22:25:28 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-24 16:45:37 +0200 |
| commit | 328b01a2dd545d70a078db8df1e91b02d65bfc9c (patch) | |
| tree | 9af69d617d9482ba4256f3d7334c9dd6cde0d5e2 /packages/meshbay-hub/tests | |
| parent | 92ea222b7aca6a3eb5f04330f0af6755e6e434e3 (diff) | |
| download | meshbay-328b01a2dd545d70a078db8df1e91b02d65bfc9c.tar.gz | |
test: read the WebRTC transport's source as a set of files
Source-reading tests take their text from node_source (node) and node_tree
(hub): webrtc_server.py plus anything under transport/webrtc/, so a check
for something's absence keeps reading the code it guards if that code moves.
test_node_source_scope holds the boundary.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/tests')
6 files changed, 71 insertions, 32 deletions
diff --git a/packages/meshbay-hub/tests/node_tree.py b/packages/meshbay-hub/tests/node_tree.py new file mode 100644 index 0000000..8704492 --- /dev/null +++ b/packages/meshbay-hub/tests/node_tree.py @@ -0,0 +1,54 @@ +""" +The node's WebRTC transport, as source text, for the hub tests that check a +client contract against what the node actually sends. + +Read from the tree rather than imported, so these tests keep working in a +checkout without the node installed. The file set is derived — the transport +is `webrtc_server.py` and whatever has been split out of it under +`transport/webrtc/` — so that code moving between those files does not leave a +test reading a file its subject has left. The node suite's `node_source.py` +draws the same line and holds it with `test_node_source_scope.py`. +""" + +import ast +from pathlib import Path + +# parents[2] is `packages/`: the tests live at packages/meshbay-hub/tests/. A +# wrong index here does not fail anything — every test using it skips, which +# is worse than not having them: a green run that measured nothing. That +# happened once. +TRANSPORT = (Path(__file__).resolve().parents[2] / "meshbay-node" / "src" + / "meshbay_node" / "transport") +SERVER = TRANSPORT / "webrtc_server.py" + + +def available() -> bool: + return SERVER.exists() + + +def webrtc_files() -> list[Path]: + files = [SERVER] + package = TRANSPORT / "webrtc" + if package.is_dir(): + files += sorted(p for p in package.rglob("*.py") + if "__pycache__" not in p.parts) + return files + + +def webrtc_source() -> str: + return "\n".join(p.read_text(encoding="utf-8") for p in webrtc_files()) + + +def method(name: str) -> str: + """The text of one method of a class in the transport, found by name.""" + found = [] + for path in webrtc_files(): + text = path.read_text(encoding="utf-8") + for node in ast.walk(ast.parse(text)): + if isinstance(node, ast.ClassDef): + for member in node.body: + if isinstance(member, (ast.FunctionDef, ast.AsyncFunctionDef)) \ + and member.name == name: + found.append(ast.get_source_segment(text, member, padded=True)) + assert len(found) == 1, f"{name}: expected one definition, found {len(found)}" + return found[0] diff --git a/packages/meshbay-hub/tests/test_app_settings_plugin.py b/packages/meshbay-hub/tests/test_app_settings_plugin.py index 027fee6..a1ecec7 100644 --- a/packages/meshbay-hub/tests/test_app_settings_plugin.py +++ b/packages/meshbay-hub/tests/test_app_settings_plugin.py @@ -15,6 +15,7 @@ fault this refactor's new import graph could otherwise reintroduce. import re from pathlib import Path +import node_tree import pytest STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" @@ -24,12 +25,6 @@ GROUP_PAGE = STATIC / "group-page.js" SETTINGS_UI = STATIC / "settings-ui.js" FOLDER_TREE = STATIC / "folder-tree.js" TRANSPORT = STATIC / "transport.js" -# parents[2] is `packages/` — the tests live at -# packages/meshbay-hub/tests/, so [0] is tests, [1] the package, [2] packages. -# Got this wrong once and the two cross-package checks below skipped silently, -# which is worse than not having them: a green run that measured nothing. -NODE_SERVER = (Path(__file__).resolve().parents[2] / "meshbay-node" / "src" - / "meshbay_node" / "transport" / "webrtc_server.py") PANES = ["chat-app-settings.js", "video-app-settings.js", "music-app-settings.js", "photos-app-settings.js"] @@ -89,11 +84,10 @@ def test_every_registered_app_has_a_key_the_node_would_accept(): key here that `ALLOWED_APPS` does not have is an app whose settings are refused by the node with no clue why. """ - node = NODE_SERVER - if not node.exists(): + if not node_tree.available(): pytest.skip("the node package is not in this checkout") m = re.search(r"ALLOWED_APPS = frozenset\(\{([^}]*)\}\)", - node.read_text(encoding="utf-8")) + node_tree.webrtc_source()) assert m, "ALLOWED_APPS moved" allowed = set(re.findall(r"'([^']+)'|\"([^\"]+)\"", m.group(1))) allowed = {a or b for a, b in allowed} @@ -207,9 +201,8 @@ def test_the_directory_op_is_signed_and_names_its_app(): assert "admin_challenge" in body and "_authorizeAdminOp" in body assert "${appKey}:${clean.join(',')}" in body - node = NODE_SERVER - if node.exists(): - assert 'f"{app}:{\',\'.join(clean)}"' in node.read_text(encoding="utf-8"), ( + if node_tree.available(): + assert 'f"{app}:{\',\'.join(clean)}"' in node_tree.webrtc_source(), ( "the node builds a different subject than the client signs") diff --git a/packages/meshbay-hub/tests/test_helloworld_proves_the_plugin_claim.py b/packages/meshbay-hub/tests/test_helloworld_proves_the_plugin_claim.py index 451b57b..7fc9d8e 100644 --- a/packages/meshbay-hub/tests/test_helloworld_proves_the_plugin_claim.py +++ b/packages/meshbay-hub/tests/test_helloworld_proves_the_plugin_claim.py @@ -31,6 +31,7 @@ the code less app-specific rather than more: import re from pathlib import Path +import node_tree import pytest STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" @@ -88,8 +89,7 @@ def test_the_node_names_it_once_and_only_in_the_allow_list(): demonstrate anything. That entry plus the client's registry line is the whole cost. """ - source = (NODE_SRC / "transport" / "webrtc_server.py").read_text( - encoding="utf-8") + source = node_tree.webrtc_source() code = re.sub(r"^\s*#.*$", "", source, flags=re.M) hits = [ln for ln in code.splitlines() if "helloworld" in ln.lower()] assert len(hits) == 1, f"expected one mention, got: {hits}" diff --git a/packages/meshbay-hub/tests/test_video_buffer_ceiling.py b/packages/meshbay-hub/tests/test_video_buffer_ceiling.py index 7dd9c81..a488976 100644 --- a/packages/meshbay-hub/tests/test_video_buffer_ceiling.py +++ b/packages/meshbay-hub/tests/test_video_buffer_ceiling.py @@ -54,12 +54,11 @@ import shutil import subprocess from pathlib import Path +import node_tree import pytest STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" APP = STATIC / "video-player.js" -NODE_SERVER = (Path(__file__).resolve().parents[2] / "meshbay-node" / "src" - / "meshbay_node" / "transport" / "webrtc_server.py") pytestmark = pytest.mark.skipif( shutil.which("node") is None or not APP.exists(), @@ -390,20 +389,17 @@ def test_a_buffered_viewer_still_tells_the_node_it_is_there(app): "the node's stall timeout ends a film that is merely paused") -@pytest.mark.skipif(not NODE_SERVER.exists(), reason="node sources unavailable") +@pytest.mark.skipif(not node_tree.available(), reason="node sources unavailable") def test_the_node_ends_a_stream_on_silence_not_on_stinginess(): """The other half of the keepalive: the node has to honour it.""" - text = NODE_SERVER.read_text() - i = text.index("async def _await_stream_credit") - body = text[i:text.index("\n async def ", i + 1)] + body = node_tree.method("_await_stream_credit") assert "self._stream_heard_at" in body, ( "the stall budget still accumulates over the whole wait, so a keepalive " "that grants no credit cannot keep a paused film alive") assert "waited += STREAM_CREDIT_POLL" not in body, ( "the budget still accumulates over the whole wait rather than being " "measured from the last thing the peer said") - grant = text[text.index("def _grant_stream_credit"):] - grant = grant[:grant.index("\n def ", 1)] + grant = node_tree.method("_grant_stream_credit") assert "self._stream_heard_at = time.monotonic()" in grant, ( "n=0 does not refresh the timeout, so the keepalive is a no-op") diff --git a/packages/meshbay-hub/tests/test_video_seek.py b/packages/meshbay-hub/tests/test_video_seek.py index 85e774d..665970d 100644 --- a/packages/meshbay-hub/tests/test_video_seek.py +++ b/packages/meshbay-hub/tests/test_video_seek.py @@ -33,13 +33,12 @@ import re import shutil from pathlib import Path +import node_tree import pytest STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" APP = STATIC / "video-player.js" TRANSPORT = STATIC / "transport.js" -NODE_SERVER = (Path(__file__).resolve().parents[2] / "meshbay-node" / "src" - / "meshbay_node" / "transport" / "webrtc_server.py") pytestmark = pytest.mark.skipif(not APP.exists(), reason="SPA sources unavailable") @@ -59,11 +58,9 @@ def _player(app: str) -> str: @pytest.fixture(scope="module") def stream_fn(): - if not NODE_SERVER.exists(): + if not node_tree.available(): pytest.skip("node sources unavailable") - text = NODE_SERVER.read_text() - i = text.index("async def _stream_video_inner") - return text[i:text.index("\n async def ", i + 1)] + return node_tree.method("_stream_video_inner") def test_the_seek_is_an_index_lookup_not_a_decode(stream_fn): diff --git a/packages/meshbay-hub/tests/test_video_stream_switch.py b/packages/meshbay-hub/tests/test_video_stream_switch.py index 80d9421..7859057 100644 --- a/packages/meshbay-hub/tests/test_video_stream_switch.py +++ b/packages/meshbay-hub/tests/test_video_stream_switch.py @@ -32,6 +32,7 @@ import shutil import subprocess from pathlib import Path +import node_tree import pytest STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" @@ -149,11 +150,9 @@ def test_stream_messages_are_matched_to_the_file_they_belong_to(app, handler): def test_the_node_actually_stamps_those_messages(): """The guard above is worth nothing if the field is not sent.""" - server = (Path(__file__).resolve().parents[2] / "meshbay-node" / "src" - / "meshbay_node" / "transport" / "webrtc_server.py") - if not server.exists(): + if not node_tree.available(): pytest.skip("the node sources are not available") - text = server.read_text() + text = node_tree.webrtc_source() for const in ("STREAM_INIT", "STREAM_DATA", "STREAM_END"): i = text.index(f"MNP.{const}") block = text[i:i + 400] |