import os import sys from pathlib import Path import pytest from fastapi.testclient import TestClient # Ensure required env vars for app import/config os.environ.setdefault("SECRET_KEY", "x" * 32) os.environ.setdefault("DATABASE_URL", "sqlite:////tmp/delphi_test.sqlite") # Ensure repository root on sys.path for direct test runs ROOT = Path(__file__).resolve().parents[1] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from app.main import app # noqa: E402 from app.auth.security import get_current_user # noqa: E402 from tests.helpers import assert_http_error # noqa: E402 from app.database.base import SessionLocal # noqa: E402 from app.models.pensions import LifeTable, NumberTable # noqa: E402 from app.services.mortality import ( # noqa: E402 get_life_values, get_number_value, InvalidCodeError, ) @pytest.fixture(scope="module") def client(): # Override auth to bypass JWT for these tests class _User: def __init__(self): self.id = "test" self.username = "tester" self.is_admin = True self.is_active = True app.dependency_overrides[get_current_user] = lambda: _User() try: yield TestClient(app) finally: app.dependency_overrides.pop(get_current_user, None) def _seed_life_and_number(): db = SessionLocal() try: # Seed a life table row for age 65 db.query(LifeTable).filter(LifeTable.age == 65).delete() lt = LifeTable( age=65, le_wm=14.5, na_wm=87000.0, le_af=20.1, na_af=92000.0, le_ha=18.2, na_ha=88000.0, ) db.add(lt) # Seed a number table row for month 305 db.query(NumberTable).filter(NumberTable.month == 305).delete() nt = NumberTable( month=305, na_wm=80000.0, na_af=90000.0, na_ha=85000.0, ) db.add(nt) db.commit() finally: db.close() def test_service_helpers_success_invalid_and_not_found(): _seed_life_and_number() db = SessionLocal() try: # Success cases res = get_life_values(db, age=65, sex="M", race="W") assert res and res["le"] == 14.5 and res["na"] == 87000.0 res = get_life_values(db, age=65, sex="F", race="A") assert res and res["le"] == 20.1 and res["na"] == 92000.0 res = get_life_values(db, age=65, sex="A", race="H") assert res and res["le"] == 18.2 and res["na"] == 88000.0 nres = get_number_value(db, month=305, sex="M", race="W") assert nres and nres["na"] == 80000.0 nres = get_number_value(db, month=305, sex="F", race="A") assert nres and nres["na"] == 90000.0 nres = get_number_value(db, month=305, sex="A", race="H") assert nres and nres["na"] == 85000.0 # Invalid codes with pytest.raises(InvalidCodeError): get_life_values(db, age=65, sex="X", race="W") with pytest.raises(InvalidCodeError): get_number_value(db, month=305, sex="M", race="Z") # Not found assert get_life_values(db, age=9999, sex="M", race="W") is None assert get_number_value(db, month=99999, sex="M", race="W") is None finally: db.close() def test_api_life_valid_invalid_not_found(client: TestClient): _seed_life_and_number() # Valid lookups resp = client.get("/api/mortality/life/65", params={"sex": "M", "race": "W"}) assert resp.status_code == 200 data = resp.json() assert data["le"] == 14.5 and data["na"] == 87000.0 resp = client.get("/api/mortality/life/65", params={"sex": "F", "race": "A"}) assert resp.status_code == 200 assert resp.json()["le"] == 20.1 # Invalid code -> 400 wrapped error resp = client.get("/api/mortality/life/65", params={"sex": "X", "race": "W"}) assert_http_error(resp, 400, "Invalid sex code") # Not found -> 404 wrapped error resp = client.get("/api/mortality/life/9999", params={"sex": "M", "race": "W"}) assert_http_error(resp, 404, "Age not found") def test_api_number_valid_invalid_not_found(client: TestClient): _seed_life_and_number() # Valid lookup resp = client.get("/api/mortality/number/305", params={"sex": "M", "race": "W"}) assert resp.status_code == 200 assert resp.json()["na"] == 80000.0 # Invalid code -> 400 resp = client.get("/api/mortality/number/305", params={"sex": "M", "race": "Z"}) assert_http_error(resp, 400, "Invalid race code") # Not found -> 404 resp = client.get("/api/mortality/number/99999", params={"sex": "M", "race": "W"}) assert_http_error(resp, 404, "Month not found") def test_api_validation_negative_inputs(client: TestClient): # Negative age -> 422 validation envelope resp = client.get("/api/mortality/life/-1", params={"sex": "M", "race": "W"}) from tests.helpers import assert_validation_error assert_validation_error(resp, "age") # Negative month -> 422 validation envelope resp = client.get("/api/mortality/number/-5", params={"sex": "F", "race": "A"}) assert_validation_error(resp, "month")