feat: Projektfundament mit Datenmodell, Migrationen und Seed
- 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
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user