agenda/tests/test_flight_search_cache.py

83 lines
2.8 KiB
Python

"""Offline tests for shared flight pacing, cooldowns and route/date caching."""
import typing
from datetime import date
from pathlib import Path
from types import SimpleNamespace
import pytest
import agenda.conference_flights as flights
import agenda.flight_search_cache as cache
from agenda.types import StrDict
@pytest.fixture
def clock(monkeypatch: typing.Any) -> tuple[list[float], list[float]]:
"""Advance simulated time instead of sleeping or contacting Google."""
now = [1000.0]
sleeps: list[float] = []
def current_time() -> float:
return now[0]
def sleep(seconds: float) -> None:
sleeps.append(seconds)
now[0] += seconds
monkeypatch.setattr(cache, "time", SimpleNamespace(time=current_time, sleep=sleep))
return now, sleeps
def test_shared_request_spacing(
tmp_path: Path, clock: tuple[list[float], list[float]]
) -> None:
"""Separate callers share the same three-second spacing state."""
cache.wait_for_request(tmp_path)
cache.wait_for_request(tmp_path)
cache.wait_for_request(tmp_path)
assert clock[1] == [3.0, 3.0]
assert cache.read_state(tmp_path / "rate-limit.json")["last_request_at"] == 1006.0
@pytest.mark.parametrize("rows", [[], [{"price": 50}]])
def test_day_cache_reuses_results_during_cooldown(
tmp_path: Path, clock: tuple[list[float], list[float]], rows: list[StrDict]
) -> None:
"""Successful and empty route/date results survive a global request cooldown."""
calls: list[bool] = []
def fetch() -> list[StrDict]:
calls.append(True)
return rows
assert cache.cached_day(tmp_path, "BRS", "CPH", "2099-01-04", True, fetch) == rows
cache.block_requests(tmp_path)
assert cache.cached_day(tmp_path, "BRS", "CPH", "2099-01-04", True, fetch) == rows
assert len(calls) == 1
clock[0][0] += cache.DAY_CACHE_SECONDS + 1
cache.cached_day(tmp_path, "BRS", "CPH", "2099-01-04", True, fetch)
assert len(calls) == 2
def test_overlapping_conferences_reuse_day_cache(
tmp_path: Path, monkeypatch: typing.Any
) -> None:
"""Two conferences sharing a departure date fetch that outbound only once."""
calls: list[tuple[str, str, date]] = []
def fetch(
origin: str, destination: str, day: date, direct: bool, directory: Path
) -> list[StrDict]:
calls.append((origin, destination, day))
return [{"arrival": day.isoformat() + "T18:00:00", "price": 50}]
monkeypatch.setattr(flights, "fetch_day", fetch)
start = date(2099, 1, 5)
for end in (date(2099, 1, 6), date(2099, 1, 7)):
path = flights.cache_path(str(tmp_path), start, end, "CPH", False)
result = flights.lookup(path, start, end, "CPH", False)
assert result["searches"][0]["outbound"]
assert len(calls) == 3
assert sum(origin == "LON" for origin, _, _ in calls) == 1
assert cache.search_directory.get() is None