diff options
Diffstat (limited to 'packages/meshbay-hub/tests/test_streamed_download_reliability.py')
| -rw-r--r-- | packages/meshbay-hub/tests/test_streamed_download_reliability.py | 104 |
1 files changed, 57 insertions, 47 deletions
diff --git a/packages/meshbay-hub/tests/test_streamed_download_reliability.py b/packages/meshbay-hub/tests/test_streamed_download_reliability.py index da745d0..bfd4a89 100644 --- a/packages/meshbay-hub/tests/test_streamed_download_reliability.py +++ b/packages/meshbay-hub/tests/test_streamed_download_reliability.py @@ -81,6 +81,10 @@ Object.defineProperty(globalThis, 'navigator', { value: { serviceWorker: { get controller() { return controller; }, + // What the document started with, which is the whole of the repair's + // evidence now. `getRegistration` is asked before anything registers. + getRegistration: async () => (PLAN.registeredAtLoad + ? {active: makeController()} : undefined), register: async () => { log.registers += 1; if (PLAN.registerThrows) throw new Error('registration blocked'); @@ -118,6 +122,8 @@ Object.defineProperty(globalThis, 'navigator', { globalThis.window = globalThis; globalThis.isSecureContext = true; +// Set before the module is imported, because it reads it at evaluation. +controller = %(controlled)s ? makeController() : null; // 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(); @@ -160,7 +166,8 @@ const out = {}; def _run(tmp_path, body, *, control_after_ms=0, active=True, serve="always", register_throws=False, control_budget_ms=800, ready_settles=True, register_hangs=False, - active_after_unregister=False, control_on_claim=False): + active_after_unregister=False, control_on_claim=False, + controlled_at_load=False, registered_at_load=False): module = tmp_path / "downloads.mjs" module.write_text(DOWNLOADS.read_text()) (tmp_path / "package.json").write_text('{"type":"module"}') @@ -173,11 +180,13 @@ def _run(tmp_path, body, *, control_after_ms=0, active=True, "registerHangs": register_hangs, "activeAfterUnregister": active_after_unregister, "controlOnClaim": control_on_claim, + "registeredAtLoad": registered_at_load, } script = tmp_path / "case.mjs" script.write_text( (PRELUDE % {"plan": json.dumps(plan), "module": module.as_posix(), - "control": control_budget_ms}) + "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, @@ -398,68 +407,70 @@ if (target) await target.writable.close(); "discarding it did not get the page a worker it could stream to") -# ── A page the worker cannot serve ────────────────────────────────────────── +# ── A page loaded with the worker bypassed ────────────────────────────────── -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. +def test_a_hard_reloaded_page_reloads_itself_once(tmp_path): + """Uncontrolled at load while an active registration already exists is a + document fetched by a hard reload — Ctrl+F5, Ctrl+Shift+R — and nothing + else. Measured on Chrome at document start: a first visit has neither, an + ordinary reload has both, a hard reload has the registration and no + controller. - 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. + Such a page can still be claimed, so every control check passes; but the + navigations it starts keep missing the worker, and the hidden iframe a + streamed download needs is one. On Firefox and Safari that is the only way + to write a file too large to hold in memory. An ordinary reload undoes it. """ out = _run(tmp_path, """ M.primeServiceWorker(); - await new Promise((r) => setTimeout(r, 6000)); + await new Promise((r) => setTimeout(r, 200)); out.reloads = log.reloads; - """, serve="never") - assert out["reloads"] == 1, ( - "a page that cannot be served by the worker was left that way") + """, controlled_at_load=False, registered_at_load=True) + assert out["reloads"] == 1 -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.""" +def test_a_first_visit_is_not_a_bypass(tmp_path): + """Also uncontrolled at load, and perfectly healthy: the worker is being + installed right now and will claim the page in a moment. Reloading here + would be a flicker on everybody's first visit — and it was, taking the + group's WebRTC session down with it when it landed mid-connection.""" out = _run(tmp_path, """ M.primeServiceWorker(); - await new Promise((r) => setTimeout(r, 3000)); + await new Promise((r) => setTimeout(r, 200)); out.reloads = log.reloads; - """) + """, controlled_at_load=False, registered_at_load=False) + assert out["reloads"] == 0 + + +def test_a_controlled_page_does_not_reload(tmp_path): + """The ordinary case, which must cost nothing at all: no reload, and no + download spent asking. Chrome rations the downloads a page may start + without a user gesture to about three, and the first version of this check + asked its question by performing one — competing with the person's own + downloads for that budget.""" + out = _run(tmp_path, """ + M.primeServiceWorker(); + await new Promise((r) => setTimeout(r, 200)); + out.reloads = log.reloads; + out.navigations = log.navigations; + """, controlled_at_load=True, registered_at_load=True) assert out["reloads"] == 0 + assert out["navigations"] == 0, ( + "priming performed a download; that budget belongs to the person") 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.""" + to survive the reload it triggers, and because a page that is still bypassed + afterwards must stop rather than reload again, and again.""" out = _run(tmp_path, """ sessionStorage.setItem('meshbay.sw-repaired', '1'); M.primeServiceWorker(); - await new Promise((r) => setTimeout(r, 6000)); + await new Promise((r) => setTimeout(r, 200)); 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 + """, controlled_at_load=False, registered_at_load=True) + assert out["reloads"] == 0 # ── The claim is asked for, not waited for ────────────────────────────────── @@ -487,10 +498,9 @@ def test_an_uncontrolled_page_asks_at_once_rather_than_after_the_budget(tmp_path "after the wait, not before it") -def test_a_download_waits_for_the_self_test(tmp_path): - """A click that lands while the check is still running must not race it. - On a page that turns out to be unservable it would otherwise spend the full - two attempts failing on a path that is about to be repaired.""" +def test_a_download_waits_for_priming(tmp_path): + """A click that lands while priming is still running must not race it: on a + page about to reload, the attempt would fail for nothing.""" out = _run(tmp_path, """ M.primeServiceWorker(); const t0 = Date.now(); |