- "backend/tests/conftest.py" - "backend/tests/test_ingest.py" - "backend/tests/fixtures/sample_transcript.json" - "backend/pytest.ini" - "backend/requirements.txt" - "backend/models.py" GSD-Task: S02/T02
93 lines
3 KiB
Python
93 lines
3 KiB
Python
"""Shared fixtures for Chrysopedia integration tests.
|
|
|
|
Provides:
|
|
- Async SQLAlchemy engine/session against a real PostgreSQL test database
|
|
- httpx.AsyncClient wired to the FastAPI app with dependency overrides
|
|
- Sample transcript fixture path and temporary storage directory
|
|
|
|
Key design choice: function-scoped engine with NullPool avoids asyncpg
|
|
"another operation in progress" errors caused by session-scoped connection
|
|
reuse between the ASGI test client and verification queries.
|
|
"""
|
|
|
|
import os
|
|
import pathlib
|
|
|
|
import pytest
|
|
import pytest_asyncio
|
|
from httpx import ASGITransport, AsyncClient
|
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
|
from sqlalchemy.pool import NullPool
|
|
|
|
# Ensure backend/ is on sys.path so "from models import ..." works
|
|
import sys
|
|
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent))
|
|
|
|
from database import Base, get_session # noqa: E402
|
|
from main import app # noqa: E402
|
|
|
|
TEST_DATABASE_URL = os.getenv(
|
|
"TEST_DATABASE_URL",
|
|
"postgresql+asyncpg://chrysopedia:changeme@localhost:5433/chrysopedia_test",
|
|
)
|
|
|
|
|
|
@pytest_asyncio.fixture()
|
|
async def db_engine():
|
|
"""Create a per-test async engine (NullPool) and create/drop all tables."""
|
|
engine = create_async_engine(TEST_DATABASE_URL, echo=False, poolclass=NullPool)
|
|
|
|
# Create all tables fresh for each test
|
|
async with engine.begin() as conn:
|
|
await conn.run_sync(Base.metadata.drop_all)
|
|
await conn.run_sync(Base.metadata.create_all)
|
|
|
|
yield engine
|
|
|
|
# Drop all tables after test
|
|
async with engine.begin() as conn:
|
|
await conn.run_sync(Base.metadata.drop_all)
|
|
|
|
await engine.dispose()
|
|
|
|
|
|
@pytest_asyncio.fixture()
|
|
async def client(db_engine, tmp_path):
|
|
"""Async HTTP test client wired to FastAPI with dependency overrides."""
|
|
session_factory = async_sessionmaker(
|
|
db_engine, class_=AsyncSession, expire_on_commit=False
|
|
)
|
|
|
|
async def _override_get_session():
|
|
async with session_factory() as session:
|
|
yield session
|
|
|
|
# Override DB session dependency
|
|
app.dependency_overrides[get_session] = _override_get_session
|
|
|
|
# Override transcript_storage_path via environment variable
|
|
os.environ["TRANSCRIPT_STORAGE_PATH"] = str(tmp_path)
|
|
# Clear the lru_cache so Settings picks up the new env var
|
|
from config import get_settings
|
|
get_settings.cache_clear()
|
|
|
|
transport = ASGITransport(app=app)
|
|
async with AsyncClient(transport=transport, base_url="http://testserver") as ac:
|
|
yield ac
|
|
|
|
# Teardown: clean overrides and restore settings cache
|
|
app.dependency_overrides.clear()
|
|
os.environ.pop("TRANSCRIPT_STORAGE_PATH", None)
|
|
get_settings.cache_clear()
|
|
|
|
|
|
@pytest.fixture()
|
|
def sample_transcript_path() -> pathlib.Path:
|
|
"""Path to the sample 5-segment transcript JSON fixture."""
|
|
return pathlib.Path(__file__).parent / "fixtures" / "sample_transcript.json"
|
|
|
|
|
|
@pytest.fixture()
|
|
def tmp_transcript_dir(tmp_path) -> pathlib.Path:
|
|
"""Temporary directory for transcript storage during tests."""
|
|
return tmp_path
|