"""BIS SDMX connector — offline tests on trimmed WS_CBPOL / WS_SPP CSVs, plus live smoke tests.""" from __future__ import annotations from datetime import date import pytest from countryatlas.connectors._series import parse_sdmx_period from countryatlas.connectors.bis import BISConnector from countryatlas.models import IndicatorSourceSpec from tests.connectors._helpers import install_fake_get, raw_from_file @pytest.fixture def fake_get(monkeypatch): return install_fake_get(monkeypatch) CBPOL =IndicatorSourceSpec(indicator_id="policy-rate", connector="bis", dataset="WS_CBPOL", code="M.", frequency="M", priority=1) SPP_R = IndicatorSourceSpec(indicator_id="real-house-price-index", connector="bis", dataset="WS_SPP", code="Q..R.628", frequency="Q", priority=1, transform="rebase:2015") SPP_G = IndicatorSourceSpec(indicator_id="house-price-growth", connector="bis", dataset="WS_SPP", code="Q..R.771", frequency="Q", priority=1) def test_sdmx_period_parsing(): assert parse_sdmx_period("2026-08") == (date(2026, 8, 1), 2026, "M") assert parse_sdmx_period("2026-Q3") == (date(2026, 7, 1), 2026, "Q") assert parse_sdmx_period("2024") == (date(2024, 1, 1), 2024, "A") assert parse_sdmx_period("2026-13") is None def test_policy_rate_iso2_mapping_and_nan(fixtures_dir): conn = BISConnector() raw = raw_from_file("bis", "WS_CBPOL", "M.", fixtures_dir / "bis" / "WS_CBPOL_M.csv", "text/csv") rows = conn.normalize(raw, CBPOL) # CA/US → CAN/USA; XM (euro area) dropped; AR rows are NaN with OBS_STATUS=M → dropped assert {r.country_id for r in rows} == {"CAN", "USA"} assert all(r.frequency == "M" and r.period.day == 1 and r.unit == "% per annum" for r in rows) us = {r.period: r.value for r in rows if r.country_id == "USA"} assert us[date(2026, 8, 1)] == pytest.approx(3.625) assert conn.validate(rows).quarantine_dataset is False def test_real_index_rebased_to_2015(fixtures_dir): conn = BISConnector() raw = raw_from_file("bis", "WS_SPP", "Q..R.628", fixtures_dir / "bis" / "WS_SPP_R_628.csv", "text/csv") rows = conn.normalize(raw, SPP_R) assert {r.country_id for r in rows} == {"CAN", "USA"} for iso in ("CAN", "USA"): base = [r.value for r in rows if r.country_id == iso and r.year == 2015] assert len(base) == 4 and sum(base) / 4 == pytest.approx(100.0) q = [r for r in rows if r.country_id == "CAN"] assert q[0].period == date(2014, 1, 1) and q[0].frequency == "Q" and q[0].unit == "index (2015 = 100)" assert len(q) == 12 def test_growth_series_taken_as_published(fixtures_dir): conn = BISConnector() raw = raw_from_file("bis", "WS_SPP", "Q..R.771", fixtures_dir / "bis" / "WS_SPP_R_771.csv", "text/csv") rows = conn.normalize(raw, SPP_G) us = {r.period: r.value for r in rows if r.country_id == "USA"} assert us[date(2025, 1, 1)] == pytest.approx(-0.0324) assert all(r.unit == "annual %" for r in rows) def test_rebase_drops_country_without_base_year(fixtures_dir): conn = BISConnector() text = (fixtures_dir / "bis" / "WS_SPP_R_628.csv").read_text() lines = [ln for ln in text.splitlines() if not (",CA," in ln and ",2015-Q" in ln)] raw = raw_from_file("bis", "WS_SPP", "Q..R.628", fixtures_dir / "bis" / "WS_SPP_R_628.csv", "text/csv") raw.body = "\n".join(lines).encode() rows = conn.normalize(raw, SPP_R) assert {r.country_id for r in rows} == {"USA"} def test_fetch_builds_verified_url(fixtures_dir, fake_get): conn = BISConnector() body = (fixtures_dir / "bis" / "WS_CBPOL_M.csv").read_bytes() calls = fake_get(conn, lambda url, params: (body, "text/csv")) p = conn.fetch(CBPOL) assert calls[0]["url"] == "https://stats.bis.org/api/v2/data/dataflow/BIS/WS_CBPOL/1.0/M." assert calls[0]["params"] == {"format": "csv"} assert p.content_type == "text/csv" and p.dataset == "WS_CBPOL" and p.code == "M." @pytest.mark.live def test_live_policy_rates_all_areas(): conn = BISConnector() spec = CBPOL.model_copy(update={"params": {"startPeriod": "2026-01"}}) rows = conn.normalize(conn.fetch(spec), spec) isos = {r.country_id for r in rows} assert len(isos) >= 30 and {"CAN", "USA", "GBR", "JPN"} <= isos assert conn.validate(rows).quarantine_dataset is False @pytest.mark.live def test_live_property_prices_real_index(): conn = BISConnector() spec = SPP_R.model_copy(update={"params": {"startPeriod": "2015-Q1"}}) rows = conn.normalize(conn.fetch(spec), spec) assert len({r.country_id for r in rows}) >= 50