diff options
Diffstat (limited to 'packages/meshbay-hub/tests')
5 files changed, 9 insertions, 164 deletions
diff --git a/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs b/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs index a6008c2..0fe7554 100644 --- a/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs +++ b/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs @@ -68,7 +68,6 @@ tp._channel = { readyState: 'open', bufferedAmount: 0, send() {}, close() {} }; tp._pc = { close() {} }; tp._gekRaw = hex(input.gek); tp._connectArgs = { groupId: input.group_id }; -tp._nodeVersion = input.node_version; const frames = []; let uploadId = null; diff --git a/packages/meshbay-hub/tests/test_app_settings_plugin.py b/packages/meshbay-hub/tests/test_app_settings_plugin.py index b8afc23..892fd09 100644 --- a/packages/meshbay-hub/tests/test_app_settings_plugin.py +++ b/packages/meshbay-hub/tests/test_app_settings_plugin.py @@ -226,9 +226,9 @@ def test_the_page_performs_exactly_one_app_specific_operation(): # The page's own settings, which belong to no app: which apps are enabled # at all, and how hard the node works watching its disk. page_level = {"setAppsEnabled", "setScanSettings"} - # Both are the same generic operation; the second is what a node too old - # for it understands, chosen by version rather than by app. - generic = {"setAppDirectories", "setAppDirectoriesLegacy"} + # One generic operation, keyed by the app's own name: adding an app adds + # no message type and no call site here. + generic = {"setAppDirectories"} assert calls - page_level == generic, ( f"the settings page performs app-specific operations: " f"{sorted(calls - page_level - generic)}") diff --git a/packages/meshbay-hub/tests/test_mnp_1_0_node_compat.py b/packages/meshbay-hub/tests/test_mnp_1_0_node_compat.py deleted file mode 100644 index b15018b..0000000 --- a/packages/meshbay-hub/tests/test_mnp_1_0_node_compat.py +++ /dev/null @@ -1,144 +0,0 @@ -""" -The page ships before the nodes do. - -The SPA is served by the hub, so deploying the hub puts this version of the -client in front of *every* node, including the ones still running MNP 1.0. That -window is not a corner case — it is the normal state for as long as it takes an -operator to update, and for a node someone else runs it may be indefinite. - -The failure mode is specific and quiet: a node logs an unknown message type and -sends **nothing back**, so a control that speaks MNP 1.1 to it produces a -thirty-second wait ending in a timeout, with nothing on screen to say the node -simply cannot do this. Three of them were like that before these tests: - -* The shared-directories toggles, eject and plug have no older equivalent at - all. -* The per-app folder pickers spoke `app_directories`, where a 1.0 node - understands `video_root` / `audio_root` / `photo_roots`. - -Source-reading, like the other SPA guards. What it cannot check is that the -degraded path is pleasant; what it does check is that each of the three exists. -""" - -import re -from pathlib import Path - -import pytest - -STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" -TRANSPORT = STATIC / "transport.js" -FILES_APP = STATIC / "files-app.js" -GROUP_PAGE = STATIC / "group-page.js" -GROUP_SETTINGS = STATIC / "group-settings.js" - -pytestmark = pytest.mark.skipif(not TRANSPORT.exists(), - reason="SPA sources unavailable") - - -def _component(source: str, name: str) -> str: - start = source.index(f"\nfunction {name}(") - end = source.find("\nfunction ", start + 1) - return source[start:end if end != -1 else len(source)] - - -# ── Knowing which node you are talking to ─────────────────────────────────── - -def test_the_client_keeps_the_version_it_checked(): - """ - `_checkNodeVersion` parsed the node's version and threw it away, so nothing - downstream could ask. Refusing to connect is not the only thing a version - is good for. - """ - source = TRANSPORT.read_text(encoding="utf-8") - assert "this._nodeVersion = String(reply.v" in source - assert "get supportsAppOps()" in source - - -def test_the_capability_reads_the_version_rather_than_guessing(): - """ - Inferring it from whether some field happens to be present is how two - unrelated things end up coupled — the flag would flip because a payload - changed shape for another reason entirely. - """ - source = TRANSPORT.read_text(encoding="utf-8") - getter = source[source.index("get supportsAppOps()"):] - getter = getter[:getter.index("\n }") + 4] - assert "_nodeVersion" in getter - assert "1" in getter, "no version comparison in the capability check" - - -# ── The three degraded paths ──────────────────────────────────────────────── - -def test_app_directories_fall_back_to_the_three_older_messages(): - """ - Videos, Music and Photos each had their own message before the generic op, - and those still work — so an operator on an un-upgraded node keeps the - ability they had rather than being handed a control that times out. - """ - source = TRANSPORT.read_text(encoding="utf-8") - legacy = source[source.index("async setAppDirectoriesLegacy("):] - legacy = legacy[:legacy.index("\n async ", 1)] - for call in ("setPhotoRoots", "setVideoRoot", "setAudioRoot"): - assert call in legacy, f"{call} is not reachable on the older path" - - panel = _component(GROUP_SETTINGS.read_text(encoding="utf-8"), - "GroupSettingsPanel") - assert "transport.supportsAppOps" in panel, ( - "the settings page sends the 1.1 message unconditionally") - - -def test_the_older_path_refuses_what_it_cannot_carry(): - """ - `video_root` and `audio_root` hold one folder. Sending several would store - the first and drop the rest silently, which is worse than refusing — the - operator would see a saved setting that is not what they chose. - """ - source = TRANSPORT.read_text(encoding="utf-8") - legacy = source[source.index("async setAppDirectoriesLegacy("):] - legacy = legacy[:legacy.index("\n async ", 1)] - assert "clean.length > 1" in legacy - assert "throw new Error" in legacy - - -def test_root_management_is_read_only_against_an_older_node(): - """ - Unlike the app directories, `writable`, `removable`, eject and plug have no - older equivalent to route to. The controls are shown without being - offered, with the reason, rather than accepting a click that goes nowhere. - """ - panel = _component(GROUP_SETTINGS.read_text(encoding="utf-8"), - "GroupSettingsPanel") - table_call = panel[panel.index("<${SharedDirectoriesTable}"):] - table_call = table_call[:table_call.index("/>")] - assert "readOnly=" in table_call - assert "nodeSupportsAppOps" in table_call - assert "settings_node.roots_node_too_old" in panel, ( - "nothing says why the controls are inert") - - -def test_chat_settings_are_hidden_rather_than_routed(): - """ - Chat's directory and link-preview switch are new in 1.1 with nothing - before them, so there is no older message to fall back to. - """ - panel = _component(GROUP_SETTINGS.read_text(encoding="utf-8"), - "GroupSettingsPanel") - assert "settings_node.app_node_too_old" in panel - - -# ── Reading an older node's handshake ─────────────────────────────────────── - -def test_the_ack_is_read_in_both_shapes(app=None): - """ - A 1.0 ack has `video_root` and no `video_directories`, and no - `chat_link_preview` at all. Reading a missing plural as "nothing - configured" empties a working Videos tab; reading a missing switch as off - silently changes what a group's chat does. - """ - page = GROUP_PAGE.read_text(encoding="utf-8") - block = page[page.index("setAppDirectories({"):] - block = block[:block.index("setNodeSupportsAppOps")] - for legacy in ("ack.video_root", "ack.audio_root", "ack.photo_roots"): - assert legacy in block, f"{legacy} is not read as a fallback" - assert "ack.chat_link_preview !== false" in page, ( - "an absent link-preview switch must read as on, not off") diff --git a/packages/meshbay-hub/tests/test_transport_contracts.py b/packages/meshbay-hub/tests/test_transport_contracts.py index fe550f9..f2f4372 100644 --- a/packages/meshbay-hub/tests/test_transport_contracts.py +++ b/packages/meshbay-hub/tests/test_transport_contracts.py @@ -372,8 +372,11 @@ def test_the_upload_itself_is_sealed(transport): "the destination is on the message in clear") assert "...sealed," in sent or "...probeSealed," in sent, ( "the message must carry the sealed pair") - assert "supportsSealedUpload" in body, ( - "an older node must be refused before a chunk is sent, not after") + # And no branch that sends anything else: an upload is sealed or it is not + # sent. A fallback here is a fallback the node would have to keep opening. + assert "filename: file.name" not in body.replace( + "msgpack_encode({ filename: file.name", ""), ( + "a filename reaches the message outside the seal") # ── MNP 1.0: the sealed handshake ack ──────────────────────────────────────── diff --git a/packages/meshbay-hub/tests/test_upload_seal_client.py b/packages/meshbay-hub/tests/test_upload_seal_client.py index 2e4bfb5..d233ccd 100644 --- a/packages/meshbay-hub/tests/test_upload_seal_client.py +++ b/packages/meshbay-hub/tests/test_upload_seal_client.py @@ -80,7 +80,7 @@ def _node_session(tmp_path: Path, gek: bytes) -> WebRTCPeerSession: def _probe_input(gek: bytes, mode: str, **extra) -> dict: return { "mode": mode, "gek": gek.hex(), "group_id": GROUP, - "node_version": "2.0", "chunk_size": CHUNK, "dir": "library", + "chunk_size": CHUNK, "dir": "library", "root": "library", "file": {"name": "holiday.jpg", "data": BODY.hex()}, **extra, @@ -156,19 +156,6 @@ def test_the_caller_is_told_the_name_the_node_chose(tmp_path, _gek, _sent): assert result["stored"]["dir"] == "library" -def test_the_client_refuses_an_older_node_before_sending_a_chunk(_gek): - """ - A 1.x node would answer "Missing filename or data" — an error about the - wrong thing, naming no upload, which fails every upload in flight. Asked - first instead, and nothing goes on the wire. - """ - result = _run_probe(_probe_input(_gek, "receive", acks=[], - node_version="1.1")) - assert result["state"] == "rejected" - assert "older MeshBay" in result["message"] - assert result["frames"] == [], "a chunk was sent to a node that cannot open it" - - def test_an_interrupted_upload_resumes_where_the_node_stopped(tmp_path, _gek): """ The browser asks, the node answers, and the second attempt sends only what |