"""Tests for /life-events and /goals.""" from __future__ import annotations from collections.abc import AsyncIterator from decimal import Decimal import pytest_asyncio from httpx import ASGITransport, AsyncClient from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker from fire_planner.api.dependencies import get_session from fire_planner.app import app from fire_planner.db import Scenario @pytest_asyncio.fixture async def client(engine: AsyncEngine, session: AsyncSession) -> AsyncIterator[AsyncClient]: factory = async_sessionmaker(engine, expire_on_commit=False) async def _override() -> AsyncIterator[AsyncSession]: async with factory() as s: yield s app.dependency_overrides[get_session] = _override transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as ac: yield ac app.dependency_overrides.clear() async def _seed_scenario(session: AsyncSession) -> int: scen = Scenario( external_id="user-host", kind="user", name="Host plan", jurisdiction="uk", strategy="trinity", leave_uk_year=0, glide_path="static", spending_gbp=Decimal("60000"), nw_seed_gbp=Decimal("1000000"), savings_per_year_gbp=Decimal("0"), config_json={}, ) session.add(scen) await session.commit() await session.refresh(scen) return scen.id # ── life events ────────────────────────────────────────────────────── async def test_create_and_list_life_events(client: AsyncClient, session: AsyncSession) -> None: sid = await _seed_scenario(session) create = await client.post( f"/scenarios/{sid}/life-events", json={ "kind": "retirement", "name": "Retire at 50", "year_start": 15, "year_end": 15, }, ) assert create.status_code == 201, create.text listed = await client.get(f"/scenarios/{sid}/life-events") assert listed.status_code == 200 body = listed.json() assert len(body) == 1 assert body[0]["name"] == "Retire at 50" async def test_life_event_year_validation(client: AsyncClient, session: AsyncSession) -> None: sid = await _seed_scenario(session) resp = await client.post( f"/scenarios/{sid}/life-events", json={ "kind": "expense_range", "name": "Bad range", "year_start": 20, "year_end": 5, }, ) assert resp.status_code == 400 async def test_life_event_unknown_scenario(client: AsyncClient) -> None: resp = await client.get("/scenarios/9999/life-events") assert resp.status_code == 404 async def test_patch_life_event(client: AsyncClient, session: AsyncSession) -> None: sid = await _seed_scenario(session) create = await client.post( f"/scenarios/{sid}/life-events", json={"kind": "retirement", "name": "Retire", "year_start": 15}, ) eid = create.json()["id"] resp = await client.patch(f"/life-events/{eid}", json={"year_start": 20, "name": "Retire at 55"}) assert resp.status_code == 200 body = resp.json() assert body["year_start"] == 20 assert body["name"] == "Retire at 55" async def test_delete_life_event(client: AsyncClient, session: AsyncSession) -> None: sid = await _seed_scenario(session) create = await client.post( f"/scenarios/{sid}/life-events", json={"kind": "retirement", "name": "X", "year_start": 5}, ) eid = create.json()["id"] resp = await client.delete(f"/life-events/{eid}") assert resp.status_code == 204 listed = await client.get(f"/scenarios/{sid}/life-events") assert listed.json() == [] # ── goals ──────────────────────────────────────────────────────────── async def test_create_and_list_goals(client: AsyncClient, session: AsyncSession) -> None: sid = await _seed_scenario(session) create = await client.post( f"/scenarios/{sid}/goals", json={ "kind": "target_nw", "name": "≥ £2M at 50", "target_amount_gbp": "2000000", "target_year": 15, "comparator": ">=", "success_threshold": "0.90", }, ) assert create.status_code == 201, create.text listed = await client.get(f"/scenarios/{sid}/goals") assert len(listed.json()) == 1 assert Decimal(listed.json()[0]["target_amount_gbp"]) == Decimal("2000000") async def test_delete_goal(client: AsyncClient, session: AsyncSession) -> None: sid = await _seed_scenario(session) create = await client.post( f"/scenarios/{sid}/goals", json={"kind": "never_run_out", "name": "Last to 95", "target_year": 65}, ) gid = create.json()["id"] resp = await client.delete(f"/goals/{gid}") assert resp.status_code == 204