forked from Rockachopa/Timmy-time-dashboard
feat: code quality audit + autoresearch integration + infra hardening (#150)
This commit is contained in:
committed by
GitHub
parent
fd0ede0d51
commit
ae3bb1cc21
@@ -5,14 +5,14 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from infrastructure.router.api import get_cascade_router, router
|
||||
from infrastructure.router.cascade import CircuitState, Provider, ProviderStatus
|
||||
from infrastructure.router.api import router, get_cascade_router
|
||||
|
||||
|
||||
def make_mock_router():
|
||||
"""Create a mock CascadeRouter."""
|
||||
router = MagicMock()
|
||||
|
||||
|
||||
# Create test providers
|
||||
provider1 = Provider(
|
||||
name="ollama-local",
|
||||
@@ -24,7 +24,7 @@ def make_mock_router():
|
||||
)
|
||||
provider1.status = ProviderStatus.HEALTHY
|
||||
provider1.circuit_state = CircuitState.CLOSED
|
||||
|
||||
|
||||
provider2 = Provider(
|
||||
name="openai-backup",
|
||||
type="openai",
|
||||
@@ -35,12 +35,12 @@ def make_mock_router():
|
||||
)
|
||||
provider2.status = ProviderStatus.DEGRADED
|
||||
provider2.circuit_state = CircuitState.CLOSED
|
||||
|
||||
|
||||
router.providers = [provider1, provider2]
|
||||
router.config.timeout_seconds = 30
|
||||
router.config.max_retries_per_provider = 2
|
||||
router.config.circuit_breaker_failure_threshold = 5
|
||||
|
||||
|
||||
return router
|
||||
|
||||
|
||||
@@ -48,74 +48,87 @@ def make_mock_router():
|
||||
def mock_router():
|
||||
"""Create test client with mocked router."""
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
|
||||
|
||||
# Create mock router
|
||||
mock = make_mock_router()
|
||||
|
||||
|
||||
# Override dependency
|
||||
async def mock_get_router():
|
||||
return mock
|
||||
|
||||
|
||||
app.dependency_overrides[get_cascade_router] = mock_get_router
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
return client, mock
|
||||
|
||||
|
||||
class TestCompleteEndpoint:
|
||||
"""Test /complete endpoint."""
|
||||
|
||||
|
||||
def test_complete_success(self, mock_router):
|
||||
"""Test successful completion."""
|
||||
client, mock = mock_router
|
||||
mock.complete = AsyncMock(return_value={
|
||||
"content": "Hello! How can I help?",
|
||||
"provider": "ollama-local",
|
||||
"model": "llama3.2",
|
||||
"latency_ms": 250.5,
|
||||
})
|
||||
|
||||
response = client.post("/api/v1/router/complete", json={
|
||||
"messages": [{"role": "user", "content": "Hi"}],
|
||||
"model": "llama3.2",
|
||||
"temperature": 0.7,
|
||||
})
|
||||
|
||||
mock.complete = AsyncMock(
|
||||
return_value={
|
||||
"content": "Hello! How can I help?",
|
||||
"provider": "ollama-local",
|
||||
"model": "llama3.2",
|
||||
"latency_ms": 250.5,
|
||||
}
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/router/complete",
|
||||
json={
|
||||
"messages": [{"role": "user", "content": "Hi"}],
|
||||
"model": "llama3.2",
|
||||
"temperature": 0.7,
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["content"] == "Hello! How can I help?"
|
||||
assert data["provider"] == "ollama-local"
|
||||
assert data["latency_ms"] == 250.5
|
||||
|
||||
|
||||
def test_complete_all_providers_fail(self, mock_router):
|
||||
"""Test 503 when all providers fail."""
|
||||
client, mock = mock_router
|
||||
mock.complete = AsyncMock(side_effect=RuntimeError("All providers failed"))
|
||||
|
||||
response = client.post("/api/v1/router/complete", json={
|
||||
"messages": [{"role": "user", "content": "Hi"}],
|
||||
})
|
||||
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/router/complete",
|
||||
json={
|
||||
"messages": [{"role": "user", "content": "Hi"}],
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 503
|
||||
assert "All providers failed" in response.json()["detail"]
|
||||
|
||||
|
||||
def test_complete_default_temperature(self, mock_router):
|
||||
"""Test completion with default temperature."""
|
||||
client, mock = mock_router
|
||||
mock.complete = AsyncMock(return_value={
|
||||
"content": "Response",
|
||||
"provider": "ollama-local",
|
||||
"model": "llama3.2",
|
||||
"latency_ms": 100.0,
|
||||
})
|
||||
|
||||
response = client.post("/api/v1/router/complete", json={
|
||||
"messages": [{"role": "user", "content": "Hi"}],
|
||||
})
|
||||
|
||||
mock.complete = AsyncMock(
|
||||
return_value={
|
||||
"content": "Response",
|
||||
"provider": "ollama-local",
|
||||
"model": "llama3.2",
|
||||
"latency_ms": 100.0,
|
||||
}
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/router/complete",
|
||||
json={
|
||||
"messages": [{"role": "user", "content": "Hi"}],
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
# Check that complete was called with correct temperature
|
||||
call_args = mock.complete.call_args
|
||||
@@ -124,35 +137,37 @@ class TestCompleteEndpoint:
|
||||
|
||||
class TestStatusEndpoint:
|
||||
"""Test /status endpoint."""
|
||||
|
||||
|
||||
def test_get_status(self, mock_router):
|
||||
"""Test getting router status."""
|
||||
client, mock = mock_router
|
||||
mock.get_status = MagicMock(return_value={
|
||||
"total_providers": 2,
|
||||
"healthy_providers": 1,
|
||||
"degraded_providers": 1,
|
||||
"unhealthy_providers": 0,
|
||||
"providers": [
|
||||
{
|
||||
"name": "ollama-local",
|
||||
"type": "ollama",
|
||||
"status": "healthy",
|
||||
"priority": 1,
|
||||
"default_model": "llama3.2",
|
||||
},
|
||||
{
|
||||
"name": "openai-backup",
|
||||
"type": "openai",
|
||||
"status": "degraded",
|
||||
"priority": 2,
|
||||
"default_model": "gpt-4o-mini",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
mock.get_status = MagicMock(
|
||||
return_value={
|
||||
"total_providers": 2,
|
||||
"healthy_providers": 1,
|
||||
"degraded_providers": 1,
|
||||
"unhealthy_providers": 0,
|
||||
"providers": [
|
||||
{
|
||||
"name": "ollama-local",
|
||||
"type": "ollama",
|
||||
"status": "healthy",
|
||||
"priority": 1,
|
||||
"default_model": "llama3.2",
|
||||
},
|
||||
{
|
||||
"name": "openai-backup",
|
||||
"type": "openai",
|
||||
"status": "degraded",
|
||||
"priority": 2,
|
||||
"default_model": "gpt-4o-mini",
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
response = client.get("/api/v1/router/status")
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total_providers"] == 2
|
||||
@@ -163,31 +178,33 @@ class TestStatusEndpoint:
|
||||
|
||||
class TestMetricsEndpoint:
|
||||
"""Test /metrics endpoint."""
|
||||
|
||||
|
||||
def test_get_metrics(self, mock_router):
|
||||
"""Test getting detailed metrics."""
|
||||
client, mock = mock_router
|
||||
# Setup the mock return value on the mock_router object
|
||||
mock.get_metrics = MagicMock(return_value={
|
||||
"providers": [
|
||||
{
|
||||
"name": "ollama-local",
|
||||
"type": "ollama",
|
||||
"status": "healthy",
|
||||
"circuit_state": "closed",
|
||||
"metrics": {
|
||||
"total_requests": 100,
|
||||
"successful": 98,
|
||||
"failed": 2,
|
||||
"error_rate": 0.02,
|
||||
"avg_latency_ms": 150.5,
|
||||
mock.get_metrics = MagicMock(
|
||||
return_value={
|
||||
"providers": [
|
||||
{
|
||||
"name": "ollama-local",
|
||||
"type": "ollama",
|
||||
"status": "healthy",
|
||||
"circuit_state": "closed",
|
||||
"metrics": {
|
||||
"total_requests": 100,
|
||||
"successful": 98,
|
||||
"failed": 2,
|
||||
"error_rate": 0.02,
|
||||
"avg_latency_ms": 150.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
response = client.get("/api/v1/router/metrics")
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data["providers"]) == 1
|
||||
@@ -199,17 +216,17 @@ class TestMetricsEndpoint:
|
||||
|
||||
class TestListProvidersEndpoint:
|
||||
"""Test /providers endpoint."""
|
||||
|
||||
|
||||
def test_list_providers(self, mock_router):
|
||||
"""Test listing all providers."""
|
||||
client, mock = mock_router
|
||||
|
||||
|
||||
response = client.get("/api/v1/router/providers")
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data) == 2
|
||||
|
||||
|
||||
# Check first provider
|
||||
assert data[0]["name"] == "ollama-local"
|
||||
assert data[0]["type"] == "ollama"
|
||||
@@ -221,40 +238,38 @@ class TestListProvidersEndpoint:
|
||||
|
||||
class TestControlProviderEndpoint:
|
||||
"""Test /providers/{name}/control endpoint."""
|
||||
|
||||
|
||||
def test_disable_provider(self, mock_router):
|
||||
"""Test disabling a provider."""
|
||||
client, mock = mock_router
|
||||
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/router/providers/ollama-local/control",
|
||||
json={"action": "disable"}
|
||||
"/api/v1/router/providers/ollama-local/control", json={"action": "disable"}
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "disabled" in response.json()["message"]
|
||||
|
||||
|
||||
# Check that the provider was disabled
|
||||
provider = mock.providers[0]
|
||||
assert provider.enabled is False
|
||||
assert provider.status == ProviderStatus.DISABLED
|
||||
|
||||
|
||||
def test_enable_provider(self, mock_router):
|
||||
"""Test enabling a provider."""
|
||||
client, mock = mock_router
|
||||
# First disable it
|
||||
mock.providers[0].enabled = False
|
||||
mock.providers[0].status = ProviderStatus.DISABLED
|
||||
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/router/providers/ollama-local/control",
|
||||
json={"action": "enable"}
|
||||
"/api/v1/router/providers/ollama-local/control", json={"action": "enable"}
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "enabled" in response.json()["message"]
|
||||
assert mock.providers[0].enabled is True
|
||||
|
||||
|
||||
def test_reset_circuit(self, mock_router):
|
||||
"""Test resetting circuit breaker."""
|
||||
client, mock = mock_router
|
||||
@@ -262,73 +277,70 @@ class TestControlProviderEndpoint:
|
||||
mock.providers[0].circuit_state = CircuitState.OPEN
|
||||
mock.providers[0].status = ProviderStatus.UNHEALTHY
|
||||
mock.providers[0].metrics.consecutive_failures = 10
|
||||
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/router/providers/ollama-local/control",
|
||||
json={"action": "reset_circuit"}
|
||||
"/api/v1/router/providers/ollama-local/control", json={"action": "reset_circuit"}
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "reset" in response.json()["message"]
|
||||
|
||||
|
||||
provider = mock.providers[0]
|
||||
assert provider.circuit_state == CircuitState.CLOSED
|
||||
assert provider.status == ProviderStatus.HEALTHY
|
||||
assert provider.metrics.consecutive_failures == 0
|
||||
|
||||
|
||||
def test_control_unknown_provider(self, mock_router):
|
||||
"""Test controlling unknown provider returns 404."""
|
||||
client, mock = mock_router
|
||||
response = client.post(
|
||||
"/api/v1/router/providers/unknown/control",
|
||||
json={"action": "disable"}
|
||||
"/api/v1/router/providers/unknown/control", json={"action": "disable"}
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 404
|
||||
assert "not found" in response.json()["detail"]
|
||||
|
||||
|
||||
def test_control_unknown_action(self, mock_router):
|
||||
"""Test unknown action returns 400."""
|
||||
client, mock = mock_router
|
||||
response = client.post(
|
||||
"/api/v1/router/providers/ollama-local/control",
|
||||
json={"action": "invalid_action"}
|
||||
"/api/v1/router/providers/ollama-local/control", json={"action": "invalid_action"}
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "Unknown action" in response.json()["detail"]
|
||||
|
||||
|
||||
class TestHealthCheckEndpoint:
|
||||
"""Test /health-check endpoint."""
|
||||
|
||||
|
||||
def test_health_check_all_healthy(self, mock_router):
|
||||
"""Test health check when all providers are healthy."""
|
||||
client, mock = mock_router
|
||||
|
||||
|
||||
with patch.object(mock, "_check_provider_available") as mock_check:
|
||||
mock_check.return_value = True
|
||||
|
||||
|
||||
response = client.post("/api/v1/router/health-check")
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["healthy_count"] == 2
|
||||
assert len(data["providers"]) == 2
|
||||
|
||||
|
||||
for p in data["providers"]:
|
||||
assert p["healthy"] is True
|
||||
|
||||
|
||||
def test_health_check_with_failure(self, mock_router):
|
||||
"""Test health check when some providers fail."""
|
||||
client, mock = mock_router
|
||||
|
||||
|
||||
with patch.object(mock, "_check_provider_available") as mock_check:
|
||||
# First provider fails, second succeeds
|
||||
mock_check.side_effect = [False, True]
|
||||
|
||||
|
||||
response = client.post("/api/v1/router/health-check")
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["healthy_count"] == 1
|
||||
@@ -338,21 +350,21 @@ class TestHealthCheckEndpoint:
|
||||
|
||||
class TestGetConfigEndpoint:
|
||||
"""Test /config endpoint."""
|
||||
|
||||
|
||||
def test_get_config(self, mock_router):
|
||||
"""Test getting router configuration."""
|
||||
client, mock = mock_router
|
||||
|
||||
|
||||
response = client.get("/api/v1/router/config")
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
|
||||
|
||||
assert data["timeout_seconds"] == 30
|
||||
assert data["max_retries_per_provider"] == 2
|
||||
assert "circuit_breaker" in data
|
||||
assert data["circuit_breaker"]["failure_threshold"] == 5
|
||||
|
||||
|
||||
# Check providers list (without secrets)
|
||||
assert len(data["providers"]) == 2
|
||||
assert "api_key" not in data["providers"][0]
|
||||
|
||||
Reference in New Issue
Block a user