Files
ss-tools/backend/tests/conftest.py
busya a9a453109c security: critical auth fixes, test migration to SQLite+FK, 44 test fixes
CRITICAL (CVE-class):
- C-4: Remove DEV_MODE fallback with hardcoded postgres:postgres credentials
- C-3: WebSocket endpoints now require JWT or API key token (?token=) auth
- C-1: Superset environment passwords encrypted via Fernet at rest in DB
- H-4: SESSION_SECRET_KEY separated from JWT AUTH_SECRET_KEY
- H-1: Log injection via %s-formatting instead of f-strings
- H-5: X-Trace-ID header validated as UUID4 to prevent trace poisoning

INFRASTRUCTURE:
- New src/core/encryption.py — EncryptionManager extracted from llm_provider
- Test DB: per-module sqlite:///:memory: with PRAGMA foreign_keys=ON
- testcontainers PostgreSQL via TEST_DB=postgres env var
- conftest temp-file global engine (no 10GB shared-cache leak)

TEST FIXES (44 pre-existing → 0):
- test_smoke_plugins: module-level sys.modules mock isolated to per-test fixture
- test_migration_engine: EXT:Python:uuid → uuid syntax fix
- test_dashboards_api: mock env attributes + correct patch target
- test_constants_audit_fixes: sync expected constant names with actual
- test_defensive_guards: patch.object instead of module-level Repo mock
- test_clean_release_cli: removed empty config.json directory
- FK violations: TaskRecord parents in log_persistence, Environment in
  mapping_service, ReleaseCandidate in candidate_manifest_services

ORTHOGONAL TESTS (18 new):
- test_security_orthogonal.py: bcrypt 72-byte limit, unicode, API key
  format invariants, Fernet robustness, encryption key lifecycle,
  log injection protection, JWT edge cases

MODEL FIXES:
- MetricSnapshot.job_id: nullable with SET NULL (was broken FK for
  aggregate prune snapshots, hidden by SQLite)

CLEANUP:
- Removed stale :memory:test_main/test_auth/test_tasks file databases
- Removed duplicate #endregion in encryption_key.py
2026-05-26 14:58:49 +03:00

94 lines
3.1 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
import sys
import tempfile
from pathlib import Path
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