Files
FabledCurator/tests/test_series_from_post.py
T
bvandeusen 3e22e78aa4
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 20s
CI / backend-lint-and-test (push) Successful in 33s
CI / integration (push) Successful in 3m11s
test(series): assert group start_page, not per-page stated_page
add_post now stamps the post's parsed START (constant) on every staged
pending page so the group start survives junk removal; list_pages
surfaces it as start_page. Update the stale per-page [9,10,11] assertion
to check grp["start_page"] == 9.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 23:17:14 -04:00

141 lines
4.8 KiB
Python

"""FC-6.x — promote a post to a series + add a post's pages + browse list."""
import pytest
from sqlalchemy import select
from backend.app.models import Artist, ImageRecord, Post, 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 _artist(db, name):
a = Artist(name=name, slug=name.lower().replace(" ", "-"))
db.add(a)
await db.flush()
return a
async def _post(db, artist, title, ext):
p = Post(artist_id=artist.id, external_post_id=ext, post_title=title)
db.add(p)
await db.flush()
return p
async def _post_images(db, post, artist, n):
global _SEQ
ids = []
for _ in range(n):
_SEQ += 1
rec = ImageRecord(
path=f"/tmp/fc_ps_{_SEQ}.png", sha256=f"{_SEQ:064d}",
size_bytes=1, mime="image/png", origin="downloaded",
primary_post_id=post.id, artist_id=artist.id,
)
db.add(rec)
await db.flush()
ids.append(rec.id)
return ids
@pytest.mark.asyncio
async def test_promote_post_to_series(db):
svc = SeriesService(db)
artist = await _artist(db, "Bikupan")
post = await _post(db, artist, "My Comic pages 1-3", "pp1")
imgs = await _post_images(db, post, artist, 3)
out = await svc.promote_post_to_series(post.id)
assert out["added"] == 3
assert out["name"] == "My Comic pages 1-3"
data = await svc.list_pages(out["series_tag_id"])
# one flat run in capture order, no dividers, stated pages parsed 1..3
assert [p["image_id"] for p in data["pages"]] == imgs
assert [p["stated_page"] for p in data["pages"]] == [1, 2, 3]
assert data["dividers"] == []
@pytest.mark.asyncio
async def test_promote_requires_images(db):
svc = SeriesService(db)
artist = await _artist(db, "Empty Artist")
post = await _post(db, artist, "No images", "pp2")
with pytest.raises(SeriesError):
await svc.promote_post_to_series(post.id)
@pytest.mark.asyncio
async def test_add_post_stages_pending(db):
svc = SeriesService(db)
artist = await _artist(db, "Story Artist")
sid = (await TagService(db).find_or_create("Story", TagKind.series)).id
# Seed the series with two PLACED pages.
seed_post = await _post(db, artist, "seed", "pp3a")
seed = await _post_images(db, seed_post, artist, 2)
await svc.add_images(sid, seed)
# A later post is STAGED as pending — not in the placed run yet.
post = await _post(db, artist, "Story pages 9-11", "pp3b")
imgs = await _post_images(db, post, artist, 3)
out = await svc.add_post(sid, post.id)
assert out["staged"] == 3
data = await svc.list_pages(sid)
# placed run unchanged (just the seed); pending grouped under the post.
assert [p["image_id"] for p in data["pages"]] == seed
assert len(data["pending"]) == 1
grp = data["pending"][0]
assert grp["post"]["id"] == post.id
assert grp["post"]["title"] == "Story pages 9-11"
assert [pg["image_id"] for pg in grp["pages"]] == imgs
# add_post stamps the post's parsed START (9) on every staged page; the
# group surfaces it as start_page (the default for "Place from page N").
assert grp["start_page"] == 9
@pytest.mark.asyncio
async def test_list_series_cards(db):
svc = SeriesService(db)
artist = await _artist(db, "Card Artist")
post = await _post(db, artist, "Card Comic pages 1-2", "pp4")
await _post_images(db, post, artist, 2)
out = await svc.promote_post_to_series(post.id)
rows = await svc.list_series()
card = next(r for r in rows if r["id"] == out["series_tag_id"])
assert card["name"] == "Card Comic pages 1-2"
assert card["chapter_count"] == 0 # a flat promote has no dividers
assert card["page_count"] == 2
assert card["artist_name"] == "Card Artist"
assert card["cover_thumbnail_url"]
assert card["has_gap"] is False
# artist filter keeps it; a different artist id drops it.
assert any(
r["id"] == out["series_tag_id"]
for r in await svc.list_series(artist_id=artist.id)
)
assert all(
r["id"] != out["series_tag_id"]
for r in await svc.list_series(artist_id=artist.id + 99999)
)
@pytest.mark.asyncio
async def test_source_post_label_is_per_page(db):
svc = SeriesService(db)
artist = await _artist(db, "Src Artist")
post = await _post(db, artist, "Source Comic pages 1-2", "pp5")
await _post_images(db, post, artist, 2)
out = await svc.promote_post_to_series(post.id)
data = await svc.list_pages(out["series_tag_id"])
sp = data["pages"][0]["source_post"]
assert sp is not None
assert sp["id"] == post.id
assert sp["title"] == "Source Comic pages 1-2"