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/harness/upload_seal_probe.mjs1
-rw-r--r--packages/meshbay-hub/tests/test_app_settings_plugin.py6
-rw-r--r--packages/meshbay-hub/tests/test_mnp_1_0_node_compat.py144
-rw-r--r--packages/meshbay-hub/tests/test_transport_contracts.py7
-rw-r--r--packages/meshbay-hub/tests/test_upload_seal_client.py15
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