from __future__ import annotations from datetime import UTC import httpx import pytest import pytest_httpx from mcp_macro.fetchers import ( fetch_economic_indicators, fetch_macro_calendar, fetch_market_overview, ) # --- fetch_economic_indicators --- @pytest.mark.asyncio async def test_economic_indicators_no_key(): result = await fetch_economic_indicators(fred_api_key="") assert "error" in result assert result["error"] == "No FRED API key configured" @pytest.mark.asyncio async def test_economic_indicators_happy_path(httpx_mock: pytest_httpx.HTTPXMock): for series_id in ("FEDFUNDS", "CPIAUCSL", "UNRATE", "DGS10"): httpx_mock.add_response( url=httpx.URL( "https://api.stlouisfed.org/fred/series/observations", params={ "series_id": series_id, "api_key": "testkey", "file_type": "json", "sort_order": "desc", "limit": "1", }, ), json={"observations": [{"value": "5.25"}]}, ) result = await fetch_economic_indicators(fred_api_key="testkey") assert result["fed_rate"] == 5.25 assert result["cpi"] == 5.25 assert result["unemployment"] == 5.25 assert result["us10y_yield"] == 5.25 assert "updated_at" in result @pytest.mark.asyncio async def test_economic_indicators_filter(httpx_mock: pytest_httpx.HTTPXMock): httpx_mock.add_response( url=httpx.URL( "https://api.stlouisfed.org/fred/series/observations", params={ "series_id": "FEDFUNDS", "api_key": "k", "file_type": "json", "sort_order": "desc", "limit": "1", }, ), json={"observations": [{"value": "5.33"}]}, ) result = await fetch_economic_indicators(fred_api_key="k", indicators=["fed_rate"]) assert "fed_rate" in result assert "cpi" not in result # --- fetch_macro_calendar --- @pytest.mark.asyncio async def test_macro_calendar_forex_factory_happy(httpx_mock: pytest_httpx.HTTPXMock): from datetime import datetime, timedelta future = (datetime.now(UTC) + timedelta(days=1)).isoformat() httpx_mock.add_response( url="https://nfs.faireconomy.media/ff_calendar_thisweek.json", json=[ { "date": future, "title": "CPI", "country": "US", "impact": "High", "forecast": "3.0%", "previous": "3.2%", } ], ) result = await fetch_macro_calendar() assert "events" in result assert len(result["events"]) >= 1 assert result["events"][0]["name"] == "CPI" @pytest.mark.asyncio async def test_macro_calendar_no_source(httpx_mock: pytest_httpx.HTTPXMock): httpx_mock.add_response( url="https://nfs.faireconomy.media/ff_calendar_thisweek.json", status_code=500, ) result = await fetch_macro_calendar(finnhub_api_key="") assert result == {"events": [], "note": "No calendar source available"} @pytest.mark.asyncio @pytest.mark.httpx_mock(assert_all_responses_were_requested=False, assert_all_requests_were_expected=False) async def test_macro_calendar_finnhub_fallback(httpx_mock: pytest_httpx.HTTPXMock): httpx_mock.add_response( url="https://nfs.faireconomy.media/ff_calendar_thisweek.json", status_code=500, ) def dispatch(request: httpx.Request) -> httpx.Response: if "finnhub.io" in str(request.url): return httpx.Response( 200, json=[{"date": "2024-01-15", "event": "FOMC", "importance": "high", "forecast": "", "prev": ""}], ) return httpx.Response(500) httpx_mock.add_callback(dispatch) result = await fetch_macro_calendar(finnhub_api_key="fkey") assert "events" in result assert result["events"][0]["name"] == "FOMC" # --- fetch_market_overview --- @pytest.mark.asyncio async def test_market_overview_happy(httpx_mock: pytest_httpx.HTTPXMock): httpx_mock.add_response( url="https://api.coingecko.com/api/v3/global", json={ "data": { "market_cap_percentage": {"btc": 52.3}, "total_market_cap": {"usd": 2_000_000_000_000}, } }, ) httpx_mock.add_response( url=httpx.URL( "https://api.coingecko.com/api/v3/simple/price", params={"ids": "bitcoin,ethereum", "vs_currencies": "usd"}, ), json={"bitcoin": {"usd": 65000}, "ethereum": {"usd": 3500}}, ) import re as _re httpx_mock.add_response( url=_re.compile( r"https://www\.deribit\.com/api/v2/public/get_volatility_index_data\?currency=BTC.*" ), json={"result": {"data": [[1, 50, 52, 49, 51.5]], "continuation": None}}, ) httpx_mock.add_response( url=_re.compile( r"https://www\.deribit\.com/api/v2/public/get_volatility_index_data\?currency=ETH.*" ), json={"result": {"data": [[1, 60, 62, 59, 61.2]], "continuation": None}}, ) import re as _re httpx_mock.add_response( url=_re.compile(r"https://query1\.finance\.yahoo\.com/v8/finance/chart/\^GSPC.*"), json={"chart": {"result": [{"meta": {"regularMarketPrice": 5830.12}}]}}, ) httpx_mock.add_response( url=_re.compile(r"https://query1\.finance\.yahoo\.com/v8/finance/chart/GC[%=].*"), json={"chart": {"result": [{"meta": {"regularMarketPrice": 2412.5}}]}}, ) httpx_mock.add_response( url=_re.compile(r"https://query1\.finance\.yahoo\.com/v8/finance/chart/\^VIX.*"), json={"chart": {"result": [{"meta": {"regularMarketPrice": 18.3}}]}}, ) # Clear module cache to force fresh fetch from mcp_macro import fetchers as _f _f._MARKET_CACHE["data"] = None _f._MARKET_CACHE["ts"] = 0.0 result = await fetch_market_overview() assert result["btc_dominance"] == 52.3 assert result["btc_price"] == 65000 assert result["eth_price"] == 3500 assert result["total_market_cap"] == 2_000_000_000_000 assert result["dvol_btc"] == 51.5 assert result["dvol_eth"] == 61.2 assert result["sp500"] == 5830.12 assert result["gold"] == 2412.5 assert result["vix"] == 18.3 assert "data_timestamp" in result