128 lines
4.6 KiB
Python
128 lines
4.6 KiB
Python
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
|