from __future__ import annotations import json from datetime import UTC, datetime from pathlib import Path import pytest from countryatlas.connectors._util import ConnectorError from countryatlas.connectors.worldbank import WorldBankConnector, _check_header from countryatlas.models import IndicatorSourceSpec, RawPayload def _raw(body: bytes, code: str = "NY.GDP.PCAP.CD") -> RawPayload: return RawPayload(connector="worldbank", dataset="WDI", code=code, url="http://test", retrieved_at=datetime.now(UTC), status_code=200, content_type="application/json", body=body, source_updated_at=datetime(2026, 7, 13, tzinfo=UTC), meta={"lastupdated": "2026-07-13", "page": 1}) def test_normalize_fixture_drops_aggregates_and_nulls(fixtures: Path) -> None: body = (fixtures / "wb_NY.GDP.PCAP.CD.json").read_bytes() doc = json.loads(body) n_country_rows = sum(1 for r in doc[1] if r["countryiso3code"] in {"CAN", "USA", "FRA", "DEU", "JPN", "NGA", "BRA", "IND"} and r["value"] is not None) spec = IndicatorSourceSpec(indicator_id="gdp-per-capita", connector="worldbank", dataset="WDI", code="NY.GDP.PCAP.CD") conn = WorldBankConnector.__new__(WorldBankConnector) # no HTTP client needed for normalize rows = conn.normalize([_raw(body)], spec) assert len(rows) == n_country_rows assert {r.country_id for r in rows} <= {"CAN", "USA", "FRA", "DEU", "JPN", "NGA", "BRA", "IND"} can = sorted((r for r in rows if r.country_id == "CAN"), key=lambda r: r.period) assert can[-1].year >= 2023 and can[-1].period.month == 1 and can[-1].frequency == "A" assert can[-1].unit == "current US$" assert can[-1].source_updated_at == datetime(2026, 7, 13, tzinfo=UTC) assert not any(r.is_forecast for r in rows) report = WorldBankConnector.validate(conn, rows) assert report.errors == 0 and not report.quarantine_dataset def test_transform_applied() -> None: body = json.dumps([{"page": 1, "pages": 1, "total": 1, "lastupdated": "2026-07-13"}, [{"countryiso3code": "CAN", "country": {"id": "CA"}, "date": "2020", "value": 2.0, "obs_status": "E"}]]).encode() spec = IndicatorSourceSpec(indicator_id="gdp", connector="worldbank", dataset="WDI", code="X", transform="x*1e9") conn = WorldBankConnector.__new__(WorldBankConnector) rows = conn.normalize(_raw(body, "X"), spec) assert rows[0].value == 2e9 and rows[0].is_estimate is True def test_retired_code_is_an_error(fixtures: Path) -> None: doc = json.loads((fixtures / "wb_retired.json").read_text()) with pytest.raises(ConnectorError, match="retired or invalid"): _check_header(doc, "XX.OLD.CODE") def test_period_parsing() -> None: from countryatlas.connectors._util import parse_period assert parse_period("2023")[1:] == (2023, "A") assert parse_period("2023Q3")[0].month == 7 assert parse_period("2023M11")[0].month == 11 assert parse_period("garbage") is None