diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-25 11:09:12 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-25 12:07:49 +0200 |
| commit | 6348698322e5bfcb80d0ef0dcd833ba4e6179640 (patch) | |
| tree | 6d4f47a0a1745ef5ae912368d078cf681b9fcd19 /packages | |
| parent | 71ea15a926d02437a65b8d788834c46f4e6df9a3 (diff) | |
| download | meshbay-6348698322e5bfcb80d0ef0dcd833ba4e6179640.tar.gz | |
test(hub): read the transport wherever it is split
spa_source.transport_files() takes the classic transport*.js scripts
from the hub's shell, in load order. Every test that read transport.js
reads them all, the Node harnesses run them joined as one scope, the
chat probe loads each, and the desktop shell must load them in order.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages')
27 files changed, 118 insertions, 36 deletions
diff --git a/packages/meshbay-hub/tests/harness/chat_send_probe.py b/packages/meshbay-hub/tests/harness/chat_send_probe.py index d343c50..7569628 100644 --- a/packages/meshbay-hub/tests/harness/chat_send_probe.py +++ b/packages/meshbay-hub/tests/harness/chat_send_probe.py @@ -77,6 +77,9 @@ import threading import time from pathlib import Path +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from spa_source import transport_files # noqa: E402 + STATIC = Path(__file__).resolve().parents[2] / "src" / "meshbay_hub" / "static" PORT = 8755 RECORDS = [] @@ -104,7 +107,10 @@ def _page() -> str: .replace("__GROUP_ID__", GROUP_ID) .replace("__GEK_HEX__", GEK.hex()) .replace("__KEYS_NONCE_HEX__", sealed["nonce"].hex()) - .replace("__KEYS_CT_HEX__", sealed["ct"].hex())) + .replace("__KEYS_CT_HEX__", sealed["ct"].hex()) + # transport.js and the parts split out of it, in the shell's order. + .replace("__TRANSPORT_SCRIPTS__", "\n".join( + f'<script src="/{p.name}"></script>' for p in transport_files()))) PAGE_TEMPLATE = r"""<!doctype html><html><head><meta charset=utf-8> <link rel="stylesheet" href="/style.css"></head> @@ -119,7 +125,7 @@ PAGE_TEMPLATE = r"""<!doctype html><html><head><meta charset=utf-8> Without them a send fails with "cannot read properties of undefined". --> <script src="/crypto.js"></script> <script src="/keyderive.js"></script> -<script src="/transport.js"></script> +__TRANSPORT_SCRIPTS__ <script type="module"> import { html, render, useRef, useState, useEffect } from '/vendor/htm-preact.js'; import { ChatPanel } from '/chat-app.js'; diff --git a/packages/meshbay-hub/tests/harness/index_seal_probe.mjs b/packages/meshbay-hub/tests/harness/index_seal_probe.mjs index 36302bb..2493a05 100644 --- a/packages/meshbay-hub/tests/harness/index_seal_probe.mjs +++ b/packages/meshbay-hub/tests/harness/index_seal_probe.mjs @@ -21,6 +21,7 @@ * the outstanding request ended. */ import fs from 'fs'; +import { delimiter } from 'path'; const STATIC = process.argv[2]; const input = JSON.parse(fs.readFileSync(process.argv[3], 'utf8')); @@ -41,7 +42,10 @@ globalThis.document = { }; new Function(fs.readFileSync(`${STATIC}/crypto.js`, 'utf8'))(); -new Function(fs.readFileSync(`${STATIC}/transport.js`, 'utf8'))(); +// argv[4]: transport.js and the parts split out of it, joined as one scope. +const transportSrc = process.argv[4].split(delimiter) + .map((p) => fs.readFileSync(p, 'utf8')).join('\n'); +new Function(transportSrc)(); const hex = (s) => Uint8Array.from(s.match(/../g).map((b) => parseInt(b, 16))); diff --git a/packages/meshbay-hub/tests/harness/offer_retry_harness.mjs b/packages/meshbay-hub/tests/harness/offer_retry_harness.mjs index 3a4b568..3e8233b 100644 --- a/packages/meshbay-hub/tests/harness/offer_retry_harness.mjs +++ b/packages/meshbay-hub/tests/harness/offer_retry_harness.mjs @@ -6,8 +6,10 @@ // // Usage: node offer_retry_harness.mjs <path to transport.js> <json config> import { readFileSync } from 'fs'; +import { delimiter } from 'path'; -const src = readFileSync(process.argv[2], 'utf8'); +// transport.js and the parts split out of it, joined. +const src = process.argv[2].split(delimiter).map((p) => readFileSync(p, 'utf8')).join('\n'); const cfg = JSON.parse(process.argv[3] || '{}'); const { diff --git a/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs b/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs index 0fe7554..60ec48a 100644 --- a/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs +++ b/packages/meshbay-hub/tests/harness/upload_seal_probe.mjs @@ -21,6 +21,7 @@ * seal, so the ciphertext stays exactly the one Python produced. */ import fs from 'fs'; +import { delimiter } from 'path'; const STATIC = process.argv[2]; const input = JSON.parse(fs.readFileSync(process.argv[3], 'utf8')); @@ -38,7 +39,9 @@ globalThis.document = { }; new Function(fs.readFileSync(`${STATIC}/crypto.js`, 'utf8'))(); -const transportSrc = fs.readFileSync(`${STATIC}/transport.js`, 'utf8'); +// argv[4]: transport.js and the parts split out of it, joined as one scope. +const transportSrc = process.argv[4].split(delimiter) + .map((p) => fs.readFileSync(p, 'utf8')).join('\n'); new Function(transportSrc)(); // The msgpack codec is private to transport.js — pulled out the same way the // groupbox parity harness pulls out sealGroup, so this probe encodes and diff --git a/packages/meshbay-hub/tests/spa_source.py b/packages/meshbay-hub/tests/spa_source.py index 7df98ca..cfeae60 100644 --- a/packages/meshbay-hub/tests/spa_source.py +++ b/packages/meshbay-hub/tests/spa_source.py @@ -7,6 +7,7 @@ without looking at it. So the tests that read the search code take it from here, the page and whatever it has been split into. """ +import re from pathlib import Path STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" @@ -26,3 +27,33 @@ def search_argv() -> str: """The same files, as the one argument the Node harnesses take.""" import os return os.pathsep.join(str(p) for p in search_files()) + + +# ── The transport: transport.js and the classic scripts split out of it ────── + +WEBAPP = STATIC.parent / "api" / "webapp.py" +CLASSIC_TAG = re.compile(r'<script src="/a/\{v\}/([^"]+)"></script>') + + +def classic_scripts() -> list[str]: + """The classic (non-module) scripts of the hub's shell, in load order.""" + return CLASSIC_TAG.findall(WEBAPP.read_text(encoding="utf-8")) + + +def transport_files() -> list[Path]: + """Every file of the transport, in the order the shell loads them: the + classic scripts named `transport*.js`. Derived from the shell, so a part + added there is read here without anybody having to list it.""" + files = [STATIC / n for n in classic_scripts() if n.startswith("transport")] + assert files and files[0].name.startswith("transport"), files + return files + + +def transport_source() -> str: + return "\n".join(p.read_text(encoding="utf-8") for p in transport_files()) + + +def transport_argv() -> str: + """The same files, as the one argument the Node harnesses take.""" + import os + return os.pathsep.join(str(p) for p in transport_files()) diff --git a/packages/meshbay-hub/tests/test_account_pinning.py b/packages/meshbay-hub/tests/test_account_pinning.py index a1419c3..529ebd5 100644 --- a/packages/meshbay-hub/tests/test_account_pinning.py +++ b/packages/meshbay-hub/tests/test_account_pinning.py @@ -24,6 +24,7 @@ import pytest from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from meshbay_common.device import device_add_transcript +from spa_source import transport_argv STATIC = (Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static") @@ -56,7 +57,10 @@ globalThis.localStorage = { // crypto.js publishes onto window; transport.js reads it from there. new Function(fs.readFileSync(process.argv[2], 'utf8'))(); const T = new Function( - fs.readFileSync(process.argv[3], 'utf8') + '\nreturn { _verifyRoster };')(); + // transport.js and the parts split out of it, joined as one scope. + process.argv[3].split(require('path').delimiter) + .map((p) => fs.readFileSync(p, 'utf8')).join('\n') + + '\nreturn { _verifyRoster };')(); (async () => { const input = JSON.parse(fs.readFileSync(process.argv[4], 'utf8')); @@ -104,7 +108,7 @@ def _verify(devices, node_pk=NODE_PK): {"payload": {"devices": devices, "node_pk": node_pk}, "node_pk": node_pk})) run = subprocess.run( - ["node", str(h), str(CRYPTO), str(TRANSPORT), str(payload)], + ["node", str(h), str(CRYPTO), transport_argv(), str(payload)], capture_output=True, timeout=60) assert run.returncode == 0, run.stderr.decode()[-2000:] return json.loads(run.stdout.decode()) diff --git a/packages/meshbay-hub/tests/test_app_settings_plugin.py b/packages/meshbay-hub/tests/test_app_settings_plugin.py index a1ecec7..a56d70f 100644 --- a/packages/meshbay-hub/tests/test_app_settings_plugin.py +++ b/packages/meshbay-hub/tests/test_app_settings_plugin.py @@ -17,6 +17,7 @@ from pathlib import Path import node_tree import pytest +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" APPS = STATIC / "apps.js" @@ -195,7 +196,7 @@ def test_the_directory_op_is_signed_and_names_its_app(): indistinguishable — so the app is in the signed subject, and both sides build it the same way. """ - transport = TRANSPORT.read_text(encoding="utf-8") + transport = transport_source() body = transport[transport.index("async setAppDirectories("):] body = body[:body.index("\n async ", 1)] assert "admin_challenge" in body and "_authorizeAdminOp" in body @@ -378,7 +379,7 @@ def test_a_saved_setting_reaches_the_page_that_renders_the_pane(): the broadcast, and the one that asked went on showing an unsaved-looking draft. Clicking Save again just re-sent it. """ - transport = TRANSPORT.read_text(encoding="utf-8") + transport = transport_source() block = transport[transport.index("const BROADCAST_ACK_TYPES"):] block = block[:block.index("]);") + 3] for ack in ("chat_directory_ack", "chat_link_preview_ack", diff --git a/packages/meshbay-hub/tests/test_challenge_signature_client.py b/packages/meshbay-hub/tests/test_challenge_signature_client.py index 90aa5f2..e3c33ac 100644 --- a/packages/meshbay-hub/tests/test_challenge_signature_client.py +++ b/packages/meshbay-hub/tests/test_challenge_signature_client.py @@ -20,6 +20,7 @@ import pytest from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from meshbay_common.crypto import pk_to_b64 from meshbay_common.handshake import challenge_transcript, webrtc_binding +from spa_source import transport_argv STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" @@ -39,7 +40,9 @@ globalThis.document = { }; const STATIC = process.argv[2]; new Function(fs.readFileSync(`${STATIC}/crypto.js`, 'utf8'))(); -const src = fs.readFileSync(`${STATIC}/transport.js`, 'utf8'); +// argv[4]: transport.js and the parts split out of it, joined as one scope. +const src = process.argv[4].split(require('path').delimiter) + .map((p) => fs.readFileSync(p, 'utf8')).join('\n'); const { _challengeProvesNodeKey } = new Function(src + '\nreturn { _challengeProvesNodeKey };')(); @@ -93,7 +96,7 @@ def test_the_browser_holds_the_node_to_its_challenge(tmp_path): harness.write_text(_HARNESS) payload = tmp_path / "cases.json" payload.write_text(json.dumps([c for c, _ in cases.values()])) - proc = subprocess.run(["node", str(harness), str(STATIC), str(payload)], + proc = subprocess.run(["node", str(harness), str(STATIC), str(payload), transport_argv()], capture_output=True, text=True, timeout=60) assert proc.returncode == 0, proc.stderr got = dict(zip(cases, json.loads(proc.stdout))) diff --git a/packages/meshbay-hub/tests/test_connect_never_hangs.py b/packages/meshbay-hub/tests/test_connect_never_hangs.py index d6f1369..5680877 100644 --- a/packages/meshbay-hub/tests/test_connect_never_hangs.py +++ b/packages/meshbay-hub/tests/test_connect_never_hangs.py @@ -22,6 +22,7 @@ import re from pathlib import Path import pytest +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" TRANSPORT = STATIC / "transport.js" @@ -33,7 +34,7 @@ pytestmark = pytest.mark.skipif(not TRANSPORT.exists(), def _gathering_block() -> str: """The wait on ICE gathering, and only it.""" - source = TRANSPORT.read_text(encoding="utf-8") + source = transport_source() start = source.index("iceGatheringState") return source[max(0, start - 900):start + 700] @@ -48,7 +49,7 @@ def test_ice_gathering_has_a_deadline(): def test_the_deadline_is_long_enough_for_a_stun_round_trip(): """Cutting gathering off too early drops the reflexive candidate and breaks every connection that is not on the same network.""" - source = TRANSPORT.read_text(encoding="utf-8") + source = transport_source() match = re.search(r"const ICE_GATHER_TIMEOUT_MS = (\d+);", source) assert match, "the constant is gone or was renamed" assert 2000 <= int(match.group(1)) <= 10000 @@ -57,7 +58,7 @@ def test_the_deadline_is_long_enough_for_a_stun_round_trip(): def test_a_timed_out_gathering_still_sends_the_offer(): """Host candidates are already gathered, which is enough on a LAN. Giving up instead would turn a slow STUN server into a refusal to connect.""" - source = TRANSPORT.read_text(encoding="utf-8") + source = transport_source() block = _gathering_block() # The deadline resolves the promise; it does not reject it. assert "reject" not in block.split("setTimeout", 1)[1][:300] diff --git a/packages/meshbay-hub/tests/test_desktop_shell.py b/packages/meshbay-hub/tests/test_desktop_shell.py index e6eefc2..b9b3319 100644 --- a/packages/meshbay-hub/tests/test_desktop_shell.py +++ b/packages/meshbay-hub/tests/test_desktop_shell.py @@ -18,6 +18,7 @@ import re from pathlib import Path import pytest +from spa_source import transport_files CLIENT = Path(__file__).resolve().parents[2] / "meshbay-client" MAIN = CLIENT / "src" / "main.js" @@ -402,6 +403,10 @@ def test_the_packaged_page_loads_the_shared_modules(): for module in ("keyderive.js", "crypto.js", "transport.js", "app.js", "style.css", "argon2.min.js"): assert module in page, f"{module} is not loaded by the packaged page" + # Every part of the transport, in the order the hub's shell loads them. + parts = [p.name for p in transport_files()] + at = [page.index(f'src="./{name}"') for name in parts] + assert at == sorted(at), f"the packaged page loads the transport out of order: {parts}" assert "/a/" not in page, "the packaged page points at the hub's asset prefix" 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 71a0ea8..9509483 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 @@ -33,6 +33,7 @@ from pathlib import Path import node_tree import pytest +import spa_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" @@ -51,7 +52,8 @@ def _code_only(source: str) -> str: # ── The claim ──────────────────────────────────────────────────────────────── @pytest.mark.parametrize("name", [ - "group-page.js", "group-settings.js", "files-app.js", "transport.js", + "group-page.js", "group-settings.js", "files-app.js", + *[p.name for p in spa_source.transport_files()], "hub-client.js", "settings-ui.js", "folder-tree.js", ]) def test_no_shared_client_file_mentions_it(name): diff --git a/packages/meshbay-hub/tests/test_hub_address_seam.py b/packages/meshbay-hub/tests/test_hub_address_seam.py index ccdac7d..64dcdc9 100644 --- a/packages/meshbay-hub/tests/test_hub_address_seam.py +++ b/packages/meshbay-hub/tests/test_hub_address_seam.py @@ -18,6 +18,7 @@ import re from pathlib import Path import pytest +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" @@ -28,6 +29,9 @@ CALLERS = ["app.js", "keyderive.js", "transport.js", "crypto.js", def _source(name: str) -> str: + # transport.js stands for every part of the transport (spa_source). + if name == "transport.js": + return transport_source() return (STATIC / name).read_text(encoding="utf-8") diff --git a/packages/meshbay-hub/tests/test_index_seal_client.py b/packages/meshbay-hub/tests/test_index_seal_client.py index e1135da..20f89d1 100644 --- a/packages/meshbay-hub/tests/test_index_seal_client.py +++ b/packages/meshbay-hub/tests/test_index_seal_client.py @@ -22,6 +22,7 @@ import msgpack import pytest from meshbay_common.crypto import generate_gek from meshbay_common.groupbox import PURPOSE_INDEX, seal +from spa_source import transport_argv STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" PROBE = Path(__file__).resolve().parent / "harness" / "index_seal_probe.mjs" @@ -70,7 +71,7 @@ def _run(frames: list[str], gek: bytes = GEK) -> dict: vectors.write_text(json.dumps( {"gek": gek.hex(), "group_id": GROUP, "frames": frames})) proc = subprocess.run( - ["node", str(PROBE), str(STATIC), str(vectors)], + ["node", str(PROBE), str(STATIC), str(vectors), transport_argv()], capture_output=True, text=True, timeout=120) if proc.returncode != 0: pytest.fail(f"probe failed:\n{proc.stderr}") diff --git a/packages/meshbay-hub/tests/test_indexing_dock.py b/packages/meshbay-hub/tests/test_indexing_dock.py index ade4173..d69564d 100644 --- a/packages/meshbay-hub/tests/test_indexing_dock.py +++ b/packages/meshbay-hub/tests/test_indexing_dock.py @@ -16,6 +16,7 @@ import subprocess from pathlib import Path import pytest +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" MODEL = STATIC / "index-dock-model.js" @@ -180,7 +181,7 @@ def test_leaving_a_group_clears_its_row(): def test_the_transport_passes_the_new_counters_through(): - source = TRANSPORT.read_text(encoding="utf-8") + source = transport_source() block = source[source.index("msg.type === 'index_progress'"):] block = block[:block.index("return;")] for field in ("files_done", "files_total", "kind", "root_pos", "queued"): diff --git a/packages/meshbay-hub/tests/test_invite_link_client.py b/packages/meshbay-hub/tests/test_invite_link_client.py index be9885a..2223ad8 100644 --- a/packages/meshbay-hub/tests/test_invite_link_client.py +++ b/packages/meshbay-hub/tests/test_invite_link_client.py @@ -28,6 +28,7 @@ from pathlib import Path import pytest from meshbay_hub import mail as mail_mod from meshbay_hub.api import invite_links +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" LINK_JS = STATIC / "invite-link.js" @@ -144,7 +145,7 @@ def test_the_code_leaves_the_address_and_stays_in_the_tab(tmp_path): def test_a_link_code_goes_to_the_node_the_link_names_and_no_other(tmp_path): - src = (STATIC / "transport.js").read_text(encoding="utf-8") + src = transport_source() fn = re.search(r"^function _linkJoinRefusal\(.*?^\}", src, re.M | re.S) assert fn, "transport.js no longer has _linkJoinRefusal" got = _run(tmp_path, fn.group(0) + """ diff --git a/packages/meshbay-hub/tests/test_offer_retry.py b/packages/meshbay-hub/tests/test_offer_retry.py index 3c968db..6389f8e 100644 --- a/packages/meshbay-hub/tests/test_offer_retry.py +++ b/packages/meshbay-hub/tests/test_offer_retry.py @@ -19,6 +19,7 @@ import subprocess from pathlib import Path import pytest +from spa_source import transport_argv STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" TRANSPORT = STATIC / "transport.js" @@ -31,7 +32,7 @@ pytestmark = pytest.mark.skipif( def _post(**cfg) -> dict: proc = subprocess.run( - ["node", str(HARNESS), str(TRANSPORT), json.dumps(cfg)], + ["node", str(HARNESS), transport_argv(), json.dumps(cfg)], capture_output=True, text=True) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) diff --git a/packages/meshbay-hub/tests/test_reconnect_refresh.py b/packages/meshbay-hub/tests/test_reconnect_refresh.py index c93e94d..ff6d7fb 100644 --- a/packages/meshbay-hub/tests/test_reconnect_refresh.py +++ b/packages/meshbay-hub/tests/test_reconnect_refresh.py @@ -25,6 +25,7 @@ import re from pathlib import Path import pytest +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" TRANSPORT = STATIC / "transport.js" @@ -37,7 +38,7 @@ pytestmark = pytest.mark.skipif( @pytest.fixture(scope="module") def transport(): - return TRANSPORT.read_text() + return transport_source() @pytest.fixture(scope="module") diff --git a/packages/meshbay-hub/tests/test_rewrap_fanout.py b/packages/meshbay-hub/tests/test_rewrap_fanout.py index 54ef67a..7c0f272 100644 --- a/packages/meshbay-hub/tests/test_rewrap_fanout.py +++ b/packages/meshbay-hub/tests/test_rewrap_fanout.py @@ -16,6 +16,7 @@ import subprocess from pathlib import Path import pytest +from spa_source import transport_argv STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" TRANSPORT = STATIC / "transport.js" @@ -40,7 +41,9 @@ global.localStorage = { // connect() is stubbed on the prototype below, so no WebRTC shim is needed. global.RTCPeerConnection = function () { throw new Error('connect() not stubbed'); }; -eval(fs.readFileSync(process.argv[2], 'utf8')); +// transport.js and the parts split out of it, joined as one scope. +eval(process.argv[2].split(require('path').delimiter) + .map((p) => fs.readFileSync(p, 'utf8')).join('\n')); const T = window.MeshBayTransport; let deriveEncCalls = 0; @@ -153,7 +156,7 @@ def result(tmp_path_factory): harness = d / "harness.cjs" harness.write_text(_HARNESS) proc = subprocess.run( - ["node", str(harness), str(TRANSPORT)], + ["node", str(harness), transport_argv()], capture_output=True, text=True, timeout=120, ) if proc.returncode != 0: diff --git a/packages/meshbay-hub/tests/test_search_unlisted.py b/packages/meshbay-hub/tests/test_search_unlisted.py index 94f3aee..430a2bb 100644 --- a/packages/meshbay-hub/tests/test_search_unlisted.py +++ b/packages/meshbay-hub/tests/test_search_unlisted.py @@ -16,6 +16,7 @@ import re from pathlib import Path import pytest +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" SEARCH_PAGE = STATIC / "search-page.js" @@ -76,7 +77,7 @@ def test_every_search_view_is_built_from_the_indexed_groups_only(): def test_the_operator_hears_back_from_their_own_change(): """Same failure as Chat's directory: an admin ack swallowed by its request.""" - transport = TRANSPORT.read_text(encoding="utf-8") + transport = transport_source() block = transport[transport.index("const BROADCAST_ACK_TYPES"):] assert "'search_listed_ack'" in block[:block.index("]);")] replay = transport[transport.index("function _replayBroadcast"):] diff --git a/packages/meshbay-hub/tests/test_spa_ordering.py b/packages/meshbay-hub/tests/test_spa_ordering.py index 1c36d09..087298f 100644 --- a/packages/meshbay-hub/tests/test_spa_ordering.py +++ b/packages/meshbay-hub/tests/test_spa_ordering.py @@ -20,6 +20,7 @@ that nothing reads a value assigned later — and then move the markers. from pathlib import Path import pytest +from spa_source import transport_source STATIC = (Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static") @@ -30,7 +31,7 @@ pytestmark = pytest.mark.skipif( def _positions(*needles: str) -> list[int]: - source = TRANSPORT.read_text() + source = transport_source() out = [] for needle in needles: idx = source.find(needle) @@ -96,7 +97,7 @@ def test_the_ack_still_verifies_the_announced_node_key(): the client compares the two. Losing that check would leave the announcement trusted on its own. """ - source = TRANSPORT.read_text() + source = transport_source() assert "Node identity changed during the handshake" in source, ( "the challenge's node_pk must be checked against the ack's") assert "verifyNodeSignature" in source, ( @@ -214,7 +215,7 @@ def test_admin_page_does_not_borrow_the_group_settings_state(): # waiting for every ack, 3.47 MB/s with 32 chunks in flight. def test_the_uploader_keeps_several_chunks_in_flight(): - src = TRANSPORT.read_text() + src = transport_source() body = src[src.index("async uploadFile("):] body = body[:body.index("\n async ", 1)] assert "UPLOAD_WINDOW" in body, "the send window is gone — uploads are serial again" diff --git a/packages/meshbay-hub/tests/test_transfers.py b/packages/meshbay-hub/tests/test_transfers.py index 9b65ca5..b1bac4a 100644 --- a/packages/meshbay-hub/tests/test_transfers.py +++ b/packages/meshbay-hub/tests/test_transfers.py @@ -14,6 +14,7 @@ import subprocess from pathlib import Path import pytest +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" TRANSFERS = STATIC / "transfers.js" @@ -381,7 +382,7 @@ def test_asking_for_a_slot_on_a_dead_channel_does_not_throw(tmp_path): module = tmp_path / "transport_lease.mjs" # The real Lease, lifted out as text — the class is not exported, and a # second copy of it here would agree with whatever it was copied from. - src = (STATIC / "transport.js").read_text() + src = transport_source() # From the constant the class depends on, not from the class: lifting only # the class left LEASE_WATCHDOG_MS undefined, which the class reads the # first time it arms its watchdog. diff --git a/packages/meshbay-hub/tests/test_transport_contracts.py b/packages/meshbay-hub/tests/test_transport_contracts.py index 76e8123..978c2e5 100644 --- a/packages/meshbay-hub/tests/test_transport_contracts.py +++ b/packages/meshbay-hub/tests/test_transport_contracts.py @@ -19,7 +19,7 @@ import re from pathlib import Path import pytest -from spa_source import search_source +from spa_source import search_source, transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" TRANSPORT = STATIC / "transport.js" @@ -54,7 +54,7 @@ pytestmark = pytest.mark.skipif( @pytest.fixture(scope="module") def transport(): - return TRANSPORT.read_text() + return transport_source() @pytest.fixture(scope="module") diff --git a/packages/meshbay-hub/tests/test_upload_controls_hidden.py b/packages/meshbay-hub/tests/test_upload_controls_hidden.py index a2f9352..6316e44 100644 --- a/packages/meshbay-hub/tests/test_upload_controls_hidden.py +++ b/packages/meshbay-hub/tests/test_upload_controls_hidden.py @@ -23,6 +23,7 @@ import re from pathlib import Path import pytest +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" APP = STATIC / "app.js" @@ -202,7 +203,7 @@ def test_a_change_reaches_people_already_connected(app): list somebody clicks. """ assert "transport.onRootsChanged" in app - transport = TRANSPORT.read_text(encoding="utf-8") + transport = transport_source() assert "root_eject_ack" in transport, "nothing routes the node's notice" @@ -219,7 +220,7 @@ def test_the_notice_also_answers_the_operators_own_request(): again on Chat's directory — where it meant the pane went on showing an unsaved-looking draft after a save that had worked. """ - transport = TRANSPORT.read_text(encoding="utf-8") + transport = transport_source() block = transport[transport.index("msg.type.endsWith('_ack')"):] block = block[:block.index("_uploaders")] assert "BROADCAST_ACK_TYPES" in block and "_replayBroadcast" in block, ( @@ -229,7 +230,7 @@ def test_the_notice_also_answers_the_operators_own_request(): # ── Changing it ───────────────────────────────────────────────────────────── def test_changing_a_root_is_signed(): - transport = TRANSPORT.read_text(encoding="utf-8") + transport = transport_source() for method in ("updateRoot", "ejectRoot", "plugRoot"): body = transport[transport.index(f"async {method}("):] body = body[:body.index("\n async ", 1)] diff --git a/packages/meshbay-hub/tests/test_upload_seal_client.py b/packages/meshbay-hub/tests/test_upload_seal_client.py index 63be6e3..bcda14c 100644 --- a/packages/meshbay-hub/tests/test_upload_seal_client.py +++ b/packages/meshbay-hub/tests/test_upload_seal_client.py @@ -30,6 +30,7 @@ from meshbay_common.protocol import MNP from meshbay_node.indexer.group_index import GroupIndex from meshbay_node.roots import RootSet from meshbay_node.transport.webrtc_server import WebRTCPeerSession +from spa_source import transport_argv STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" PROBE = Path(__file__).resolve().parent / "harness" / "upload_seal_probe.mjs" @@ -49,7 +50,7 @@ def _run_probe(payload: dict) -> dict: f = Path(d) / "input.json" f.write_text(json.dumps(payload)) proc = subprocess.run( - ["node", str(PROBE), str(STATIC), str(f)], + ["node", str(PROBE), str(STATIC), str(f), transport_argv()], capture_output=True, text=True, timeout=60, ) if proc.returncode != 0 or not proc.stdout: diff --git a/packages/meshbay-hub/tests/test_video_audio_track.py b/packages/meshbay-hub/tests/test_video_audio_track.py index 6eb98ba..54beca2 100644 --- a/packages/meshbay-hub/tests/test_video_audio_track.py +++ b/packages/meshbay-hub/tests/test_video_audio_track.py @@ -29,6 +29,7 @@ import subprocess from pathlib import Path import pytest +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" APP = STATIC / "video-player.js" @@ -182,7 +183,7 @@ def test_no_track_is_ever_sent_to_a_node_that_did_not_offer_one(): makes that reachable, so the field has to be absent unless the caller was given a real one. """ - src = TRANSPORT.read_text() + src = transport_source() fn = src[src.index("requestStream(fileId"):] fn = fn[:fn.index("\n /** Room for")] assert "Number.isInteger(audioTrack)" in fn, \ diff --git a/packages/meshbay-hub/tests/test_video_seek.py b/packages/meshbay-hub/tests/test_video_seek.py index 665970d..9436065 100644 --- a/packages/meshbay-hub/tests/test_video_seek.py +++ b/packages/meshbay-hub/tests/test_video_seek.py @@ -35,6 +35,7 @@ from pathlib import Path import node_tree import pytest +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" APP = STATIC / "video-player.js" @@ -98,7 +99,7 @@ def test_seeking_past_the_end_is_pulled_back(stream_fn): def test_the_request_carries_it(): - text = TRANSPORT.read_text() + text = transport_source() fn = text[text.index("requestStream(fileId"):] fn = fn[:fn.index("\n }")] assert "start" in fn, "requestStream cannot express a seek" diff --git a/packages/meshbay-hub/tests/test_video_subtitles.py b/packages/meshbay-hub/tests/test_video_subtitles.py index 7fdc9cd..5a4b215 100644 --- a/packages/meshbay-hub/tests/test_video_subtitles.py +++ b/packages/meshbay-hub/tests/test_video_subtitles.py @@ -34,6 +34,7 @@ import subprocess from pathlib import Path import pytest +from spa_source import transport_source STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" APP = STATIC / "video-player.js" @@ -51,7 +52,7 @@ def app(): @pytest.fixture(scope="module") def transport(): - return TRANSPORT.read_text() + return transport_source() def _lift(src: str, name: str) -> str: |