diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index e89aa01..4411060 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -1,15 +1,25 @@ """ pytest configuration: isolate each test with a temporary data directory. + +Async fixtures (db_session, async_client) are added for Phase 1 — sync fixtures remain until Plan 05 cuts over. """ +from __future__ import annotations + import os import json import pytest +import pytest_asyncio import tempfile import shutil from pathlib import Path from fastapi.testclient import TestClient +from httpx import AsyncClient, ASGITransport +from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession +from sqlalchemy.pool import StaticPool +# ── Sync fixtures (legacy — retained until Plan 05 cuts over) ────────────────── + @pytest.fixture(autouse=True) def isolated_data_dir(monkeypatch, tmp_path): """Each test gets its own clean data directory.""" @@ -68,3 +78,55 @@ def sample_pdf(tmp_path): doc.save(str(pdf_path)) doc.close() return pdf_path + + +# ── Async fixtures (Phase 1 additions — Plan 03+ tests use these) ────────────── + +@pytest_asyncio.fixture +async def db_session(): + """In-memory async SQLite session for unit tests. + + Tries to import db.models.Base (available after Plan 03). If the module + does not yet exist the fixture skips the test gracefully so the suite + stays green during Wave 1. + """ + engine = create_async_engine( + "sqlite+aiosqlite:///:memory:", + connect_args={"check_same_thread": False}, + poolclass=StaticPool, + ) + + try: + from db.models import Base + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + except ImportError: + await engine.dispose() + pytest.skip("db.models not yet implemented — plan 03") + + AsyncTestSession = async_sessionmaker(engine, expire_on_commit=False) + async with AsyncTestSession() as session: + yield session + + await engine.dispose() + + +@pytest_asyncio.fixture +async def async_client(db_session): + """Async HTTP test client with DB dependency overridden. + + Tries to import deps.db.get_db (available after Plan 03). If the module + does not yet exist the fixture skips the test gracefully. + """ + try: + from deps.db import get_db + from main import app + except ImportError as exc: + pytest.skip(f"deps.db.get_db not yet implemented — plan 03: {exc}") + + app.dependency_overrides[get_db] = lambda: db_session + + async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as c: + yield c + + app.dependency_overrides.clear()