fix(thumbs): the cache aged the booth it cached, and two more surfaces
Two corrections to the thumbnail work, the first of them a live bug shipped an
hour ago and caught by design-dev before its worst form landed.
⚠ GENERATING A THUMBNAIL RESET THE BOOTH'S EXPIRY CLOCK. `_newest_mtime`
excludes `.lock` sidecars because machinery is not the operator doing something;
the thumbnail cache is machinery too, and it is written by the SERVER on a mere
view. Excluding the cache's CONTENTS turned out not to be enough — creating
`.thumbs/` touches the BOOTH DIRECTORY's own mtime, which is exactly what
_newest_mtime seeds from. The booth's stamp is now restored across the mkdir,
which cannot hide real activity because any file an agent adds is counted by its
own mtime in the same walk.
The failure this prevents is not small. Once the Desk's preview strip pulls a
thumbnail per booth, ONE INDEX LOAD would have pushed every booth's expiry out
and the TTL would never have fired again — nothing would ever sweep. It was
already live for the gallery, one booth at a time.
TWO MORE SURFACES, because the fix only helped where it was wired:
Desk preview strip four small images per booth on the page he opens FIRST.
design-dev measured 28 originals / 24.1 MB on a 12-booth
copy; live has 28. The heaviest surface in the service,
heavier than the gallery it previews.
flag tray _marks.html rendered originals as tray thumbnails.
The review stage stays on the original, because that is the full-size review.
754 green plus the new guards.
This commit is contained in:
@@ -1096,3 +1096,42 @@ def test_a_booth_name_cannot_reach_a_js_string_context(client):
|
||||
|
||||
assert 'data-confirm="wipe"' in html, "the name travels as data, where escaping is escaping"
|
||||
assert ">'+xssCanary7+'<" in html, "and still renders as the name it is"
|
||||
|
||||
|
||||
def test_generating_a_thumbnail_does_not_age_a_booth(tmp_path):
|
||||
"""⚠ A VIEW-DRIVEN WRITE MUST NOT RESET THE EXPIRY CLOCK, and the thumbnail
|
||||
cache is the first thing in this repo that writes without the operator
|
||||
doing anything.
|
||||
|
||||
`.viewed` counts as activity ON PURPOSE — U4's "viewing is activity" — but
|
||||
that is a DELIBERATE look. A derived cache is machinery, exactly like the
|
||||
`.lock` sidecars already excluded here, and it is written by the SERVER.
|
||||
|
||||
The failure this prevents is not small. Once the Desk's preview strip pulls
|
||||
a thumbnail for every booth, loading the index would touch every booth's
|
||||
cache and push every expiry out — the TTL would never fire again and
|
||||
nothing would ever sweep. Caught by design-dev before the strip landed;
|
||||
the bug was already live for the gallery.
|
||||
|
||||
Defeating change: dropping the THUMB_DIR arm of the exclusion."""
|
||||
import os
|
||||
import time
|
||||
|
||||
from booth.app import _newest_mtime
|
||||
from booth.thumbs import ensure_thumb
|
||||
|
||||
pytest.importorskip("PIL.Image")
|
||||
from PIL import Image
|
||||
|
||||
b = tmp_path / "g"
|
||||
b.mkdir()
|
||||
Image.new("RGB", (1024, 1024), (9, 9, 9)).save(b / "a.png")
|
||||
old = time.time() - 86400 * 3
|
||||
for p in b.rglob("*"):
|
||||
os.utime(p, (old, old))
|
||||
os.utime(b, (old, old))
|
||||
|
||||
before = _newest_mtime(b)
|
||||
assert ensure_thumb(b, "a.png") is not None, "nothing was generated to test"
|
||||
assert _newest_mtime(b) == pytest.approx(before, abs=2), \
|
||||
"generating a thumbnail reset the booth's expiry clock"
|
||||
|
||||
Reference in New Issue
Block a user