import pytest from backend.app.celery_app import celery from backend.app.models import ImageRecord, TagKind from backend.app.services.tag_service import TagService pytestmark = pytest.mark.integration @pytest.fixture(autouse=True) def eager(): celery.conf.task_always_eager = True yield celery.conf.task_always_eager = False async def _img(db, preds): from tests._prediction_helpers import seed_predictions img = ImageRecord( path="/images/s.jpg", sha256="s" * 64, size_bytes=1, mime="image/jpeg", width=1, height=1, origin="imported_filesystem", integrity_status="unknown", ) db.add(img) await db.commit() await seed_predictions(db, img.id, preds) await db.commit() return img @pytest.mark.asyncio async def test_get_suggestions(client, db): img = await _img( db, {"sword": {"category": "general", "confidence": 0.97}} ) resp = await client.get(f"/api/images/{img.id}/suggestions") assert resp.status_code == 200 body = await resp.get_json() assert "general" in body["by_category"] @pytest.mark.asyncio async def test_accept_requires_tag_id(client, db): img = await _img(db, {}) resp = await client.post( f"/api/images/{img.id}/suggestions/accept", json={} ) assert resp.status_code == 400 @pytest.mark.asyncio async def test_accept_then_applied(client, db): img = await _img(db, {}) tag = await TagService(db).find_or_create("AcceptMe", TagKind.character) await db.commit() resp = await client.post( f"/api/images/{img.id}/suggestions/accept", json={"tag_id": tag.id} ) assert resp.status_code == 204 @pytest.mark.asyncio async def test_dismiss(client, db): img = await _img(db, {}) tag = await TagService(db).find_or_create("DismissMe", TagKind.general) await db.commit() resp = await client.post( f"/api/images/{img.id}/suggestions/dismiss", json={"tag_id": tag.id} ) assert resp.status_code == 204 @pytest.mark.asyncio async def test_alias_requires_fields(client, db): img = await _img(db, {}) resp = await client.post( f"/api/images/{img.id}/suggestions/alias", json={"alias_string": "x"} ) assert resp.status_code == 400 async def _img_at(db, path, sha, preds): from tests._prediction_helpers import seed_predictions img = ImageRecord( path=path, sha256=sha, size_bytes=1, mime="image/jpeg", width=1, height=1, origin="imported_filesystem", integrity_status="unknown", ) db.add(img) await db.commit() await seed_predictions(db, img.id, preds) await db.commit() return img @pytest.mark.asyncio async def test_alias_roundtrip_resolves_by_raw_key(client, db): """Locks the modal-alias contract: the suggestion exposes the RAW model key, an alias authored with that key resolves on a later image, and the resolved suggestion is flagged via_alias. (Pre-fix the modal stored the normalized display name, which never resolved.)""" canonical = await TagService(db).find_or_create( "Sasuke Uchiha", TagKind.character ) await db.commit() preds = {"uchiha_sasuke": {"category": "character", "confidence": 0.99}} img_a = await _img_at(db, "/images/alias_a.jpg", "a" * 64, preds) # (a) raw_name is exposed so the modal can author the alias with it; the # raw prediction doesn't textually match the tag, so it'd otherwise be +new. body = await ( await client.get(f"/api/images/{img_a.id}/suggestions") ).get_json() sug = body["by_category"]["character"][0] assert sug["raw_name"] == "uchiha_sasuke" assert sug["via_alias"] is False assert sug["creates_new_tag"] is True # Author the alias keyed by the RAW key (what the frontend now sends). resp = await client.post( f"/api/images/{img_a.id}/suggestions/alias", json={ "alias_string": sug["raw_name"], "alias_category": "character", "canonical_tag_id": canonical.id, }, ) assert resp.status_code == 204 # (b) A DIFFERENT image with the same prediction now resolves via the alias # (image A's tag is applied, so it's filtered there). Had the alias been # stored under the display name, this would NOT resolve. img_b = await _img_at(db, "/images/alias_b.jpg", "b" * 64, preds) body_b = await ( await client.get(f"/api/images/{img_b.id}/suggestions") ).get_json() sug_b = body_b["by_category"]["character"][0] assert sug_b["canonical_tag_id"] == canonical.id assert sug_b["via_alias"] is True assert sug_b["creates_new_tag"] is False assert sug_b["raw_name"] == "uchiha_sasuke"