Files
FabledCurator/tests/test_series_service.py
T
2026-05-17 16:28:20 -04:00

129 lines
4.1 KiB
Python

import pytest
from sqlalchemy import func, select
from backend.app.models import ImageRecord, TagKind
from backend.app.models.series_page import SeriesPage
from backend.app.services.series_service import SeriesError, SeriesService
from backend.app.services.tag_service import TagService
pytestmark = pytest.mark.integration
_SEQ = 0
async def _img(db):
global _SEQ
_SEQ += 1
rec = ImageRecord(
path=f"/tmp/fc_ss_{_SEQ}.png", sha256=f"{_SEQ:064d}",
size_bytes=1, mime="image/png", origin="uploaded",
)
db.add(rec)
await db.flush()
return rec.id
async def _order(db, sid):
rows = (
await db.execute(
select(SeriesPage.image_id, SeriesPage.page_number)
.where(SeriesPage.series_tag_id == sid)
.order_by(SeriesPage.page_number)
)
).all()
return [r.image_id for r in rows]
@pytest.mark.asyncio
async def test_add_appends_in_order(db):
s = await TagService(db).find_or_create("S", TagKind.series)
i1, i2 = await _img(db), await _img(db)
n = await SeriesService(db).add_images(s.id, [i1, i2])
assert n == 2
assert await _order(db, s.id) == [i1, i2]
@pytest.mark.asyncio
async def test_add_moves_from_other_series(db):
svc = SeriesService(db)
a = await TagService(db).find_or_create("A", TagKind.series)
b = await TagService(db).find_or_create("B", TagKind.series)
i1 = await _img(db)
await svc.add_images(a.id, [i1])
moved = await svc.add_images(b.id, [i1])
assert moved == 1
assert await _order(db, a.id) == []
assert await _order(db, b.id) == [i1]
cnt = await db.scalar(
select(func.count())
.select_from(SeriesPage).where(SeriesPage.image_id == i1)
)
assert cnt == 1
@pytest.mark.asyncio
async def test_add_idempotent_for_same_series(db):
s = await TagService(db).find_or_create("S2", TagKind.series)
i1 = await _img(db)
svc = SeriesService(db)
await svc.add_images(s.id, [i1])
again = await svc.add_images(s.id, [i1])
assert again == 0
assert await _order(db, s.id) == [i1]
@pytest.mark.asyncio
async def test_remove(db):
s = await TagService(db).find_or_create("S3", TagKind.series)
i1, i2 = await _img(db), await _img(db)
svc = SeriesService(db)
await svc.add_images(s.id, [i1, i2])
removed = await svc.remove_images(s.id, [i1])
assert removed == 1
assert await _order(db, s.id) == [i2]
@pytest.mark.asyncio
async def test_reorder_rewrites_and_rejects_mismatch(db):
s = await TagService(db).find_or_create("S4", TagKind.series)
i1, i2, i3 = await _img(db), await _img(db), await _img(db)
svc = SeriesService(db)
await svc.add_images(s.id, [i1, i2, i3])
await svc.reorder(s.id, [i3, i1, i2])
assert await _order(db, s.id) == [i3, i1, i2]
with pytest.raises(SeriesError):
await svc.reorder(s.id, [i1, i2])
with pytest.raises(SeriesError):
await svc.reorder(s.id, [i1, i2, i3, 99999])
@pytest.mark.asyncio
async def test_set_cover_makes_page_one_preserving_rest(db):
s = await TagService(db).find_or_create("S5", TagKind.series)
i1, i2, i3 = await _img(db), await _img(db), await _img(db)
svc = SeriesService(db)
await svc.add_images(s.id, [i1, i2, i3])
await svc.set_cover(s.id, i3)
assert await _order(db, s.id) == [i3, i1, i2]
@pytest.mark.asyncio
async def test_list_pages_shape_and_guards(db):
svc = SeriesService(db)
s = await TagService(db).find_or_create("S6", TagKind.series)
i1 = await _img(db)
await svc.add_images(s.id, [i1])
out = await svc.list_pages(s.id)
assert out["series"]["id"] == s.id
assert out["pages"][0]["image_id"] == i1
assert out["pages"][0]["page_number"] == 1
assert out["pages"][0]["thumbnail_url"]
assert out["pages"][0]["image_url"].startswith("/images/")
g = await TagService(db).find_or_create("notser", TagKind.general)
with pytest.raises(SeriesError):
await svc.list_pages(g.id)
with pytest.raises(SeriesError):
await svc.add_images(g.id, [i1])
with pytest.raises(SeriesError):
await svc.add_images(s.id, list(range(1, 502)))