Files
2026-07-13 13:12:33 +08:00

171 lines
4.7 KiB
Python

"""Unit tests for the rolling provider-call stats store (offline, injected clock)."""
from __future__ import annotations
from opensquilla.gateway.boot import build_provider_call_observer
from opensquilla.gateway.provider_stats import ProviderStatsStore
class _Clock:
def __init__(self, start: float = 1_000_000.0) -> None:
self.value = start
def __call__(self) -> float:
return self.value
def advance(self, seconds: float) -> None:
self.value += seconds
def _fill(
store: ProviderStatsStore,
*,
provider_id: str = "openrouter",
count: int = 5,
ttft_ms: int | None = 120,
) -> None:
for _ in range(count):
store.record(
provider_id=provider_id,
model="openai/gpt-5.1",
ttft_ms=ttft_ms,
duration_ms=900,
ok=True,
)
def test_snapshot_none_for_unknown_provider() -> None:
store = ProviderStatsStore(now=_Clock())
assert store.snapshot("openrouter") is None
def test_snapshot_none_below_min_samples() -> None:
store = ProviderStatsStore(now=_Clock())
_fill(store, count=4)
assert store.snapshot("openrouter") is None
def test_snapshot_reports_p50_and_gates_p95_below_ten_samples() -> None:
store = ProviderStatsStore(now=_Clock())
_fill(store, count=5, ttft_ms=100)
snap = store.snapshot("openrouter")
assert snap == {
"p50TtftMs": 100,
"p95TtftMs": None,
"samples": 5,
"windowMinutes": 60,
}
def test_snapshot_reports_p95_at_ten_samples() -> None:
store = ProviderStatsStore(now=_Clock())
for ttft in (10, 20, 30, 40, 50, 60, 70, 80, 90, 1000):
store.record(
provider_id="openrouter",
model="m",
ttft_ms=ttft,
duration_ms=100,
ok=True,
)
snap = store.snapshot("openrouter")
assert snap is not None
assert snap["samples"] == 10
assert snap["p50TtftMs"] == 50
assert snap["p95TtftMs"] == 1000
def test_snapshot_p95_gates_on_ttft_carrying_samples_not_window_size() -> None:
store = ProviderStatsStore(now=_Clock())
# 9 failed calls without a TTFT plus one success: the window holds 10
# samples, but a single TTFT must not become a p95 readout.
_fill(store, count=9, ttft_ms=None)
_fill(store, count=1, ttft_ms=5000)
snap = store.snapshot("openrouter")
assert snap is not None
assert snap["samples"] == 10
assert snap["p50TtftMs"] == 5000
assert snap["p95TtftMs"] is None
def test_snapshot_windowing_drops_old_samples() -> None:
clock = _Clock()
store = ProviderStatsStore(now=clock)
_fill(store, count=3, ttft_ms=50)
clock.advance(3601.0)
_fill(store, count=3, ttft_ms=200)
# Only the 3 recent samples are in the window — below the minimum.
assert store.snapshot("openrouter") is None
_fill(store, count=2, ttft_ms=200)
snap = store.snapshot("openrouter")
assert snap is not None
assert snap["samples"] == 5
assert snap["p50TtftMs"] == 200
def test_snapshot_ttft_percentiles_skip_none_ttft_entries() -> None:
store = ProviderStatsStore(now=_Clock())
_fill(store, count=4, ttft_ms=None)
_fill(store, count=2, ttft_ms=300)
snap = store.snapshot("openrouter")
assert snap is not None
assert snap["samples"] == 6
assert snap["p50TtftMs"] == 300
assert snap["p95TtftMs"] is None
def test_snapshot_all_none_ttft_yields_none_percentiles() -> None:
store = ProviderStatsStore(now=_Clock())
_fill(store, count=12, ttft_ms=None)
snap = store.snapshot("openrouter")
assert snap is not None
assert snap["samples"] == 12
assert snap["p50TtftMs"] is None
assert snap["p95TtftMs"] is None
def test_record_ignores_empty_provider_id() -> None:
store = ProviderStatsStore(now=_Clock())
_fill(store, provider_id="", count=10)
assert store.snapshot("") is None
def test_record_bounds_samples_per_provider() -> None:
store = ProviderStatsStore(now=_Clock())
_fill(store, count=500, ttft_ms=10)
snap = store.snapshot("openrouter")
assert snap is not None
assert snap["samples"] == 200
def test_boot_observer_adapter_records_into_store() -> None:
store = ProviderStatsStore(now=_Clock())
observer = build_provider_call_observer(store)
assert observer is not None
for _ in range(5):
observer(
provider_id="openrouter",
model="openai/gpt-5.1",
ttft_ms=80,
duration_ms=500,
ok=True,
failure_kind="",
)
snap = store.snapshot("openrouter")
assert snap is not None
assert snap["p50TtftMs"] == 80
assert snap["samples"] == 5
def test_boot_observer_adapter_is_none_without_store() -> None:
assert build_provider_call_observer(None) is None