"""Test fixtures. Every test runs against a throwaway SQLite file in a tmp_path, never the real data directory. The environment has to be set before lembas.config is imported, because Settings is a cached singleton read at import time. """ from __future__ import annotations import os import tempfile from collections.abc import Iterator from pathlib import Path import pytest # Must happen before any lembas import. _TMP = tempfile.mkdtemp(prefix="lembas-tests-") os.environ.update( { "LEMBAS_SECRET_KEY": "test-secret-key-not-for-real-use", "LEMBAS_DATA_DIR": _TMP, "LEMBAS_ALLOW_SIGNUP": "true", "LEMBAS_LOG_LEVEL": "warning", } ) from fastapi.testclient import TestClient # noqa: E402 from sqlalchemy.orm import Session # noqa: E402 from lembas.config import settings # noqa: E402 from lembas.db.base import Base # noqa: E402 from lembas.db.session import get_engine, get_session_factory, reset_engine # noqa: E402 @pytest.fixture(autouse=True) def fresh_database(tmp_path: Path) -> Iterator[None]: """Point the engine at a per-test database and build the schema. reset_engine() is essential: the engine is a module-level singleton, so without it every test after the first would share the first one's file. """ settings.data_dir = tmp_path reset_engine() settings.ensure_dirs() import lembas.db.models # noqa: F401 (registers the tables) Base.metadata.create_all(bind=get_engine()) yield reset_engine() @pytest.fixture def db() -> Iterator[Session]: session = get_session_factory()() try: yield session finally: session.close() @pytest.fixture def client() -> Iterator[TestClient]: from lembas.main import app # raise_server_exceptions=False so error-handler behaviour is exercised # rather than the exception propagating into the test. with TestClient(app, raise_server_exceptions=False) as test_client: yield test_client @pytest.fixture def registered(client: TestClient) -> dict[str, str]: """Register the first account. It becomes the administrator.""" credentials = { "name": "Frodo", "email": "frodo@shire.test", "password": "speak-friend-and-enter", } response = client.post("/auth/register", data=credentials, follow_redirects=False) assert response.status_code == 303, response.text return credentials @pytest.fixture def user_id(db: Session, registered: dict[str, str]) -> str: """The registered user's id. Chats have a real foreign key to users and SQLite enforces it (the connect-time PRAGMA in db/session.py turns that on), so tests that build a Chat directly need a user that actually exists. """ from sqlalchemy import select from lembas.db.models import User return db.scalar(select(User).where(User.email == registered["email"])).id