"""IMF WEO connector — recorded SDMX-CSV fixtures (CAN, USA, KOS, G001 for NGDP_RPCH; CAN, USA for NGDPD).""" from __future__ import annotations from datetime import date import pytest from countryatlas.connectors.imf import IMFConnector from countryatlas.models import IndicatorSourceSpec, RawPayload def _raw(fixtures_dir, name: str, code: str, retrieved_at) -> RawPayload: body = (fixtures_dir / "imf" / name).read_bytes() return RawPayload( connector="imf", dataset="WEO", code=code, url="fixture://imf", retrieved_at=retrieved_at, status_code=200, content_type="text/csv", body=body, meta={"publication_date": "2026-04-14T13:00:00Z"}, ) @pytest.fixture def imf() -> IMFConnector: return IMFConnector() def test_normalize_growth_maps_iso3_and_flags_forecasts(imf, fixtures_dir, retrieved_at): spec = IndicatorSourceSpec(indicator_id="gdp-growth", connector="imf", dataset="WEO", code="NGDP_RPCH", priority=2) rows = imf.normalize(_raw(fixtures_dir, "weo_NGDP_RPCH_sample.csv", "NGDP_RPCH", retrieved_at), spec) countries = {r.country_id for r in rows} assert countries == {"CAN", "USA", "XKX"}, "G001 (World aggregate) dropped, KOS mapped to XKX" can = sorted((r for r in rows if r.country_id == "CAN"), key=lambda r: r.year) assert [r.year for r in can] == list(range(2022, 2029)) assert can[0].period == date(2022, 1, 1) and can[0].frequency == "A" # LATEST_ACTUAL_ANNUAL_DATA = 2025 → 2026+ are projections assert {r.year: r.is_forecast for r in can} == {2022: False, 2023: False, 2024: False, 2025: False, 2026: True, 2027: True, 2028: True} assert all(r.is_estimate is False for r in rows) assert can[0].value == pytest.approx(4.69542) assert can[0].unit == "annual %" assert can[0].source_updated_at is not None and can[0].source_updated_at.date() == date(2026, 4, 14) assert can[0].metadata["latest_actual_annual_data"] == 2025 assert can[0].source_dataset == "WEO" and can[0].source_series_code == "NGDP_RPCH" def test_normalize_gdp_values_are_base_units_without_transform(imf, fixtures_dir, retrieved_at): spec = IndicatorSourceSpec(indicator_id="gdp", connector="imf", dataset="WEO", code="NGDPD", priority=2) rows = imf.normalize(_raw(fixtures_dir, "weo_NGDPD_sample.csv", "NGDPD", retrieved_at), spec) can2023 = next(r for r in rows if r.country_id == "CAN" and r.year == 2023) assert can2023.value == pytest.approx(2.196593836e12, rel=1e-6), "OBS_VALUE already in US$, SCALE=9 is display-only" assert can2023.metadata.get("scale") == "9" assert can2023.unit == "current US$" def test_transform_is_applied_when_present(imf, fixtures_dir, retrieved_at): spec = IndicatorSourceSpec(indicator_id="gdp", connector="imf", dataset="WEO", code="NGDPD", priority=2, transform="x/1e9") rows = imf.normalize(_raw(fixtures_dir, "weo_NGDPD_sample.csv", "NGDPD", retrieved_at), spec) can2023 = next(r for r in rows if r.country_id == "CAN" and r.year == 2023) assert can2023.value == pytest.approx(2196.593836, rel=1e-6) def test_validate_no_duplicates(imf, fixtures_dir, retrieved_at): spec = IndicatorSourceSpec(indicator_id="gdp-growth", connector="imf", dataset="WEO", code="NGDP_RPCH", priority=2) rows = imf.normalize(_raw(fixtures_dir, "weo_NGDP_RPCH_sample.csv", "NGDP_RPCH", retrieved_at), spec) report = imf.validate(rows) assert report.errors == 0 and not report.quarantine_dataset def test_registry_specs_for_imf_include_inline_and_extra(): from countryatlas import registry specs = registry.source_specs("imf") codes = {s.code for s in specs} assert {"NGDP_RPCH", "PCPIPCH", "LUR", "GGXWDG_NGDP", "NGSD_NGDP", "NID_NGDP", "LP"} <= codes for s in specs: assert s.dataset == "WEO" assert s.transform is None or "1e9" not in s.transform, f"{s.code}: OBS_VALUE is already in base units" @pytest.mark.live def test_live_fetch_small_key(): imf = IMFConnector() spec = IndicatorSourceSpec(indicator_id="unemployment-rate", connector="imf", dataset="WEO", code="LUR", priority=2, params={"countries": ["CAN", "USA"], "startPeriod": 2023, "endPeriod": 2027}) raw = imf.fetch(spec) assert raw.status_code == 200 and raw.body.startswith(b"DATAFLOW") rows = imf.normalize(raw, spec) assert {r.country_id for r in rows} == {"CAN", "USA"} assert any(r.is_forecast for r in rows) and any(not r.is_forecast for r in rows) imf.close()