IIIF-Studio / backend /tests /conftest_api.py
Claude
fix(sprint-f0): fondations — lazy imports, schémas conformes, pydantic-settings
193eb98 unverified
"""
Fixtures partagées pour les tests d'API FastAPI (Sprint 4 — Session A).
Usage dans les fichiers de test :
from tests.conftest_api import async_client, db_session # noqa: F401
Stratégie :
- BDD SQLite en mémoire (":memory:") — isolation totale entre tests
- Dependency override de `get_db` pour injecter la session de test
- `httpx.AsyncClient` + `ASGITransport` pour simuler des requêtes HTTP
"""
# 1. stdlib
import pytest
import pytest_asyncio
from unittest.mock import AsyncMock, patch
# 2. third-party
from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
# 3. local
import app.models # noqa: F401 — enregistrement des modèles dans Base.metadata
from app.models.database import Base, get_db
_TEST_DB_URL = "sqlite+aiosqlite:///:memory:"
@pytest_asyncio.fixture
async def db_session():
"""Session AsyncSession sur une BDD SQLite en mémoire."""
test_engine = create_async_engine(_TEST_DB_URL, echo=False)
async with test_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
session_factory = async_sessionmaker(test_engine, expire_on_commit=False)
async with session_factory() as session:
yield session
async with test_engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await test_engine.dispose()
@pytest_asyncio.fixture
async def async_client(db_session: AsyncSession):
"""AsyncClient HTTP connecté à l'app FastAPI, avec BDD de test injectée."""
from app.main import app
async def _override_get_db():
yield db_session
app.dependency_overrides[get_db] = _override_get_db
# Les background tasks (execute_corpus_job, execute_page_job) créent leur
# propre session via async_session_factory. On les neutralise en mockant
# les modules sources pour éviter qu'elles tentent de se connecter à la
# BDD réelle pendant les tests d'API.
with patch("app.services.corpus_runner.execute_corpus_job", AsyncMock(return_value={"total": 0, "done": 0, "failed": 0})), \
patch("app.services.job_runner.execute_page_job", AsyncMock(return_value=None)):
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as client:
yield client
app.dependency_overrides.clear()