aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-27 22:21:26 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-27 22:21:26 +0200
commita45e77df1b0024707914a446aa89d33baa223787 (patch)
tree969a4976c91d7aaa27b11ba212f99d69e2ccce0f /packages/meshbay-hub
parentc7899e2218af26f3bab6a157c70acf5eb7871da6 (diff)
downloadmeshbay-a45e77df1b0024707914a446aa89d33baa223787.tar.gz
test: make both suites pass on Windows
Most of these failed on Windows for reasons that had nothing to do with the code under test, which is how real Windows defects hid among them: - Read and write files as UTF-8, and talk to Node in UTF-8. read_text(), write_text() and subprocess text=True use the locale codepage, cp1252 on Windows: "é", "—" and "→" arrived as "?" or crashed, some sixty tests. Calls to PowerShell and schtasks are left alone -- they answer in the console codepage. - Import ESM harness modules by file URL (as_uri): a raw "C:\..." path is not a module specifier. - test_cli_golden: mask the tmp path in its JSON-escaped form, spell it the POSIX way, record on Linux, mask the protocol version (the recording had failed everywhere since the MNP 4.0 bump) and argparse's version-dependent quoting; point USERPROFILE at the tmp home, or `member invite` and `operator pair` wrote their codes into the developer's profile. - test_disk_io_off_loop: expect what a free loop can reach on the platform's timer, 15.6 ms on Windows, not an assumed 5 ms. - test_root_paths_are_operator_only: expect the OS's spelling of the path. - test_audio_meta_cache: find ffprobe with shutil.which. Node suite on Windows: 1489 passed, none failed. Hub suite: 3 failures left, all older than this change (two SQLite concurrency tests, one transfer resume). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub')
-rw-r--r--packages/meshbay-hub/tests/harness/layout_probe.py2
-rw-r--r--packages/meshbay-hub/tests/harness/scroll_probe.py2
-rw-r--r--packages/meshbay-hub/tests/test_account_pinning.py4
-rw-r--r--packages/meshbay-hub/tests/test_argon2_off_loop.py2
-rw-r--r--packages/meshbay-hub/tests/test_asset_versioning.py2
-rw-r--r--packages/meshbay-hub/tests/test_availability_between_members.py2
-rw-r--r--packages/meshbay-hub/tests/test_browser_idle_signout.py2
-rw-r--r--packages/meshbay-hub/tests/test_bundle_kdf_parity.py8
-rw-r--r--packages/meshbay-hub/tests/test_captcha_host_check.py9
-rw-r--r--packages/meshbay-hub/tests/test_cast_subtitles.py6
-rw-r--r--packages/meshbay-hub/tests/test_challenge_signature_client.py6
-rw-r--r--packages/meshbay-hub/tests/test_chat_scroll_bottom.py2
-rw-r--r--packages/meshbay-hub/tests/test_client_version_gate.py8
-rw-r--r--packages/meshbay-hub/tests/test_downloads.py54
-rw-r--r--packages/meshbay-hub/tests/test_files_drop_upload.py2
-rw-r--r--packages/meshbay-hub/tests/test_files_sorting.py2
-rw-r--r--packages/meshbay-hub/tests/test_hook_ordering.py6
-rw-r--r--packages/meshbay-hub/tests/test_index_seal_client.py4
-rw-r--r--packages/meshbay-hub/tests/test_indexing_dock.py8
-rw-r--r--packages/meshbay-hub/tests/test_invite_link_client.py4
-rw-r--r--packages/meshbay-hub/tests/test_layout_measured.py4
-rw-r--r--packages/meshbay-hub/tests/test_layout_responsive.py4
-rw-r--r--packages/meshbay-hub/tests/test_locales.py11
-rw-r--r--packages/meshbay-hub/tests/test_media_pager.py4
-rw-r--r--packages/meshbay-hub/tests/test_member_removal.py2
-rw-r--r--packages/meshbay-hub/tests/test_memory_ceiling.py8
-rw-r--r--packages/meshbay-hub/tests/test_music_queue.py2
-rw-r--r--packages/meshbay-hub/tests/test_node_page_width_measured.py2
-rw-r--r--packages/meshbay-hub/tests/test_notification_dismissal.py4
-rw-r--r--packages/meshbay-hub/tests/test_offer_retry.py2
-rw-r--r--packages/meshbay-hub/tests/test_playlist_crypto.py6
-rw-r--r--packages/meshbay-hub/tests/test_playlist_key.py6
-rw-r--r--packages/meshbay-hub/tests/test_playlist_merge.py6
-rw-r--r--packages/meshbay-hub/tests/test_queue_ops.py6
-rw-r--r--packages/meshbay-hub/tests/test_reconnect_refresh.py12
-rw-r--r--packages/meshbay-hub/tests/test_recovery_key.py4
-rw-r--r--packages/meshbay-hub/tests/test_resume_position.py2
-rw-r--r--packages/meshbay-hub/tests/test_rewrap_fanout.py4
-rw-r--r--packages/meshbay-hub/tests/test_search_connect_deadline.py2
-rw-r--r--packages/meshbay-hub/tests/test_search_fanout.py2
-rw-r--r--packages/meshbay-hub/tests/test_search_files_unmerged.py2
-rw-r--r--packages/meshbay-hub/tests/test_search_media_merge.py10
-rw-r--r--packages/meshbay-hub/tests/test_search_pool.py2
-rw-r--r--packages/meshbay-hub/tests/test_search_source_merge.py6
-rw-r--r--packages/meshbay-hub/tests/test_season_panel_placement.py10
-rw-r--r--packages/meshbay-hub/tests/test_session_renewal.py14
-rw-r--r--packages/meshbay-hub/tests/test_site_basics.py2
-rw-r--r--packages/meshbay-hub/tests/test_spa_ordering.py12
-rw-r--r--packages/meshbay-hub/tests/test_spa_syntax.py6
-rw-r--r--packages/meshbay-hub/tests/test_streamed_download_reliability.py14
-rw-r--r--packages/meshbay-hub/tests/test_table_rows_measured.py2
-rw-r--r--packages/meshbay-hub/tests/test_transfers.py34
-rw-r--r--packages/meshbay-hub/tests/test_transport_contracts.py12
-rw-r--r--packages/meshbay-hub/tests/test_upload_seal_client.py4
-rw-r--r--packages/meshbay-hub/tests/test_versions_agree.py6
-rw-r--r--packages/meshbay-hub/tests/test_video_audio_track.py6
-rw-r--r--packages/meshbay-hub/tests/test_video_buffer_ceiling.py6
-rw-r--r--packages/meshbay-hub/tests/test_video_default_season.py6
-rw-r--r--packages/meshbay-hub/tests/test_video_detail_measured.py4
-rw-r--r--packages/meshbay-hub/tests/test_video_reconnect.py4
-rw-r--r--packages/meshbay-hub/tests/test_video_seek.py12
-rw-r--r--packages/meshbay-hub/tests/test_video_stream_switch.py6
-rw-r--r--packages/meshbay-hub/tests/test_video_subtitles.py6
-rw-r--r--packages/meshbay-hub/tests/test_zip_size_limit.py6
-rw-r--r--packages/meshbay-hub/tests/test_zipstream.py16
65 files changed, 219 insertions, 217 deletions
diff --git a/packages/meshbay-hub/tests/harness/layout_probe.py b/packages/meshbay-hub/tests/harness/layout_probe.py
index 65b3083..0abbda6 100644
--- a/packages/meshbay-hub/tests/harness/layout_probe.py
+++ b/packages/meshbay-hub/tests/harness/layout_probe.py
@@ -81,7 +81,7 @@ RECORDS = []
def main() -> int:
widths = [int(w) for w in sys.argv[1].split(",")]
- fragment = Path(sys.argv[2]).read_text()
+ fragment = Path(sys.argv[2]).read_text(encoding="utf-8")
selectors = sys.argv[3:]
class H(http.server.BaseHTTPRequestHandler):
diff --git a/packages/meshbay-hub/tests/harness/scroll_probe.py b/packages/meshbay-hub/tests/harness/scroll_probe.py
index ae407b8..55d5b2b 100644
--- a/packages/meshbay-hub/tests/harness/scroll_probe.py
+++ b/packages/meshbay-hub/tests/harness/scroll_probe.py
@@ -32,7 +32,7 @@ STATIC = Path(__file__).resolve().parents[2] / "src" / "meshbay_hub" / "static"
# group-page refactor.
APP = STATIC / "chat-app.js"
PORT = 8736
-FRAG = Path(sys.argv[1]).read_text()
+FRAG = Path(sys.argv[1]).read_text(encoding="utf-8")
HEIGHTS = ([int(h) for h in sys.argv[2].split(",")]
if len(sys.argv) > 2 else [700, 900, 1200])
diff --git a/packages/meshbay-hub/tests/test_account_pinning.py b/packages/meshbay-hub/tests/test_account_pinning.py
index 529ebd5..ebd29bd 100644
--- a/packages/meshbay-hub/tests/test_account_pinning.py
+++ b/packages/meshbay-hub/tests/test_account_pinning.py
@@ -102,11 +102,11 @@ def _entry(user, pk_ed, pk_x="cGtY", *, added_by="", sk_signer=None,
def _verify(devices, node_pk=NODE_PK):
with tempfile.TemporaryDirectory() as tmp:
h = Path(tmp) / "h.js"
- h.write_text(_HARNESS)
+ h.write_text(_HARNESS, encoding="utf-8")
payload = Path(tmp) / "in.json"
payload.write_text(json.dumps(
{"payload": {"devices": devices, "node_pk": node_pk},
- "node_pk": node_pk}))
+ "node_pk": node_pk}), encoding="utf-8")
run = subprocess.run(
["node", str(h), str(CRYPTO), transport_argv(), str(payload)],
capture_output=True, timeout=60)
diff --git a/packages/meshbay-hub/tests/test_argon2_off_loop.py b/packages/meshbay-hub/tests/test_argon2_off_loop.py
index 5c25a8e..ae6cc08 100644
--- a/packages/meshbay-hub/tests/test_argon2_off_loop.py
+++ b/packages/meshbay-hub/tests/test_argon2_off_loop.py
@@ -28,7 +28,7 @@ def test_nothing_derives_argon2_on_the_event_loop():
for path in SRC.rglob("*.py"):
if path.name == "auth.py":
continue
- for n, line in enumerate(path.read_text().splitlines(), 1):
+ for n, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
if direct.search(line):
offenders.append(f"{path.relative_to(SRC)}:{n}: {line.strip()}")
assert not offenders, (
diff --git a/packages/meshbay-hub/tests/test_asset_versioning.py b/packages/meshbay-hub/tests/test_asset_versioning.py
index aa2dc6d..7057311 100644
--- a/packages/meshbay-hub/tests/test_asset_versioning.py
+++ b/packages/meshbay-hub/tests/test_asset_versioning.py
@@ -135,7 +135,7 @@ def test_a_new_file_moves_the_fingerprint():
before = _asset_version()
extra = STATIC_DIR / "locales" / "zz-test-only.js"
try:
- extra.write_text("export default {};\n")
+ extra.write_text("export default {};\n", encoding="utf-8")
assert _asset_version() != before
finally:
extra.unlink()
diff --git a/packages/meshbay-hub/tests/test_availability_between_members.py b/packages/meshbay-hub/tests/test_availability_between_members.py
index cd10046..24d85a0 100644
--- a/packages/meshbay-hub/tests/test_availability_between_members.py
+++ b/packages/meshbay-hub/tests/test_availability_between_members.py
@@ -537,7 +537,7 @@ def test_no_mail_is_sent_from_the_event_loop():
for path in root.rglob("*.py"):
if path.name == "mail.py":
continue
- for n, line in enumerate(path.read_text().splitlines(), 1):
+ for n, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
if direct_call.search(line):
offenders.append(f"{path.name}:{n}: {line.strip()}")
assert not offenders, (
diff --git a/packages/meshbay-hub/tests/test_browser_idle_signout.py b/packages/meshbay-hub/tests/test_browser_idle_signout.py
index 50f7f04..454874a 100644
--- a/packages/meshbay-hub/tests/test_browser_idle_signout.py
+++ b/packages/meshbay-hub/tests/test_browser_idle_signout.py
@@ -84,7 +84,7 @@ def outcome():
pytest.skip("node is not available")
proc = subprocess.run(
[NODE, "--input-type=module", "--eval", HARNESS, IDLE.as_uri()],
- capture_output=True, text=True, timeout=30)
+ capture_output=True, text=True, encoding="utf-8", timeout=30)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout.strip().splitlines()[-1])
diff --git a/packages/meshbay-hub/tests/test_bundle_kdf_parity.py b/packages/meshbay-hub/tests/test_bundle_kdf_parity.py
index 27e10d4..c62aace 100644
--- a/packages/meshbay-hub/tests/test_bundle_kdf_parity.py
+++ b/packages/meshbay-hub/tests/test_bundle_kdf_parity.py
@@ -77,19 +77,19 @@ const argon2 = require(process.argv[3]);
def js_hashes(tmp_path_factory):
d = tmp_path_factory.mktemp("kdf")
harness = d / "harness.cjs"
- harness.write_text(_HARNESS)
+ harness.write_text(_HARNESS, encoding="utf-8")
vectors = [
{"username": u, "password": p,
"mem": MEM_KIB, "time": TIME_COST, "lanes": LANES}
for u in CASES for p in PASSWORDS
]
payload = d / "vectors.json"
- payload.write_text(json.dumps(vectors))
+ payload.write_text(json.dumps(vectors), encoding="utf-8")
proc = subprocess.run(
["node", str(harness), str(VENDOR / "argon2.wasm"),
str(VENDOR / "argon2.min.js"), str(payload)],
- capture_output=True, text=True, timeout=300,
+ capture_output=True, text=True, encoding="utf-8", timeout=300,
)
if proc.returncode != 0:
pytest.fail(f"node harness failed:\n{proc.stderr[-2000:]}")
@@ -124,7 +124,7 @@ def test_parameters_still_match_the_client():
The numbers live in keyderive.js; this test is the second copy. Tuning one
without the other orphans every bundle already written, so make it fail.
"""
- source = (STATIC / "keyderive.js").read_text()
+ source = (STATIC / "keyderive.js").read_text(encoding="utf-8")
assert f"ARGON2_MEM_KIB = {MEM_KIB}" in source
assert f"ARGON2_TIME = {TIME_COST}" in source
assert f"ARGON2_LANES = {LANES}" in source
diff --git a/packages/meshbay-hub/tests/test_captcha_host_check.py b/packages/meshbay-hub/tests/test_captcha_host_check.py
index 778009f..a0ff509 100644
--- a/packages/meshbay-hub/tests/test_captcha_host_check.py
+++ b/packages/meshbay-hub/tests/test_captcha_host_check.py
@@ -208,7 +208,7 @@ def test_hub_toml_carries_the_allowed_hosts(tmp_path):
'[captcha]\n'
'site_key = "6Lsite"\n'
'secret_key = "6Lsecret"\n'
- 'allowed_hosts = ["meshbay.org", " meshbay ", "", "localhost"]\n')
+ 'allowed_hosts = ["meshbay.org", " meshbay ", "", "localhost"]\n', encoding="utf-8")
cfg = load_config(cfg_file)
@@ -222,7 +222,8 @@ def test_a_hub_toml_without_the_key_checks_nothing(tmp_path):
"""The upgrade path. A hub that never heard of this setting keeps the
behaviour it has, with reCAPTCHA doing the origin check."""
cfg_file = tmp_path / "hub.toml"
- cfg_file.write_text('[captcha]\nsite_key = "6Lsite"\nsecret_key = "6Lsecret"\n')
+ cfg_file.write_text('[captcha]\nsite_key = "6Lsite"\nsecret_key = "6Lsecret"\n',
+ encoding="utf-8")
assert load_config(cfg_file).captcha.host_check is None
@@ -231,10 +232,10 @@ def test_the_unattributed_flag_comes_from_the_config(tmp_path, monkeypatch):
cfg_file = tmp_path / "hub.toml"
cfg_file.write_text(
'[captcha]\nsite_key = "k"\nsecret_key = "s"\n'
- 'allowed_hosts = ["meshbay.org"]\nallow_unattributed_host = true\n')
+ 'allowed_hosts = ["meshbay.org"]\nallow_unattributed_host = true\n', encoding="utf-8")
assert load_config(cfg_file).captcha.allow_unattributed_host is True
- cfg_file.write_text('[captcha]\nsite_key = "k"\nsecret_key = "s"\n')
+ cfg_file.write_text('[captcha]\nsite_key = "k"\nsecret_key = "s"\n', encoding="utf-8")
assert load_config(cfg_file).captcha.allow_unattributed_host is False, (
"the default has to stay off — it is the looser of the two")
diff --git a/packages/meshbay-hub/tests/test_cast_subtitles.py b/packages/meshbay-hub/tests/test_cast_subtitles.py
index fdf7fcc..bdc279b 100644
--- a/packages/meshbay-hub/tests/test_cast_subtitles.py
+++ b/packages/meshbay-hub/tests/test_cast_subtitles.py
@@ -66,7 +66,7 @@ def _run(tmp_path, body: str, *args: str):
+ "\n" + body, encoding="utf-8")
proc = subprocess.run(
["node", str(script), *args],
- capture_output=True, text=True, timeout=30)
+ capture_output=True, text=True, encoding="utf-8", timeout=30)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
@@ -260,7 +260,7 @@ def served(tmp_path_factory):
script.write_text(RELAY_SCRIPT, encoding="utf-8")
proc = subprocess.run(
["node", str(script), str(RELAY)],
- capture_output=True, text=True, timeout=60)
+ capture_output=True, text=True, encoding="utf-8", timeout=60)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout.strip().splitlines()[-1])
@@ -363,7 +363,7 @@ def loaded(tmp_path_factory):
script.write_text(CHROMECAST_SCRIPT, encoding="utf-8")
proc = subprocess.run(
["node", str(script), str(CHROMECAST)],
- capture_output=True, text=True, timeout=60)
+ capture_output=True, text=True, encoding="utf-8", timeout=60)
if proc.returncode != 0:
pytest.skip(f"cast-chromecast.js is not loadable here: {proc.stderr}")
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_challenge_signature_client.py b/packages/meshbay-hub/tests/test_challenge_signature_client.py
index e3c33ac..b904698 100644
--- a/packages/meshbay-hub/tests/test_challenge_signature_client.py
+++ b/packages/meshbay-hub/tests/test_challenge_signature_client.py
@@ -93,11 +93,11 @@ def test_the_browser_holds_the_node_to_its_challenge(tmp_path):
"garbage for a signature": (case(sig=b"\x00" * 64), "refused"),
}
harness = tmp_path / "harness.js"
- harness.write_text(_HARNESS)
+ harness.write_text(_HARNESS, encoding="utf-8")
payload = tmp_path / "cases.json"
- payload.write_text(json.dumps([c for c, _ in cases.values()]))
+ payload.write_text(json.dumps([c for c, _ in cases.values()]), encoding="utf-8")
proc = subprocess.run(["node", str(harness), str(STATIC), str(payload), transport_argv()],
- capture_output=True, text=True, timeout=60)
+ capture_output=True, text=True, encoding="utf-8", timeout=60)
assert proc.returncode == 0, proc.stderr
got = dict(zip(cases, json.loads(proc.stdout)))
assert got == {name: want for name, (_, want) in cases.items()}
diff --git a/packages/meshbay-hub/tests/test_chat_scroll_bottom.py b/packages/meshbay-hub/tests/test_chat_scroll_bottom.py
index 7b66c00..42b1055 100644
--- a/packages/meshbay-hub/tests/test_chat_scroll_bottom.py
+++ b/packages/meshbay-hub/tests/test_chat_scroll_bottom.py
@@ -42,7 +42,7 @@ pytestmark = pytest.mark.skipif(not CHAT.exists(), reason="SPA sources not prese
def _chat_panel_source() -> str:
- src = CHAT.read_text()
+ src = CHAT.read_text(encoding="utf-8")
start = src.index("\nfunction ChatPanel(")
end = src.find("\nfunction ", start + 1)
return src[start:end if end != -1 else len(src)]
diff --git a/packages/meshbay-hub/tests/test_client_version_gate.py b/packages/meshbay-hub/tests/test_client_version_gate.py
index 4dc9b98..91cb99a 100644
--- a/packages/meshbay-hub/tests/test_client_version_gate.py
+++ b/packages/meshbay-hub/tests/test_client_version_gate.py
@@ -32,7 +32,7 @@ pytestmark = pytest.mark.skipif(
def _lift(name: str) -> str:
- src = MAIN.read_text()
+ src = MAIN.read_text(encoding="utf-8")
cut = src[src.index(name):]
return cut[:cut.index("\n}\n") + 2]
@@ -69,8 +69,8 @@ out.compare = [
compareVersions('nonsense', '1.1.0'),
];
console.log(JSON.stringify(out));
-""")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+""", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
@@ -133,7 +133,7 @@ def test_a_first_run_with_no_hub_yet_is_not_stopped(tmp_path):
def test_the_gate_runs_before_the_window_is_built():
"""A window that opens and then cannot connect is the failure this
replaces, so the order is the whole point."""
- src = MAIN.read_text()
+ src = MAIN.read_text(encoding="utf-8")
ready = src[src.index("app.whenReady().then("):]
ready = ready[:ready.index("createWindow();")]
assert "await refuseIfTooOld()" in ready, (
diff --git a/packages/meshbay-hub/tests/test_downloads.py b/packages/meshbay-hub/tests/test_downloads.py
index 395053b..c8c68a9 100644
--- a/packages/meshbay-hub/tests/test_downloads.py
+++ b/packages/meshbay-hub/tests/test_downloads.py
@@ -26,7 +26,7 @@ pytestmark = pytest.mark.skipif(
def _run(body, tmp_path):
module = tmp_path / "downloads.mjs"
- module.write_text(DOWNLOADS.read_text())
+ module.write_text(DOWNLOADS.read_text(encoding="utf-8"), encoding="utf-8")
script = tmp_path / "case.mjs"
script.write_text(
# A localStorage good enough for a preference, so the module can be
@@ -36,12 +36,12 @@ def _run(body, tmp_path):
" getItem: k => (store.has(k) ? store.get(k) : null),\n"
" setItem: (k, v) => store.set(k, String(v)),\n"
"};\n"
- f"const M = await import('{module.as_posix()}');\n"
+ f"const M = await import('{module.as_uri()}');\n"
"const out = [];\n"
"const say = (...a) => out.push(...a);\n"
f"{body}\n"
- "console.log(JSON.stringify(out));\n")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+ "console.log(JSON.stringify(out));\n", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
@@ -104,7 +104,7 @@ def test_the_open_action_reads_the_file_back(tmp_path):
manager either. It is only offered for a file written into a granted folder,
since that is the one a page can read back.
"""
- src = DOWNLOADS.read_text()
+ src = DOWNLOADS.read_text(encoding="utf-8")
target = src[src.index("export async function openTarget"):]
assert "getFile()" in target and "window.open(" in target
assert "revokeObjectURL" in target, "the blob URL must not be leaked"
@@ -122,7 +122,7 @@ def test_the_worker_only_answers_its_own_urls():
service worker that answers more than it should is a cache bug waiting to
happen.
"""
- src = SW.read_text()
+ src = SW.read_text(encoding="utf-8")
assert "startsWith(PREFIX)" in src
assert "self.location.origin" in src, "cross-origin requests must fall through"
# The API, not the word: the file explains in prose that it caches nothing.
@@ -131,7 +131,7 @@ def test_the_worker_only_answers_its_own_urls():
def test_the_download_is_announced_as_an_attachment():
- src = SW.read_text()
+ src = SW.read_text(encoding="utf-8")
assert "Content-Disposition" in src and "attachment" in src
assert "filename*=UTF-8''" in src, "a name with accents would be mangled"
assert "Content-Length" in src
@@ -142,7 +142,7 @@ def test_a_length_is_only_promised_when_it_is_known(tmp_path):
An archive is assembled as it goes and is larger than the files in it.
Announcing the sum of their sizes would truncate the download at that mark.
"""
- src = SW.read_text()
+ src = SW.read_text(encoding="utf-8")
assert "if (entry.size > 0)" in src
# The zip-directory download started in files-app.js (group-page refactor)
@@ -153,7 +153,7 @@ def test_a_length_is_only_promised_when_it_is_known(tmp_path):
# became a bare `target = ...` inside a try when _openDownloadTarget gained
# the ability to refuse an oversized download (test_memory_ceiling.py).
# What this test is about -- the `0` -- did not move.
- app = (STATIC / "file-utils.js").read_text()
+ app = (STATIC / "file-utils.js").read_text(encoding="utf-8")
# Anchored on the argument list, not on the function name: the call became
# `_openTargetInTurn(suggested, …)` when target openings were serialised.
# The `0` this test is about did not move.
@@ -169,7 +169,7 @@ def test_backpressure_is_real(tmp_path):
is transferred gives `writer.write()` something to wait on; posting chunks
to a port would queue them in memory and look identical from here.
"""
- src = DOWNLOADS.read_text()
+ src = DOWNLOADS.read_text(encoding="utf-8")
fn = src[src.index("export async function openStreamedDownload"):]
assert "new TransformStream()" in fn
# The transfer list may carry more than the stream — a reply port rides
@@ -210,13 +210,13 @@ def test_the_streamed_path_gives_up_rather_than_blocking_for_ever():
So the worker confirms that it actually answered, and this path reports
failure instead of returning a sink nobody drains.
"""
- src = DOWNLOADS.read_text()
+ src = DOWNLOADS.read_text(encoding="utf-8")
fn = src[src.index("export async function openStreamedDownload"):]
assert "mbdl-serving" in fn, "the worker has to confirm it served the request"
assert "Promise.race" in fn, "the confirmation needs a deadline"
assert "writable.abort" in fn, "give up cleanly so the caller can fall back"
- sw = (DOWNLOADS.parent / "sw.js").read_text()
+ sw = (DOWNLOADS.parent / "sw.js").read_text(encoding="utf-8")
assert "mbdl-serving" in sw, "and the worker has to send that confirmation"
@@ -227,7 +227,7 @@ def test_an_uncontrolled_page_is_not_treated_as_ready():
# became a parameter, and `serviceWorker()` no longer contains the words.
# The behaviour itself is executed in test_streamed_download_reliability.py;
# this stays as the cheap guard on the module's shape.
- src = DOWNLOADS.read_text()
+ src = DOWNLOADS.read_text(encoding="utf-8")
section = src[src.index("// ── Streaming to disk"):]
assert "navigator.serviceWorker.controller" in section
assert "controllerchange" in section, (
@@ -251,7 +251,7 @@ def test_an_apostrophe_in_a_name_does_not_lose_the_name(tmp_path):
The real function is lifted out of sw.js and run — a second copy here would
have the same blind spot as the first.
"""
- src = SW.read_text()
+ src = SW.read_text(encoding="utf-8")
fn = src[src.index("function contentDisposition"):]
fn = fn[:fn.index("\n}") + 2]
@@ -263,8 +263,8 @@ for (const name of ["S03E02. Queen's Landing.mp4", 'Caf\\u00e9 (2019).mkv',
out[name] = contentDisposition(name);
}
console.log(JSON.stringify(out));
-""")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+""", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
out = json.loads(proc.stdout)
@@ -304,7 +304,7 @@ def test_a_sink_that_stops_consuming_fails_instead_of_hanging(tmp_path):
wrong. Bounding it does not fix whatever stopped the sink — it turns an
unexplainable freeze into a failed transfer that names itself.
"""
- src = (STATIC / "file-utils.js").read_text()
+ src = (STATIC / "file-utils.js").read_text(encoding="utf-8")
fn = src[src.index("async function _writeOrStall"):]
fn = fn[:fn.index("\n}\n") + 2]
@@ -328,8 +328,8 @@ const live = { write: async () => {} };
await _writeOrStall(live, new Uint8Array(4), 0);
out.liveOk = true;
console.log(JSON.stringify(out));
-""")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+""", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
out = json.loads(proc.stdout)
assert out["threw"], "a dead sink hung for ever instead of failing"
@@ -358,8 +358,8 @@ def test_the_worker_is_kept_alive_while_it_streams():
clock. What it protects is that the ping exists at all, is cleared on both
exits, and is answered by the worker.
"""
- dl = DOWNLOADS.read_text()
- sw = SW.read_text()
+ dl = DOWNLOADS.read_text(encoding="utf-8")
+ sw = SW.read_text(encoding="utf-8")
assert "SW_KEEPALIVE_MS" in dl and "mbdl-ping" in dl, (
"nothing keeps the worker alive; downloads longer than ~30 s will "
@@ -389,7 +389,7 @@ def _turn_harness(tmp_path, name, body, *, picker=True, budget_ms=90000):
the budget is supplied here, so a case about the budget need not wait a
minute and a half for it.
"""
- src = (STATIC / "file-utils.js").read_text()
+ src = (STATIC / "file-utils.js").read_text(encoding="utf-8")
def lift(decl):
cut = src[src.index(decl):]
@@ -422,8 +422,8 @@ const TARGET_QUEUE_BUDGET_MS = {budget_ms};
""" + lift("function _openTargetInTurn") + lift("function _waitBriefly") + f"""
{body}
console.log(JSON.stringify(out));
-""")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+""", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
@@ -518,7 +518,7 @@ def test_a_pause_falls_between_chunks_and_resumes_at_one(tmp_path):
rule this repo follows for the video player: model the environment, never
the code under test.
"""
- src = (STATIC / "file-utils.js").read_text()
+ src = (STATIC / "file-utils.js").read_text(encoding="utf-8")
fn = src[src.index("async function pipelinedDownload"):]
fn = fn[:fn.index("\n}\n") + 2]
@@ -558,8 +558,8 @@ await pipelinedDownload({}, 'k', 'file', 10, () => {}, {}, signal, '',
out.resumeFrom);
out.writtenAfterResume = written.slice();
console.log(JSON.stringify(out));
-""")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+""", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
out = json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_files_drop_upload.py b/packages/meshbay-hub/tests/test_files_drop_upload.py
index fc15c47..aa39f30 100644
--- a/packages/meshbay-hub/tests/test_files_drop_upload.py
+++ b/packages/meshbay-hub/tests/test_files_drop_upload.py
@@ -41,7 +41,7 @@ def source():
def _run(tmp_path, source, expr):
script = tmp_path / "case.js"
script.write_text(f"{source}\nconsole.log(JSON.stringify({expr}));", encoding="utf-8")
- out = subprocess.run(["node", str(script)], capture_output=True, text=True, check=True)
+ out = subprocess.run(["node", str(script)], capture_output=True, encoding="utf-8", check=True)
return json.loads(out.stdout)
diff --git a/packages/meshbay-hub/tests/test_files_sorting.py b/packages/meshbay-hub/tests/test_files_sorting.py
index 730d1e8..f3aaf34 100644
--- a/packages/meshbay-hub/tests/test_files_sorting.py
+++ b/packages/meshbay-hub/tests/test_files_sorting.py
@@ -38,7 +38,7 @@ def _names(tmp_path, source, rows, key, asc):
f"{source}\nconsole.log(JSON.stringify("
f"sortRows({json.dumps(rows)}, {json.dumps(key)}, {json.dumps(asc)}).map((r) => r.name)));",
encoding="utf-8")
- out = subprocess.run(["node", str(script)], capture_output=True, text=True, check=True)
+ out = subprocess.run(["node", str(script)], capture_output=True, encoding="utf-8", check=True)
return json.loads(out.stdout)
diff --git a/packages/meshbay-hub/tests/test_hook_ordering.py b/packages/meshbay-hub/tests/test_hook_ordering.py
index d5ffcfe..0172127 100644
--- a/packages/meshbay-hub/tests/test_hook_ordering.py
+++ b/packages/meshbay-hub/tests/test_hook_ordering.py
@@ -67,7 +67,7 @@ DEPS = re.compile(r"^ \}, \[([^\]]*)\]\);", re.M)
@pytest.fixture(scope="module")
def app():
- return APP.read_text()
+ return APP.read_text(encoding="utf-8")
def _components(src: str):
@@ -84,7 +84,7 @@ def _all_components():
path = STATIC / name
if not path.exists():
continue
- for cname, body in _components(path.read_text()):
+ for cname, body in _components(path.read_text(encoding="utf-8")):
yield f"{name}:{cname}", body
@@ -147,7 +147,7 @@ def test_the_mse_harness_reads_functions_in_source_order():
running it, which is the one class of defect it would otherwise be well
placed to catch.
"""
- harness = (Path(__file__).parent / "harness" / "mse_harness.mjs").read_text()
+ harness = (Path(__file__).parent / "harness" / "mse_harness.mjs").read_text(encoding="utf-8")
assert "sort" in harness and "indexOf" in harness, (
"the harness still extracts the player functions in a hardcoded order, "
"so it cannot see one declared before its own dependency")
diff --git a/packages/meshbay-hub/tests/test_index_seal_client.py b/packages/meshbay-hub/tests/test_index_seal_client.py
index 20f89d1..f12d791 100644
--- a/packages/meshbay-hub/tests/test_index_seal_client.py
+++ b/packages/meshbay-hub/tests/test_index_seal_client.py
@@ -69,10 +69,10 @@ def _run(frames: list[str], gek: bytes = GEK) -> dict:
with tempfile.TemporaryDirectory() as tmp:
vectors = Path(tmp) / "vectors.json"
vectors.write_text(json.dumps(
- {"gek": gek.hex(), "group_id": GROUP, "frames": frames}))
+ {"gek": gek.hex(), "group_id": GROUP, "frames": frames}), encoding="utf-8")
proc = subprocess.run(
["node", str(PROBE), str(STATIC), str(vectors), transport_argv()],
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", timeout=120)
if proc.returncode != 0:
pytest.fail(f"probe failed:\n{proc.stderr}")
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_indexing_dock.py b/packages/meshbay-hub/tests/test_indexing_dock.py
index d69564d..93803a0 100644
--- a/packages/meshbay-hub/tests/test_indexing_dock.py
+++ b/packages/meshbay-hub/tests/test_indexing_dock.py
@@ -33,11 +33,11 @@ needs_node = pytest.mark.skipif(shutil.which("node") is None, reason="node is no
def _run(tmp_path, body: str):
- (tmp_path / "package.json").write_text('{"type":"module"}')
- (tmp_path / "model.js").write_text(MODEL.read_text(encoding="utf-8"))
+ (tmp_path / "package.json").write_text('{"type":"module"}', encoding="utf-8")
+ (tmp_path / "model.js").write_text(MODEL.read_text(encoding="utf-8"), encoding="utf-8")
script = tmp_path / "case.js"
- script.write_text("import * as m from './model.js';\n" + body)
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True,
+ script.write_text("import * as m from './model.js';\n" + body, encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8",
cwd=str(tmp_path))
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_invite_link_client.py b/packages/meshbay-hub/tests/test_invite_link_client.py
index 313e224..aa8c194 100644
--- a/packages/meshbay-hub/tests/test_invite_link_client.py
+++ b/packages/meshbay-hub/tests/test_invite_link_client.py
@@ -54,8 +54,8 @@ def _module_body() -> str:
def _run(tmp_path, script: str):
harness = tmp_path / "h.js"
- harness.write_text(script)
- out = subprocess.run(["node", str(harness)], capture_output=True, text=True, timeout=60)
+ harness.write_text(script, encoding="utf-8")
+ out = subprocess.run(["node", str(harness)], capture_output=True, encoding="utf-8", timeout=60)
assert out.returncode == 0, out.stderr
return json.loads(out.stdout)
diff --git a/packages/meshbay-hub/tests/test_layout_measured.py b/packages/meshbay-hub/tests/test_layout_measured.py
index 9bf1bde..c5d6280 100644
--- a/packages/meshbay-hub/tests/test_layout_measured.py
+++ b/packages/meshbay-hub/tests/test_layout_measured.py
@@ -78,7 +78,7 @@ def measured(tmp_path_factory):
"""One browser for every width, because launching one apiece cost the
suite three minutes."""
fragment = tmp_path_factory.mktemp("layout") / "fragment.html"
- fragment.write_text(NAV)
+ fragment.write_text(NAV, encoding="utf-8")
proc = subprocess.run(
["python3", str(HARNESS), ",".join(str(w) for w in WIDTHS),
str(fragment), *SELECTORS],
@@ -211,7 +211,7 @@ GROUPED_SELECTORS = [".transfer-panel", ".transfer-head", ".transfer-head-summar
@pytest.fixture(scope="module")
def grouped(tmp_path_factory):
fragment = tmp_path_factory.mktemp("grouped") / "fragment.html"
- fragment.write_text(GROUPED)
+ fragment.write_text(GROUPED, encoding="utf-8")
proc = subprocess.run(
["python3", str(HARNESS), ",".join(str(w) for w in WIDTHS),
str(fragment), *GROUPED_SELECTORS],
diff --git a/packages/meshbay-hub/tests/test_layout_responsive.py b/packages/meshbay-hub/tests/test_layout_responsive.py
index 4ee07d0..bb73557 100644
--- a/packages/meshbay-hub/tests/test_layout_responsive.py
+++ b/packages/meshbay-hub/tests/test_layout_responsive.py
@@ -38,7 +38,7 @@ pytestmark = pytest.mark.skipif(
@pytest.fixture(scope="module")
def css():
- return CSS.read_text()
+ return CSS.read_text(encoding="utf-8")
def _rule(css: str, selector: str) -> str:
@@ -119,7 +119,7 @@ APP = STATIC / "chat-app.js"
@pytest.fixture(scope="module")
def app():
- return APP.read_text()
+ return APP.read_text(encoding="utf-8")
def test_the_chat_panel_is_measured_not_guessed(app):
diff --git a/packages/meshbay-hub/tests/test_locales.py b/packages/meshbay-hub/tests/test_locales.py
index 602d161..05e1115 100644
--- a/packages/meshbay-hub/tests/test_locales.py
+++ b/packages/meshbay-hub/tests/test_locales.py
@@ -35,19 +35,20 @@ EXPECTED = ["en", "fr", "es", "pt-BR", "zh-CN", "ja", "de", "it", "nl", "pl"]
def _sandbox(tmp_path):
"""A directory node will treat as ESM, holding the real sources."""
- (tmp_path / "package.json").write_text('{"type":"module"}')
+ (tmp_path / "package.json").write_text('{"type":"module"}', encoding="utf-8")
(tmp_path / "locales").mkdir(exist_ok=True)
for src in LOCALES.glob("*.js"):
- (tmp_path / "locales" / src.name).write_text(src.read_text())
- (tmp_path / "i18n.js").write_text(I18N.read_text())
+ (tmp_path / "locales" / src.name).write_text(src.read_text(encoding="utf-8"),
+ encoding="utf-8")
+ (tmp_path / "i18n.js").write_text(I18N.read_text(encoding="utf-8"), encoding="utf-8")
return tmp_path
def _node(tmp_path, body):
script = tmp_path / "case.js"
- script.write_text(body)
+ script.write_text(body, encoding="utf-8")
proc = subprocess.run(
- ["node", str(script)], capture_output=True, text=True, cwd=str(tmp_path))
+ ["node", str(script)], capture_output=True, text=True, encoding="utf-8", cwd=str(tmp_path))
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_media_pager.py b/packages/meshbay-hub/tests/test_media_pager.py
index a8a0569..835ed6f 100644
--- a/packages/meshbay-hub/tests/test_media_pager.py
+++ b/packages/meshbay-hub/tests/test_media_pager.py
@@ -36,8 +36,8 @@ def source():
def _run(tmp_path, source, expr):
script = tmp_path / "case.js"
- script.write_text(f"{source}\nconsole.log(JSON.stringify({expr}));")
- out = subprocess.run(["node", str(script)], capture_output=True, text=True, check=True)
+ script.write_text(f"{source}\nconsole.log(JSON.stringify({expr}));", encoding="utf-8")
+ out = subprocess.run(["node", str(script)], capture_output=True, encoding="utf-8", check=True)
return json.loads(out.stdout)
diff --git a/packages/meshbay-hub/tests/test_member_removal.py b/packages/meshbay-hub/tests/test_member_removal.py
index 7af73a0..5073873 100644
--- a/packages/meshbay-hub/tests/test_member_removal.py
+++ b/packages/meshbay-hub/tests/test_member_removal.py
@@ -27,7 +27,7 @@ pytestmark = pytest.mark.skipif(
def _remove_member_body() -> str:
- source = SETTINGS.read_text()
+ source = SETTINGS.read_text(encoding="utf-8")
start = source.find("const removeMember = useCallback(")
assert start != -1, "removeMember is gone from group-settings.js"
end = source.find("\n }, [", start)
diff --git a/packages/meshbay-hub/tests/test_memory_ceiling.py b/packages/meshbay-hub/tests/test_memory_ceiling.py
index 5984292..9ea6ad3 100644
--- a/packages/meshbay-hub/tests/test_memory_ceiling.py
+++ b/packages/meshbay-hub/tests/test_memory_ceiling.py
@@ -51,7 +51,7 @@ def _lift(name, source):
@pytest.fixture(scope="module")
def target_fn():
"""The ceiling, its error and the real function — read, never re-typed."""
- src = FILE_UTILS.read_text()
+ src = FILE_UTILS.read_text(encoding="utf-8")
ceiling = re.search(r"^const MEMORY_CEILING = .*?;$", src, re.M)
assert ceiling, "MEMORY_CEILING is gone from file-utils.js"
# The test's own CEILING constant must agree with the source's, or every
@@ -123,8 +123,8 @@ try {{
outcome = {{ kind: 'refused', name: err.name, message: err.message }};
}}
console.log(JSON.stringify(outcome));
-""")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+""", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
@@ -259,7 +259,7 @@ def test_no_unguarded_memory_floor(target_fn):
def test_the_guard_is_what_the_preview_uses_too(target_fn):
"""`FilePreview` decrypts a whole entry with no writable at all, so it needs
the same ceiling — and must import it rather than keep a second number."""
- files_app = (STATIC / "files-app.js").read_text()
+ files_app = (STATIC / "files-app.js").read_text(encoding="utf-8")
assert "MEMORY_CEILING" in files_app
assert re.search(r"entry\.size\s*>\s*MEMORY_CEILING", files_app), (
"the preview modal must refuse an oversized entry before fetching it")
diff --git a/packages/meshbay-hub/tests/test_music_queue.py b/packages/meshbay-hub/tests/test_music_queue.py
index 5bf9e97..8f3f9bd 100644
--- a/packages/meshbay-hub/tests/test_music_queue.py
+++ b/packages/meshbay-hub/tests/test_music_queue.py
@@ -123,7 +123,7 @@ def test_an_unreachable_group_is_skipped_whole_rather_than_one_track_at_a_time(s
def test_the_music_wrapper_names_the_op_it_forwards(name):
"""A wrapper that takes two arguments forwards two, and the third is lost
without a word. Both of these did exactly that."""
- src = (STATIC / name).read_text()
+ src = (STATIC / name).read_text(encoding="utf-8")
marker = ("const onPlayQueue = useCallback((tracks, startIndex, op)"
if name == "group-page.js"
else "const handleMusicPlay = useCallback((tracks, startIndex, op)")
diff --git a/packages/meshbay-hub/tests/test_node_page_width_measured.py b/packages/meshbay-hub/tests/test_node_page_width_measured.py
index bd6a231..ae49641 100644
--- a/packages/meshbay-hub/tests/test_node_page_width_measured.py
+++ b/packages/meshbay-hub/tests/test_node_page_width_measured.py
@@ -122,7 +122,7 @@ SELECTORS = ["#node.node-page", "#settings", "#node .node-table-scroll",
def measured(tmp_path_factory):
"""One browser for every width — launching one apiece cost three minutes."""
fragment = tmp_path_factory.mktemp("nodewidth") / "fragment.html"
- fragment.write_text(FRAGMENT)
+ fragment.write_text(FRAGMENT, encoding="utf-8")
proc = subprocess.run(
["python3", str(HARNESS), ",".join(str(w) for w in WIDTHS),
str(fragment), *SELECTORS],
diff --git a/packages/meshbay-hub/tests/test_notification_dismissal.py b/packages/meshbay-hub/tests/test_notification_dismissal.py
index d498b4d..0198dee 100644
--- a/packages/meshbay-hub/tests/test_notification_dismissal.py
+++ b/packages/meshbay-hub/tests/test_notification_dismissal.py
@@ -149,7 +149,7 @@ def test_the_spa_asks_for_unread_only():
exercising it, and here it is the only thing that catches the defect that
actually happened.
"""
- src = APP.read_text()
+ src = APP.read_text(encoding="utf-8")
fetches = re.findall(r"hubFetch\('(/v1/notifications\?[^']*)'", src)
assert fetches, "the notification list fetch is no longer where this reads it"
for url in fetches:
@@ -167,7 +167,7 @@ def test_the_feed_does_not_style_a_state_it_can_no_longer_show():
was dead code that described behaviour the application had abandoned —
and reading it is what made the two halves' disagreement visible.
"""
- src = APP.read_text()
+ src = APP.read_text(encoding="utf-8")
assert "n.read ?" not in src, (
"the feed branches on `read` again; either it is dead code or the "
"dismissal contract has changed and this file should say how")
diff --git a/packages/meshbay-hub/tests/test_offer_retry.py b/packages/meshbay-hub/tests/test_offer_retry.py
index 6389f8e..1239ae0 100644
--- a/packages/meshbay-hub/tests/test_offer_retry.py
+++ b/packages/meshbay-hub/tests/test_offer_retry.py
@@ -33,7 +33,7 @@ pytestmark = pytest.mark.skipif(
def _post(**cfg) -> dict:
proc = subprocess.run(
["node", str(HARNESS), transport_argv(), json.dumps(cfg)],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_playlist_crypto.py b/packages/meshbay-hub/tests/test_playlist_crypto.py
index 4ff3080..4dcfcb6 100644
--- a/packages/meshbay-hub/tests/test_playlist_crypto.py
+++ b/packages/meshbay-hub/tests/test_playlist_crypto.py
@@ -69,11 +69,11 @@ const bigBody = (n) => ({
def _run(tmp_path, body):
- src = SRC.read_text().replace("export {", "const _unused_export = {")
+ src = SRC.read_text(encoding="utf-8").replace("export {", "const _unused_export = {")
script = tmp_path / "case.mjs"
- script.write_text(f"{src}\n{PRELUDE}\n{body}\n")
+ script.write_text(f"{src}\n{PRELUDE}\n{body}\n", encoding="utf-8")
out = subprocess.run(["node", str(script)],
- capture_output=True, text=True, timeout=60)
+ capture_output=True, text=True, encoding="utf-8", timeout=60)
assert out.returncode == 0, out.stderr
return json.loads(out.stdout)
diff --git a/packages/meshbay-hub/tests/test_playlist_key.py b/packages/meshbay-hub/tests/test_playlist_key.py
index dbed8ae..261cc32 100644
--- a/packages/meshbay-hub/tests/test_playlist_key.py
+++ b/packages/meshbay-hub/tests/test_playlist_key.py
@@ -58,11 +58,11 @@ globalThis.argon2 = {
def _run(tmp_path, body):
- src = KEYDERIVE.read_text()
+ src = KEYDERIVE.read_text(encoding="utf-8")
script = tmp_path / "case.mjs"
- script.write_text(f"{PRELUDE}\n{src}\n{body}\n")
+ script.write_text(f"{PRELUDE}\n{src}\n{body}\n", encoding="utf-8")
out = subprocess.run(["node", str(script)],
- capture_output=True, text=True, timeout=60)
+ capture_output=True, text=True, encoding="utf-8", timeout=60)
assert out.returncode == 0, out.stderr
return json.loads(out.stdout)
diff --git a/packages/meshbay-hub/tests/test_playlist_merge.py b/packages/meshbay-hub/tests/test_playlist_merge.py
index 2c7b612..dd089e8 100644
--- a/packages/meshbay-hub/tests/test_playlist_merge.py
+++ b/packages/meshbay-hub/tests/test_playlist_merge.py
@@ -39,7 +39,7 @@ EXPORT = re.compile(r"^export \{[^}]*\};?\s*$", re.M | re.S)
@pytest.fixture(scope="module")
def module_source():
- text = SRC.read_text()
+ text = SRC.read_text(encoding="utf-8")
assert not IMPORT.search(text), (
"playlist-merge.js has gained an import. It is executed standalone "
"here, and the merge is untested from the moment it cannot be — keep "
@@ -53,9 +53,9 @@ def module_source():
def _run(tmp_path, module_source, body):
script = tmp_path / "case.js"
- script.write_text(f"{module_source}\n{body}\n")
+ script.write_text(f"{module_source}\n{body}\n", encoding="utf-8")
out = subprocess.run(
- ["node", str(script)], capture_output=True, text=True, timeout=30)
+ ["node", str(script)], capture_output=True, text=True, encoding="utf-8", timeout=30)
assert out.returncode == 0, out.stderr
return json.loads(out.stdout)
diff --git a/packages/meshbay-hub/tests/test_queue_ops.py b/packages/meshbay-hub/tests/test_queue_ops.py
index 64b5ca1..e3853f2 100644
--- a/packages/meshbay-hub/tests/test_queue_ops.py
+++ b/packages/meshbay-hub/tests/test_queue_ops.py
@@ -38,7 +38,7 @@ EXPORT = re.compile(r"^export \{[^}]*\};?\s*$", re.M)
@pytest.fixture(scope="module")
def module_source():
- text = SRC.read_text()
+ text = SRC.read_text(encoding="utf-8")
assert not IMPORT.search(text), (
"queue-ops.js has gained an import. It is executed standalone here, "
"and the queue is untested from the moment it cannot be — keep the "
@@ -52,9 +52,9 @@ def module_source():
def _run(tmp_path, module_source, body):
script = tmp_path / "case.js"
- script.write_text(f"{module_source}\n{body}\n")
+ script.write_text(f"{module_source}\n{body}\n", encoding="utf-8")
out = subprocess.run(
- ["node", str(script)], capture_output=True, text=True, timeout=30)
+ ["node", str(script)], capture_output=True, text=True, encoding="utf-8", timeout=30)
assert out.returncode == 0, out.stderr
return json.loads(out.stdout)
diff --git a/packages/meshbay-hub/tests/test_reconnect_refresh.py b/packages/meshbay-hub/tests/test_reconnect_refresh.py
index ff6d7fb..cd702d3 100644
--- a/packages/meshbay-hub/tests/test_reconnect_refresh.py
+++ b/packages/meshbay-hub/tests/test_reconnect_refresh.py
@@ -43,12 +43,12 @@ def transport():
@pytest.fixture(scope="module")
def group_page():
- return GROUP_PAGE.read_text()
+ return GROUP_PAGE.read_text(encoding="utf-8")
@pytest.fixture(scope="module")
def video_player():
- return VIDEO_PLAYER.read_text()
+ return VIDEO_PLAYER.read_text(encoding="utf-8")
def _reconnect_loop(transport: str) -> str:
@@ -93,7 +93,7 @@ def test_one_listener_throwing_does_not_rob_the_next(transport):
def test_nothing_assigns_the_old_setter():
"""`grep onReconnected =` is what this is, spelled so it cannot rot."""
offenders = [p.name for p in STATIC.glob("*.js")
- if re.search(r"\.onReconnected\s*=", p.read_text())]
+ if re.search(r"\.onReconnected\s*=", p.read_text(encoding="utf-8"))]
assert offenders == [], (
f"{offenders} still assign a slot that no longer exists")
@@ -159,10 +159,10 @@ def test_every_harness_that_renders_the_group_page_stubs_the_subscription():
"""
harness = Path(__file__).parent / "harness"
stubs = [p for p in harness.glob("*.py")
- if "window.MeshBayTransport" in p.read_text()
- and "GroupPage" in p.read_text()]
+ if "window.MeshBayTransport" in p.read_text(encoding="utf-8")
+ and "GroupPage" in p.read_text(encoding="utf-8")]
assert stubs, "no harness stubs the transport any more — has this moved?"
missing = [p.name for p in stubs
- if "addReconnectListener" not in p.read_text()]
+ if "addReconnectListener" not in p.read_text(encoding="utf-8")]
assert missing == [], (
f"{missing} render GroupPage against a node that cannot be subscribed to")
diff --git a/packages/meshbay-hub/tests/test_recovery_key.py b/packages/meshbay-hub/tests/test_recovery_key.py
index 54415f6..e43e6de 100644
--- a/packages/meshbay-hub/tests/test_recovery_key.py
+++ b/packages/meshbay-hub/tests/test_recovery_key.py
@@ -102,10 +102,10 @@ const fp = async (key) => hex(await webcrypto.subtle.encrypt(
def result(tmp_path_factory):
d = tmp_path_factory.mktemp("recovery")
harness = d / "harness.cjs"
- harness.write_text(_HARNESS)
+ harness.write_text(_HARNESS, encoding="utf-8")
proc = subprocess.run(
["node", str(harness), str(KEYDERIVE)],
- capture_output=True, text=True, timeout=120,
+ capture_output=True, text=True, encoding="utf-8", timeout=120,
)
if proc.returncode != 0:
pytest.fail(f"node harness failed:\n{proc.stderr[-2000:]}")
diff --git a/packages/meshbay-hub/tests/test_resume_position.py b/packages/meshbay-hub/tests/test_resume_position.py
index 07ac23a..67eb786 100644
--- a/packages/meshbay-hub/tests/test_resume_position.py
+++ b/packages/meshbay-hub/tests/test_resume_position.py
@@ -85,7 +85,7 @@ def _run(body: str, tmp_path: Path):
f"{body}\n"
"console.log(JSON.stringify(out));\n",
encoding="utf-8")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_rewrap_fanout.py b/packages/meshbay-hub/tests/test_rewrap_fanout.py
index 7c0f272..b278d0c 100644
--- a/packages/meshbay-hub/tests/test_rewrap_fanout.py
+++ b/packages/meshbay-hub/tests/test_rewrap_fanout.py
@@ -154,10 +154,10 @@ const names = (a) => a.map((x) => x.name).sort();
def result(tmp_path_factory):
d = tmp_path_factory.mktemp("rewrap")
harness = d / "harness.cjs"
- harness.write_text(_HARNESS)
+ harness.write_text(_HARNESS, encoding="utf-8")
proc = subprocess.run(
["node", str(harness), transport_argv()],
- capture_output=True, text=True, timeout=120,
+ capture_output=True, text=True, encoding="utf-8", timeout=120,
)
if proc.returncode != 0:
pytest.fail(f"node harness failed:\n{proc.stderr[-2000:]}")
diff --git a/packages/meshbay-hub/tests/test_search_connect_deadline.py b/packages/meshbay-hub/tests/test_search_connect_deadline.py
index 2d3db06..953f027 100644
--- a/packages/meshbay-hub/tests/test_search_connect_deadline.py
+++ b/packages/meshbay-hub/tests/test_search_connect_deadline.py
@@ -49,7 +49,7 @@ CAP_MS = 30000
def _attempt(**cfg) -> dict:
proc = subprocess.run(
["node", str(HARNESS), search_argv(), json.dumps(cfg)],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_search_fanout.py b/packages/meshbay-hub/tests/test_search_fanout.py
index fca879a..23a8b67 100644
--- a/packages/meshbay-hub/tests/test_search_fanout.py
+++ b/packages/meshbay-hub/tests/test_search_fanout.py
@@ -67,7 +67,7 @@ DEAD_MS = 10000 # a node that does not, to the connection deadline
def _sweep(**cfg) -> dict:
proc = subprocess.run(
["node", str(HARNESS), search_argv(), json.dumps(cfg)],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_search_files_unmerged.py b/packages/meshbay-hub/tests/test_search_files_unmerged.py
index b84b25d..b6621e5 100644
--- a/packages/meshbay-hub/tests/test_search_files_unmerged.py
+++ b/packages/meshbay-hub/tests/test_search_files_unmerged.py
@@ -38,7 +38,7 @@ MERGE_CALL = "mergeUnitEntries"
def _memo(name):
"""The body of `const <name> = useMemo(() => { ... }, [...]);`."""
- src = SEARCH_PAGE.read_text()
+ src = SEARCH_PAGE.read_text(encoding="utf-8")
m = re.search(
r"^ const " + re.escape(name) + r" = useMemo\(\(\) => \{.*?^ \}, \[.*?\]\);",
src, re.M | re.S)
diff --git a/packages/meshbay-hub/tests/test_search_media_merge.py b/packages/meshbay-hub/tests/test_search_media_merge.py
index 3464902..082de7e 100644
--- a/packages/meshbay-hub/tests/test_search_media_merge.py
+++ b/packages/meshbay-hub/tests/test_search_media_merge.py
@@ -50,7 +50,7 @@ EXPORT = re.compile(r"^export \{[^}]*\};?\s*$", re.M)
def _block(path, header):
"""One top-level `function name(...) {` ... `}` read out of a module."""
- src = path.read_text()
+ src = path.read_text(encoding="utf-8")
m = re.search(r"^" + re.escape(header) + r".*?^\}", src, re.M | re.S)
assert m, (
f"{header} is no longer where this test reads it from in {path.name} — "
@@ -60,7 +60,7 @@ def _block(path, header):
def _const(name):
m = re.search(r"^const " + re.escape(name) + r" = .*?;$",
- SEARCH_PAGE.read_text(), re.M)
+ SEARCH_PAGE.read_text(encoding="utf-8"), re.M)
assert m, f"{name} moved — the search-page units cannot be lifted"
return m.group(0)
@@ -70,7 +70,7 @@ def pipeline():
"""Everything the three views need, in one script."""
return "\n".join([
"const t = (k) => k;",
- EXPORT.sub("", MERGE.read_text()),
+ EXPORT.sub("", MERGE.read_text(encoding="utf-8")),
_block(VIDEO_APP, "function underVideoRoot(entry, directories) {"),
_block(VIDEO_APP, "function buildSeasons(episodes) {"),
_block(VIDEO_APP, "function groupVideoEntries(entries, videoDirectories) {"),
@@ -90,9 +90,9 @@ def pipeline():
def _node(tmp_path, pipeline, body):
script = tmp_path / "case.js"
- script.write_text(f"{pipeline}\n{body}\n")
+ script.write_text(f"{pipeline}\n{body}\n", encoding="utf-8")
out = subprocess.run(
- ["node", str(script)], capture_output=True, text=True, timeout=30)
+ ["node", str(script)], capture_output=True, text=True, encoding="utf-8", timeout=30)
assert out.returncode == 0, out.stderr
return json.loads(out.stdout)
diff --git a/packages/meshbay-hub/tests/test_search_pool.py b/packages/meshbay-hub/tests/test_search_pool.py
index cf9eeb8..89b46cd 100644
--- a/packages/meshbay-hub/tests/test_search_pool.py
+++ b/packages/meshbay-hub/tests/test_search_pool.py
@@ -36,7 +36,7 @@ pytestmark = pytest.mark.skipif(
def _run(**cfg) -> dict:
proc = subprocess.run(
["node", str(HARNESS), search_argv(), json.dumps(cfg)],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_search_source_merge.py b/packages/meshbay-hub/tests/test_search_source_merge.py
index b471891..10d10fc 100644
--- a/packages/meshbay-hub/tests/test_search_source_merge.py
+++ b/packages/meshbay-hub/tests/test_search_source_merge.py
@@ -44,7 +44,7 @@ EXPORT = re.compile(r"^export \{[^}]*\};?\s*$", re.M)
@pytest.fixture(scope="module")
def module_source():
- text = SRC.read_text()
+ text = SRC.read_text(encoding="utf-8")
assert not IMPORT.search(text), (
"source-merge.js has gained an import. It is executed standalone here, "
"and the merge is untested from the moment it cannot be — keep the "
@@ -58,9 +58,9 @@ def module_source():
def _run(tmp_path, module_source, body):
script = tmp_path / "case.js"
- script.write_text(f"{module_source}\n{body}\n")
+ script.write_text(f"{module_source}\n{body}\n", encoding="utf-8")
out = subprocess.run(
- ["node", str(script)], capture_output=True, text=True, timeout=30)
+ ["node", str(script)], capture_output=True, text=True, encoding="utf-8", timeout=30)
assert out.returncode == 0, out.stderr
return json.loads(out.stdout)
diff --git a/packages/meshbay-hub/tests/test_season_panel_placement.py b/packages/meshbay-hub/tests/test_season_panel_placement.py
index 8c04ea7..6e5f78b 100644
--- a/packages/meshbay-hub/tests/test_season_panel_placement.py
+++ b/packages/meshbay-hub/tests/test_season_panel_placement.py
@@ -43,7 +43,7 @@ BLOCK = re.compile(
@pytest.fixture(scope="module")
def source():
- m = BLOCK.search(APP.read_text())
+ m = BLOCK.search(APP.read_text(encoding="utf-8"))
assert m, ("placeSeasonPanel is no longer where this test reads it from — "
"the season menu's placement is untested until this is fixed")
return m.group(0)
@@ -58,8 +58,8 @@ def _place(tmp_path, source, *, top, bottom, left=40, width=300, inner_height=74
const el = {{ getBoundingClientRect: () => ({{
top: {top}, bottom: {bottom}, left: {left}, width: {width} }}) }};
console.log(JSON.stringify(placeSeasonPanel(el)));
- """)
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+ """, encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
@@ -157,7 +157,7 @@ def test_no_element_means_no_position(tmp_path, source):
globalThis.window = {{ innerHeight: 740 }};
{source}
console.log(JSON.stringify(placeSeasonPanel(null)));
- """)
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+ """, encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
assert json.loads(proc.stdout) is None
diff --git a/packages/meshbay-hub/tests/test_session_renewal.py b/packages/meshbay-hub/tests/test_session_renewal.py
index 7b8c108..ecf9ac5 100644
--- a/packages/meshbay-hub/tests/test_session_renewal.py
+++ b/packages/meshbay-hub/tests/test_session_renewal.py
@@ -50,7 +50,7 @@ pytestmark = pytest.mark.skipif(
def _run(scenario: str, app: Path = APP) -> dict:
proc = subprocess.run(
["node", str(HARNESS), str(app), json.dumps({"scenario": scenario})],
- capture_output=True, text=True, timeout=60)
+ capture_output=True, text=True, encoding="utf-8", timeout=60)
assert proc.returncode == 0, f"{proc.stdout}\n{proc.stderr}"
return json.loads(proc.stdout.strip().splitlines()[-1])
@@ -59,14 +59,14 @@ def _run(scenario: str, app: Path = APP) -> dict:
def broken(tmp_path_factory):
"""The client as it shipped: the rotated refresh token dropped."""
out = tmp_path_factory.mktemp("session") / "broken.js"
- src = APP.read_text()
+ src = APP.read_text(encoding="utf-8")
replaced = src.replace(
" refreshToken: data.refresh_token || _auth.refreshToken,",
" refreshToken: _auth.refreshToken,")
assert replaced != src, (
"could not reconstruct the defect — the line it hinged on has moved, "
"and the A/B below would be comparing the fix against itself")
- out.write_text(replaced)
+ out.write_text(replaced, encoding="utf-8")
return out
@@ -186,13 +186,13 @@ def test_the_session_is_much_longer_than_the_token():
def test_renewal_happens_before_expiry_not_after():
"""A margin, so the first click after a long film does not pay for a 401."""
- src = APP.read_text()
+ src = APP.read_text(encoding="utf-8")
import re
margin = int(re.search(r"const TOKEN_RENEW_MARGIN_S = (\d+)", src).group(1))
assert margin >= 300, (
f"{margin} s of margin against a one-hour token is thin: a backgrounded "
"tab has its timers throttled and may not check for minutes")
- assert "visibilitychange" in APP_JS.read_text(), (
+ assert "visibilitychange" in APP_JS.read_text(encoding="utf-8"), (
"nothing re-checks when the tab comes back, which is exactly when the "
"token is most likely to have aged out unnoticed")
@@ -213,7 +213,7 @@ def test_renewing_does_not_tear_down_the_webrtc_connection():
Signing in or out must still re-run it, so the dependency is whether there
is a token, not which one.
"""
- src = GROUP_PAGE.read_text()
+ src = GROUP_PAGE.read_text(encoding="utf-8")
i = src.index("means tearing down the WebRTC connection")
deps = src[i:src.index(");", i)]
assert "Boolean(token)" in deps, (
@@ -228,7 +228,7 @@ def test_the_connection_signs_its_offer_with_a_live_token():
It signs the offer relayed through the hub, where an expired one is a 401
and no connection at all.
"""
- src = GROUP_PAGE.read_text()
+ src = GROUP_PAGE.read_text(encoding="utf-8")
connect = src[src.index("const connect = async () => {"):]
connect = connect[:connect.index("\n };")]
assert "await ensureFreshToken()" in connect, (
diff --git a/packages/meshbay-hub/tests/test_site_basics.py b/packages/meshbay-hub/tests/test_site_basics.py
index a3da39a..78cad83 100644
--- a/packages/meshbay-hub/tests/test_site_basics.py
+++ b/packages/meshbay-hub/tests/test_site_basics.py
@@ -47,7 +47,7 @@ def test_the_icons_are_served_from_the_root(client, path, magic):
def test_meshbay_org_sends_them_to_the_hub(path):
"""The public site owns only the paths its matcher names; a file claimed
there would be looked for in /srv/meshbay/site and 404."""
- m = re.search(r"^\s*@site path (.+)$", CADDYFILE.read_text(), re.M)
+ m = re.search(r"^\s*@site path (.+)$", CADDYFILE.read_text(encoding="utf-8"), re.M)
assert m, "the @site matcher is gone"
assert path not in m.group(1).split()
diff --git a/packages/meshbay-hub/tests/test_spa_ordering.py b/packages/meshbay-hub/tests/test_spa_ordering.py
index 087298f..fdedaf8 100644
--- a/packages/meshbay-hub/tests/test_spa_ordering.py
+++ b/packages/meshbay-hub/tests/test_spa_ordering.py
@@ -132,7 +132,7 @@ COMPONENT_FILES = {
def _component(name: str) -> str:
"""The source of one top-level `function Name(...)`, up to the next one."""
- source = COMPONENT_FILES.get(name, APP).read_text()
+ source = COMPONENT_FILES.get(name, APP).read_text(encoding="utf-8")
start = source.find(f"\nfunction {name}(")
if start == -1:
start = source.find(f"\nexport function {name}(")
@@ -225,7 +225,7 @@ def test_the_uploader_keeps_several_chunks_in_flight():
def test_no_caller_waits_for_one_chunk_at_a_time():
- app = APP.read_text()
+ app = APP.read_text(encoding="utf-8")
assert "uploadChunk(" not in app, (
"a per-chunk await is back in the SPA; use transport.uploadFile()")
@@ -248,7 +248,7 @@ def test_leaving_a_group_hands_the_transport_over_rather_than_closing_it():
def test_signing_out_stops_them():
- app = APP.read_text()
+ app = APP.read_text(encoding="utf-8")
logout = app[app.index(" logout: () => {"):]
logout = logout[:logout.index("navigate('/login')")]
assert "transfers.reset()" in logout, (
@@ -257,7 +257,7 @@ def test_signing_out_stops_them():
def test_the_files_panel_no_longer_carries_its_own_progress_bars():
"""They moved next to the bell, where they stay visible across the app."""
- app = APP.read_text()
+ app = APP.read_text(encoding="utf-8")
for gone in ("setUlState", "setDlState", "dl-bar"):
assert gone not in app, f"{gone} survived the move to the transfer widget"
@@ -270,7 +270,7 @@ def test_a_multi_file_download_waits_for_each_picker():
meant the first opened a dialog and the rest were rejected — two files
selected, one file downloaded.
"""
- app = STATIC.joinpath("files-app.js").read_text()
+ app = STATIC.joinpath("files-app.js").read_text(encoding="utf-8")
# Anchored on the loop rather than on the markup around it: the toolbar
# moved from a dropdown to icon buttons and took the old wrapper with it,
# while the property under test — one picker at a time — did not change.
@@ -289,7 +289,7 @@ def test_links_in_chat_are_built_as_elements_not_markup():
never HTML, and only for http(s) — otherwise javascript: would be one
message away from running here.
"""
- app = STATIC.joinpath("chat-app.js").read_text()
+ app = STATIC.joinpath("chat-app.js").read_text(encoding="utf-8")
fn = app[app.index("function linkify("):]
fn = fn[:fn.index("\nfunction ", 1)]
assert "innerHTML" not in fn and "dangerouslySetInnerHTML" not in fn
diff --git a/packages/meshbay-hub/tests/test_spa_syntax.py b/packages/meshbay-hub/tests/test_spa_syntax.py
index 352f84b..aac4010 100644
--- a/packages/meshbay-hub/tests/test_spa_syntax.py
+++ b/packages/meshbay-hub/tests/test_spa_syntax.py
@@ -47,7 +47,7 @@ def test_every_module_parses(tmp_path):
copy = tmp_path / (path.stem + ".mjs")
copy.write_text(path.read_text(encoding="utf-8"), encoding="utf-8")
proc = subprocess.run(["node", "--check", str(copy)],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
if proc.returncode != 0:
first = (proc.stderr or "").strip().splitlines()
detail = next((ln for ln in first if "Error" in ln), first[:1] and first[0] or "")
@@ -67,12 +67,12 @@ def test_the_check_would_notice_a_broken_file(tmp_path):
as_js = tmp_path / "sample.js"
as_js.write_text(bad, encoding="utf-8")
lenient = subprocess.run(["node", "--check", str(as_js)],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
as_mjs = tmp_path / "sample.mjs"
as_mjs.write_text(bad, encoding="utf-8")
strict = subprocess.run(["node", "--check", str(as_mjs)],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
assert strict.returncode != 0, (
"the .mjs check no longer reports a module syntax error — this whole "
diff --git a/packages/meshbay-hub/tests/test_streamed_download_reliability.py b/packages/meshbay-hub/tests/test_streamed_download_reliability.py
index e1b3800..e60e833 100644
--- a/packages/meshbay-hub/tests/test_streamed_download_reliability.py
+++ b/packages/meshbay-hub/tests/test_streamed_download_reliability.py
@@ -195,8 +195,8 @@ def _run(tmp_path, body, *, control_after_ms=0, active=True,
controlled_at_load=False, registered_at_load=False,
worker_asleep=False):
module = tmp_path / "downloads.mjs"
- module.write_text(DOWNLOADS.read_text())
- (tmp_path / "package.json").write_text('{"type":"module"}')
+ module.write_text(DOWNLOADS.read_text(encoding="utf-8"), encoding="utf-8")
+ (tmp_path / "package.json").write_text('{"type":"module"}', encoding="utf-8")
plan = {
"controlAfterMs": control_after_ms,
"active": active,
@@ -211,12 +211,12 @@ def _run(tmp_path, body, *, control_after_ms=0, active=True,
}
script = tmp_path / "case.mjs"
script.write_text(
- (PRELUDE % {"plan": json.dumps(plan), "module": module.as_posix(),
+ (PRELUDE % {"plan": json.dumps(plan), "module": module.as_uri(),
"control": control_budget_ms,
"controlled": json.dumps(controlled_at_load)})
+ body
- + "\nout.log = log;\nconsole.log(JSON.stringify(out));\n")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True,
+ + "\nout.log = log;\nconsole.log(JSON.stringify(out));\n", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8",
timeout=120)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
@@ -338,7 +338,7 @@ def test_the_worker_is_primed_at_boot_not_at_the_first_click(tmp_path):
from a module that actually imports it — `node --check` would not notice a
missing import, which is a mistake this repo has already shipped once.
"""
- app = (STATIC / "app.js").read_text()
+ app = (STATIC / "app.js").read_text(encoding="utf-8")
assert "downloads.primeServiceWorker()" in app, "nothing primes the worker"
assert "import * as downloads from './downloads.js'" in app, (
"app.js calls downloads.primeServiceWorker() without importing downloads")
@@ -350,7 +350,7 @@ def test_the_worker_is_primed_at_boot_not_at_the_first_click(tmp_path):
def test_the_worker_answers_a_re_claim(tmp_path):
"""The page's last resort before declaring the path unavailable only works
if sw.js implements the other half."""
- sw = (STATIC / "sw.js").read_text()
+ sw = (STATIC / "sw.js").read_text(encoding="utf-8")
assert "mbdl-claim" in sw and "clients.claim()" in sw
diff --git a/packages/meshbay-hub/tests/test_table_rows_measured.py b/packages/meshbay-hub/tests/test_table_rows_measured.py
index f203624..b1f36ea 100644
--- a/packages/meshbay-hub/tests/test_table_rows_measured.py
+++ b/packages/meshbay-hub/tests/test_table_rows_measured.py
@@ -61,7 +61,7 @@ SELECTORS = [f"tbody tr:nth-child({r}) td:nth-child({c})"
@pytest.fixture(scope="module")
def measured(tmp_path_factory):
fragment = tmp_path_factory.mktemp("table") / "fragment.html"
- fragment.write_text(TABLE)
+ fragment.write_text(TABLE, encoding="utf-8")
proc = subprocess.run(
["python3", str(HARNESS), ",".join(str(w) for w in WIDTHS),
str(fragment), *SELECTORS],
diff --git a/packages/meshbay-hub/tests/test_transfers.py b/packages/meshbay-hub/tests/test_transfers.py
index b1bac4a..1d0c0f0 100644
--- a/packages/meshbay-hub/tests/test_transfers.py
+++ b/packages/meshbay-hub/tests/test_transfers.py
@@ -26,15 +26,15 @@ pytestmark = pytest.mark.skipif(
def _run(body, tmp_path):
module = tmp_path / "transfers.mjs"
- module.write_text(TRANSFERS.read_text())
+ module.write_text(TRANSFERS.read_text(encoding="utf-8"), encoding="utf-8")
script = tmp_path / "case.mjs"
script.write_text(
- f"import {{ TransferStore, formatSpeed }} from '{module.as_posix()}';\n"
+ f"import {{ TransferStore, formatSpeed }} from '{module.as_uri()}';\n"
"const out = [];\n"
"const say = (...a) => out.push(...a);\n"
f"{body}\n"
- "console.log(JSON.stringify(out));\n")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+ "console.log(JSON.stringify(out));\n", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
@@ -388,11 +388,11 @@ def test_asking_for_a_slot_on_a_dead_channel_does_not_throw(tmp_path):
# first time it arms its watchdog.
start = src.index("const LEASE_WATCHDOG_MS")
end = src.index("\nclass MeshBayTransport")
- module.write_text(src[start:end] + "\nexport { Lease };\n")
+ module.write_text(src[start:end] + "\nexport { Lease };\n", encoding="utf-8")
script = tmp_path / "case.mjs"
script.write_text(f"""
-import {{ Lease }} from '{module.as_posix()}';
+import {{ Lease }} from '{module.as_uri()}';
const out = [];
const transport = {{
supportsTransferSlots: true,
@@ -409,8 +409,8 @@ try {{ lease.release('cancelled'); out.push('release ok'); }}
catch (e) {{ out.push('release threw: ' + e.message); }}
clearTimeout(lease._watchdog);
console.log(JSON.stringify(out));
-""")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+""", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
out = json.loads(proc.stdout)
assert out[0] is None, f"asking for a slot threw: {out[0]}"
@@ -433,7 +433,7 @@ def test_the_slot_is_asked_for_after_there_is_somewhere_to_write():
Source-reading, because the ordering is the whole property and it has no
behaviour of its own to drive: what matters is which call comes first.
"""
- src = (STATIC / "file-utils.js").read_text()
+ src = (STATIC / "file-utils.js").read_text(encoding="utf-8")
fn = src[src.index("async function downloadEntry"):]
fn = fn[:fn.index("\n}\n")]
# `_openTargetInTurn` since target openings were serialised — same call,
@@ -832,7 +832,7 @@ def test_a_paused_transfer_is_not_filed_under_finished(tmp_path):
here: a copy of them in this file would agree with a broken version by
construction.
"""
- src = (STATIC / "app.js").read_text()
+ src = (STATIC / "app.js").read_text(encoding="utf-8")
start = src.index(" const running = items.filter(")
block = src[start:src.index("const active =", start)]
@@ -851,8 +851,8 @@ const items = [
const seen = { running, waiting, paused, finished };
console.log(JSON.stringify(Object.fromEntries(
Object.entries(seen).map(([k, v]) => [k, v.map(i => i.id)]))));
-""")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+""", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
groups = json.loads(proc.stdout)
@@ -870,7 +870,7 @@ def test_a_paused_transfer_still_counts_as_active(tmp_path):
"""The badge says how much is going on. A paused transfer is not over — the
person means to come back to it — so counting it as nothing would be a
panel that says "0" over work that is still there."""
- src = (STATIC / "app.js").read_text()
+ src = (STATIC / "app.js").read_text(encoding="utf-8")
start = src.index(" const running = items.filter(")
block = src[start:src.index("\n\n", src.index("const active =", start))]
@@ -879,8 +879,8 @@ def test_a_paused_transfer_still_counts_as_active(tmp_path):
const items = [{ id: 1, status: 'paused' }, { id: 2, status: 'done' }];
""" + block + """
console.log(JSON.stringify({ active }));
-""")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+""", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
assert json.loads(proc.stdout)["active"] == 1
@@ -897,7 +897,7 @@ def test_a_row_that_cannot_pause_says_so_where_the_button_would_be():
Shown only where a folder can actually be chosen: Firefox and Safari have
none to choose, and "choose a folder" would be advice that cannot be taken.
"""
- src = (STATIC / "app.js").read_text()
+ src = (STATIC / "app.js").read_text(encoding="utf-8")
row = src[src.index("function TransferRow"):]
row = row[:row.index("\n}\n")]
@@ -917,4 +917,4 @@ def test_the_reason_is_translated_everywhere():
"""`t()` falls back to the key, so a missing catalogue entry shows
`transfers.not_pausable` in a tooltip rather than a sentence."""
for path in sorted((STATIC / "locales").glob("*.js")):
- assert "'transfers.not_pausable'" in path.read_text(), path.name
+ assert "'transfers.not_pausable'" in path.read_text(encoding="utf-8"), path.name
diff --git a/packages/meshbay-hub/tests/test_transport_contracts.py b/packages/meshbay-hub/tests/test_transport_contracts.py
index 978c2e5..c1ca36b 100644
--- a/packages/meshbay-hub/tests/test_transport_contracts.py
+++ b/packages/meshbay-hub/tests/test_transport_contracts.py
@@ -59,22 +59,22 @@ def transport():
@pytest.fixture(scope="module")
def app():
- return APP.read_text()
+ return APP.read_text(encoding="utf-8")
@pytest.fixture(scope="module")
def chat():
- return CHAT_APP.read_text()
+ return CHAT_APP.read_text(encoding="utf-8")
@pytest.fixture(scope="module")
def group_page():
- return GROUP_PAGE.read_text()
+ return GROUP_PAGE.read_text(encoding="utf-8")
@pytest.fixture(scope="module")
def create_group():
- return CREATE_GROUP.read_text()
+ return CREATE_GROUP.read_text(encoding="utf-8")
def test_chat_history_pages_backwards(transport):
@@ -187,7 +187,7 @@ def test_messages_are_keyed_by_id_not_index(chat):
def test_presence_has_three_states_and_a_label_for_each(app):
for state in ("online", "offline", "unknown"):
- assert f"presence-{state}" in (STATIC / "style.css").read_text()
+ assert f"presence-{state}" in (STATIC / "style.css").read_text(encoding="utf-8")
assert "t('presence.' + state)" in app, (
"red and green are the pair colour-blind readers cannot separate, so "
"the dot needs a title and an aria-label, not just a colour")
@@ -278,7 +278,7 @@ def test_no_setter_survives_the_state_it_belonged_to():
"""
import re
for path in SPLIT_FILES:
- app = path.read_text()
+ app = path.read_text(encoding="utf-8")
declared = set(re.findall(r"const \[\s*\w+\s*,\s*(set\w+)\s*\]\s*=\s*useState", app))
# Names brought in from another module are defined, just not here.
imported = set()
diff --git a/packages/meshbay-hub/tests/test_upload_seal_client.py b/packages/meshbay-hub/tests/test_upload_seal_client.py
index bcda14c..f62aa68 100644
--- a/packages/meshbay-hub/tests/test_upload_seal_client.py
+++ b/packages/meshbay-hub/tests/test_upload_seal_client.py
@@ -48,10 +48,10 @@ BODY = bytes(range(256)) * 3 # 768 bytes → 24 chunks of 32
def _run_probe(payload: dict) -> dict:
with tempfile.TemporaryDirectory() as d:
f = Path(d) / "input.json"
- f.write_text(json.dumps(payload))
+ f.write_text(json.dumps(payload), encoding="utf-8")
proc = subprocess.run(
["node", str(PROBE), str(STATIC), str(f), transport_argv()],
- capture_output=True, text=True, timeout=60,
+ capture_output=True, text=True, encoding="utf-8", timeout=60,
)
if proc.returncode != 0 or not proc.stdout:
pytest.fail(f"upload probe failed:\n{proc.stderr}")
diff --git a/packages/meshbay-hub/tests/test_versions_agree.py b/packages/meshbay-hub/tests/test_versions_agree.py
index 4466c93..46887c0 100644
--- a/packages/meshbay-hub/tests/test_versions_agree.py
+++ b/packages/meshbay-hub/tests/test_versions_agree.py
@@ -27,11 +27,11 @@ PACKAGES = ROOT / "packages"
def _python_versions() -> dict[str, str]:
found = {}
for pyproject in sorted(PACKAGES.glob("*/pyproject.toml")):
- m = re.search(r'^version = "([^"]+)"', pyproject.read_text(), re.M)
+ m = re.search(r'^version = "([^"]+)"', pyproject.read_text(encoding="utf-8"), re.M)
if m:
found[f"{pyproject.parent.name}/pyproject.toml"] = m.group(1)
for init in sorted(PACKAGES.glob("*/src/*/__init__.py")):
- m = re.search(r'^__version__ = "([^"]+)"', init.read_text(), re.M)
+ m = re.search(r'^__version__ = "([^"]+)"', init.read_text(encoding="utf-8"), re.M)
if m:
found[f"{init.parent.name}/__init__.py"] = m.group(1)
return found
@@ -41,7 +41,7 @@ def _client_version() -> str | None:
pkg = PACKAGES / "meshbay-client" / "package.json"
if not pkg.exists():
return None
- return json.loads(pkg.read_text()).get("version")
+ return json.loads(pkg.read_text(encoding="utf-8")).get("version")
@pytest.mark.skipif(not PACKAGES.is_dir(), reason="package layout not present")
diff --git a/packages/meshbay-hub/tests/test_video_audio_track.py b/packages/meshbay-hub/tests/test_video_audio_track.py
index 54beca2..82d6e18 100644
--- a/packages/meshbay-hub/tests/test_video_audio_track.py
+++ b/packages/meshbay-hub/tests/test_video_audio_track.py
@@ -42,7 +42,7 @@ pytestmark = pytest.mark.skipif(
@pytest.fixture(scope="module")
def app():
- return APP.read_text()
+ return APP.read_text(encoding="utf-8")
def _player(app: str) -> str:
@@ -88,10 +88,10 @@ def _label_cases(tmp_path, app, cases, locale="en"):
"const t = (k, p) => `${k}:${p.n}`;\n"
+ src
+ "\nconst out = JSON.parse(process.argv[2]).map(audioTrackLabel);\n"
- "console.log(JSON.stringify(out));\n")
+ "console.log(JSON.stringify(out));\n", encoding="utf-8")
proc = subprocess.run(
["node", str(script), json.dumps(cases)],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_video_buffer_ceiling.py b/packages/meshbay-hub/tests/test_video_buffer_ceiling.py
index a488976..da9766e 100644
--- a/packages/meshbay-hub/tests/test_video_buffer_ceiling.py
+++ b/packages/meshbay-hub/tests/test_video_buffer_ceiling.py
@@ -67,7 +67,7 @@ pytestmark = pytest.mark.skipif(
@pytest.fixture(scope="module")
def app():
- return APP.read_text()
+ return APP.read_text(encoding="utf-8")
def _player(app: str) -> str:
@@ -95,7 +95,7 @@ HARNESS = Path(__file__).parent / "harness" / "mse_harness.mjs"
def _harness(**cfg) -> dict:
proc = subprocess.run(
["node", str(HARNESS), str(APP), json.dumps(cfg)],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
@@ -437,7 +437,7 @@ def test_credit_is_a_window_and_not_a_debt(app):
pump = pump[:pump.index("\n }, [")]
assert "STREAM_WINDOW - outstandingRef.current" in pump, (
"pump() no longer tops a window up to what is allowed in flight")
- src = APP.read_text()
+ src = APP.read_text(encoding="utf-8")
window = int(re.search(r"const STREAM_WINDOW = (\d+)", src).group(1))
assert 2 <= window <= 16, (
f"a window of {window} segments is either too small to keep the pipe "
diff --git a/packages/meshbay-hub/tests/test_video_default_season.py b/packages/meshbay-hub/tests/test_video_default_season.py
index 898d3f5..ab1d7c1 100644
--- a/packages/meshbay-hub/tests/test_video_default_season.py
+++ b/packages/meshbay-hub/tests/test_video_default_season.py
@@ -43,7 +43,7 @@ BLOCK = re.compile(r"^function defaultSeason\(show\) \{.*?^\}", re.M | re.S)
@pytest.fixture(scope="module")
def source():
- m = BLOCK.search(APP.read_text())
+ m = BLOCK.search(APP.read_text(encoding="utf-8"))
assert m, ("defaultSeason is no longer where this test reads it from — the "
"season a show opens on is untested until this is fixed")
return m.group(0)
@@ -57,8 +57,8 @@ def _default(tmp_path, source, seasons):
script.write_text(f"""
{source}
console.log(JSON.stringify(defaultSeason({json.dumps(show)})));
- """)
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+ """, encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_video_detail_measured.py b/packages/meshbay-hub/tests/test_video_detail_measured.py
index 1ac8586..4cd5969 100644
--- a/packages/meshbay-hub/tests/test_video_detail_measured.py
+++ b/packages/meshbay-hub/tests/test_video_detail_measured.py
@@ -182,7 +182,7 @@ SELECTORS = [
def measured(tmp_path_factory):
"""One browser for every width — launching one apiece cost three minutes."""
fragment = tmp_path_factory.mktemp("videodetail") / "fragment.html"
- fragment.write_text(FRAGMENT)
+ fragment.write_text(FRAGMENT, encoding="utf-8")
proc = subprocess.run(
["python3", str(HARNESS), ",".join(str(w) for w in WIDTHS),
str(fragment), *SELECTORS],
@@ -292,7 +292,7 @@ def test_the_episode_list_reserves_its_scrollbar():
rectangle — `test_layout_responsive.py` says so at length — but it is
worth more than a test that cannot fail.
"""
- css = (STATIC / "style.css").read_text()
+ css = (STATIC / "style.css").read_text(encoding="utf-8")
rule = re.search(
r"\.video-detail\.video-detail-steady \.video-season-list \{([^}]*)\}", css)
assert rule, "the steady episode-list rule is gone"
diff --git a/packages/meshbay-hub/tests/test_video_reconnect.py b/packages/meshbay-hub/tests/test_video_reconnect.py
index beeeace..91aa87b 100644
--- a/packages/meshbay-hub/tests/test_video_reconnect.py
+++ b/packages/meshbay-hub/tests/test_video_reconnect.py
@@ -53,7 +53,7 @@ pytestmark = pytest.mark.skipif(
@pytest.fixture(scope="module")
def app():
- return APP.read_text()
+ return APP.read_text(encoding="utf-8")
def _player(app: str) -> str:
@@ -87,7 +87,7 @@ def _plan(app: str, cases: list[dict]) -> list[dict]:
"(c) => reconnectPlan(c.playhead, c.range, c.ended, c.cast))));"
)
proc = subprocess.run(["node", "--input-type=module", "-e", script],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_video_seek.py b/packages/meshbay-hub/tests/test_video_seek.py
index 9436065..3289eda 100644
--- a/packages/meshbay-hub/tests/test_video_seek.py
+++ b/packages/meshbay-hub/tests/test_video_seek.py
@@ -46,7 +46,7 @@ pytestmark = pytest.mark.skipif(not APP.exists(), reason="SPA sources unavailabl
@pytest.fixture(scope="module")
def app():
- return APP.read_text()
+ return APP.read_text(encoding="utf-8")
def _player(app: str) -> str:
@@ -164,7 +164,7 @@ def test_the_leak_deadlocks_the_window_and_the_fix_clears_it():
proc = subprocess.run(
["node", str(harness), str(APP),
json.dumps({"decrementFirst": decrement_first})],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
@@ -269,7 +269,7 @@ def test_seeks_are_debounced(app):
"""Dragging fires `seeking` continuously; each one we act on costs a spawn."""
player = _player(app)
assert "SEEK_DEBOUNCE_MS" in player, "every intermediate drag position seeks"
- ms = int(re.search(r"const SEEK_DEBOUNCE_MS = (\d+)", APP.read_text()).group(1))
+ ms = int(re.search(r"const SEEK_DEBOUNCE_MS = (\d+)", APP.read_text(encoding="utf-8")).group(1))
assert 150 <= ms <= 1000, (
f"{ms} ms is either short enough to still storm the node or long "
"enough to feel broken")
@@ -289,7 +289,7 @@ def test_a_seek_inside_the_buffer_does_not_reach_the_node(app):
def test_the_position_is_kept_in_this_browser(app):
"""localStorage: no protocol, no storage for anyone else to keep, and
nothing new learns what you watch."""
- src = APP.read_text()
+ src = APP.read_text(encoding="utf-8")
assert "mb:pos:" in src, "no position is stored"
read = src[src.index("function readResumePosition"):]
read = read[:read.index("\n}")]
@@ -299,7 +299,7 @@ def test_the_position_is_kept_in_this_browser(app):
def test_a_finished_film_does_not_offer_to_resume(app):
- src = APP.read_text()
+ src = APP.read_text(encoding="utf-8")
write = src[src.index("function writeResumePosition"):]
write = write[:write.index("\n}")]
assert "RESUME_MAX_FRACTION" in write and "removeItem" in write, (
@@ -317,6 +317,6 @@ def test_the_viewer_can_refuse_the_resume(app):
@pytest.mark.parametrize("locale", ["en", "fr", "es", "pt-BR", "zh-CN", "ja",
"de", "it", "nl", "pl"])
def test_the_resume_strings_exist_everywhere(locale):
- text = (STATIC / "locales" / f"{locale}.js").read_text()
+ text = (STATIC / "locales" / f"{locale}.js").read_text(encoding="utf-8")
for key in ("video.resumed_at", "video.from_start"):
assert key in text, f"{locale} is missing {key}"
diff --git a/packages/meshbay-hub/tests/test_video_stream_switch.py b/packages/meshbay-hub/tests/test_video_stream_switch.py
index 7859057..6c67430 100644
--- a/packages/meshbay-hub/tests/test_video_stream_switch.py
+++ b/packages/meshbay-hub/tests/test_video_stream_switch.py
@@ -45,7 +45,7 @@ pytestmark = pytest.mark.skipif(
@pytest.fixture(scope="module")
def app():
- return APP.read_text()
+ return APP.read_text(encoding="utf-8")
def _player(app: str) -> str:
@@ -125,8 +125,8 @@ def test_the_stall_is_reproduced_and_the_reset_clears_it(tmp_path):
out[name] = p.st.appended - afterFirst;
}
console.log(JSON.stringify(out));
- """)
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+ """, encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
got = json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_video_subtitles.py b/packages/meshbay-hub/tests/test_video_subtitles.py
index 5a4b215..68058cd 100644
--- a/packages/meshbay-hub/tests/test_video_subtitles.py
+++ b/packages/meshbay-hub/tests/test_video_subtitles.py
@@ -47,7 +47,7 @@ pytestmark = pytest.mark.skipif(
@pytest.fixture(scope="module")
def app():
- return APP.read_text()
+ return APP.read_text(encoding="utf-8")
@pytest.fixture(scope="module")
@@ -117,10 +117,10 @@ def _label_cases(tmp_path, app, cases, locale="en"):
"const t = (k, p) => (p ? `${k}:${p.n}` : k);\n"
+ src
+ "\nconst out = JSON.parse(process.argv[2]).map(subtitleTrackLabel);\n"
- "console.log(JSON.stringify(out));\n")
+ "console.log(JSON.stringify(out));\n", encoding="utf-8")
proc = subprocess.run(
["node", str(script), json.dumps(cases)],
- capture_output=True, text=True)
+ capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_zip_size_limit.py b/packages/meshbay-hub/tests/test_zip_size_limit.py
index 9243fd1..6ef0989 100644
--- a/packages/meshbay-hub/tests/test_zip_size_limit.py
+++ b/packages/meshbay-hub/tests/test_zip_size_limit.py
@@ -46,7 +46,7 @@ def _run(total_bytes, tmp_path, picker=False):
for src in STATIC.glob("*.js"):
(sandbox / src.name).write_text(src.read_text(encoding="utf-8"),
encoding="utf-8")
- (tmp_path / "package.json").write_text('{"type":"module"}')
+ (tmp_path / "package.json").write_text('{"type":"module"}', encoding="utf-8")
# ask.js draws a dialog in the DOM, which Node has none of; the question is
# answered here instead, exactly where `confirm` used to be stubbed.
(sandbox / "ask.js").write_text(
@@ -87,7 +87,7 @@ if ({picker_js}) {{
}});
}}
-const M = await import('{(sandbox / "file-utils.js").as_posix()}');
+const M = await import('{(sandbox / "file-utils.js").as_uri()}');
// Faithful enough to the real store: it runs `prepare` and honours what it
// returns. The target is opened there now — the row exists from the click and
@@ -126,7 +126,7 @@ out.limit = M.ZIP_MAX_BYTES;
console.log(JSON.stringify(out));
""", encoding="utf-8")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
diff --git a/packages/meshbay-hub/tests/test_zipstream.py b/packages/meshbay-hub/tests/test_zipstream.py
index 51a42d0..55a4d97 100644
--- a/packages/meshbay-hub/tests/test_zipstream.py
+++ b/packages/meshbay-hub/tests/test_zipstream.py
@@ -32,12 +32,12 @@ def _build(files, force_zip64=False, tmp_path=None):
# <script type="module">, but Node reads a bare .js as CommonJS unless a
# package.json says otherwise, and there is none next to the SPA.
module = tmp_path / "zipstream.mjs"
- module.write_text(ZIPSTREAM.read_text())
+ module.write_text(ZIPSTREAM.read_text(encoding="utf-8"), encoding="utf-8")
script = tmp_path / "build.mjs"
out = tmp_path / "out.zip"
script.write_text(f"""
import {{ writeFileSync }} from 'node:fs';
-import {{ ZipStream }} from '{module.as_posix()}';
+import {{ ZipStream }} from '{module.as_uri()}';
const files = {json.dumps({k: list(v) for k, v in files.items()})};
const parts = [];
@@ -55,8 +55,8 @@ for (const [name, bytes] of Object.entries(files)) {{
}}
await zip.finish();
writeFileSync('{out.as_posix()}', Buffer.concat(parts));
-""")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+""", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return out.read_bytes()
@@ -140,14 +140,14 @@ def test_an_empty_archive_is_still_an_archive(tmp_path):
def _under(entries, dir_, tmp_path):
module = tmp_path / "zipstream.mjs"
- module.write_text(ZIPSTREAM.read_text())
+ module.write_text(ZIPSTREAM.read_text(encoding="utf-8"), encoding="utf-8")
script = tmp_path / "under.mjs"
script.write_text(f"""
-import {{ entriesUnder }} from '{module.as_posix()}';
+import {{ entriesUnder }} from '{module.as_uri()}';
const out = entriesUnder({json.dumps(entries)}, {json.dumps(dir_)});
console.log(JSON.stringify(out.map(o => o.name)));
-""")
- proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+""", encoding="utf-8")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8")
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)