161 lines
5.4 KiB
Python
161 lines
5.4 KiB
Python
"""Project list metadata uses real DB state for activity and pending review counts."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
from collections.abc import AsyncIterator
|
|
from datetime import UTC, datetime, timedelta
|
|
|
|
import pytest
|
|
from sqlalchemy import delete, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
|
from ww_core.domain.project_repo import SqlProjectRepo
|
|
from ww_db import get_sessionmaker
|
|
from ww_db.models import Chapter, ChapterReview, Project, User
|
|
|
|
|
|
@pytest.fixture
|
|
async def sm() -> AsyncIterator[async_sessionmaker[AsyncSession]]:
|
|
get_sessionmaker.cache_clear()
|
|
maker = get_sessionmaker()
|
|
try:
|
|
async with maker() as probe:
|
|
await probe.execute(select(1))
|
|
except Exception:
|
|
pytest.skip("postgres not reachable")
|
|
yield maker
|
|
await maker.kw["bind"].dispose()
|
|
get_sessionmaker.cache_clear()
|
|
|
|
|
|
async def _cleanup(
|
|
sm: async_sessionmaker[AsyncSession], owner_id: uuid.UUID, project_id: uuid.UUID
|
|
) -> None:
|
|
async with sm() as session:
|
|
await session.execute(delete(ChapterReview).where(ChapterReview.project_id == project_id))
|
|
await session.execute(delete(Chapter).where(Chapter.project_id == project_id))
|
|
await session.execute(delete(Project).where(Project.id == project_id))
|
|
await session.execute(delete(User).where(User.id == owner_id))
|
|
await session.commit()
|
|
|
|
|
|
async def _project_view(
|
|
sm: async_sessionmaker[AsyncSession], owner_id: uuid.UUID, project_id: uuid.UUID
|
|
):
|
|
async with sm() as session:
|
|
view = await SqlProjectRepo(session).get(owner_id, project_id)
|
|
assert view is not None
|
|
listed = await SqlProjectRepo(session).list_for_owner(owner_id)
|
|
listed_view = next((item for item in listed if item.id == project_id), None)
|
|
assert listed_view is not None
|
|
assert listed_view.pending_review_count == view.pending_review_count
|
|
assert listed_view.updated_at == view.updated_at
|
|
return view
|
|
|
|
|
|
async def test_project_metadata_tracks_review_and_acceptance_state(
|
|
sm: async_sessionmaker[AsyncSession],
|
|
) -> None:
|
|
owner_id = uuid.uuid4()
|
|
project_id = uuid.uuid4()
|
|
base = datetime(2026, 6, 1, tzinfo=UTC)
|
|
|
|
await _cleanup(sm, owner_id, project_id)
|
|
try:
|
|
async with sm() as session:
|
|
session.add(
|
|
User(
|
|
id=owner_id,
|
|
email=f"{owner_id}@example.test",
|
|
display_name="metadata test",
|
|
)
|
|
)
|
|
await session.commit()
|
|
|
|
async with sm() as session:
|
|
session.add(
|
|
Project(
|
|
id=project_id,
|
|
owner_id=owner_id,
|
|
title="元数据测试",
|
|
created_at=base,
|
|
updated_at=base,
|
|
)
|
|
)
|
|
await session.commit()
|
|
|
|
async with sm() as session:
|
|
session.add(
|
|
Chapter(
|
|
project_id=project_id,
|
|
volume=1,
|
|
chapter_no=1,
|
|
content="草稿一",
|
|
status="draft",
|
|
version=1,
|
|
created_at=base + timedelta(hours=1),
|
|
updated_at=base + timedelta(hours=1),
|
|
)
|
|
)
|
|
await session.commit()
|
|
|
|
view = await _project_view(sm, owner_id, project_id)
|
|
assert view.pending_review_count == 1
|
|
assert view.updated_at == base + timedelta(hours=1)
|
|
|
|
async with sm() as session:
|
|
session.add(
|
|
ChapterReview(
|
|
project_id=project_id,
|
|
chapter_no=1,
|
|
chapter_version=1,
|
|
conflicts=[],
|
|
foreshadow_sug=[],
|
|
created_at=base + timedelta(hours=2),
|
|
)
|
|
)
|
|
await session.commit()
|
|
|
|
view = await _project_view(sm, owner_id, project_id)
|
|
assert view.pending_review_count == 0
|
|
assert view.updated_at == base + timedelta(hours=2)
|
|
|
|
async with sm() as session:
|
|
session.add(
|
|
Chapter(
|
|
project_id=project_id,
|
|
volume=1,
|
|
chapter_no=1,
|
|
content="终稿一",
|
|
status="accepted",
|
|
version=2,
|
|
created_at=base + timedelta(hours=3),
|
|
updated_at=base + timedelta(hours=3),
|
|
)
|
|
)
|
|
await session.commit()
|
|
|
|
view = await _project_view(sm, owner_id, project_id)
|
|
assert view.pending_review_count == 0
|
|
assert view.updated_at == base + timedelta(hours=3)
|
|
|
|
async with sm() as session:
|
|
draft = (
|
|
await session.execute(
|
|
select(Chapter).where(
|
|
Chapter.project_id == project_id,
|
|
Chapter.chapter_no == 1,
|
|
Chapter.version == 1,
|
|
)
|
|
)
|
|
).scalar_one()
|
|
draft.content = "验收后再改草稿"
|
|
draft.updated_at = base + timedelta(hours=4)
|
|
await session.commit()
|
|
|
|
view = await _project_view(sm, owner_id, project_id)
|
|
assert view.pending_review_count == 1
|
|
assert view.updated_at == base + timedelta(hours=4)
|
|
finally:
|
|
await _cleanup(sm, owner_id, project_id)
|