from __future__ import annotations from copy import deepcopy from sqlalchemy import create_engine from sqlalchemy.orm import Session from modelforge_api.persistence.models import Base from modelforge_api.services.recovery_fingerprint import ( CURRENT_TRUTH_TABLES, MODEL_CHANGE_SCHEMA, MODEL_CHANGE_TABLES, PORTFOLIO_SCHEMA, SERVING_GENERATION_SCHEMA, _without_lab_execution, _without_model_changes, audit_fingerprint_differences_are_compatible, classified_tables, fingerprint_compatibility, fingerprint_session, unclassified_tables, ) def _current_fingerprint() -> dict[str, object]: engine = create_engine("sqlite+pysqlite:///:memory:") Base.metadata.create_all(engine) with Session(engine) as session: return _without_lab_execution(fingerprint_session(session)) def test_model_change_cases_are_authoritative_history() -> None: assert frozenset({"model_change_cases"}) == MODEL_CHANGE_TABLES assert classified_tables() >= MODEL_CHANGE_TABLES assert not MODEL_CHANGE_TABLES & CURRENT_TRUTH_TABLES assert unclassified_tables() == frozenset() def test_schema_0035_requires_exact_model_change_history() -> None: from modelforge_api.services.recovery_fingerprint import _without_portfolio source = _without_portfolio(_current_fingerprint()) restored = deepcopy(source) contract = fingerprint_compatibility( source, restored, source_schema_revision=MODEL_CHANGE_SCHEMA, ) assert contract.compatible is True assert contract.mode.startswith("ALERT_NOTIFICATION_UPGRADE|MODEL_CHANGE_STRICT|") assert audit_fingerprint_differences_are_compatible(source, restored, contract) is True changed = deepcopy(restored) entry = changed["tables"]["model_change_cases"] # type: ignore[index] entry["digest"] = "f" * 64 # type: ignore[index] assert audit_fingerprint_differences_are_compatible(source, changed, contract) is False def test_schema_0034_upgrade_may_introduce_only_empty_model_change_history() -> None: restored = _current_fingerprint() from modelforge_api.services.recovery_fingerprint import _without_portfolio source = _without_portfolio(_without_model_changes(restored)) contract = fingerprint_compatibility( source, restored, source_schema_revision=SERVING_GENERATION_SCHEMA, ) assert contract.compatible is True assert contract.mode.startswith("ALERT_NOTIFICATION_UPGRADE|PORTFOLIO_UPGRADE|MODEL_CHANGE_UPGRADE|") assert audit_fingerprint_differences_are_compatible(source, restored, contract) is True populated = deepcopy(restored) entry = populated["tables"]["model_change_cases"] # type: ignore[index] entry["row_count"] = 1 # type: ignore[index] entry["digest"] = "e" * 64 # type: ignore[index] rejected = fingerprint_compatibility( source, populated, source_schema_revision=SERVING_GENERATION_SCHEMA, ) assert rejected.compatible is False def test_schema_0036_requires_exact_portfolio_history() -> None: source = _current_fingerprint() restored = deepcopy(source) contract = fingerprint_compatibility( source, restored, source_schema_revision=PORTFOLIO_SCHEMA, ) assert contract.compatible is True assert contract.mode.startswith("ALERT_NOTIFICATION_UPGRADE|PORTFOLIO_STRICT|MODEL_CHANGE_STRICT|") assert audit_fingerprint_differences_are_compatible(source, restored, contract) is True changed = deepcopy(restored) changed["tables"]["portfolio_reconciliation_runs"]["digest"] = "f" * 64 # type: ignore[index] assert audit_fingerprint_differences_are_compatible(source, changed, contract) is False def test_schema_0035_upgrade_may_introduce_only_empty_portfolio_history() -> None: from modelforge_api.services.recovery_fingerprint import _without_portfolio restored = _current_fingerprint() source = _without_portfolio(restored) contract = fingerprint_compatibility( source, restored, source_schema_revision=MODEL_CHANGE_SCHEMA, ) assert contract.compatible is True assert contract.mode.startswith("ALERT_NOTIFICATION_UPGRADE|PORTFOLIO_UPGRADE|MODEL_CHANGE_STRICT|") assert audit_fingerprint_differences_are_compatible(source, restored, contract) is True populated = deepcopy(restored) populated["tables"]["portfolio_reconciliation_runs"]["row_count"] = 1 # type: ignore[index] rejected = fingerprint_compatibility( source, populated, source_schema_revision=MODEL_CHANGE_SCHEMA, ) assert rejected.compatible is False