- requirements.txt, config.yaml, .env, Dockerfile, docker-compose.yml - app/core: config (YAML+env override), logging (JSON structured), exceptions (typed hierarchy), json_utils (Markdown fence stripping) - app/clients: LLMClient ABC + ZhipuAIClient (run_in_executor), StorageClient ABC + RustFSClient (boto3 head_object for size check) - app/main.py: FastAPI app with health endpoint and router registration - app/core/dependencies.py: lru_cache singleton factories - tests/conftest.py: mock_llm, mock_storage, test_app, client fixtures - pytest.ini: asyncio_mode=auto - 11 unit tests passing
40 lines
1.2 KiB
Python
40 lines
1.2 KiB
Python
import pytest
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.clients.llm.base import LLMClient
|
|
from app.clients.storage.base import StorageClient
|
|
from app.core.dependencies import get_llm_client, get_storage_client
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_llm() -> LLMClient:
|
|
client = MagicMock(spec=LLMClient)
|
|
client.chat = AsyncMock(return_value='[]')
|
|
client.chat_vision = AsyncMock(return_value='[]')
|
|
return client
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_storage() -> StorageClient:
|
|
client = MagicMock(spec=StorageClient)
|
|
client.download_bytes = AsyncMock(return_value=b"")
|
|
client.upload_bytes = AsyncMock(return_value=None)
|
|
client.get_presigned_url = AsyncMock(return_value="http://example.com/presigned")
|
|
client.get_object_size = AsyncMock(return_value=10 * 1024 * 1024) # 10 MB default
|
|
return client
|
|
|
|
|
|
@pytest.fixture
|
|
def test_app(mock_llm, mock_storage):
|
|
from app.main import app
|
|
app.dependency_overrides[get_llm_client] = lambda: mock_llm
|
|
app.dependency_overrides[get_storage_client] = lambda: mock_storage
|
|
yield app
|
|
app.dependency_overrides.clear()
|
|
|
|
|
|
@pytest.fixture
|
|
def client(test_app):
|
|
return TestClient(test_app)
|