summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_hub_api.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-16 20:57:52 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-16 20:57:52 +0200
commit5dea19d9950518887be7eb14696f600ee023dbbd (patch)
tree024b5f946f737713b175f33b27426f9d9a74cb5a /packages/meshbay-hub/tests/test_hub_api.py
parent373f83236816aaff536dfa2f9589801acac44012 (diff)
downloadmeshbay-5dea19d9950518887be7eb14696f600ee023dbbd.tar.gz
fix(hub): serve the SPA under a fingerprint of what it is
`Cache-Control: no-cache` requires a browser to revalidate, but it only binds one that asks. A browser that cached app.js before that header existed applies heuristic freshness instead — a fraction of the file's age, which for a file dated weeks ago is days — and never asks. It then runs an old player against a new node. That cost most of a session. A phone kept a player without the read-ahead bound and filled the browser's buffer ceiling at 106 MB, the exact symptom the bound had been written to remove, for an hour after the bounded player went live. A fix that is written, tested, deployed and served, and still not what runs, is indistinguishable from a fix that does not work. The whole module graph now lives under `/a/<content-hash>/`. A path prefix rather than a query string, because relative imports inherit it: `app.js` reaching for `./i18n.js` gets the build it was written against, and never a mixture of two — which does not render a stale page, it fails to link. The URL changes with the content, so those may be cached hard. `sw.js` stays at the root. Its scope is its own path, and under the prefix it would no longer control the pages whose downloads it exists to intercept.
Diffstat (limited to 'packages/meshbay-hub/tests/test_hub_api.py')
-rw-r--r--packages/meshbay-hub/tests/test_hub_api.py19
1 files changed, 13 insertions, 6 deletions
diff --git a/packages/meshbay-hub/tests/test_hub_api.py b/packages/meshbay-hub/tests/test_hub_api.py
index 5314b93..724c8ec 100644
--- a/packages/meshbay-hub/tests/test_hub_api.py
+++ b/packages/meshbay-hub/tests/test_hub_api.py
@@ -722,19 +722,26 @@ async def test_ip_log_cleanup(app):
@pytest.mark.asyncio
async def test_webapp_html_includes_scripts(client):
- """SPA HTML shell includes all required script tags."""
+ """SPA HTML shell includes all required script tags.
+
+ The paths carry a build fingerprint (`/a/<hash>/app.js`) so that a browser
+ cannot serve an older build out of its own cache — see
+ test_asset_versioning.py. What this test still owns is that every piece is
+ referenced at all, and in an order where each one's dependencies are
+ already loaded.
+ """
+ from meshbay_hub.api.webapp import ASSET_V
+
r = await client.get("/")
assert r.status_code == 200
html = r.text
assert "<!DOCTYPE html>" in html
assert '<div id="app">' in html
- assert 'src="/keyderive.js"' in html
- assert 'src="/crypto.js"' in html
- assert 'src="/transport.js"' in html
- assert 'src="/app.js"' in html
+ for asset in ("keyderive.js", "crypto.js", "transport.js", "app.js"):
+ assert f'src="/a/{ASSET_V}/{asset}"' in html, f"{asset} is not loaded"
assert 'type="module"' in html
assert 'rel="stylesheet"' in html
- assert 'href="/style.css"' in html
+ assert f'href="/a/{ASSET_V}/style.css"' in html
# ── Password split (T1 fix) ─────────────────────────────────────────────────