SPB Git forge

spb/countryatlas

Public
20commits 1branches 0releases
268.3 MBsize
maindefault branch
12 days agolast push
TypeScript 57% Python 38.6% JavaScript 3.6% CSS 0.6%
8.8 KB · 162 lines python
Raw Blame History
1from __future__ import annotations23import csv4import io56import pytest78from tests.api.conftest import COUNTRIES, assert_meta, assert_provenance, value91011def test_list_countries(get):12    body = get("/countries").json()13    assert_meta(body)14    assert body["n"] == len(COUNTRIES)15    ids = [c["id"] for c in body["items"]]16    assert ids == sorted(ids, key=lambda i: next(c["name"] for c in body["items"] if c["id"] == i))17    can = next(c for c in body["items"] if c["id"] == "CAN")18    assert can["slug"] == "canada" and can["flag"] and can["region_name"] == "North America" and can["income"] == "HIC"19    assert can["population_latest"] == pytest.approx(value("CAN", "population", 2024))20    assert can["gdp_per_capita_latest"] == pytest.approx(value("CAN", "gdp-per-capita", 2024))21    assert can["coverage_pct"] == 100.0222324def test_list_countries_filters_and_sort(get):25    g7 = get("/countries?region=g7&sort=gdp").json()26    assert {c["id"] for c in g7["items"]} == {"CAN", "USA", "FRA", "DEU", "JPN"}27    vals = [c["gdp_latest"] for c in g7["items"]]28    assert vals == sorted(vals, reverse=True)29    assert get("/countries?region=north-america").json()["n"] == 230    assert {c["id"] for c in get("/countries?income=LMC").json()["items"]} == {"IND", "NGA"}31    assert get("/countries?q=jap").json()["items"][0]["id"] == "JPN"32    assert get("/countries?region=nowhere", status=404)33    assert get("/countries?sort=bogus", status=422)343536def test_country_overview(get):37    body = get("/countries/canada").json()38    assert_meta(body)39    c = body["country"]40    assert c["id"] == "CAN" and c["iso2"] == "CA" and c["capital"] == "Ottawa" and c["borders"] == ["USA"]41    assert "g7" in {g["id"] for g in body["groups"]} and "world" in {g["id"] for g in body["groups"]}42    assert body["coverage"]["n_indicators"] == 1443    assert body["freshness"]["source_updated_at"].startswith("2026-07-01") and body["freshness"]["built_at"]44    from countryatlas.registry import topics as registry_topics4546    headline = {m["indicator"]: m for m in body["headline"]}47    assert list(headline) == registry_topics()["headline"]  # read from topics.yaml at request time (cache cleared per snapshot)48    assert list(headline)[:3] == ["population", "gdp", "gdp-per-capita"]49    # indicators in the headline but absent from the snapshot still render as "no data"50    absent = [m for m in body["headline"] if m["indicator"] not in ("population", "gdp", "gdp-per-capita", "gdp-growth", "inflation",51              "unemployment-rate", "life-expectancy", "median-age", "co2-per-capita", "renewable-electricity-share", "internet-users",52              "government-debt-pct-gdp")]53    assert all(m["has_data"] is False and m["formatted"] == "—" and m["provenance"] is None for m in absent)54    gpc = headline["gdp-per-capita"]55    assert gpc["has_data"] and gpc["year"] == 2024 and gpc["value"] == pytest.approx(value("CAN", "gdp-per-capita", 2024))56    assert gpc["change"]["pct"] == pytest.approx(3.0) and gpc["change"]["formatted"] == "+3.0 %"57    assert gpc["rank_world"] and gpc["n_world"] == 8 and gpc["rank_region"] and gpc["n_region"] == 258    assert gpc["formatted"].startswith("US$") and gpc["formatted"].endswith("k") and gpc["formatted_short"] == gpc["formatted"][3:]59    assert headline["inflation"]["formatted"].endswith(" %") and headline["life-expectancy"]["formatted"].endswith(" yrs")60    assert len(gpc["sparkline"]) == 30 and gpc["sparkline"][-1][0] == 2024 and isinstance(gpc["sparkline"][-1][0], int)61    assert_provenance(gpc["provenance"])62    assert gpc["provenance"]["url"] == "https://data.worldbank.org/indicator/NY.GDP.PCAP.CD?locations=CA"63    assert gpc["provenance"]["series_code"] == "NY.GDP.PCAP.CD" and gpc["provenance"]["licence"] == "CC BY 4.0"64    # OWID provenance for co265    assert headline["co2-per-capita"]["provenance"]["url"] == "https://github.com/owid/co2-data"66    assert headline["median-age"]["provenance"]["url"] == "https://ourworldindata.org/grapher/median-age"67    topics = {t["id"]: t for t in body["topics"]}68    assert topics["economy"]["n_with_data"] >= 5 and topics["income"]["n_with_data"] == 069    assert [t["order"] for t in body["topics"]] == sorted(t["order"] for t in body["topics"])70    assert body["neighbours"][0]["id"] == "USA"717273def test_country_case_insensitive_and_iso2(get):74    assert get("/countries/can").json()["country"]["id"] == "CAN"75    assert get("/countries/CANADA").json()["country"]["id"] == "CAN"76    assert get("/countries/ca").json()["country"]["id"] == "CAN"777879def test_country_404_problem_json(client):80    r = client.get("/api/v1/countries/atlantis")81    assert r.status_code == 40482    assert r.headers["content-type"].startswith("application/problem+json")83    body = r.json()84    assert body["title"] == "Country not found" and body["status"] == 404 and "atlantis" in body["detail"]85    assert body["instance"] == "/api/v1/countries/atlantis"868788def test_country_topic(get):89    body = get("/countries/CAN/topics/economy").json()90    assert body["topic"]["id"] == "economy" and body["n_indicators"] > body["n_with_data"] > 091    subs = [s["subtopic"] for s in body["subtopics"]]92    assert subs[0] == "Output"  # registry order93    all_inds = [i for s in body["subtopics"] for i in s["indicators"]]94    with_data = [i for i in all_inds if i["has_data"]]95    without = [i for i in all_inds if not i["has_data"]]96    assert with_data and without97    assert without[0]["provenance"] is None and without[0]["value"] is None98    for m in with_data:99        assert_provenance(m["provenance"])100        assert m["sparkline"]101    get("/countries/CAN/topics/astrology", status=404)102103104def test_country_series(get):105    body = get("/countries/CAN/series/gdp?from=2010&to=2026&include_alt=true").json()106    assert_meta(body)107    assert body["indicator"]["id"] == "gdp" and body["country"]["id"] == "CAN" and body["unit"] == "current US$"108    years = [v["year"] for v in body["values"]]109    assert years == list(range(2010, 2027))110    fc = [v for v in body["values"] if v["is_forecast"]]111    assert [v["year"] for v in fc] == [2025, 2026] and fc[0]["source_id"] == "imf"112    assert body["values"][0]["value"] == pytest.approx(value("CAN", "gdp", 2010))113    for v in body["values"]:114        assert_provenance(v["provenance"])115    assert body["provenance"]["source"] == "worldbank"116    assert {s["source"] for s in body["sources"]} == {"worldbank", "imf"}117    assert body["alternatives"] and body["alternatives"][0]["source_id"] == "imf"118    assert body["stats"]["last"]["year"] == 2024 and body["stats"]["cagr"] == pytest.approx(4.03, abs=0.01)119    no_fc = get("/countries/CAN/series/gdp?include_forecast=false").json()120    assert all(not v["is_forecast"] for v in no_fc["values"])121    get("/countries/CAN/series/not-an-indicator", status=404)122    get("/countries/CAN/series/gdp?from=2020&to=2010", status=400)123124125def test_country_changes_events(get):126    ch = get("/countries/BRA/changes").json()127    assert ch["n"] > 0128    sev = [c["severity"] for c in ch["items"]]129    assert sev == sorted(sev, reverse=True)130    assert ch["items"][0]["headline"] and ch["items"][0]["indicator"]["slug"]131    assert_provenance(ch["items"][0]["provenance"])132    ev = get("/countries/USA/events?limit=5").json()133    assert 0 < ev["n"] <= 5 and ev["items"][0]["kind"] in ("sign_flip", "yoy_jump")134    get("/countries/ZZZ/changes", status=404)135136137def test_country_similar_insights_dna(get):138    sim = get("/countries/CAN/similar?mode=overall").json()139    assert sim["mode"] == "overall" and "economic" in sim["modes"]140    assert sim["peers"][0]["country"]["id"] in ("USA", "DEU", "FRA", "JPN") and sim["peers"][0]["score"] > 50141    assert sim["peers"][0]["contributions"]142    ins = get("/countries/CAN/insights").json()143    assert ins["items"] and "population grew" in ins["items"][0]["text"] or "Life expectancy" in ins["items"][0]["text"]144    assert any(i["provenance"] for i in ins["items"])145    dna = get("/countries/CAN/dna").json()146    assert dna["year_ref"] == 2024 and 0 <= dna["dims"]["income"] <= 100 and dna["dims"]["trade"] is None147    assert any(d["id"] == "income" and d["label"] == "Income" for d in dna["dimensions"])148149150def test_country_download(client):151    r = client.get("/api/v1/countries/CAN/download.csv")152    assert r.status_code == 200 and r.headers["content-type"].startswith("text/csv")153    assert 'filename="countryatlas-canada.csv"' in r.headers["content-disposition"]154    lines = r.text.splitlines()155    assert lines[0].startswith("# CountryAtlas export")156    rows = list(csv.DictReader(io.StringIO("\n".join(lines[1:]))))157    assert len(rows) > 400 and {"source", "series_code", "retrieved_at", "url", "licence"} <= set(rows[0])158    assert all(r["country_id"] == "CAN" for r in rows)159    j = client.get("/api/v1/countries/CAN/download.json?include_forecast=false").json()160    assert j["n"] == sum(1 for r in rows if r["is_forecast"] == "False") and j["rows"][0]["source"]161    assert client.get("/api/v1/countries/CAN/download.xml").status_code == 400162