spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
1"""ILOSTAT connector — offline tests on a trimmed DF_UNE_2EAP_SEX_AGE_RT CSV (CAN, FRA, SOM, X01), plus live smoke."""2from __future__ import annotations34from datetime import date56import pytest78from countryatlas.connectors.ilo import ILOConnector9from countryatlas.models import IndicatorSourceSpec10from tests.connectors._helpers import install_fake_get, raw_from_file111213@pytest.fixture14def fake_get(monkeypatch):15 return install_fake_get(monkeypatch)161718SPEC =IndicatorSourceSpec(indicator_id="unemployment-rate", connector="ilo", dataset="DF_UNE_2EAP_SEX_AGE_RT",19 code=".A..SEX_T.AGE_YTHADULT_YGE15", frequency="A", priority=4)202122def _raw(fixtures_dir, **meta):23 return raw_from_file("ilo", SPEC.dataset, SPEC.code, fixtures_dir / "ilo" / "DF_UNE_2EAP_SEX_AGE_RT.csv", "text/csv", **meta)242526def test_normalize_drops_aggregates_flags_projections_and_imputations(fixtures_dir):27 conn = ILOConnector()28 rows = conn.normalize(_raw(fixtures_dir, dataflow_meta={"release_year": 2025, "name": "… Nov. 2025"}), SPEC)29 assert {r.country_id for r in rows} == {"CAN", "FRA", "SOM"} # X01 aggregate dropped30 can = {r.year: r for r in rows if r.country_id == "CAN"}31 assert can[2024].is_estimate is False and can[2024].is_forecast is False # OBS_STATUS = R32 assert can[2025].is_estimate is True and can[2025].is_forecast is False # nowcast (blank status), release year33 assert can[2026].is_forecast is True and can[2027].is_forecast is True34 assert can[2025].value == pytest.approx(6.907) # = WB SL.UEM.TOTL.ZS 202535 som = {r.year: r for r in rows if r.country_id == "SOM"}36 assert som[2024].is_estimate is True and som[2024].metadata["low"] == pytest.approx(4.737)37 assert all(r.frequency == "A" and r.period == date(r.year, 1, 1) and r.unit == "% of labour force" for r in rows)38 assert conn.validate(rows).quarantine_dataset is False394041def test_release_year_fallback_is_current_year(fixtures_dir):42 from datetime import UTC, datetime4344 rows = ILOConnector().normalize(_raw(fixtures_dir), SPEC) # no dataflow_meta → years > now.year are forecasts45 y = datetime.now(UTC).year46 assert all(r.is_forecast == (r.year > y) for r in rows)474849def test_fetch_builds_verified_url_and_parses_release_year(fixtures_dir, fake_get):50 conn = ILOConnector()51 csv_body = (fixtures_dir / "ilo" / "DF_UNE_2EAP_SEX_AGE_RT.csv").read_bytes()52 flow = (fixtures_dir / "ilo" / "dataflow.json").read_bytes()5354 def router(url, params):55 if "/dataflow/ILO/" in url:56 return flow, "application/vnd.sdmx.structure+json"57 return csv_body, "text/csv"5859 calls = fake_get(conn, router)60 p = conn.fetch(SPEC)61 assert calls[0]["url"] == "https://sdmx.ilo.org/rest/dataflow/ILO/DF_UNE_2EAP_SEX_AGE_RT"62 assert calls[1]["url"] == "https://sdmx.ilo.org/rest/data/ILO,DF_UNE_2EAP_SEX_AGE_RT/.A..SEX_T.AGE_YTHADULT_YGE15"63 assert calls[1]["params"] == {"format": "csv"}64 assert p.meta["dataflow_meta"]["release_year"] == 202565 assert "Nov. 2025" in p.meta["dataflow_meta"]["name"]66 rows = conn.normalize(p, SPEC)67 assert {r.year for r in rows if r.is_forecast} == {2026, 2027}686970@pytest.mark.live71def test_live_youth_unemployment():72 conn = ILOConnector()73 spec = SPEC.model_copy(update={"indicator_id": "youth-unemployment-rate", "code": ".A..SEX_T.AGE_YTHADULT_Y15-24",74 "params": {"startPeriod": "2023"}})75 rows = conn.normalize(conn.fetch(spec), spec)76 assert len({r.country_id for r in rows}) >= 150 and "CAN" in {r.country_id for r in rows}77 assert conn.validate(rows).quarantine_dataset is False78