Files
F-r-ldraledigplannerare/backend/tests/unit/test_analytics_router.py
T
jacobh2 0e9b20ec37 Track preschool planning days, partner invites and real vacation forfeiture
Three strands of work that grew together across the same settings screen and
API client, so they land as one change.

Per-year preschool planning days ("studiedagar"): new table, repository
(SQLite + Postgres), service, router and migration 007, plus the hooks and
Settings UI to plan and record them per child and year. Allocation now
reserves the recorded figure for a year where one exists and only falls back
to the child-level estimate otherwise, marking that fallback as assumed.

Partner invitations: workspaces issue and list invitations, registration
accepts an invite token to join the inviter's workspace instead of creating a
personal one, and a /join/:token page plus the settings invite panel drive it.

Vacation forfeiture: tips and the dashboard now distinguish days a parent
genuinely loses from days that merely age, so the warning stops firing on
balances that were never at risk.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 11:22:07 +02:00

97 lines
3.1 KiB
Python

"""API-level tests for `GET /api/analytics/{workspace_id}/overview` and the
per-parent age4 endpoint — specifically that the new per-year balance series
(`year_balances`/`year_totals`) survives the wire boundary (§4.2's
service-computed-but-dropped-on-the-wire gotcha), matching the
`test_calendar_router.py` pattern.
"""
from datetime import date, datetime
from uuid import uuid4
from httpx import AsyncClient
from app.entities.child import Child
from app.repositories_impl.sqlite.child_repository import SQLiteChildRepository
from tests.conftest import SeededIds
class TestGetOverviewYearBalances:
async def test_wire_shape_includes_year_balances_and_year_totals(
self,
authed_client: AsyncClient,
child_repo: SQLiteChildRepository,
seeded_ids: SeededIds,
) -> None:
ws = seeded_ids.workspace_id
pd_uuid = seeded_ids.personal_data_uuid
birth = date(2025, 1, 1)
child = await child_repo.create(
Child(
id=uuid4(),
workspace_id=ws,
name="Kid",
birth_date=birth,
created_at=datetime.now(),
)
)
response = await authed_client.get(
f"/api/analytics/{ws}/overview", params={"child_id": str(child.id)}
)
assert response.status_code == 200
body = response.json()
assert "year_totals" in body
assert [yt["year"] for yt in body["year_totals"]] == [2025, 2026, 2027, 2028, 2029]
for yt in body["year_totals"]:
assert set(yt) == {"year", "pl_left", "vacation_left"}
assert len(body["parent_projections"]) == 1
projection = body["parent_projections"][0]
assert projection["personal_data_uuid"] == str(pd_uuid)
assert "year_balances" in projection
assert [yb["year"] for yb in projection["year_balances"]] == [
2025,
2026,
2027,
2028,
2029,
]
birth_year = projection["year_balances"][0]
assert birth_year["personal_data_uuid"] == str(pd_uuid)
assert birth_year["child_id"] == str(child.id)
vacation = birth_year["vacation"]
assert set(vacation) == {
"year",
"earned",
"is_assumed",
"carried_in",
"planned_use",
"left",
"carried_out",
"expiring",
"expiring_age",
"expiring_over_cap",
}
assert vacation["earned"] == 25 # statutory default — no vacation_days row entered
assert vacation["is_assumed"] is True
assert vacation["carried_in"] == 0
assert vacation["planned_use"] == 0
assert vacation["left"] == 25
parental_leave = birth_year["parental_leave"]
assert set(parental_leave) == {
"year",
"sjukpenning_used",
"lagsta_used",
"sjukpenning_left",
"lagsta_left",
}
# `seeded_ids`'s workspace has exactly one member -> full pool.
assert parental_leave["sjukpenning_left"] == 390
assert parental_leave["lagsta_left"] == 90