"""Tests for expanded economy routes.""" from unittest.mock import patch, AsyncMock import pytest from httpx import AsyncClient, ASGITransport from main import app @pytest.fixture async def client(): transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as c: yield c # --- CPI --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_cpi", new_callable=AsyncMock) async def test_macro_cpi_happy_path(mock_fn, client): mock_fn.return_value = [ {"date": "2026-02-01", "value": 312.5, "country": "united_states"} ] resp = await client.get("/api/v1/macro/cpi") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["value"] == 312.5 mock_fn.assert_called_once_with(country="united_states") @pytest.mark.asyncio @patch("routes_economy.economy_service.get_cpi", new_callable=AsyncMock) async def test_macro_cpi_custom_country(mock_fn, client): mock_fn.return_value = [{"date": "2026-02-01", "value": 120.0}] resp = await client.get("/api/v1/macro/cpi?country=germany") assert resp.status_code == 200 mock_fn.assert_called_once_with(country="germany") @pytest.mark.asyncio async def test_macro_cpi_invalid_country(client): resp = await client.get("/api/v1/macro/cpi?country=INVALID!!!COUNTRY") assert resp.status_code == 422 @pytest.mark.asyncio @patch("routes_economy.economy_service.get_cpi", new_callable=AsyncMock) async def test_macro_cpi_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/macro/cpi") assert resp.status_code == 200 assert resp.json()["data"] == [] @pytest.mark.asyncio @patch("routes_economy.economy_service.get_cpi", new_callable=AsyncMock) async def test_macro_cpi_service_error_returns_502(mock_fn, client): mock_fn.side_effect = RuntimeError("FRED down") resp = await client.get("/api/v1/macro/cpi") assert resp.status_code == 502 # --- GDP --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_gdp", new_callable=AsyncMock) async def test_macro_gdp_default_real(mock_fn, client): mock_fn.return_value = [{"date": "2026-01-01", "value": 22.5}] resp = await client.get("/api/v1/macro/gdp") assert resp.status_code == 200 data = resp.json() assert data["success"] is True mock_fn.assert_called_once_with(gdp_type="real") @pytest.mark.asyncio @patch("routes_economy.economy_service.get_gdp", new_callable=AsyncMock) async def test_macro_gdp_nominal(mock_fn, client): mock_fn.return_value = [{"date": "2026-01-01", "value": 28.3}] resp = await client.get("/api/v1/macro/gdp?gdp_type=nominal") assert resp.status_code == 200 mock_fn.assert_called_once_with(gdp_type="nominal") @pytest.mark.asyncio async def test_macro_gdp_invalid_type(client): resp = await client.get("/api/v1/macro/gdp?gdp_type=invalid") assert resp.status_code == 422 @pytest.mark.asyncio @patch("routes_economy.economy_service.get_gdp", new_callable=AsyncMock) async def test_macro_gdp_forecast(mock_fn, client): mock_fn.return_value = [{"date": "2027-01-01", "value": 23.1}] resp = await client.get("/api/v1/macro/gdp?gdp_type=forecast") assert resp.status_code == 200 mock_fn.assert_called_once_with(gdp_type="forecast") @pytest.mark.asyncio @patch("routes_economy.economy_service.get_gdp", new_callable=AsyncMock) async def test_macro_gdp_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/macro/gdp") assert resp.status_code == 200 assert resp.json()["data"] == [] # --- Unemployment --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_unemployment", new_callable=AsyncMock) async def test_macro_unemployment_happy_path(mock_fn, client): mock_fn.return_value = [{"date": "2026-02-01", "value": 3.7, "country": "united_states"}] resp = await client.get("/api/v1/macro/unemployment") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["value"] == 3.7 mock_fn.assert_called_once_with(country="united_states") @pytest.mark.asyncio @patch("routes_economy.economy_service.get_unemployment", new_callable=AsyncMock) async def test_macro_unemployment_custom_country(mock_fn, client): mock_fn.return_value = [{"date": "2026-02-01", "value": 5.1}] resp = await client.get("/api/v1/macro/unemployment?country=france") assert resp.status_code == 200 mock_fn.assert_called_once_with(country="france") @pytest.mark.asyncio @patch("routes_economy.economy_service.get_unemployment", new_callable=AsyncMock) async def test_macro_unemployment_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/macro/unemployment") assert resp.status_code == 200 assert resp.json()["data"] == [] # --- PCE --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_pce", new_callable=AsyncMock) async def test_macro_pce_happy_path(mock_fn, client): mock_fn.return_value = [{"date": "2026-02-01", "value": 2.8}] resp = await client.get("/api/v1/macro/pce") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["value"] == 2.8 @pytest.mark.asyncio @patch("routes_economy.economy_service.get_pce", new_callable=AsyncMock) async def test_macro_pce_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/macro/pce") assert resp.status_code == 200 assert resp.json()["data"] == [] @pytest.mark.asyncio @patch("routes_economy.economy_service.get_pce", new_callable=AsyncMock) async def test_macro_pce_service_error_returns_502(mock_fn, client): mock_fn.side_effect = RuntimeError("FRED unavailable") resp = await client.get("/api/v1/macro/pce") assert resp.status_code == 502 # --- Money Measures --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_money_measures", new_callable=AsyncMock) async def test_macro_money_measures_happy_path(mock_fn, client): mock_fn.return_value = [{"date": "2026-02-01", "m1": 18200.0, "m2": 21000.0}] resp = await client.get("/api/v1/macro/money-measures") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["m2"] == 21000.0 @pytest.mark.asyncio @patch("routes_economy.economy_service.get_money_measures", new_callable=AsyncMock) async def test_macro_money_measures_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/macro/money-measures") assert resp.status_code == 200 assert resp.json()["data"] == [] # --- CLI --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_composite_leading_indicator", new_callable=AsyncMock) async def test_macro_cli_happy_path(mock_fn, client): mock_fn.return_value = [{"date": "2026-01-01", "value": 99.2, "country": "united_states"}] resp = await client.get("/api/v1/macro/cli") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["value"] == 99.2 mock_fn.assert_called_once_with(country="united_states") @pytest.mark.asyncio @patch("routes_economy.economy_service.get_composite_leading_indicator", new_callable=AsyncMock) async def test_macro_cli_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/macro/cli") assert resp.status_code == 200 assert resp.json()["data"] == [] # --- House Price Index --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_house_price_index", new_callable=AsyncMock) async def test_macro_hpi_happy_path(mock_fn, client): mock_fn.return_value = [{"date": "2026-01-01", "value": 350.0, "country": "united_states"}] resp = await client.get("/api/v1/macro/house-price-index") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["value"] == 350.0 mock_fn.assert_called_once_with(country="united_states") @pytest.mark.asyncio @patch("routes_economy.economy_service.get_house_price_index", new_callable=AsyncMock) async def test_macro_hpi_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/macro/house-price-index") assert resp.status_code == 200 assert resp.json()["data"] == [] # --- FRED Regional --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_fred_regional", new_callable=AsyncMock) async def test_economy_fred_regional_happy_path(mock_fn, client): mock_fn.return_value = [{"region": "CA", "value": 5.2}] resp = await client.get("/api/v1/economy/fred-regional?series_id=CAUR") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["region"] == "CA" mock_fn.assert_called_once_with(series_id="CAUR", region=None) @pytest.mark.asyncio @patch("routes_economy.economy_service.get_fred_regional", new_callable=AsyncMock) async def test_economy_fred_regional_with_region(mock_fn, client): mock_fn.return_value = [{"region": "state", "value": 4.1}] resp = await client.get("/api/v1/economy/fred-regional?series_id=CAUR®ion=state") assert resp.status_code == 200 mock_fn.assert_called_once_with(series_id="CAUR", region="state") @pytest.mark.asyncio async def test_economy_fred_regional_missing_series_id(client): resp = await client.get("/api/v1/economy/fred-regional") assert resp.status_code == 422 @pytest.mark.asyncio @patch("routes_economy.economy_service.get_fred_regional", new_callable=AsyncMock) async def test_economy_fred_regional_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/economy/fred-regional?series_id=UNKNOWN") assert resp.status_code == 200 assert resp.json()["data"] == [] # --- Primary Dealer Positioning --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_primary_dealer_positioning", new_callable=AsyncMock) async def test_economy_primary_dealer_happy_path(mock_fn, client): mock_fn.return_value = [{"date": "2026-03-12", "treasuries": 250000.0, "mbs": 80000.0}] resp = await client.get("/api/v1/economy/primary-dealer-positioning") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["treasuries"] == 250000.0 @pytest.mark.asyncio @patch("routes_economy.economy_service.get_primary_dealer_positioning", new_callable=AsyncMock) async def test_economy_primary_dealer_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/economy/primary-dealer-positioning") assert resp.status_code == 200 assert resp.json()["data"] == [] # --- FRED Search --- @pytest.mark.asyncio @patch("routes_economy.economy_service.fred_search", new_callable=AsyncMock) async def test_economy_fred_search_happy_path(mock_fn, client): mock_fn.return_value = [ {"id": "FEDFUNDS", "title": "Effective Federal Funds Rate", "frequency": "Monthly"} ] resp = await client.get("/api/v1/economy/fred-search?query=federal+funds") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["id"] == "FEDFUNDS" mock_fn.assert_called_once_with(query="federal funds") @pytest.mark.asyncio async def test_economy_fred_search_missing_query(client): resp = await client.get("/api/v1/economy/fred-search") assert resp.status_code == 422 @pytest.mark.asyncio @patch("routes_economy.economy_service.fred_search", new_callable=AsyncMock) async def test_economy_fred_search_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/economy/fred-search?query=nothingtofind") assert resp.status_code == 200 assert resp.json()["data"] == [] # --- Balance of Payments --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_balance_of_payments", new_callable=AsyncMock) async def test_economy_bop_happy_path(mock_fn, client): mock_fn.return_value = [{"date": "2026-01-01", "current_account": -200.0, "capital_account": 5.0}] resp = await client.get("/api/v1/economy/balance-of-payments") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["current_account"] == -200.0 @pytest.mark.asyncio @patch("routes_economy.economy_service.get_balance_of_payments", new_callable=AsyncMock) async def test_economy_bop_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/economy/balance-of-payments") assert resp.status_code == 200 assert resp.json()["data"] == [] # --- Central Bank Holdings --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_central_bank_holdings", new_callable=AsyncMock) async def test_economy_central_bank_holdings_happy_path(mock_fn, client): mock_fn.return_value = [{"date": "2026-03-13", "treasuries": 4500000.0, "mbs": 2300000.0}] resp = await client.get("/api/v1/economy/central-bank-holdings") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["treasuries"] == 4500000.0 @pytest.mark.asyncio @patch("routes_economy.economy_service.get_central_bank_holdings", new_callable=AsyncMock) async def test_economy_central_bank_holdings_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/economy/central-bank-holdings") assert resp.status_code == 200 assert resp.json()["data"] == [] # --- FOMC Documents --- @pytest.mark.asyncio @patch("routes_economy.economy_service.get_fomc_documents", new_callable=AsyncMock) async def test_economy_fomc_happy_path(mock_fn, client): mock_fn.return_value = [ {"date": "2026-01-28", "type": "Minutes", "url": "https://federalreserve.gov/fomc"} ] resp = await client.get("/api/v1/economy/fomc-documents") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert data["data"][0]["type"] == "Minutes" mock_fn.assert_called_once_with(year=None) @pytest.mark.asyncio @patch("routes_economy.economy_service.get_fomc_documents", new_callable=AsyncMock) async def test_economy_fomc_with_year(mock_fn, client): mock_fn.return_value = [{"date": "2024-01-30", "type": "Statement"}] resp = await client.get("/api/v1/economy/fomc-documents?year=2024") assert resp.status_code == 200 mock_fn.assert_called_once_with(year=2024) @pytest.mark.asyncio async def test_economy_fomc_invalid_year_too_low(client): resp = await client.get("/api/v1/economy/fomc-documents?year=1999") assert resp.status_code == 422 @pytest.mark.asyncio async def test_economy_fomc_invalid_year_too_high(client): resp = await client.get("/api/v1/economy/fomc-documents?year=2100") assert resp.status_code == 422 @pytest.mark.asyncio @patch("routes_economy.economy_service.get_fomc_documents", new_callable=AsyncMock) async def test_economy_fomc_empty(mock_fn, client): mock_fn.return_value = [] resp = await client.get("/api/v1/economy/fomc-documents") assert resp.status_code == 200 assert resp.json()["data"] == []