- Repo-Struktur für Backend, Frontend, Dokumentation und Gitea-Workflows - FastAPI-Skeleton mit pydantic-settings und RFC-7807-artigem Fehlerformat - Vollständiges Datenmodell (15 Tabellen) als SQLAlchemy-2.0-Modelle - Alembic gegen die Async-Engine inklusive Erstmigration - Idempotenter Seed für den deutschen Kategoriebaum und Standardregeln - Endpunkte /api/health und /api/version - pytest-Infrastruktur mit transaktionsisolierten Fixtures Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
58 lines
1.8 KiB
Python
58 lines
1.8 KiB
Python
"""Tests des Stammdaten-Seeds."""
|
|
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models import Category, NotificationRule
|
|
from app.models.enums import EntryKind
|
|
from app.services.seed import seed_all
|
|
|
|
|
|
async def test_seed_erzeugt_kategoriebaum(session: AsyncSession) -> None:
|
|
await seed_all(session)
|
|
|
|
total = (await session.execute(select(func.count()).select_from(Category))).scalar_one()
|
|
assert total == 35
|
|
|
|
roots = (
|
|
(await session.execute(select(Category).where(Category.parent_id.is_(None))))
|
|
.scalars()
|
|
.all()
|
|
)
|
|
assert {root.name for root in roots} == {
|
|
"Wohnen",
|
|
"Versicherungen",
|
|
"Abos & Medien",
|
|
"Mobilität",
|
|
"Lebenshaltung",
|
|
"Finanzen",
|
|
"Einkünfte",
|
|
}
|
|
|
|
einkuenfte = next(root for root in roots if root.name == "Einkünfte")
|
|
assert einkuenfte.kind is EntryKind.INCOME
|
|
|
|
miete = (await session.execute(select(Category).where(Category.name == "Miete"))).scalar_one()
|
|
assert miete.is_fixed_cost is True
|
|
assert miete.kind is EntryKind.EXPENSE
|
|
assert miete.parent_id is not None
|
|
|
|
|
|
async def test_seed_ist_idempotent(session: AsyncSession) -> None:
|
|
first = await seed_all(session)
|
|
second = await seed_all(session)
|
|
|
|
assert first["categories"] == 35
|
|
assert second["categories"] == 0
|
|
assert second["notification_rules"] == 0
|
|
|
|
total = (await session.execute(select(func.count()).select_from(Category))).scalar_one()
|
|
assert total == 35
|
|
|
|
|
|
async def test_seed_legt_benachrichtigungsregeln_an(session: AsyncSession) -> None:
|
|
await seed_all(session)
|
|
rules = (await session.execute(select(NotificationRule))).scalars().all()
|
|
assert len(rules) == 4
|
|
assert all(rule.is_active is False for rule in rules)
|