diff options
Diffstat (limited to 'packages/meshbay-hub/tests/test_mnp_1_0_node_compat.py')
| -rw-r--r-- | packages/meshbay-hub/tests/test_mnp_1_0_node_compat.py | 144 |
1 files changed, 0 insertions, 144 deletions
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") |