"""Region storage/service for the crop pipeline (#114).""" import pytest from backend.app.models import ImageRecord from backend.app.services.ml.regions import RegionService pytestmark = pytest.mark.integration async def _img(db, sha) -> ImageRecord: img = ImageRecord( path=f"/images/{sha}.jpg", sha256=sha, size_bytes=1, mime="image/jpeg", width=1, height=1, origin="imported_filesystem", integrity_status="unknown", ) db.add(img) await db.flush() return img @pytest.mark.asyncio async def test_replace_and_get_regions(db): img = await _img(db, "a" * 64) svc = RegionService(db) n = await svc.replace_regions(img.id, ["figure"], [ {"kind": "figure", "bbox": (0.1, 0.1, 0.3, 0.4), "score": 0.9, "detector_version": "det-v1", "frame_time": 42.5}, ]) await db.commit() assert n == 1 regs = await svc.get_regions(img.id) assert len(regs) == 1 r = regs[0] assert r.kind == "figure" assert r.rw == pytest.approx(0.3) and r.rh == pytest.approx(0.4) assert r.score == pytest.approx(0.9) assert r.frame_time == pytest.approx(42.5) # video frame timestamp @pytest.mark.asyncio async def test_replace_is_scoped_by_kind(db): img = await _img(db, "b" * 64) svc = RegionService(db) await svc.replace_regions(img.id, ["figure"], [ {"kind": "figure", "bbox": (0.0, 0.0, 0.5, 0.5)}, ]) await svc.replace_regions(img.id, ["concept"], [ {"kind": "concept", "bbox": (0.5, 0.5, 0.2, 0.2)}, ]) await db.commit() # Re-running the figure detector must NOT wipe the concept region. await svc.replace_regions(img.id, ["figure"], [ {"kind": "figure", "bbox": (0.1, 0.1, 0.4, 0.4)}, ]) await db.commit() kinds = sorted(r.kind for r in await svc.get_regions(img.id)) assert kinds == ["concept", "figure"] @pytest.mark.asyncio async def test_ccip_vector_round_trips(db): img = await _img(db, "c" * 64) svc = RegionService(db) await svc.replace_regions(img.id, ["figure"], [ {"kind": "figure", "bbox": (0.0, 0.0, 0.5, 0.5), "ccip_embedding": [0.1] * 768, "embedding_version": "ccip-test"}, ]) await db.commit() r = (await svc.get_regions(img.id, kinds=["figure"]))[0] assert r.ccip_embedding is not None assert len(list(r.ccip_embedding)) == 768 assert r.siglip_embedding is None