import os import time import jwt import pytest from httpx import ASGITransport, AsyncClient from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine import app.models # noqa: F401 — ensures all models register with Base.metadata from app.core.auth import AuthUser, get_current_user from app.core.database import Base, get_session from app.main import app TEST_JWT_SECRET = "test-jwt-secret-for-testing-only" TEST_DATABASE_URL = os.getenv( "TEST_DATABASE_URL", "postgresql+asyncpg://postgres:postgres@localhost:5433/nuzlocke_test", ) @pytest.fixture(scope="session") async def engine(): """Create the test engine and schema once for the entire session.""" eng = create_async_engine(TEST_DATABASE_URL, echo=False) async with eng.begin() as conn: await conn.run_sync(Base.metadata.drop_all) await conn.run_sync(Base.metadata.create_all) yield eng async with eng.begin() as conn: await conn.run_sync(Base.metadata.drop_all) await eng.dispose() @pytest.fixture async def db_session(engine): """ Provide a database session for a single test. Overrides the FastAPI get_session dependency so endpoint handlers use the same session. Truncates all tables after the test to isolate state. """ session_factory = async_sessionmaker(engine, expire_on_commit=False) session = session_factory() async def override_get_session(): yield session app.dependency_overrides[get_session] = override_get_session yield session await session.close() app.dependency_overrides.clear() async with engine.begin() as conn: for table in reversed(Base.metadata.sorted_tables): await conn.execute(table.delete()) @pytest.fixture async def client(db_session): """Async HTTP client wired to the FastAPI app with the test database session.""" async with AsyncClient( transport=ASGITransport(app=app), base_url="http://test" ) as ac: yield ac @pytest.fixture def mock_auth_user(): """Return a mock authenticated user for tests.""" return AuthUser(id="test-user-123", email="test@example.com", role="authenticated") @pytest.fixture def auth_override(mock_auth_user): """Override get_current_user to return a mock user.""" def _override(): return mock_auth_user app.dependency_overrides[get_current_user] = _override yield app.dependency_overrides.pop(get_current_user, None) @pytest.fixture async def auth_client(db_session, auth_override): """Async HTTP client with mocked authentication.""" async with AsyncClient( transport=ASGITransport(app=app), base_url="http://test" ) as ac: yield ac @pytest.fixture def valid_token(): """Generate a valid JWT token for testing.""" payload = { "sub": "test-user-123", "email": "test@example.com", "role": "authenticated", "aud": "authenticated", "exp": int(time.time()) + 3600, } return jwt.encode(payload, TEST_JWT_SECRET, algorithm="HS256")