aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/meshbay-hub/tests/harness/chat_send_probe.py10
-rw-r--r--packages/meshbay-hub/tests/harness/index_seal_probe.mjs6
-rw-r--r--packages/meshbay-hub/tests/harness/offer_retry_harness.mjs4
-rw-r--r--packages/meshbay-hub/tests/harness/upload_seal_probe.mjs5
-rw-r--r--packages/meshbay-hub/tests/spa_source.py31
-rw-r--r--packages/meshbay-hub/tests/test_account_pinning.py8
-rw-r--r--packages/meshbay-hub/tests/test_app_settings_plugin.py5
-rw-r--r--packages/meshbay-hub/tests/test_challenge_signature_client.py7
-rw-r--r--packages/meshbay-hub/tests/test_connect_never_hangs.py7
-rw-r--r--packages/meshbay-hub/tests/test_desktop_shell.py5
-rw-r--r--packages/meshbay-hub/tests/test_helloworld_proves_the_plugin_claim.py4
-rw-r--r--packages/meshbay-hub/tests/test_hub_address_seam.py4
-rw-r--r--packages/meshbay-hub/tests/test_index_seal_client.py3
-rw-r--r--packages/meshbay-hub/tests/test_indexing_dock.py3
-rw-r--r--packages/meshbay-hub/tests/test_invite_link_client.py3
-rw-r--r--packages/meshbay-hub/tests/test_offer_retry.py3
-rw-r--r--packages/meshbay-hub/tests/test_reconnect_refresh.py3
-rw-r--r--packages/meshbay-hub/tests/test_rewrap_fanout.py7
-rw-r--r--packages/meshbay-hub/tests/test_search_unlisted.py3
-rw-r--r--packages/meshbay-hub/tests/test_spa_ordering.py7
-rw-r--r--packages/meshbay-hub/tests/test_transfers.py3
-rw-r--r--packages/meshbay-hub/tests/test_transport_contracts.py4
-rw-r--r--packages/meshbay-hub/tests/test_upload_controls_hidden.py7
-rw-r--r--packages/meshbay-hub/tests/test_upload_seal_client.py3
-rw-r--r--packages/meshbay-hub/tests/test_video_audio_track.py3
-rw-r--r--packages/meshbay-hub/tests/test_video_seek.py3
-rw-r--r--packages/meshbay-hub/tests/test_video_subtitles.py3
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: