Files
zzrouter/backend/tests/test_routes_anthropic.py
tech d080e67c29 test: add comprehensive test suite with 89% coverage
- 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>
2026-03-11 19:12:57 +08:00

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()