"""U1 — the item record. The headline here is `zoom_carries_the_caption`. The operator reported that zoomed-in images lose their annotations; the cause was not a rendering bug but three independent readers of one truth, of which the zoom route was the one that never resolved a caption at all. These tests pin the record and pin the bug. See docs/contracts/u1_item_record.contract.md. """ import pytest from fastapi.testclient import TestClient from booth.app import build_gallery, create_app, list_booths from booth.items import ( Item, booth_items, find_item, image_chain, render_doc_body, ) def _touch(path, data=b"x"): path.parent.mkdir(parents=True, exist_ok=True) path.write_bytes(data) @pytest.fixture def client(tmp_path): app = create_app(tmp_path, ttl_hours=24, start_sweeper=False) return TestClient(app), tmp_path # ---- the record ------------------------------------------------------------- def test_section_is_the_parent_dir(tmp_path): _touch(tmp_path / "root.png") _touch(tmp_path / "v3" / "x.png") _touch(tmp_path / "v3" / "deep" / "y.png") by_rel = {it.rel: it for it in booth_items(tmp_path)} assert by_rel["root.png"].section is None assert by_rel["v3/x.png"].section == "v3" assert by_rel["v3/deep/y.png"].section == "v3/deep" def test_caption_sidecars_are_not_items(tmp_path): _touch(tmp_path / "a.png") (tmp_path / "a.txt").write_text("variant A") _touch(tmp_path / "b.png") (tmp_path / "b.png.txt").write_text("variant B") (tmp_path / "loose.txt").write_text("captions nothing") by_rel = {it.rel: it for it in booth_items(tmp_path)} assert by_rel["a.png"].caption == "variant A" assert by_rel["b.png"].caption == "variant B" assert "a.txt" not in by_rel and "b.png.txt" not in by_rel assert "loose.txt" in by_rel # a caption with nothing to caption stays visible def test_a_txt_beside_a_bin_is_its_own_item(tmp_path): """The `classify != "other"` guard: a .txt only captions a MEDIA sibling.""" _touch(tmp_path / "data.bin") (tmp_path / "data.txt").write_text("not a caption for a blob") by_rel = {it.rel: it for it in booth_items(tmp_path)} assert "data.txt" in by_rel assert by_rel["data.bin"].caption is None def test_ask_sidecars_are_not_items(tmp_path): _touch(tmp_path / "a.png") (tmp_path / "pick.ask.json").write_text("{}") (tmp_path / "pick.answer.json").write_text("{}") rels = {it.rel for it in booth_items(tmp_path)} assert rels == {"a.png"} def test_dotfiles_are_not_items(tmp_path): _touch(tmp_path / "a.png") _touch(tmp_path / ".forever") _touch(tmp_path / ".blurred") assert {it.rel for it in booth_items(tmp_path)} == {"a.png"} def test_blur_state_rides_on_the_item(tmp_path): _touch(tmp_path / "a.png") _touch(tmp_path / "b.png") (tmp_path / ".blurred").write_text("a.png\n") by_rel = {it.rel: it for it in booth_items(tmp_path)} assert by_rel["a.png"].blurred is True assert by_rel["b.png"].blurred is False def test_order_matches_todays_gallery(tmp_path): """INV-3: this unit reorganises who computes what. It must not move a tile.""" _touch(tmp_path / "z.png") _touch(tmp_path / "a.png") (tmp_path / "a.txt").write_text("cap") _touch(tmp_path / "v3" / "b.png") _touch(tmp_path / "v4" / "b.png") (tmp_path / "notes.md").write_text("# hi") _touch(tmp_path / "blob.bin") assert [it.rel for it in booth_items(tmp_path)] == [ it["name"] for it in build_gallery(tmp_path) ] # ---- helpers ---------------------------------------------------------------- def test_image_chain_is_the_images_in_order(tmp_path): _touch(tmp_path / "b.png") _touch(tmp_path / "a.png") _touch(tmp_path / "clip.webm") (tmp_path / "notes.md").write_text("# hi") assert image_chain(booth_items(tmp_path)) == ["a.png", "b.png"] def test_find_item(tmp_path): _touch(tmp_path / "a.png") items = booth_items(tmp_path) assert find_item(items, "a.png").rel == "a.png" assert find_item(items, "nope.png") is None def test_render_doc_body_markdown_and_text(tmp_path): (tmp_path / "r.md").write_text("# Title\n\n- a\n- b\n") (tmp_path / "n.txt").write_text("plain\ntext") items = {it.rel: it for it in booth_items(tmp_path)} html, is_html = render_doc_body(tmp_path, items["r.md"]) assert is_html and "