150 lines
4.4 KiB
Python
150 lines
4.4 KiB
Python
"""Tests for provider health history store and API endpoint."""
|
|
|
|
import time
|
|
from datetime import UTC, datetime, timedelta
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
from src.infrastructure.router.history import HealthHistoryStore
|
|
|
|
|
|
@pytest.fixture
|
|
def store():
|
|
"""In-memory history store for testing."""
|
|
s = HealthHistoryStore(db_path=":memory:")
|
|
yield s
|
|
s.close()
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_providers():
|
|
return [
|
|
{
|
|
"name": "anthropic",
|
|
"status": "healthy",
|
|
"error_rate": 0.01,
|
|
"avg_latency_ms": 250.5,
|
|
"circuit_state": "closed",
|
|
"total_requests": 100,
|
|
},
|
|
{
|
|
"name": "local",
|
|
"status": "degraded",
|
|
"error_rate": 0.15,
|
|
"avg_latency_ms": 80.0,
|
|
"circuit_state": "closed",
|
|
"total_requests": 50,
|
|
},
|
|
]
|
|
|
|
|
|
def test_record_and_retrieve(store, sample_providers):
|
|
store.record_snapshot(sample_providers)
|
|
history = store.get_history(hours=1)
|
|
assert len(history) == 1
|
|
assert len(history[0]["providers"]) == 2
|
|
assert history[0]["providers"][0]["name"] == "anthropic"
|
|
assert history[0]["providers"][1]["name"] == "local"
|
|
assert "timestamp" in history[0]
|
|
|
|
|
|
def test_multiple_snapshots(store, sample_providers):
|
|
store.record_snapshot(sample_providers)
|
|
time.sleep(0.01)
|
|
store.record_snapshot(sample_providers)
|
|
history = store.get_history(hours=1)
|
|
assert len(history) == 2
|
|
|
|
|
|
def test_hours_filtering(store, sample_providers):
|
|
old_ts = (datetime.now(UTC) - timedelta(hours=48)).isoformat()
|
|
store._conn.execute(
|
|
"""INSERT INTO snapshots
|
|
(timestamp, provider_name, status, error_rate,
|
|
avg_latency_ms, circuit_state, total_requests)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)""",
|
|
(old_ts, "anthropic", "healthy", 0.0, 100.0, "closed", 10),
|
|
)
|
|
store._conn.commit()
|
|
store.record_snapshot(sample_providers)
|
|
|
|
history = store.get_history(hours=24)
|
|
assert len(history) == 1
|
|
|
|
history = store.get_history(hours=72)
|
|
assert len(history) == 2
|
|
|
|
|
|
def test_prune(store, sample_providers):
|
|
old_ts = (datetime.now(UTC) - timedelta(hours=200)).isoformat()
|
|
store._conn.execute(
|
|
"""INSERT INTO snapshots
|
|
(timestamp, provider_name, status, error_rate,
|
|
avg_latency_ms, circuit_state, total_requests)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)""",
|
|
(old_ts, "anthropic", "healthy", 0.0, 100.0, "closed", 10),
|
|
)
|
|
store._conn.commit()
|
|
store.record_snapshot(sample_providers)
|
|
|
|
deleted = store.prune(keep_hours=168)
|
|
assert deleted == 1
|
|
history = store.get_history(hours=999)
|
|
assert len(history) == 1
|
|
|
|
|
|
def test_empty_history(store):
|
|
assert store.get_history(hours=24) == []
|
|
|
|
|
|
def test_capture_snapshot_from_router(store):
|
|
mock_metrics = MagicMock()
|
|
mock_metrics.error_rate = 0.05
|
|
mock_metrics.avg_latency_ms = 200.0
|
|
mock_metrics.total_requests = 42
|
|
|
|
mock_provider = MagicMock()
|
|
mock_provider.name = "test-provider"
|
|
mock_provider.status.value = "healthy"
|
|
mock_provider.metrics = mock_metrics
|
|
mock_provider.circuit_state.value = "closed"
|
|
|
|
mock_router = MagicMock()
|
|
mock_router.providers = [mock_provider]
|
|
|
|
store._capture_snapshot(mock_router)
|
|
history = store.get_history(hours=1)
|
|
assert len(history) == 1
|
|
p = history[0]["providers"][0]
|
|
assert p["name"] == "test-provider"
|
|
assert p["status"] == "healthy"
|
|
assert p["error_rate"] == 0.05
|
|
assert p["total_requests"] == 42
|
|
|
|
|
|
def test_history_api_endpoint(store, sample_providers):
|
|
"""GET /api/v1/router/history returns snapshot data."""
|
|
store.record_snapshot(sample_providers)
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
from src.infrastructure.router.api import get_cascade_router
|
|
from src.infrastructure.router.api import router as api_router
|
|
from src.infrastructure.router.history import get_history_store
|
|
|
|
app = FastAPI()
|
|
app.include_router(api_router)
|
|
|
|
app.dependency_overrides[get_history_store] = lambda: store
|
|
app.dependency_overrides[get_cascade_router] = lambda: MagicMock()
|
|
|
|
client = TestClient(app)
|
|
resp = client.get("/api/v1/router/history?hours=1")
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert len(data) == 1
|
|
assert len(data[0]["providers"]) == 2
|
|
assert data[0]["providers"][0]["name"] == "anthropic"
|
|
|
|
app.dependency_overrides.clear()
|