Add opt-in live API end-to-end test
- test_e2e_api.py: minimal 2-address (4-element) call to the real Google Routes API; reads the real key from env/.env and skips on a placeholder. - Marked `e2e` and deselected by default via addopts; run with `pytest -m e2e`.
This commit is contained in:
+5
-1
@@ -28,4 +28,8 @@ build-backend = "hatchling.build"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
addopts = "-q"
|
||||
# Deselect live API tests by default; run them with `pytest -m e2e`.
|
||||
addopts = "-q -m 'not e2e'"
|
||||
markers = [
|
||||
"e2e: live tests that call the real Google Routes API (opt-in, costs quota)",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"""Live end-to-end test against the real Google Routes API.
|
||||
|
||||
This is intentionally excluded from the default test run (it is marked ``e2e``
|
||||
and ``addopts`` in pyproject.toml deselects that marker). It also costs billable
|
||||
API elements, so it is kept minimal: two addresses -> a 2x2 (4-element) matrix.
|
||||
|
||||
Run it explicitly when you actually want to exercise the live API:
|
||||
|
||||
uv run pytest -m e2e
|
||||
|
||||
It requires a real GOOGLE_MAPS_API_KEY (read from the environment or the local
|
||||
.env file); the test skips itself if only a placeholder/dummy key is available.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from dotenv import load_dotenv
|
||||
|
||||
import tatami.traveltimes as tt
|
||||
from tatami.classes import Participant
|
||||
|
||||
pytestmark = pytest.mark.e2e
|
||||
|
||||
# Values that are not a usable real key (conftest's dummy and the template value).
|
||||
_PLACEHOLDER_KEYS = {"", "test-key", "your-api-key-here"}
|
||||
|
||||
|
||||
def _real_api_key() -> str | None:
|
||||
# Prefer a real value from the environment / .env over the dummy that conftest
|
||||
# sets for the rest of the suite.
|
||||
load_dotenv(override=True)
|
||||
key = os.environ.get("GOOGLE_MAPS_API_KEY", "")
|
||||
return key if key not in _PLACEHOLDER_KEYS else None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def live_api(monkeypatch):
|
||||
key = _real_api_key()
|
||||
if key is None:
|
||||
pytest.skip("No real GOOGLE_MAPS_API_KEY available; skipping live API test.")
|
||||
# traveltimes captured the key at import time, so point the module global at
|
||||
# the real key for this test.
|
||||
monkeypatch.setattr(tt, "GOOGLE_MAPS_API_KEY", key)
|
||||
return key
|
||||
|
||||
|
||||
def test_live_route_matrix_minimal(live_api):
|
||||
# Two real addresses -> a 2x2 matrix (4 billable elements), the smallest
|
||||
# meaningful request.
|
||||
participants = [
|
||||
Participant("A", "Römerstr. 12, 76189 Karlsruhe", "", 10, ""),
|
||||
Participant("B", "Gottesauerstr. 30, 76131 Karlsruhe", "", 10, ""),
|
||||
]
|
||||
|
||||
matrix = tt.get_participant_distance_matrix(participants, mode="BICYCLE")
|
||||
|
||||
uuids = [p.uuid for p in participants]
|
||||
assert matrix.shape == (2, 2)
|
||||
assert list(matrix.index) == uuids
|
||||
assert list(matrix.columns) == uuids
|
||||
|
||||
# A real bike trip between two distinct addresses takes a positive duration.
|
||||
travel = matrix.loc[uuids[0], uuids[1]]
|
||||
assert isinstance(travel, pd.Timedelta)
|
||||
assert travel > pd.Timedelta(0)
|
||||
Reference in New Issue
Block a user