- Add tests for all backend modules: config, database, models, services, routes, middleware, and providers - Separate dev dependencies using dependency-groups - Update CLAUDE.md with test commands and project structure - Add .coverage and .pytest_cache to gitignore 88 tests covering: - Authentication and authorization - Model routing and key selection - Async logging with batch processing - All API endpoints (chat, openai, anthropic, health) - LiteLLM wrapper (mocked external calls) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
130 lines
4.1 KiB
Python
130 lines
4.1 KiB
Python
"""Tests for Anthropic native routes."""
|
|
import pytest
|
|
from unittest.mock import MagicMock
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.main import app
|
|
from app.models import ClientKey, Provider
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_client_key():
|
|
"""Return a valid client key for testing."""
|
|
return ClientKey(
|
|
id=1,
|
|
key="test-api-key",
|
|
name="test-client",
|
|
is_active=True,
|
|
created_at="2024-01-01"
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_anthropic_provider():
|
|
"""Return a mock Anthropic provider."""
|
|
return Provider(
|
|
id=2,
|
|
name="anthropic",
|
|
base_url="https://api.anthropic.com/v1",
|
|
api_type="anthropic",
|
|
is_active=True
|
|
)
|
|
|
|
|
|
class TestAnthropicMessages:
|
|
"""Test /v1/anthropic/messages endpoint."""
|
|
|
|
def test_anthropic_unauthorized(self):
|
|
"""Test Anthropic messages without auth."""
|
|
client = TestClient(app)
|
|
response = client.post(
|
|
"/v1/anthropic/messages",
|
|
json={
|
|
"model": "claude-3-opus",
|
|
"messages": [{"role": "user", "content": "hi"}],
|
|
"max_tokens": 1024
|
|
}
|
|
)
|
|
assert response.status_code == 401
|
|
|
|
def test_anthropic_success(self, mock_client_key, mock_anthropic_provider):
|
|
"""Test successful Anthropic messages."""
|
|
from app.middleware.auth import get_current_client
|
|
from app.services.router import ModelRouter
|
|
from app.services.key_selector import key_selector
|
|
from app.services.logger import async_logger
|
|
from app.providers.litellm_wrapper import LiteLLMWrapper
|
|
|
|
app.dependency_overrides[get_current_client] = lambda: mock_client_key
|
|
|
|
mock_response = MagicMock()
|
|
mock_response.model_dump.return_value = {
|
|
"id": "msg-test",
|
|
"content": [{"text": "Hello!"}]
|
|
}
|
|
|
|
original_get_provider = ModelRouter.get_provider
|
|
original_get_key = key_selector.get_next_key
|
|
original_chat = LiteLLMWrapper.chat_completion
|
|
original_log = async_logger.log
|
|
|
|
async def mock_get_provider(name):
|
|
return mock_anthropic_provider
|
|
|
|
async def mock_get_key(provider_id):
|
|
return MagicMock(key="sk-ant-test")
|
|
|
|
async def mock_chat(*args, **kwargs):
|
|
return (mock_response, 10, 20)
|
|
|
|
async def mock_log(entry):
|
|
pass
|
|
|
|
ModelRouter.get_provider = staticmethod(lambda name: mock_get_provider(name))
|
|
key_selector.get_next_key = mock_get_key
|
|
LiteLLMWrapper.chat_completion = staticmethod(lambda *a, **k: mock_chat(*a, **k))
|
|
async_logger.log = mock_log
|
|
|
|
try:
|
|
client = TestClient(app)
|
|
response = client.post(
|
|
"/v1/anthropic/messages",
|
|
json={
|
|
"model": "claude-3-opus",
|
|
"messages": [{"role": "user", "content": "hi"}],
|
|
"max_tokens": 1024
|
|
},
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
assert response.status_code == 200
|
|
finally:
|
|
ModelRouter.get_provider = original_get_provider
|
|
key_selector.get_next_key = original_get_key
|
|
LiteLLMWrapper.chat_completion = original_chat
|
|
async_logger.log = original_log
|
|
app.dependency_overrides.clear()
|
|
|
|
|
|
class TestAnthropicModels:
|
|
"""Test /v1/anthropic/models endpoint."""
|
|
|
|
def test_anthropic_models_success(self, mock_client_key):
|
|
"""Test list Anthropic models."""
|
|
from app.middleware.auth import get_current_client
|
|
|
|
app.dependency_overrides[get_current_client] = lambda: mock_client_key
|
|
|
|
try:
|
|
client = TestClient(app)
|
|
response = client.get(
|
|
"/v1/anthropic/models",
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["object"] == "list"
|
|
for model in data["data"]:
|
|
assert model["owned_by"] == "anthropic"
|
|
finally:
|
|
app.dependency_overrides.clear()
|