aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_streamed_download_reliability.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-09 10:05:30 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-09 10:05:30 +0200
commitfc148e185c01b2e25361c7625a67d310d2e1d288 (patch)
tree63d5b8f290a424cec235c47f55e42c290b4a3e46 /packages/meshbay-hub/tests/test_streamed_download_reliability.py
parent7000082b748bf608484f94d30d63b2c6b83e6814 (diff)
downloadmeshbay-fc148e185c01b2e25361c7625a67d310d2e1d288.tar.gz
fix(spa): repair a page the download worker cannot serve
Downloads on Firefox failed with "the worker did not answer the download within 15s", every time, for one operator, while the same profile driven from here succeeded every time. Their own test sequence found it: a freshly started browser downloaded four files out of four, twice; one Ctrl+F5 and every attempt afterwards failed; restart, fine again; Ctrl+F5 before any attempt and the very first one failed. A document fetched by a hard reload is loaded with the service worker bypassed. It can still be claimed afterwards, so `navigator.serviceWorker.controller` comes back and every check in `_claimController` passes — but the navigations that document starts keep missing the worker, and the hidden iframe a streamed download needs is a navigation. On Firefox and Safari that is the only way to write a file too large to hold in memory, so the download cannot happen at all, for the life of the page. Being controlled is not being servable, so priming now asks the question directly instead of inferring it: a four-byte stream and a hidden iframe, exactly as a real download would, torn down completely so nothing lands in the download folder. When it goes unanswered the page reloads once, ordinarily, which puts it back under the worker. The flag lives in sessionStorage rather than a variable because it has to survive the reload it triggers, and because a page that is still unservable afterwards must stop rather than loop. Also stops telling people to change browser. The message said "use the desktop app, or Chrome or Edge" for a state an ordinary reload undoes, on the one path Firefox has no alternative to; all ten catalogues now say to reload first. The hard reloads were on my instruction: the SPA's HTML is served `no-store`, so a plain reload has always picked up a new build and Ctrl+F5 was never needed. Hub suite 834 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HCGdheDLxGReuKHga3BtST
Diffstat (limited to 'packages/meshbay-hub/tests/test_streamed_download_reliability.py')
-rw-r--r--packages/meshbay-hub/tests/test_streamed_download_reliability.py74
1 files changed, 74 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_streamed_download_reliability.py b/packages/meshbay-hub/tests/test_streamed_download_reliability.py
index 9e6f77d..3033a95 100644
--- a/packages/meshbay-hub/tests/test_streamed_download_reliability.py
+++ b/packages/meshbay-hub/tests/test_streamed_download_reliability.py
@@ -110,6 +110,16 @@ Object.defineProperty(globalThis, 'navigator', {
globalThis.window = globalThis;
globalThis.isSecureContext = true;
+// The self-test's repair reloads once and remembers it for the tab; both have
+// to exist here or priming the worker throws instead of repairing.
+const session = new Map();
+globalThis.sessionStorage = {
+ getItem: k => (session.has(k) ? session.get(k) : null),
+ setItem: (k, v) => session.set(k, String(v)),
+ removeItem: k => session.delete(k),
+};
+log.reloads = 0;
+globalThis.location = { reload: () => { log.reloads += 1; } };
globalThis.document = {
createElement: () => ({ hidden: false, src: '', remove() {} }),
body: {
@@ -377,3 +387,67 @@ if (target) await target.writable.close();
"the stuck registration was left in place")
assert out["target"] is True, (
"discarding it did not get the page a worker it could stream to")
+
+
+# ── A page the worker cannot serve ──────────────────────────────────────────
+
+def test_a_page_the_worker_cannot_serve_reloads_itself_once(tmp_path):
+ """Being controlled is not being servable, and the gap is a real failure.
+
+ A document fetched by a hard reload — Ctrl+F5, Ctrl+Shift+R — is loaded with
+ the service worker bypassed. It can be claimed afterwards, so `controller`
+ comes back and every check in `_claimController` passes; but the navigations
+ it starts keep missing the worker, and the hidden iframe a streamed download
+ needs is a navigation. Every download then fails with "the worker did not
+ answer" for the life of that page — on Firefox and Safari, the only path
+ there is for a file too large to hold in memory.
+
+ Reported after an operator was told to hard-reload after each deployment:
+ four downloads out of four worked on a freshly started browser, and the
+ first attempt after a Ctrl+F5 failed, every time. An ordinary reload puts
+ the document back under the worker, so priming does exactly that, once.
+ """
+ out = _run(tmp_path, """
+ M.primeServiceWorker();
+ await new Promise((r) => setTimeout(r, 6000));
+ out.reloads = log.reloads;
+ """, serve="never")
+ assert out["reloads"] == 1, (
+ "a page that cannot be served by the worker was left that way")
+
+
+def test_a_page_that_works_is_not_reloaded(tmp_path):
+ """The self-test costs milliseconds when it passes, and must cost nothing
+ else. Reloading a healthy page at boot would be a flicker on every visit."""
+ out = _run(tmp_path, """
+ M.primeServiceWorker();
+ await new Promise((r) => setTimeout(r, 3000));
+ out.reloads = log.reloads;
+ """)
+ assert out["reloads"] == 0
+
+
+def test_the_repair_happens_at_most_once(tmp_path):
+ """The flag is in sessionStorage rather than a variable because the point is
+ to survive the reload it triggers. If reloading does not help, the page
+ stays broken and says so — it does not reload again, and again."""
+ out = _run(tmp_path, """
+ sessionStorage.setItem('meshbay.sw-repaired', '1');
+ M.primeServiceWorker();
+ await new Promise((r) => setTimeout(r, 6000));
+ out.reloads = log.reloads;
+ """, serve="never")
+ assert out["reloads"] == 0, "a page that had already been repaired reloaded again"
+
+
+def test_the_self_test_leaves_no_file_behind(tmp_path):
+ """It opens a real download target to ask a real question, so it must also
+ tear it down: a completed one would drop `meshbay-selftest.bin` into the
+ download folder on every page load."""
+ src = DOWNLOADS.read_text()
+ fn = src[src.index("async function _canServeDownloads"):]
+ fn = fn[:fn.index("\n}\n")]
+ assert "writable.abort" in fn, (
+ "the self-test's stream is never aborted, so the browser keeps what it "
+ "was given")
+ assert "frame.remove" in fn