"""FC-6.x — cosmetic chapter dividers over the flat series page run.""" import pytest from sqlalchemy import func, select from backend.app.models import ImageRecord, TagKind from backend.app.models.series_chapter import SeriesChapter 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_sc_{_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 _series(db, name): return (await TagService(db).find_or_create(name, TagKind.series)).id async def _order(db, sid): rows = ( await db.execute( select(SeriesPage.image_id) .where(SeriesPage.series_tag_id == sid) .order_by(SeriesPage.page_number) ) ).scalars().all() return list(rows) async def _dividers(db, sid): """(anchor_image_id, title, stated_part) ordered by anchor page position.""" rows = ( await db.execute( select( SeriesChapter.id, SeriesPage.image_id, SeriesChapter.title, SeriesChapter.stated_part, ) .join(SeriesPage, SeriesPage.id == SeriesChapter.anchor_page_id) .where(SeriesChapter.series_tag_id == sid) .order_by(SeriesPage.page_number) ) ).all() return rows @pytest.mark.asyncio async def test_create_divider_anchors_before_a_page(db): svc = SeriesService(db) sid = await _series(db, "C1") i1, i2, i3 = await _img(db), await _img(db), await _img(db) await svc.add_images(sid, [i1, i2, i3]) await svc.create_divider(sid, i2, title="Two", stated_part=2) divs = await _dividers(db, sid) assert len(divs) == 1 assert divs[0].image_id == i2 assert divs[0].title == "Two" assert divs[0].stated_part == 2 @pytest.mark.asyncio async def test_create_divider_rejects_image_not_in_series(db): svc = SeriesService(db) sid = await _series(db, "C2") i1 = await _img(db) await svc.add_images(sid, [i1]) with pytest.raises(SeriesError): await svc.create_divider(sid, 999999) @pytest.mark.asyncio async def test_one_divider_per_page(db): svc = SeriesService(db) sid = await _series(db, "C3") i1, i2 = await _img(db), await _img(db) await svc.add_images(sid, [i1, i2]) await svc.create_divider(sid, i2) with pytest.raises(SeriesError): await svc.create_divider(sid, i2) @pytest.mark.asyncio async def test_update_divider_sets_part_title_and_moves_anchor(db): svc = SeriesService(db) sid = await _series(db, "C4") i1, i2, i3 = await _img(db), await _img(db), await _img(db) await svc.add_images(sid, [i1, i2, i3]) d = await svc.create_divider(sid, i2) await svc.update_divider( sid, d["id"], set_title=True, title="X", set_part=True, stated_part=3 ) await svc.update_divider( sid, d["id"], set_anchor=True, anchor_image_id=i3 ) divs = await _dividers(db, sid) assert divs[0].image_id == i3 assert divs[0].title == "X" assert divs[0].stated_part == 3 @pytest.mark.asyncio async def test_delete_divider_leaves_pages(db): svc = SeriesService(db) sid = await _series(db, "C5") i1, i2, i3 = await _img(db), await _img(db), await _img(db) await svc.add_images(sid, [i1, i2, i3]) d = await svc.create_divider(sid, i2) await svc.delete_divider(sid, d["id"]) assert await _order(db, sid) == [i1, i2, i3] assert len(await _dividers(db, sid)) == 0 @pytest.mark.asyncio async def test_removing_anchor_page_cascades_its_divider(db): svc = SeriesService(db) sid = await _series(db, "C6") i1, i2, i3 = await _img(db), await _img(db), await _img(db) await svc.add_images(sid, [i1, i2, i3]) await svc.create_divider(sid, i2) await svc.remove_images(sid, [i2]) # the divider anchored at i2 cascaded away; pages compacted to 1..N. assert len(await _dividers(db, sid)) == 0 assert await _order(db, sid) == [i1, i3] cnt = await db.scalar( select(func.count()).select_from(SeriesChapter) .where(SeriesChapter.series_tag_id == sid) ) assert cnt == 0 @pytest.mark.asyncio async def test_list_pages_returns_dividers_and_part_gaps(db): svc = SeriesService(db) sid = await _series(db, "C7") i1, i2, i3 = await _img(db), await _img(db), await _img(db) await svc.add_images(sid, [i1, i2, i3]) await svc.create_divider(sid, i1, stated_part=1) await svc.create_divider(sid, i3, stated_part=3) out = await svc.list_pages(sid) assert len(out["dividers"]) == 2 assert len(out["part_gaps"]) == 1 assert out["part_gaps"][0]["start"] == 2 assert out["part_gaps"][0]["end"] == 2 @pytest.mark.asyncio async def test_add_with_stated_pages_records_them(db): svc = SeriesService(db) sid = await _series(db, "C8") i1, i2 = await _img(db), await _img(db) await svc.add_images(sid, [i1, i2], stated_pages={i1: 9, i2: 10}) rows = dict( ( await db.execute( select(SeriesPage.image_id, SeriesPage.stated_page) .where(SeriesPage.series_tag_id == sid) ) ).all() ) assert rows[i1] == 9 assert rows[i2] == 10