Files
ModelForge-Public/backend/tests/test_lifecycle_serving_postgres.py
T

114 lines
5.0 KiB
Python

"""Disposable PostgreSQL route CAS and runtime-role transaction qualification."""
import importlib
import threading
from concurrent.futures import ThreadPoolExecutor
import pytest
from sqlalchemy.orm import Session
from modelforge_api.domain.lifecycle_contracts import ApprovalDecision
from modelforge_api.persistence.models import CapabilityContract, LifecycleOperation
from modelforge_api.persistence.serving_repository import ServingRepository
from modelforge_api.services.lifecycle import LifecycleError, LifecycleService
from tests.test_lifecycle_serving import approved_plan, build_pair
from tests.test_managed_identities_postgres import IdentityDatabase, identity_database
__all__ = ["identity_database"]
def test_two_committers_switch_once_and_exact_rollback_increments_generation(
identity_database: IdentityDatabase,
) -> None:
database = identity_database
importlib.import_module("alembic.command").upgrade(database.migration(), "head")
with Session(database.runtime) as session:
pair = build_pair(session)
_, _, current, candidate, selection, _ = pair
_, _, operation, _ = approved_plan(pair)
operation_id, current_id, candidate_id = operation.id, current.id, candidate.id
contract_id, generation = (
current.capability_contract_id,
selection.expected_route_generation,
)
barrier = threading.Barrier(2)
def contender(_index: int) -> str:
with Session(database.runtime) as session:
barrier.wait(timeout=30)
try:
return (
LifecycleService(session)
.commit_operation(
operation_id,
ApprovalDecision(
actor="qualification", reason="Concurrent explicit commit qualification"
),
)
.stage
)
except LifecycleError:
session.rollback()
return "CONFLICT"
with ThreadPoolExecutor(max_workers=2) as pool:
# Same-operation retries return its persisted result, without a second switch.
assert list(pool.map(contender, [1, 2])) == ["COMMITTED", "COMMITTED"]
with Session(database.runtime) as session:
assert ServingRepository(session).stable_deployment(contract_id).id == candidate_id
assert session.get(CapabilityContract, contract_id).serving_generation == generation + 1
assert session.get(LifecycleOperation, operation_id).stage == "COMMITTED"
restored = LifecycleService(session).rollback_operation(
operation_id,
ApprovalDecision(actor="qualification", reason="Restore exact prior serving route"),
)
assert restored.stage == "ROLLED_BACK"
assert ServingRepository(session).stable_deployment(contract_id).id == current_id
assert session.get(CapabilityContract, contract_id).serving_generation == generation + 2
with pytest.raises(RuntimeError, match="Serving route history exists"):
importlib.import_module("alembic.command").downgrade(database.migration(), "20260909_0033")
def test_current_route_generation_survives_postgres_dump_restore(
identity_database: IdentityDatabase, tmp_path
):
from modelforge_api.services.recovery_fingerprint import (
BENCHMARK_RUN_SCHEMA,
audit_fingerprint_differences_are_compatible,
fingerprint_compatibility,
semantic_fingerprint,
)
database = identity_database
importlib.import_module("alembic.command").upgrade(database.migration(), "head")
with Session(database.runtime) as session:
pair = build_pair(session)
_, _, operation, _ = approved_plan(pair)
LifecycleService(session).commit_operation(
operation.id,
ApprovalDecision(
actor="qualification", reason="Qualify retained committed route after restore"
),
)
contract_id = pair[2].capability_contract_id
generation = session.get(CapabilityContract, contract_id).serving_generation
assert generation > 0
source = semantic_fingerprint(session.connection())
archive = tmp_path / "committed-route-current.dump"
database.archive(archive)
database.archive(archive, restore=True)
with Session(database.restored_runtime) as session:
restored = semantic_fingerprint(session.connection())
compatibility = fingerprint_compatibility(
source, restored, source_schema_revision=BENCHMARK_RUN_SCHEMA
)
assert compatibility.compatible
assert audit_fingerprint_differences_are_compatible(source, restored, compatibility)
assert session.get(CapabilityContract, contract_id).serving_generation == generation
session.get(CapabilityContract, contract_id).serving_generation = 0
session.flush()
changed = semantic_fingerprint(session.connection())
assert not audit_fingerprint_differences_are_compatible(source, changed, compatibility)
session.rollback()