summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_downloads.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-09 12:43:08 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-09 12:43:08 +0200
commitdee57df42a525cead93fa30b4e7fa38a489d5b11 (patch)
tree544f952e55f23205c26492d5cc76fe37118f3e98 /packages/meshbay-hub/tests/test_downloads.py
parent4f5d3d4ac151874f03c6fcc451d6b1d5bb1efb78 (diff)
downloadmeshbay-dee57df42a525cead93fa30b4e7fa38a489d5b11.tar.gz
fix(spa): wake the download worker before handing it a stream
Reported from Chrome: a download started while an upload was running took thirty seconds to begin, every time. The console named it exactly — /_mbdl/mtty5btz-sbmgdegx 404 () [MeshBay] the worker did not answer the download within 15s (attempt 1) A 404 from the hub means the request reached the *network*: the worker looked, found no entry for that id and let it through. So the worker was alive and controlling the page, and the message handing it the stream had simply never been processed. `pending` lives in the worker's memory, and a worker with nothing to do is terminated within tens of seconds. A WebRTC upload gives it no events at all, so minutes of uploading leave it dead; the stream posted to it is lost, silently, and the iframe then wakes it with nothing to find. `mbdl-ping` already existed for this exact reason -- sent every ten seconds *while* writing, because a streaming response does not count as activity. Nothing sent one before *starting*. So a download now wakes the worker and waits for the pong, and `sw.js` answers `mbdl-ready` once it has actually stored the entry, which the page waits for before navigating: confirmed rather than assumed. A worker that predates the ack sends nothing and the page navigates anyway, which is what it did before. This cause was measured and wrongly dismissed hours earlier, with an idle probe that made the worker work between its own attempts -- it never actually slept. A measurement that does not reproduce the conditions refutes nothing. The harness now models a worker that is asleep: a ping wakes it, and anything else posted while it sleeps is lost, which is what made the failure silent. `test_backpressure_is_real` read the first `worker.postMessage` in the function to check that the readable half is transferred rather than copied. The wake-up put a ping in front of it, so it began inspecting a call that carries only a port -- and kept passing. It now checks every post, each bounded by its own call, since the keep-alive ping transfers nothing at all. Same shape as the upload-seal contract this morning: a guard that reads "the first" stops guarding the moment something is inserted before it. Hub suite 851 passed. Both new cases checked against the unfixed source. 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_downloads.py')
-rw-r--r--packages/meshbay-hub/tests/test_downloads.py20
1 files changed, 17 insertions, 3 deletions
diff --git a/packages/meshbay-hub/tests/test_downloads.py b/packages/meshbay-hub/tests/test_downloads.py
index 41ae5d3..afb85d6 100644
--- a/packages/meshbay-hub/tests/test_downloads.py
+++ b/packages/meshbay-hub/tests/test_downloads.py
@@ -175,9 +175,23 @@ def test_backpressure_is_real(tmp_path):
# The transfer list may carry more than the stream — a reply port rides
# along now — so this asserts that `readable` is transferred, not the exact
# shape of the list.
- transfer = fn[fn.index("worker.postMessage("):]
- transfer = transfer[transfer.index("["):transfer.index("]") + 1]
- assert "readable" in transfer, "the readable half must be transferred, not copied"
+ #
+ # And every `postMessage` in here, not the first: a ping is sent to wake the
+ # worker before it is handed anything, and it carries only a port. Reading
+ # the first one would have moved this check onto the ping the day it was
+ # added, leaving the stream unguarded while still passing.
+ posts = []
+ rest = fn
+ while "worker.postMessage(" in rest:
+ rest = rest[rest.index("worker.postMessage("):]
+ # Bounded by the call's own end: the keep-alive ping transfers nothing
+ # at all, and reaching past it for a `[` would read the next call's.
+ posts.append(rest[:rest.index(");") + 2])
+ rest = rest[len("worker.postMessage("):]
+ lists = [c[c.index("["):c.index("]") + 1] for c in posts if "[" in c]
+ assert len(posts) >= 2, "the wake-up and the stream are both posted from here"
+ assert any("readable" in t for t in lists), (
+ "the readable half must be transferred, not copied")
assert "writer.write(bytes)" in fn
assert "return null" in fn, "a browser that cannot transfer streams must say so"