aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests')
-rw-r--r--packages/meshbay-hub/tests/node_tree.py54
-rw-r--r--packages/meshbay-hub/tests/test_app_settings_plugin.py17
-rw-r--r--packages/meshbay-hub/tests/test_helloworld_proves_the_plugin_claim.py4
-rw-r--r--packages/meshbay-hub/tests/test_video_buffer_ceiling.py12
-rw-r--r--packages/meshbay-hub/tests/test_video_seek.py9
-rw-r--r--packages/meshbay-hub/tests/test_video_stream_switch.py7
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]