"""Pytest fixtures: in-memory async SQLite DB, app override, HTTP client. Tests run fully offline — no PostgreSQL, no OpenRouter — using an in-memory SQLite database and the built-in mock AI explainer. """ from __future__ import annotations import os from collections.abc import AsyncGenerator import pytest_asyncio # Force a deterministic, secret-free test configuration before app import. os.environ.setdefault("ENVIRONMENT", "testing") os.environ.setdefault("JWT_SECRET_KEY", "test-secret-key-please-ignore") os.environ.setdefault("OPENROUTER_API_KEY", "") # ensure mock explainer os.environ.setdefault("DATABASE_URL", "sqlite+aiosqlite:///:memory:") from httpx import ASGITransport, AsyncClient # noqa: E402 from sqlalchemy.ext.asyncio import ( # noqa: E402 AsyncSession, async_sessionmaker, create_async_engine, ) from sqlalchemy.pool import StaticPool # noqa: E402 from app.core.database import get_db # noqa: E402 from app.main import app # noqa: E402 from app.models import Base # noqa: E402 @pytest_asyncio.fixture async def engine() -> AsyncGenerator: eng = create_async_engine( "sqlite+aiosqlite:///:memory:", connect_args={"check_same_thread": False}, poolclass=StaticPool, ) async with eng.begin() as conn: await conn.run_sync(Base.metadata.create_all) yield eng await eng.dispose() @pytest_asyncio.fixture async def session_factory(engine): return async_sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False) @pytest_asyncio.fixture async def client(session_factory) -> AsyncGenerator[AsyncClient, None]: async def override_get_db() -> AsyncGenerator[AsyncSession, None]: async with session_factory() as session: try: yield session await session.commit() except Exception: await session.rollback() raise app.dependency_overrides[get_db] = override_get_db transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as ac: yield ac app.dependency_overrides.clear() @pytest_asyncio.fixture async def auth_client(client: AsyncClient) -> AsyncClient: """An AsyncClient with a registered+logged-in user's bearer token set.""" email = "tester@example.com" password = "supersecret123" await client.post( "/api/v1/auth/register", json={"email": email, "password": password, "full_name": "Tester"}, ) resp = await client.post( "/api/v1/auth/login", data={"username": email, "password": password}, ) token = resp.json()["access_token"] client.headers.update({"Authorization": f"Bearer {token}"}) return client @pytest_asyncio.fixture async def machine_id(auth_client: AsyncClient) -> str: resp = await auth_client.post( "/api/v1/machines", json={"name": "Pump-1", "type": "pump", "location": "Plant A"}, ) return resp.json()["id"]