Add pytest suite for sample locations with recorded fixtures
Tests replay pre-recorded WDQS and Wikidata API responses so they run offline against only the local PostGIS database. capture_fixtures.py records live responses into tests/fixtures/ for later replay.
This commit is contained in:
parent
6a5d5d0c2f
commit
7790d10f08
3 changed files with 214 additions and 0 deletions
96
tests/test_examples.py
Normal file
96
tests/test_examples.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
"""
|
||||
Tests for the sample locations listed in geocode.samples.
|
||||
|
||||
Fixtures are pre-recorded snapshots of WDQS and Wikidata API responses.
|
||||
Generate or refresh them (requires network + DB) with:
|
||||
|
||||
python tests/capture_fixtures.py
|
||||
|
||||
The tests themselves only need the local PostGIS database.
|
||||
"""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from geocode import samples
|
||||
from lookup import lat_lon_to_wikidata
|
||||
|
||||
FIXTURES_DIR = Path(__file__).parent / "fixtures"
|
||||
|
||||
|
||||
def _load_fixture(i: int) -> dict:
|
||||
path = FIXTURES_DIR / f"sample_{i:02d}.json"
|
||||
if not path.exists():
|
||||
pytest.skip(f"No fixture file — run: python tests/capture_fixtures.py {i}")
|
||||
return json.loads(path.read_text())
|
||||
|
||||
|
||||
def _make_mock_wdqs(fixture: dict, name: str):
|
||||
"""Return a mock for geocode.wikidata.wdqs that replays saved responses."""
|
||||
saved = fixture["wdqs"]
|
||||
|
||||
def mock_wdqs(query: str) -> list:
|
||||
if query not in saved:
|
||||
short = query[:120].replace("\n", " ")
|
||||
raise AssertionError(
|
||||
f"[{name}] Unexpected WDQS query (not in fixture):\n {short}…"
|
||||
)
|
||||
return saved[query]
|
||||
|
||||
return mock_wdqs
|
||||
|
||||
|
||||
def _make_mock_api_call(fixture: dict, name: str):
|
||||
"""Return a mock for geocode.wikidata.api_call that replays saved responses."""
|
||||
saved = fixture["api"]
|
||||
|
||||
def mock_api_call(params: dict) -> dict:
|
||||
key = json.dumps(params, sort_keys=True)
|
||||
if key not in saved:
|
||||
raise AssertionError(
|
||||
f"[{name}] Unexpected api_call (not in fixture): {key}"
|
||||
)
|
||||
return saved[key]
|
||||
|
||||
return mock_api_call
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"i,lat,lon,name",
|
||||
[(i, lat, lon, name) for i, (lat, lon, name) in enumerate(samples)],
|
||||
ids=[name for _, _, name in samples],
|
||||
)
|
||||
def test_example(app_ctx, mocker, i: int, lat: float, lon: float, name: str) -> None:
|
||||
"""Each sample location resolves to the expected Wikidata item and Commons category."""
|
||||
fixture = _load_fixture(i)
|
||||
|
||||
mocker.patch("geocode.wikidata.wdqs", side_effect=_make_mock_wdqs(fixture, name))
|
||||
mocker.patch(
|
||||
"geocode.wikidata.api_call", side_effect=_make_mock_api_call(fixture, name)
|
||||
)
|
||||
|
||||
reply = lat_lon_to_wikidata(lat, lon)
|
||||
result = reply["result"]
|
||||
|
||||
expected_qid = fixture["expected_wikidata"]
|
||||
assert result.get("wikidata") == expected_qid, (
|
||||
f"{name}: wikidata mismatch — expected {expected_qid!r}, "
|
||||
f"got {result.get('wikidata')!r}"
|
||||
)
|
||||
|
||||
expected_cat = fixture["expected_commons_cat"]
|
||||
if expected_cat:
|
||||
commons = result.get("commons_cat")
|
||||
assert isinstance(commons, dict), (
|
||||
f"{name}: expected commons_cat={expected_cat!r} but result has none"
|
||||
)
|
||||
assert commons["title"] == expected_cat, (
|
||||
f"{name}: commons_cat mismatch — expected {expected_cat!r}, "
|
||||
f"got {commons['title']!r}"
|
||||
)
|
||||
else:
|
||||
assert not result.get("commons_cat"), (
|
||||
f"{name}: expected no commons_cat but got {result.get('commons_cat')!r}"
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue