diff options
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: |