from __future__ import annotations import pytest from tests.api.conftest import COUNTRIES, assert_meta, assert_provenance, value def test_list_indicators(get): body = get("/indicators").json() assert_meta(body) assert body["n"] == 14 assert body["items"][0]["featured"] is True # featured first gdp = next(i for i in body["items"] if i["id"] == "gdp") assert gdp["n_countries"] == 8 and gdp["coverage_pct"] == 100.0 and gdp["last_year"] == 2024 and gdp["primary_source_id"] == "worldbank" eco = get("/indicators?topic=economy").json() assert [i["id"] for i in eco["items"]][:3] == ["gdp", "gdp-per-capita", "gdp-per-capita-ppp"] # topics.yaml order assert all(i["featured"] for i in get("/indicators?featured=true").json()["items"]) assert get("/indicators?q=infl").json()["items"][0]["id"] == "inflation" def test_indicator_detail(get): body = get("/indicators/gdp-per-capita").json() ind = body["indicator"] assert ind["slug"] == "gdp-per-capita" and ind["unit"] == "current US$" and ind["format"] == "currency" and ind["higher_is_better"] is True assert ind["source_priority"] == ["worldbank", "imf"] srcs = body["sources"] assert [s["source_id"] for s in srcs] == ["worldbank", "imf"] and srcs[0]["series_code"] == "NY.GDP.PCAP.CD" assert srcs[0]["url"].startswith("https://data.worldbank.org/indicator/NY.GDP.PCAP.CD") and srcs[0]["licence"] == "CC BY 4.0" assert body["coverage"]["n_countries"] == 8 and body["coverage"]["by_year"][0]["year"] == 1990 wl = body["world_latest"] assert wl["kind"] == "weighted_mean" and wl["weights"] == "population" and wl["year"] == 2024 and wl["n"] == 8 pops = {c: value(c, "population", 2024) for c in COUNTRIES} expected = sum(value(c, "gdp-per-capita", 2024) * pops[c] for c in COUNTRIES) / sum(pops.values()) assert wl["value"] == pytest.approx(expected, rel=1e-6) assert body["top5"][0]["country"]["id"] == "JPN" and body["top5"][0]["rank"] == 1 assert body["bottom5"][-1]["country"]["id"] == "IND" and body["bottom5"][-1]["rank"] == 8 # IND has the lowest synthetic GDP pc assert_provenance(body["top5"][0]["provenance"]) assert body["freshness"]["retrieved_at"] and body["years"] == {"first": 1990, "last": 2026, "last_actual": 2024, "latest_common": 2024} assert "economy" in body["topics"] def test_indicator_world_latest_kinds(get): assert get("/indicators/gdp").json()["world_latest"]["kind"] == "sum" le = get("/indicators/life-expectancy").json()["world_latest"] assert le["kind"] == "weighted_mean" and le["median"] is not None # lower-is-better indicator → top5 = lowest values infl = get("/indicators/inflation").json() assert infl["top5"][0]["value"] <= infl["top5"][-1]["value"] def test_indicator_404(client): r = client.get("/api/v1/indicators/happiness-of-cats") assert r.status_code == 404 and r.json()["title"] == "Indicator not found" and "gdp-per-capita" in r.json()["detail"] def test_indicator_map(get): body = get("/indicators/life-expectancy/map").json() assert body["year"] is None and body["year_used"] == 2024 and body["n"] == 8 and body["nearest"] is False assert body["values"]["CAN"] == pytest.approx(value("CAN", "life-expectancy", 2024)) assert body["formatted"]["CAN"].endswith("yrs") lg = body["legend"] assert lg["min"] < lg["max"] and lg["breaks"] == sorted(lg["breaks"]) and 3 <= lg["n_classes"] <= 7 assert_provenance(body["provenance"]) y23 = get("/indicators/life-expectancy/map?year=2023").json() assert y23["year_used"] == 2023 and y23["values"]["CAN"] == pytest.approx(value("CAN", "life-expectancy", 2023)) # internet-users has no values for NGA before 2005 → nearest within 3 years m = get("/indicators/internet-users/map?year=2006&nearest=true").json() assert m["years"]["NGA"] == 2006 and m["years"]["CAN"] == 2006 m2 = get("/indicators/internet-users/map?year=2004&nearest=true").json() assert "NGA" not in m2["values"] and m2["years"]["CAN"] == 2004 empty = get("/indicators/internet-users/map?year=1990").json() assert empty["n"] == 0 and empty["legend"]["breaks"] == [] def test_indicator_trend(get): body = get("/indicators/gdp/trend?group=world").json() assert body["group"]["id"] == "world" and body["preferred"] == "sum" p = body["points"] assert p[0]["year"] == 1990 and p[-1]["year"] == 2024 and p[-1]["n"] == 8 assert p[-1]["sum"] == pytest.approx(sum(value(c, "gdp", 2024) for c in COUNTRIES)) assert p[-1]["median"] is not None and p[-1]["mean"] is not None g7 = get("/indicators/life-expectancy/trend?group=g7&from=2020").json() assert g7["preferred"] == "weighted_mean" and g7["weights"] == "population" and g7["points"][0]["year"] == 2020 and g7["points"][0]["n"] == 5 assert g7["points"][-1]["weighted_mean"] is not None assert body["provenance"] and body["provenance"][0]["source"] == "worldbank" get("/indicators/gdp/trend?group=narnia", status=404) def test_indicator_download(client): r = client.get("/api/v1/indicators/gdp/download.csv?from=2020&include_forecast=false") assert r.status_code == 200 and r.headers["content-type"].startswith("text/csv") lines = [l for l in r.text.splitlines() if l and not l.startswith("#")] assert lines[0].split(",")[:4] == ["country_id", "country_name", "indicator_id", "indicator_name"] assert len(lines) - 1 == 8 * 5 j = client.get("/api/v1/indicators/gdp/download.json?from=2024&to=2024").json() assert j["n"] == 8 and j["rows"][0]["series_code"] == "NY.GDP.MKTP.CD"