- Dockerfile.agent: fix CMD (python -m src.agent.run), use backend/requirements.txt, minimal COPY (only src.agent + src.core.cot_logger), add GRACE contract - docker-compose.yml: SERVICE_TOKEN_SECRET -> SERVICE_JWT (match code) - docker-compose.enterprise-clean.yml: same env var fix - docker/.env.agent.example: same env var fix - build.sh: same env var fix - chore: semantics-testing SKILL.md, backend tests, pyproject.toml
119 lines
3.9 KiB
Python
119 lines
3.9 KiB
Python
# #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
|
|
|
|
|
|
# ── CLI options ──
|
|
|
|
def pytest_addoption(parser):
|
|
parser.addoption(
|
|
"--run-integration",
|
|
action="store_true",
|
|
default=False,
|
|
help="Run integration tests (require Docker, Testcontainers, Superset container)",
|
|
)
|
|
|
|
|
|
# ── Test session lifecycle ──
|
|
|
|
def pytest_configure(config):
|
|
print(
|
|
f"\n[conftest] SQLite (global: {_TEST_DB_PATH}) + FK enforcement",
|
|
file=sys.stderr,
|
|
)
|
|
print(
|
|
" Integration tests skipped by default. Use --run-integration to enable.\n",
|
|
file=sys.stderr,
|
|
)
|
|
|
|
|
|
def pytest_unconfigure(config):
|
|
try:
|
|
from src.core.database import engine as _global_engine
|
|
from src.core.database import tasks_engine as _tasks_engine
|
|
from src.core.database import auth_engine as _auth_engine
|
|
_global_engine.dispose()
|
|
_tasks_engine.dispose()
|
|
_auth_engine.dispose()
|
|
except Exception:
|
|
pass
|
|
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
|