1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
"""
Making sure the browser runs the build we deployed.
A fix can be written, tested, deployed, and served, and still not be what runs.
`Cache-Control: no-cache` requires a browser to revalidate — but it only binds
one that asks, and a browser that cached the SPA *before* that header existed
applies heuristic freshness instead: a fraction of the file's age, which for a
file dated weeks ago is days. It does not ask, so it never learns.
That happened here. A phone ran a player without the read-ahead bound and filled
the browser's buffer ceiling at 106 MB — the exact symptom the bound was written
to remove — for an hour after the bounded player went live. Two sessions were
spent looking at the node.
So the URL now carries a fingerprint of what is being served, and the whole
module graph lives under it: `/a/<hash>/app.js` importing `./i18n.js` resolves
to `/a/<hash>/i18n.js`. A URL that changes with the content cannot serve
yesterday's build, and cannot serve half of each.
"""
import re
import pytest
from fastapi.testclient import TestClient
from meshbay_hub.api.webapp import ASSET_V, STATIC_DIR, _asset_version
from meshbay_hub.app import create_app
@pytest.fixture(scope="module")
def client():
return TestClient(create_app())
def _shell_refs(html: str) -> list[str]:
return re.findall(r'(?:src|href)="([^"]+)"', html)
def test_every_asset_the_shell_loads_is_versioned(client):
refs = _shell_refs(client.get("/").text)
assert refs, "the shell references nothing at all"
stale = [u for u in refs if not u.startswith(f"/a/{ASSET_V}/")]
assert not stale, (
f"served from an unversioned URL, so a stale cache can win: {stale}")
def test_the_module_graph_resolves_under_the_same_prefix(client):
"""The point of a path prefix rather than a query string.
`import './i18n.js'` from a versioned app.js resolves against the versioned
directory. A query string on app.js alone would not: its imports would fall
back to the bare paths, and the graph could be assembled from two builds —
which does not render a stale page, it fails to link at all.
"""
for path in ("app.js", "i18n.js", "locales/fr.js", "transport.js",
"vendor/argon2.min.js"):
r = client.get(f"/a/{ASSET_V}/{path}")
assert r.status_code == 200, f"{path} is not served under the version"
def test_versioned_assets_may_be_cached_hard(client):
"""Which is the trade the fingerprint buys."""
cc = client.get(f"/a/{ASSET_V}/app.js").headers.get("cache-control", "")
assert "immutable" in cc and "max-age=31536000" in cc, (
f"versioned assets are served as {cc!r} — the whole point of a URL that "
"changes with the content is that it need never be revalidated")
def test_the_unversioned_path_still_revalidates(client):
"""It still answers — old bookmarks, and sw.js has to live there."""
r = client.get("/app.js")
assert r.status_code == 200
assert r.headers.get("cache-control") == "no-cache"
def test_the_service_worker_is_not_versioned(client):
"""Its scope is its own path.
Served from /a/<hash>/sw.js it would only control /a/<hash>/, and the
pages whose downloads it exists to intercept are not under there.
"""
assert client.get("/sw.js").status_code == 200
assert "/sw.js" not in _shell_refs(client.get("/").text)
def test_the_fingerprint_follows_the_content(tmp_path, monkeypatch):
"""Otherwise it is decoration.
Pinning this to the file contents rather than to the package version is
deliberate: a redeploy without a version bump is the common case during a
debugging session, and that is exactly when a stale player costs the most.
"""
before = _asset_version()
target = STATIC_DIR / "app.js"
original = target.read_bytes()
try:
target.write_bytes(original + b"\n// touched\n")
assert _asset_version() != before, (
"the fingerprint did not move when app.js did, so a redeploy "
"serves the new file at the old URL")
finally:
target.write_bytes(original)
assert _asset_version() == before, "the fingerprint is not reproducible"
|