# ruff: noqa: S106 - isolated synthetic persistence fixtures """Secret history fingerprint and migration contracts without external credentials.""" import copy import importlib.util from datetime import UTC, datetime from pathlib import Path import pytest from alembic.migration import MigrationContext from alembic.operations import Operations from sqlalchemy import create_engine, select from sqlalchemy.orm import Session from modelforge_api.persistence.models import Base, SecretSlotState, SecretSlotVersion from modelforge_api.services.audit import AuditWriter from modelforge_api.services.recovery_fingerprint import ( SECRET_SCHEMA, SECRET_TABLES, FingerprintCompatibility, _without_creative, _without_lab_execution, _without_model_changes, _without_model_updates, _without_secrets, _without_selected_comparisons, audit_fingerprint_differences_are_compatible, fingerprint_compatibility, unclassified_tables, ) from modelforge_api.services.recovery_fingerprint import ( fingerprint_session as _current_fingerprint_session, ) def fingerprint_session(session: Session): return _without_lab_execution(_without_model_changes(_current_fingerprint_session(session))) def test_secret_history_is_strict_except_verified_recovery_marker() -> None: engine = create_engine("sqlite://") Base.metadata.create_all(engine) with Session(engine) as session: assert not unclassified_tables() session.add(SecretSlotVersion(version=1, key_id="qualification", secret_ciphertext="opaque", secret_nonce="nonce", created_at=datetime.now(UTC))) session.commit() before = _without_creative(_without_model_updates(_without_selected_comparisons(fingerprint_session(session)))) assert before["tables"]["platform_secret_versions"]["redacted_columns"] == ["secret_ciphertext", "secret_nonce"] AuditWriter(session, "operator", "qualification").write("RECOVERY_RECONCILIATION_COMPLETED", "restore", None, {"restored": True}) session.commit() after = _without_creative(_without_model_updates(_without_selected_comparisons(fingerprint_session(session)))) contract = fingerprint_compatibility(before, after, source_schema_revision=SECRET_SCHEMA) assert contract.mode == "ALERT_NOTIFICATION_UPGRADE|PORTFOLIO_UPGRADE|SECRET_STRICT" assert audit_fingerprint_differences_are_compatible(before, after, contract) unrelated_marker = FingerprintCompatibility( True, f"UNRELATED|{contract.mode}", contract.allow_audit_checkpoint_introduction, contract.reason, ) assert not audit_fingerprint_differences_are_compatible(before, after, unrelated_marker) for table in SECRET_TABLES: altered = copy.deepcopy(after) altered["tables"][table]["digest"] = "0" * 64 assert not audit_fingerprint_differences_are_compatible(before, altered, contract) engine.dispose() def test_prior_schema_requires_exact_empty_secret_introduction() -> None: engine = create_engine("sqlite://") Base.metadata.create_all(engine) with Session(engine) as session: after = _without_creative(_without_model_updates(_without_selected_comparisons(fingerprint_session(session)))) before = _without_secrets(after) contract = fingerprint_compatibility(before, after, source_schema_revision="20260908_0027") assert contract.compatible assert audit_fingerprint_differences_are_compatible(before, after, contract) for table in SECRET_TABLES: altered = copy.deepcopy(after) altered["tables"][table]["digest"] = "0" * 64 assert not fingerprint_compatibility(before, altered, source_schema_revision="20260908_0027").compatible engine.dispose() def test_secret_migration_seed_empty_downgrade_and_history_refusal() -> None: path = Path(__file__).resolve().parents[1] / "alembic/versions/20260908_0028_platform_secret_slots.py" spec = importlib.util.spec_from_file_location("secret_migration", path) assert spec and spec.loader migration = importlib.util.module_from_spec(spec) spec.loader.exec_module(migration) engine = create_engine("sqlite://") with engine.begin() as connection, Operations.context(MigrationContext.configure(connection)): migration.upgrade() assert connection.scalar(select(SecretSlotState.revision)) == 0 migration.downgrade() migration.upgrade() connection.execute(SecretSlotVersion.__table__.insert().values(version=1, key_id="qualification", secret_ciphertext="opaque", secret_nonce="nonce", created_at=datetime.now(UTC))) with pytest.raises(RuntimeError, match="history exists"): migration.downgrade() engine.dispose()