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
|
"""
A group's index is never written to browser storage.
`group_indexes` was an IndexedDB store holding a decrypted copy of every group's
index — each file's name, path, size, hash and uploader — written on every index
and every delta. The cross-group search of the time read it instead of dialling
anything, which is what it was for.
Search has dialled the nodes since 2026-08-28. That change removed the reader and
kept the writers, so the browser went on building a cleartext file listing that
nothing consulted, that no sign-out removed (the key database is a different
one), and that grew with every group ever opened. **L7**, at rest.
Showing a group's files while its node is unreachable is the only thing such a
cache buys, and it is not wanted: a listing you cannot open is worse than an
honest absence. So there is nothing left to read it with, and these tests keep it
that way — a writer reintroduced without a reader would be invisible again, and
the second time it would be invisible for the same reason as the first.
"""
import re
from pathlib import Path
STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static"
HUB_CLIENT = STATIC / "hub-client.js"
APP = STATIC / "app.js"
# The store name, read from the source rather than written down here: renaming it
# must not quietly take these tests out of the picture.
STORE = re.search(r"const IDB_STORE = '([^']+)';",
HUB_CLIENT.read_text(encoding="utf-8")).group(1)
def _functions(src: str) -> dict[str, str]:
"""Every top-level function in a module, by name."""
out = {}
starts = [(m.start(), m.group(1)) for m in
re.finditer(r"^(?:async )?function (\w+)\(", src, re.M)]
for i, (at, name) in enumerate(starts):
end = starts[i + 1][0] if i + 1 < len(starts) else len(src)
out[name] = src[at:end]
return out
def test_only_the_purge_touches_the_old_store():
"""
Creating it and emptying it, and nothing else.
`openDB` still creates the store because dropping it needs a version bump,
and a version bump is an upgrade another tab can block — which would take
playlists down with it, since they share this database. An empty store costs
nothing; the point is that nothing writes to it.
"""
fns = _functions(HUB_CLIENT.read_text(encoding="utf-8"))
touching = sorted(n for n, body in fns.items() if "IDB_STORE" in body)
assert touching == ["openDB", "purgeGroupIndexCache"], (
f"{touching} touch the {STORE!r} store; only creating and emptying it "
"are allowed, and a write to it is a file listing kept on disk that "
"nothing will ever read")
def test_nothing_writes_a_group_index_to_the_store():
"""Stated on the operation rather than on the callers, so a new one is caught."""
fns = _functions(HUB_CLIENT.read_text(encoding="utf-8"))
purge = fns["purgeGroupIndexCache"]
assert ".clear()" in purge
for write in (".put(", ".add(", ".putAll("):
assert write not in purge, f"the purge does a {write} — it must only clear"
def test_no_module_carries_a_cache_writer_any_more():
"""
The functions are gone, so the way this comes back is a new one. Any export
of hub-client.js whose name is about caching an index is refused here rather
than discovered months later with a store full of filenames.
"""
src = HUB_CLIENT.read_text(encoding="utf-8")
for name in re.findall(r"^(?:async )?function (\w+)\(", src, re.M):
assert not re.search(r"cache.*index|index.*cache", name, re.I) \
or name == "purgeGroupIndexCache", (
f"{name} looks like an index cache again — the store it would write "
"to has no reader, and adding one was decided against")
def test_the_purge_is_actually_called():
"""
The defect being cleaned up was a function nobody called. A purge nobody
calls is the same defect wearing the opposite hat: the data stays on every
machine that already has it, and nothing says so.
"""
app = APP.read_text(encoding="utf-8")
assert "purgeGroupIndexCache" in app, "app.js no longer imports the purge"
call = re.search(r"purgeGroupIndexCache\(\)", app)
assert call, "the purge is imported and never called"
mount = app.index("const mount = () => {")
assert "purgeOnce()" in app[mount:mount + 400], (
"the purge is no longer run at start-up, so a browser that still holds "
"the old store keeps it")
|