# #region TestSessionConfig [C:2] [TYPE Module] [SEMANTICS test, conftest, db, postgres, testcontainers] # @BRIEF Shared pytest fixtures and session configuration for backend tests. # @RELATION BINDS_TO -> [init_db] # @PRE All test modules use sys.path patching to import from src/. # @POST Database tables exist before test session executes. # @RATIONALE # Architecture: # - Global DATABASE_URL → temp-file SQLite (for modules importing src.core.database) # - Per-module engines → sqlite:///:memory: (for modules with local create_engine) # - FK enforcement on ALL engines via PRAGMA foreign_keys=ON # # Why: # - In-process SQLite is 5-10x faster than Docker PostgreSQL # - FK enforcement catches constraint violations (unlike plain SQLite) # - Per-module engines prevent 10GB+ shared-cache memory leak # - Temp file for global engine also prevents memory leak # # PostgreSQL mode (TEST_DB=postgres) → testcontainers or explicit DATABASE_URL. # # @REJECTED # sqlite:///file::memory:...?cache=shared — 10GB+ memory leak (all modules share one DB). # Plain SQLite without FK — silently ignores FK violations, masks bugs. # Always-on PostgresContainer — 5-10x slower, Docker dependency. import os from pathlib import Path import sys import tempfile from sqlalchemy import create_engine, event sys.path.insert(0, str(Path(__file__).parent.parent / "src")) import pytest os.environ.setdefault("SECRET_KEY", "test-secret-key-for-testing") # ── Global engine: temp-file SQLite (not in-memory, not shared cache) ── # This prevents the 10GB memory leak from shared in-memory databases. _TEST_DB_FILE = tempfile.NamedTemporaryFile(suffix="_ss_tools.db", delete=False) _TEST_DB_PATH = _TEST_DB_FILE.name _TEST_DB_FILE.close() os.environ.setdefault("DATABASE_URL", f"sqlite:///{_TEST_DB_PATH}") os.environ.setdefault("AUTH_DATABASE_URL", f"sqlite:///{_TEST_DB_PATH}") # ── FK enforcement on the global engine ── from src.core.database import engine event.listen(engine, "connect", lambda c, _: c.execute("PRAGMA foreign_keys=ON")) # ── Helper: FK-enabled SQLite engine for per-module isolation ── def make_fk_engine(): """Create a per-module SQLite in-memory engine with FK enforcement. Each test module calls this once at module level to get its own isolated database. No shared state → no memory leak. """ eng = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False}) event.listen(eng, "connect", lambda c, _: c.execute("PRAGMA foreign_keys=ON")) return eng # ── Test session lifecycle ── def pytest_configure(config): print( f"\n[conftest] SQLite (global: {_TEST_DB_PATH}) + FK enforcement", file=sys.stderr, ) def pytest_unconfigure(config): try: os.unlink(_TEST_DB_PATH) except Exception: pass @pytest.fixture(scope="session", autouse=True) def ensure_db_tables(): """Create all tables on the global database.""" try: from src.core.database import init_db init_db() except Exception as exc: print(f"\n[conftest] init_db failed: {exc}", file=sys.stderr) raise # #endregion TestSessionConfig