import pytest_asyncio from httpx import ASGITransport, AsyncClient from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine from sqlalchemy.pool import NullPool from app.config import settings from app.db import Base, get_db from app.main import app # pytest-asyncio gives each test function its own event loop by default; # asyncpg connections are bound to the loop they were opened on, so a pooled # connection from one test's loop breaks the next test. NullPool sidesteps # this by opening a fresh connection per checkout — scoped to this dedicated # test engine so app.db.engine (used by production) is unaffected. test_engine = create_async_engine(settings.database_url, poolclass=NullPool) TestSessionLocal = async_sessionmaker(test_engine, expire_on_commit=False) @pytest_asyncio.fixture(autouse=True) async def _reset_db(): async with test_engine.begin() as conn: await conn.run_sync(Base.metadata.drop_all) await conn.run_sync(Base.metadata.create_all) yield async def _override_get_db(): async with TestSessionLocal() as session: yield session app.dependency_overrides[get_db] = _override_get_db @pytest_asyncio.fixture async def client(): transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as ac: yield ac