"""TDD gate: versioned, redacted platform LLM settings. LLM settings are currently environment-only. This gate proves a durable, versioned settings store that (a) keeps the API key encrypted (never plaintext in the record or logs), (b) exposes only masked values to the API surface, and (c) snapshots an effective settings version for reproducible jobs. """ import pytest from sqlalchemy import create_engine from app.config import Config from app.db import Base, create_session_factory from app.services.settings_service import SettingsService @pytest.fixture() def session_factory(monkeypatch): monkeypatch.setattr(Config, "SECRET_KEY", "test-encryption-secret-key") engine = create_engine("sqlite+pysqlite:///:memory:") Base.metadata.create_all(engine) factory = create_session_factory(engine) try: yield factory finally: engine.dispose() def test_settings_service_saves_and_reads_masked(session_factory): session = session_factory() try: svc = SettingsService(session) version = svc.save_settings( {"model": "gpt-4o", "base_url": "https://api.example.com/v1"}, api_key="sk-secret-123", updated_by="user-1", ) assert version active = svc.active_settings() assert active["settings"]["model"] == "gpt-4o" # API key must be masked, never plaintext. assert active["api_key"] != "sk-secret-123" assert "sk-secret-123" not in repr(active) # Version metadata present. assert active["version"] assert active["updated_by"] == "user-1" finally: session.close() def test_settings_service_never_stores_plaintext_api_key(session_factory): session = session_factory() try: svc = SettingsService(session) svc.save_settings({"model": "m"}, api_key="sk-plain-abc", updated_by="user-1") row = svc._latest_row() secret_ref = getattr(row, "secret_ref", None) or "" blob = repr(row.settings) + " " + str(secret_ref) assert "sk-plain-abc" not in blob finally: session.close() def test_settings_service_snapshots_effective_settings_for_job(session_factory): session = session_factory() try: svc = SettingsService(session) version = svc.save_settings( {"model": "gpt-4o"}, api_key="sk-secret-9", updated_by="user-1" ) snapshot = svc.snapshot_for_job() assert snapshot["version"] == version assert snapshot["settings"]["model"] == "gpt-4o" # The job snapshot must NOT contain the plaintext api key. assert "sk-secret-9" not in repr(snapshot) # It carries a settings version, not the secret. assert snapshot["settings_version"] == version finally: session.close() def test_platform_settings_migration_round_trip(tmp_path, monkeypatch): from pathlib import Path from alembic import command from alembic.config import Config as AlembicConfig from sqlalchemy import inspect from app.db import create_database_engine database_url = f"sqlite+pysqlite:///{tmp_path / 'settings-roundtrip.db'}" monkeypatch.setenv("DATABASE_URL", database_url) alembic_config = AlembicConfig(str(Path(__file__).resolve().parents[1] / "alembic.ini")) alembic_config.set_main_option("sqlalchemy.url", database_url) command.upgrade(alembic_config, "head") engine = create_database_engine(database_url) try: inspector = inspect(engine) assert "platform_settings" in set(inspector.get_table_names()) cols = {c["name"] for c in inspector.get_columns("platform_settings")} assert {"version", "settings", "secret_ref", "active"}.issubset(cols) finally: engine.dispose() command.downgrade(alembic_config, "0007_product_resources") engine = create_database_engine(database_url) try: assert "platform_settings" not in set(inspect(engine).get_table_names()) finally: engine.dispose() command.upgrade(alembic_config, "head") command.check(alembic_config)