Files
FabledCurator/tests/test_hidden_review.py
T
bvandeusen 6c34f86477
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 19s
CI / backend-lint-and-test (push) Successful in 36s
CI / integration (push) Successful in 3m46s
feat(gallery): hidden-view review endpoints — list + keep + un-hide (#141 step 5)
GET /api/gallery/hidden-review lists unresolved presentation auto-hide flags
(image + presentation tag + conflict tag/score), most-concerning first. POST
.../keep resolves the flag (the tag stays). POST .../unhide removes the
presentation tag (image returns to the gallery), records a TagSuggestionRejection
so the head learns it misfired, and resolves the flag. Tests for list/keep/unhide.
Frontend review strip (shown when Show-hidden is on) next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
2026-07-06 23:34:18 -04:00

90 lines
3.1 KiB
Python

"""Hidden-view review endpoints (#141): list flagged auto-hides + keep / un-hide."""
import pytest
from sqlalchemy import select
from backend.app.models import (
ImageRecord,
PresentationReview,
Tag,
TagKind,
TagSuggestionRejection,
)
from backend.app.models.tag import image_tag
pytestmark = pytest.mark.integration
async def _setup_flag(db):
banner = (await db.execute(
select(Tag).where(Tag.is_system.is_(True), Tag.name == "banner")
)).scalar_one()
content = Tag(name="looksreal", kind=TagKind.general)
db.add(content)
img = ImageRecord(
path="/images/rv.jpg", sha256="rv" * 32, size_bytes=1, mime="image/jpeg",
width=1, height=1, origin="imported_filesystem",
integrity_status="unknown",
)
db.add(img)
await db.flush()
await db.execute(image_tag.insert().values(
image_record_id=img.id, tag_id=banner.id, source="presentation_auto"))
db.add(PresentationReview(
image_record_id=img.id, tag_id=banner.id,
conflict_tag_id=content.id, conflict_score=0.8,
))
await db.commit()
return img, banner, content
async def _source(db, image_id, tag_id):
return (await db.execute(
select(image_tag.c.source).where(
image_tag.c.image_record_id == image_id,
image_tag.c.tag_id == tag_id,
)
)).scalar_one_or_none()
@pytest.mark.asyncio
async def test_hidden_review_lists_flag(client, db):
img, banner, content = await _setup_flag(db)
body = await (await client.get("/api/gallery/hidden-review")).get_json()
assert len(body["items"]) == 1
it = body["items"][0]
assert it["image_id"] == img.id
assert it["tag_id"] == banner.id
assert it["conflict_tag_id"] == content.id
assert it["conflict_name"] == "looksreal"
assert it["conflict_score"] == 0.8
assert it["thumbnail_url"]
@pytest.mark.asyncio
async def test_hidden_review_keep_resolves_but_keeps_tag(client, db):
img, banner, _ = await _setup_flag(db)
resp = await client.post(
f"/api/gallery/hidden-review/{img.id}/{banner.id}/keep")
assert resp.status_code == 204
body = await (await client.get("/api/gallery/hidden-review")).get_json()
assert body["items"] == [] # flag resolved
assert await _source(db, img.id, banner.id) == "presentation_auto" # tag stays
@pytest.mark.asyncio
async def test_hidden_review_unhide_removes_tag_and_records_rejection(client, db):
img, banner, _ = await _setup_flag(db)
resp = await client.post(
f"/api/gallery/hidden-review/{img.id}/{banner.id}/unhide")
assert resp.status_code == 204
assert await _source(db, img.id, banner.id) is None # back in the gallery
rej = (await db.execute(
select(TagSuggestionRejection).where(
TagSuggestionRejection.image_record_id == img.id,
TagSuggestionRejection.tag_id == banner.id,
)
)).scalar_one_or_none()
assert rej is not None # head learns it misfired
body = await (await client.get("/api/gallery/hidden-review")).get_json()
assert body["items"] == []