"""FC-2d-vi: library-wide phash backfill (keyset, idempotent).""" import pytest from PIL import Image from backend.app.models import ImageRecord from backend.app.tasks.maintenance import backfill_phash pytestmark = pytest.mark.integration class _Ctx: def __init__(self, s): self.s = s def __enter__(self): return self.s def __exit__(self, *a): return False def _sf(db_sync): """sessionmaker-like returning the test's bound session, so the task runs against the same transaction / sees the seeded rows.""" class _SM: def __call__(self): return _Ctx(db_sync) return _SM() def _img(path, color): path.parent.mkdir(parents=True, exist_ok=True) Image.new("RGB", (64, 64), color).save(path, "JPEG") def _rec(db_sync, path, *, sha, mime="image/jpeg", phash=None): rec = ImageRecord( path=str(path), sha256=sha, phash=phash, size_bytes=1, mime=mime, width=64, height=64, origin="imported_filesystem", integrity_status="unknown", ) db_sync.add(rec) db_sync.flush() return rec def test_backfill_fills_null_image_phash(db_sync, tmp_path, monkeypatch): from backend.app.tasks import maintenance as m p = tmp_path / "a.jpg" _img(p, (10, 120, 200)) pre = tmp_path / "preset.jpg" _img(pre, (5, 5, 5)) rec = _rec(db_sync, p, sha="a" + "0" * 63, phash=None) vid = _rec( db_sync, tmp_path / "v.mp4", sha="v" + "0" * 63, mime="video/mp4", phash=None, ) missing = _rec( db_sync, tmp_path / "gone.jpg", sha="g" + "0" * 63, phash=None, ) preset = _rec( db_sync, pre, sha="p" + "0" * 63, phash="deadbeefdeadbeef", ) db_sync.commit() monkeypatch.setattr(m, "_sync_session_factory", lambda: _sf(db_sync)) updated = backfill_phash() db_sync.expire_all() assert updated == 1 assert db_sync.get(ImageRecord, rec.id).phash is not None assert db_sync.get(ImageRecord, vid.id).phash is None assert db_sync.get(ImageRecord, missing.id).phash is None assert db_sync.get(ImageRecord, preset.id).phash == "deadbeefdeadbeef" # idempotent: a second run touches nothing assert backfill_phash() == 0