Files
F-r-ldraledigplannerare/backend/tests/unit/test_analytics_router.py
T
jacobh2 4b802ba0be Give the analytics overview a per-parent, per-year balance series
Each parent projection now carries one entry per calendar year from
birth to the fourth birthday: vacation earned, carried in, planned,
left, carried out and expiring under Semesterlagen's 20-day floor and
5-year carry-over, plus parental-leave days used and left at year end
with the age-4 clamp applied in the birthday year. The overview sums
them into per-year totals. Frontend types, mapper and fixtures follow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBRLomdLK9jF7wU7a8AA6H
2026-09-19 19:02:02 +02:00

95 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",
}
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