examples: /api/examples + /api/examples/summary router
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
9e14909ca6
commit
249991557b
3 changed files with 144 additions and 0 deletions
81
tests/test_api_examples.py
Normal file
81
tests/test_api_examples.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
"""Tests for /examples and /examples/summary."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from datetime import date
|
||||
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 FireExample
|
||||
|
||||
|
||||
@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()
|
||||
|
||||
|
||||
def _make_example(reddit_id: str,
|
||||
country: str,
|
||||
portfolio: Decimal) -> FireExample:
|
||||
return FireExample(
|
||||
reddit_id=reddit_id,
|
||||
source_sub="ExpatFIRE",
|
||||
post_url=f"https://reddit.com/r/ExpatFIRE/comments/{reddit_id}",
|
||||
post_date=date(2025, 1, 1),
|
||||
post_title=f"Example {reddit_id}",
|
||||
country=country,
|
||||
portfolio_gbp=portfolio,
|
||||
annual_exp_gbp=Decimal("30000"),
|
||||
fi_status="FIRE",
|
||||
llm_model="qwen3-8b",
|
||||
)
|
||||
|
||||
|
||||
async def test_list_examples_filters_by_country(
|
||||
client: AsyncClient, session: AsyncSession) -> None:
|
||||
session.add(_make_example("ph01", "Philippines", Decimal("100000")))
|
||||
session.add(_make_example("th01", "Thailand", Decimal("200000")))
|
||||
await session.commit()
|
||||
|
||||
resp = await client.get("/examples?country=Philippines")
|
||||
assert resp.status_code == 200, resp.text
|
||||
rows = resp.json()
|
||||
assert len(rows) == 1
|
||||
assert rows[0]["reddit_id"] == "ph01"
|
||||
assert rows[0]["country"] == "Philippines"
|
||||
assert rows[0]["portfolio_gbp"] == 100000.0
|
||||
|
||||
|
||||
async def test_summary_quartiles(
|
||||
client: AsyncClient, session: AsyncSession) -> None:
|
||||
for i, amount in enumerate(
|
||||
[100_000, 200_000, 300_000, 400_000, 500_000], start=1):
|
||||
session.add(
|
||||
_make_example(f"ph{i:02d}", "Philippines", Decimal(str(amount))))
|
||||
await session.commit()
|
||||
|
||||
resp = await client.get("/examples/summary?country=Philippines")
|
||||
assert resp.status_code == 200, resp.text
|
||||
body = resp.json()
|
||||
assert body["country"] == "Philippines"
|
||||
assert body["count"] == 5
|
||||
portfolio = body["portfolio_gbp"]
|
||||
assert Decimal(portfolio["median"]) == Decimal("300000.00")
|
||||
assert Decimal(portfolio["p25"]) == Decimal("200000.00")
|
||||
assert Decimal(portfolio["p75"]) == Decimal("400000.00")
|
||||
Loading…
Add table
Add a link
Reference in a new issue