Files
Unraid-AppOps-Gateway-Public/backend/tests/test_registered_legacy_followup_review.py
T

846 lines
35 KiB
Python

from __future__ import annotations
import importlib.util
import json
import sys
import types
from contextlib import contextmanager
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
MODULE_PATH = ROOT / "ops" / "gitea-deploy-broker" / "registered_legacy_adoption.py"
REPOSITORY = "jens/quiz-monitor"
CONTAINER = "quiz-monitor-quiz-server-1"
IMAGE_ID = "sha256:" + "a" * 64
SOURCE_FINGERPRINT = "b" * 64
RUNTIME_FINGERPRINT = "c" * 64
STATE_SHA256 = "d" * 64
CONTAINER_ID = "e" * 64
CAPTURED_VALUE = "gpt-6-luna"
CAPTURED_KEY = "quiz-server/KNOWLEDGE_OPTIMIZER_MODEL"
def _load_module():
spec = importlib.util.spec_from_file_location(
"registered_legacy_followup_review", MODULE_PATH
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def _load_deploy_module():
if "fcntl" not in sys.modules:
fcntl = types.ModuleType("fcntl")
fcntl.LOCK_EX = 2
fcntl.LOCK_UN = 8
fcntl.flock = lambda *_args: None
sys.modules["fcntl"] = fcntl
name = "registered_legacy_followup_deploy_review"
spec = importlib.util.spec_from_file_location(
name, ROOT / "ops" / "gitea-deploy-broker" / "deploy.py"
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
spec.loader.exec_module(module)
return module
def _captured_lineage(module):
binding = {
"schema": "itworx.registered-legacy-env-capture/v1",
"sourceFingerprint": SOURCE_FINGERPRINT,
"runtimeFingerprint": RUNTIME_FINGERPRINT,
"expectedStateSha256": STATE_SHA256,
"containerIDs": {CONTAINER: CONTAINER_ID},
"capturedEnvironmentKeys": [CAPTURED_KEY],
}
runtime_container = {
"Image": IMAGE_ID,
"Config": {
"Labels": {"com.docker.compose.service": "quiz-server"},
"Env": [f"KNOWLEDGE_OPTIMIZER_MODEL={CAPTURED_VALUE}"],
},
}
baseline_model = {
"services": {
"quiz-server": {
"environment": {"KNOWLEDGE_OPTIMIZER_MODEL": CAPTURED_VALUE},
"image": "quiz-monitor:baseline",
}
}
}
private = {
"environmentCapture": binding,
"runtimeConfiguration": {
"composeModel": baseline_model,
"containers": {CONTAINER: runtime_container},
"imageEnvironments": {IMAGE_ID: {}},
},
}
baseline = {
"environmentCapture": binding,
"sourceFingerprint": SOURCE_FINGERPRINT,
"runtimeFingerprint": RUNTIME_FINGERPRINT,
"expectedStateSha256": STATE_SHA256,
}
project = {"containers": [CONTAINER]}
return baseline_model, baseline, private, project
def test_followup_candidate_cannot_mask_a_wrong_captured_environment_overlay():
module = _load_module()
baseline_model, baseline, private, project = _captured_lineage(module)
candidate_model = {
"services": {
"quiz-server": {
"environment": {"KNOWLEDGE_OPTIMIZER_MODEL": "gpt-5.5"},
"image": "quiz-monitor:candidate",
}
}
}
with pytest.raises(module.RegisteredLegacyAdoptionError):
module._candidate_runtime_comparison_model(
candidate_model, baseline_model, baseline, private, project
)
def test_main_revalidates_active_lineage_before_same_sha_fast_return(
monkeypatch, tmp_path, capsys
):
"""A matching receipt cannot hide drift in a previously adopted runtime."""
module = _load_deploy_module()
revision = "f" * 40
receipt = {"repository": REPOSITORY, "revision": revision, "tree": "a" * 40}
state = {
"schema_version": 3,
"repository": REPOSITORY,
"slug": "quiz-monitor",
"compose_project": "quiz-monitor",
"revision": revision,
"live_revision": revision,
"live_status": "healthy",
"live_legacy_provenance": False,
"live_artifacts": {
"registered_legacy": {
"requestId": "6f9619ff-8b86-4011-b42d-00c04fc964ff",
"revision": revision,
"runtimeReceiptSha256": "b" * 64,
"runtimeFingerprint": "c" * 64,
}
},
"live_receipt": receipt,
"status": "success",
}
project = {
"slug": "quiz-monitor",
"branch": "main",
"source_dir": str(tmp_path / "source"),
"compose_project": "quiz-monitor",
"compose_files": ["docker-compose.yml"],
"containers": [CONTAINER],
"exact_source": True,
"verify_oci_revision": True,
"health_timeout": 180,
}
projects = {REPOSITORY: project}
metadata = {"revision": revision, "tree": "a" * 40}
events = []
@contextmanager
def project_lock(_path, _description):
events.append("project-lock")
yield
class Adoption:
def active_lineage(self, repository, observed_project, observed_state, **_kwargs):
assert repository == REPOSITORY
assert observed_state["live_legacy_provenance"] is False
events.append("active-lineage")
raise module.DeployError("registered live runtime drift")
monkeypatch.setattr(sys, "argv", ["deploy.py", "deploy", REPOSITORY, revision])
monkeypatch.delenv("APPOPS_REGISTERED_LEGACY_REQUEST_ID", raising=False)
monkeypatch.setattr(module, "load_projects", lambda: projects)
monkeypatch.setattr(module, "repository_path", lambda *_args: tmp_path)
monkeypatch.setattr(module, "validate_commit", lambda _repo, _branch, value: value)
monkeypatch.setattr(module, "deployment_metadata", lambda *_args: metadata)
monkeypatch.setattr(module, "validate_project_config", lambda *_args: None)
monkeypatch.setattr(module, "exclusive_lock", project_lock)
monkeypatch.setattr(module, "read_state", lambda _project: state)
monkeypatch.setattr(module, "verified_current_receipt", lambda *_args: (
events.append("same-sha-receipt") or receipt
))
monkeypatch.setattr(module, "_load_registered_legacy_adoption", Adoption)
monkeypatch.setattr(module, "_registered_legacy_receipt_key", lambda: b"k" * 32)
monkeypatch.setattr(module, "sync_source", lambda *_args, **_kwargs: (
events.append("sync-source"), pytest.fail("drift must be rejected before source sync")
))
with pytest.raises(module.DeployError, match="registered live runtime drift"):
module.main()
assert "active-lineage" in events
assert "same-sha-receipt" not in events
assert "sync-source" not in events
assert json.loads(capsys.readouterr().out or "{}") == {}
def test_main_defers_active_followup_source_sync_until_global_lock(
monkeypatch, tmp_path
):
"""Ordinary main deployment must attest the active target before source sync."""
module = _load_deploy_module()
previous_revision = "1" * 40
revision = "2" * 40
state_sha = "d" * 64
request_id = "6f9619ff-8b86-4011-b42d-00c04fc964ff"
state = {
"schema_version": 3,
"repository": REPOSITORY,
"slug": "quiz-monitor",
"compose_project": "quiz-monitor",
"revision": previous_revision,
"live_revision": previous_revision,
"live_status": "healthy",
"live_legacy_provenance": False,
"live_artifacts": {"registered_legacy": {"requestId": request_id}},
"live_receipt": {"repository": REPOSITORY, "revision": previous_revision},
"status": "success",
}
project = {
"slug": "quiz-monitor",
"branch": "main",
"source_dir": str(tmp_path / "source"),
"compose_project": "quiz-monitor",
"compose_files": ["docker-compose.yml"],
"containers": [CONTAINER],
"exact_source": True,
"verify_oci_revision": True,
"health_timeout": 180,
}
projects = {REPOSITORY: project}
metadata = {"revision": revision, "tree": "a" * 40}
events = []
lock_state = {"global": False}
@contextmanager
def project_lock(_path, _description):
events.append("project-lock-enter")
yield
@contextmanager
def global_lock():
events.append("global-lock-enter")
lock_state["global"] = True
try:
yield
finally:
lock_state["global"] = False
events.append("global-lock-exit")
class Adoption:
def active_lineage(self, repository, observed_project, observed_state, **_kwargs):
assert repository == REPOSITORY
assert observed_state == state
events.append("active-lineage")
return {
"requestId": request_id,
"revision": previous_revision,
"sourceFingerprint": SOURCE_FINGERPRINT,
"runtimeFingerprint": RUNTIME_FINGERPRINT,
"runtimeReceiptSha256": "e" * 64,
"originUnknownSnapshot": {"revision": "unknown"},
}
def prepare_followup_deployment(self, repository, observed_project, target, observed_state, **_kwargs):
assert lock_state["global"]
assert repository == REPOSITORY
assert target == revision
assert observed_state == state
assert "sync-source" not in events
events.append("prepare-followup")
return {
"requestId": request_id,
"authorization": {
"requestId": request_id,
"revision": target,
"expectedStateSha256": state_sha,
},
"rollbackSnapshot": {"revision": previous_revision},
"originUnknownSnapshot": {"revision": "unknown"},
"expectedStateSha256": state_sha,
"validationEvidenceSha256": "f" * 64,
}
monkeypatch.setattr(sys, "argv", ["deploy.py", "deploy", REPOSITORY, revision])
monkeypatch.delenv("APPOPS_REGISTERED_LEGACY_REQUEST_ID", raising=False)
monkeypatch.setattr(module, "load_projects", lambda: projects)
monkeypatch.setattr(module, "repository_path", lambda *_args: tmp_path)
monkeypatch.setattr(module, "validate_commit", lambda _repo, _branch, value: value)
monkeypatch.setattr(module, "deployment_metadata", lambda *_args: metadata)
monkeypatch.setattr(module, "validate_project_config", lambda *_args: None)
monkeypatch.setattr(module, "exclusive_lock", project_lock)
monkeypatch.setattr(module, "exclusive_global_deploy_lock", global_lock)
monkeypatch.setattr(module, "read_state", lambda _project: state)
monkeypatch.setattr(module, "verified_current_receipt", lambda *_args: None)
monkeypatch.setattr(module, "validate_forward_revision", lambda *_args, **_kwargs: None)
monkeypatch.setattr(module, "_load_registered_legacy_adoption", Adoption)
monkeypatch.setattr(module, "_registered_legacy_receipt_key", lambda: b"k" * 32)
def stop_after_sync(*_args, **_kwargs):
events.append("sync-source")
if not lock_state["global"]:
events.append("sync-before-global")
raise module.DeployError("test stop after source sync")
monkeypatch.setattr(module, "sync_source", stop_after_sync)
monkeypatch.setattr(module, "copy_environment", lambda *_args: None)
monkeypatch.setattr(module, "restore_live_source", lambda *_args: None)
monkeypatch.setattr(module, "write_state", lambda *_args, **_kwargs: None)
with pytest.raises(module.DeployError, match="test stop after source sync"):
module.main()
assert events.index("global-lock-enter") < events.index("active-lineage")
assert events.index("active-lineage") < events.index("prepare-followup")
assert events.index("prepare-followup") < events.index("sync-source")
assert "sync-before-global" not in events
def test_main_commits_followup_as_verified_not_legacy_and_keeps_unknown_origin(
monkeypatch, tmp_path, capsys
):
module = _load_deploy_module()
previous_revision = "1" * 40
revision = "2" * 40
state_sha = "d" * 64
request_id = "6f9619ff-8b86-4011-b42d-00c04fc964ff"
origin = {"revision": "unknown", "legacy_provenance": True, "opaque": "signed-origin"}
known_snapshot = {
"revision": previous_revision,
"legacy_provenance": False,
"registered_legacy": {"requestId": request_id},
"originUnknownSnapshot": origin,
}
state = {
"schema_version": 3,
"repository": REPOSITORY,
"slug": "quiz-monitor",
"compose_project": "quiz-monitor",
"revision": previous_revision,
"live_revision": previous_revision,
"live_status": "healthy",
"live_legacy_provenance": False,
"live_artifacts": {"registered_legacy": {"requestId": request_id}},
"live_receipt": {"repository": REPOSITORY, "revision": previous_revision},
"status": "success",
}
project = {
"slug": "quiz-monitor",
"branch": "main",
"source_dir": str(tmp_path / "source"),
"compose_project": "quiz-monitor",
"compose_files": ["docker-compose.yml"],
"containers": [CONTAINER],
"exact_source": True,
"verify_oci_revision": True,
"health_timeout": 180,
}
projects = {REPOSITORY: project}
metadata = {"revision": revision, "tree": "a" * 40}
events = []
writes = []
@contextmanager
def project_lock(_path, _description):
events.append("project-lock-enter")
yield
@contextmanager
def global_lock():
events.append("global-lock-enter")
yield
events.append("global-lock-exit")
class Adoption:
def active_lineage(self, repository, _observed_project, observed_state, **_kwargs):
assert repository == REPOSITORY
assert observed_state == state
events.append("active-lineage")
return {
"requestId": request_id,
"revision": previous_revision,
"sourceFingerprint": SOURCE_FINGERPRINT,
"runtimeFingerprint": RUNTIME_FINGERPRINT,
"runtimeReceiptSha256": "e" * 64,
"originUnknownSnapshot": origin,
}
def prepare_followup_deployment(self, repository, _observed_project, target, observed_state, **_kwargs):
assert repository == REPOSITORY and target == revision
assert observed_state == state
events.append("prepare-followup")
return {
"requestId": request_id,
"authorization": {
"requestId": request_id,
"revision": target,
"expectedStateSha256": state_sha,
},
"rollbackSnapshot": known_snapshot,
"originUnknownSnapshot": origin,
"expectedStateSha256": state_sha,
"validationEvidenceSha256": "f" * 64,
}
def record_live_runtime(self, repository, observed_project, observed_request, target, _receipt, **_kwargs):
assert repository == REPOSITORY and observed_request == request_id
assert target == revision
assert observed_project["_registered_legacy_request_id"] == request_id
events.append("record-live")
return {
"requestId": request_id,
"revision": target,
"runtimeReceiptSha256": "9" * 64,
"runtimeFingerprint": "8" * 64,
"originUnknownSnapshot": origin,
}
receipt = {"repository": REPOSITORY, "revision": revision, "tree": metadata["tree"]}
monkeypatch.setattr(sys, "argv", ["deploy.py", "deploy", REPOSITORY, revision])
monkeypatch.delenv("APPOPS_REGISTERED_LEGACY_REQUEST_ID", raising=False)
monkeypatch.setattr(module, "load_projects", lambda: projects)
monkeypatch.setattr(module, "repository_path", lambda *_args: tmp_path)
monkeypatch.setattr(module, "validate_commit", lambda _repo, _branch, value: value)
monkeypatch.setattr(module, "deployment_metadata", lambda *_args: metadata)
monkeypatch.setattr(module, "validate_project_config", lambda *_args: None)
monkeypatch.setattr(module, "exclusive_lock", project_lock)
monkeypatch.setattr(module, "exclusive_global_deploy_lock", global_lock)
monkeypatch.setattr(module, "read_state", lambda _project: state)
monkeypatch.setattr(module, "verified_current_receipt", lambda *_args: None)
monkeypatch.setattr(module, "validate_forward_revision", lambda *_args, **_kwargs: None)
monkeypatch.setattr(module, "revalidate_deployment_candidate", lambda *_args: None)
monkeypatch.setattr(module, "_load_registered_legacy_adoption", Adoption)
monkeypatch.setattr(module, "_registered_legacy_receipt_key", lambda: b"k" * 32)
monkeypatch.setattr(module, "_registered_legacy_validation_reader", lambda _repository: lambda *_args: {})
monkeypatch.setattr(module, "sync_source", lambda *_args, **_kwargs: events.append("sync-source"))
monkeypatch.setattr(module, "copy_environment", lambda *_args: None)
monkeypatch.setattr(module, "_mcp_hub_assert_no_unresolved_migration_intent", lambda *_args: None)
monkeypatch.setattr(module, "ensure_docker_capacity", lambda: None)
monkeypatch.setattr(module, "deploy_project", lambda observed_project, _revision, _repository: (
events.append("deploy"),
pytest.fail("follow-up deploy lost its derived internal request ID")
if observed_project.get("_registered_legacy_request_id") != request_id else None,
))
monkeypatch.setattr(module, "verify_deployment", lambda *_args: receipt)
monkeypatch.setattr(module, "finish_compose_migration", lambda *_args: None)
monkeypatch.setattr(module, "write_state", lambda *_args, **kwargs: writes.append(kwargs))
monkeypatch.setattr(module, "cleanup_rollback_image_tags", lambda *_args, **_kwargs: None)
assert module.main() == 0
assert events.index("global-lock-enter") < events.index("active-lineage")
assert events.index("prepare-followup") < events.index("sync-source") < events.index("deploy")
assert "record-live" in events
assert len(writes) == 1
assert writes[0]["live_legacy_provenance"] is False
assert writes[0]["rollback_snapshot"] == known_snapshot
assert writes[0]["expected_state_sha256"] == state_sha
assert json.loads(capsys.readouterr().out)["status"] == "deployed"
def test_main_known_current_rollback_restores_source_then_allows_retry(
monkeypatch, tmp_path, capsys
):
"""A failed candidate returns to the verified prior revision and can retry."""
module = _load_deploy_module()
previous_revision = "1" * 40
revision = "2" * 40
state_sha = "d" * 64
request_ids = [
"6f9619ff-8b86-4011-b42d-00c04fc964ff",
"8c7b4a2d-3d70-4a49-93e4-41d42aeecc03",
]
origin = {
"revision": "unknown",
"legacy_provenance": True,
"registered_legacy": {"schema": "origin-unknown-snapshot"},
}
state = {
"schema_version": 3,
"repository": REPOSITORY,
"slug": "quiz-monitor",
"compose_project": "quiz-monitor",
"revision": previous_revision,
"live_revision": previous_revision,
"live_status": "healthy",
"live_legacy_provenance": False,
"live_artifacts": {"registered_legacy": {"requestId": request_ids[0]}},
"live_receipt": {"schema": "gitea-deploy-receipt.v1", "repository": REPOSITORY,
"revision": previous_revision},
"rollback_snapshot": {"revision": "unknown", "legacy_provenance": True,
"registered_legacy": {"schema": "origin-unknown-snapshot"}},
"status": "success",
}
project_template = {
"slug": "quiz-monitor",
"branch": "main",
"source_dir": str(tmp_path / "source"),
"compose_project": "quiz-monitor",
"compose_files": ["docker-compose.yml"],
"containers": [CONTAINER],
"verify_oci_revision": True,
"health_timeout": 180,
}
metadata = {"revision": revision, "tree": "a" * 40}
known_snapshot = {
"revision": previous_revision,
"legacy_provenance": False,
"receipt": {"schema": "gitea-deploy-receipt.v1", "repository": REPOSITORY,
"revision": previous_revision},
"containers": [{"name": CONTAINER, "image_id": IMAGE_ID}],
"registered_legacy": {"schema": "known-current-snapshot"},
"originUnknownSnapshot": origin,
}
restored_artifacts = {
"registered_legacy": {"requestId": request_ids[0], "revision": previous_revision},
"containers": {CONTAINER: {"container_id": CONTAINER_ID, "image_id": IMAGE_ID}},
}
restored_receipt = known_snapshot["receipt"]
events = []
candidate_attempts = 0
writes = []
@contextmanager
def project_lock(_path, _description):
yield
@contextmanager
def global_lock():
events.append("global-lock-enter")
try:
yield
finally:
events.append("global-lock-exit")
class Adoption:
def active_lineage(self, repository, _project, observed_state, **_kwargs):
assert repository == REPOSITORY
assert observed_state["live_revision"] == previous_revision
assert observed_state["live_status"] == "healthy"
events.append(f"active-lineage:{observed_state['status']}")
return {
"requestId": request_ids[0],
"revision": previous_revision,
"originUnknownSnapshot": origin,
}
def prepare_followup_deployment(self, repository, _project, target, _state, **_kwargs):
assert repository == REPOSITORY and target == revision
request_id = request_ids[len([e for e in events if e == "prepare-followup"]) ]
events.append("prepare-followup")
return {
"requestId": request_id,
"authorization": {
"requestId": request_id,
"revision": target,
"expectedStateSha256": state_sha,
},
"rollbackSnapshot": known_snapshot,
"originUnknownSnapshot": origin,
"expectedStateSha256": state_sha,
"validationEvidenceSha256": "f" * 64,
}
def restore_deployment_snapshot(self, repository, _project, snapshot, failed, **kwargs):
assert repository == REPOSITORY and failed == revision
assert snapshot == known_snapshot
callback = kwargs.get("restore_source")
assert callable(callback)
events.append("rollback-core-accepted-snapshot")
callback(previous_revision)
events.append("prior-source-restored")
return {
"revision": previous_revision,
"artifacts": restored_artifacts,
"imageIDs": {CONTAINER: IMAGE_ID},
"liveReceipt": restored_receipt,
}
def verify_current_live_runtime(self, *_args, **_kwargs):
pytest.fail("candidate failure must not claim the failed revision is healthy")
def record_live_runtime(self, repository, project, request_id, target, _receipt, **_kwargs):
assert repository == REPOSITORY and target == revision
assert project["_registered_legacy_request_id"] == request_id
events.append("record-live")
return {
"requestId": request_id,
"revision": target,
"runtimeReceiptSha256": "9" * 64,
"runtimeFingerprint": "8" * 64,
"originRequestId": request_ids[0],
"originUnknownSnapshot": origin,
}
adoption = Adoption()
candidate_receipt = {
"schema": "gitea-deploy-receipt.v1", "repository": REPOSITORY,
"revision": revision, "tree": metadata["tree"],
}
monkeypatch.setattr(sys, "argv", ["deploy.py", "deploy", REPOSITORY, revision])
monkeypatch.delenv("APPOPS_REGISTERED_LEGACY_REQUEST_ID", raising=False)
monkeypatch.setattr(module, "load_projects", lambda: {REPOSITORY: dict(project_template)})
monkeypatch.setattr(module, "repository_path", lambda *_args: tmp_path)
monkeypatch.setattr(module, "validate_commit", lambda _repo, _branch, value: value)
monkeypatch.setattr(module, "deployment_metadata", lambda *_args: metadata)
monkeypatch.setattr(module, "validate_project_config", lambda *_args: None)
monkeypatch.setattr(module, "exclusive_lock", project_lock)
monkeypatch.setattr(module, "exclusive_global_deploy_lock", global_lock)
monkeypatch.setattr(module, "read_state", lambda _project: state)
monkeypatch.setattr(module, "verified_current_receipt", lambda *_args: None)
monkeypatch.setattr(module, "validate_forward_revision", lambda *_args, **_kwargs: None)
monkeypatch.setattr(module, "_load_registered_legacy_adoption", lambda: adoption)
monkeypatch.setattr(module, "_registered_legacy_receipt_key", lambda: b"k" * 32)
monkeypatch.setattr(module, "_registered_legacy_validation_reader", lambda _repo: lambda *_args: {})
monkeypatch.setattr(module, "sync_source", lambda *_args, **_kwargs: events.append("sync-source"))
monkeypatch.setattr(module, "copy_environment", lambda *_args: None)
monkeypatch.setattr(module, "restore_live_source", lambda _project, _repo, rev: events.append(f"restore-source:{rev}"))
monkeypatch.setattr(module, "mcp_hub_prepare_current_receipt_model", lambda *_args: None)
monkeypatch.setattr(module, "_mcp_hub_assert_no_unresolved_migration_intent", lambda *_args: None)
monkeypatch.setattr(module, "revalidate_deployment_candidate", lambda *_args: None)
monkeypatch.setattr(module, "validate_managed_v1_runtime", lambda *_args: None)
monkeypatch.setattr(module, "capture_compose_rollback", lambda *_args: pytest.fail("signed rollback snapshot must be reused"))
monkeypatch.setattr(module, "ensure_docker_capacity", lambda: None)
monkeypatch.setattr(module, "finish_compose_migration", lambda *_args: None)
monkeypatch.setattr(module, "restore_compose_migration", lambda *_args: None)
monkeypatch.setattr(module, "validate_recorded_receipt", lambda *_args: {"revision": previous_revision})
monkeypatch.setattr(module, "verify_deployment", lambda _project, meta, **_kwargs: (
restored_receipt if meta.get("revision") == previous_revision else candidate_receipt
))
def deploy(_project, _revision, _repository):
nonlocal candidate_attempts
candidate_attempts += 1
events.append(f"deploy:{candidate_attempts}")
if candidate_attempts == 1:
raise module.DeployError("simulated candidate health failure")
monkeypatch.setattr(module, "deploy_project", deploy)
def write_state(_repository, project, target, status, message, **kwargs):
writes.append((target, status, dict(kwargs)))
state.update({
"repository": REPOSITORY,
"status": status,
"revision": kwargs.get("live_revision", state["live_revision"]),
"live_revision": kwargs.get("live_revision", state["live_revision"]),
"live_status": kwargs.get("live_status", state["live_status"]),
"live_legacy_provenance": kwargs.get(
"live_legacy_provenance", state["live_legacy_provenance"]
),
"live_artifacts": kwargs.get("live_artifacts", state["live_artifacts"]),
"live_receipt": kwargs.get("live_receipt", state["live_receipt"]),
"rollback_snapshot": kwargs.get("rollback_snapshot", state["rollback_snapshot"]),
"last_attempt": {"revision": target, "status": status, "message": message},
})
if status == "success":
state["live_revision"] = revision
state["revision"] = revision
state["live_status"] = "healthy"
state["live_legacy_provenance"] = False
state["live_receipt"] = candidate_receipt
state["live_artifacts"] = {
"registered_legacy": dict(project["_registered_legacy_runtime_binding"]),
"containers": restored_artifacts["containers"],
}
state["rollback_snapshot"] = known_snapshot
monkeypatch.setattr(module, "write_state", write_state)
monkeypatch.setattr(module, "cleanup_rollback_image_tags", lambda *_args, **_kwargs: None)
with pytest.raises(module.DeployError, match="simulated candidate health failure"):
module.main()
assert "rollback-core-accepted-snapshot" in events
assert events.index("rollback-core-accepted-snapshot") < events.index(
f"restore-source:{previous_revision}"
)
assert state["status"] == "failed"
assert state["live_revision"] == previous_revision
assert state["live_status"] == "healthy"
assert state["live_legacy_provenance"] is False
assert state["rollback_snapshot"]["revision"] == "unknown"
assert module.main() == 0
assert candidate_attempts == 2
assert len([event for event in events if event == "prepare-followup"]) == 2
assert state["status"] == "success"
assert state["live_revision"] == revision
assert state["live_legacy_provenance"] is False
assert state["live_artifacts"]["registered_legacy"]["originUnknownSnapshot"] == origin
assert state["rollback_snapshot"] == known_snapshot
assert any(event == "active-lineage:failed" for event in events)
assert json.loads(capsys.readouterr().out)["status"] == "deployed"
def test_unsigned_known_snapshot_cannot_mutate_source(monkeypatch, tmp_path):
"""A forged known-current rollback reference is rejected before source restore."""
deploy = _load_deploy_module()
adoption = _load_module()
project = {
"slug": "quiz-monitor",
"branch": "main",
"source_dir": "/srv/example/appdata/quiz-monitor/source",
"compose_project": "quiz-monitor",
"compose_files": ["docker-compose.yml"],
"containers": [CONTAINER],
}
previous_revision = "1" * 40
failed_revision = "2" * 40
request_id = "6f9619ff-8b86-4011-b42d-00c04fc964ff"
source_changes = []
forged_snapshot = {
"revision": previous_revision,
"legacy_provenance": False,
"registered_legacy": {
"schema": adoption.KNOWN_SNAPSHOT_SCHEMA,
"requestId": request_id,
"snapshotSha256": "0" * 64,
"targetRevision": failed_revision,
"previousRevision": previous_revision,
"originRequestId": request_id,
"originUnknownSnapshot": {},
},
}
monkeypatch.setattr(deploy, "STATE_ROOT", tmp_path)
monkeypatch.setattr(deploy, "read_state", lambda _project: {})
monkeypatch.setattr(deploy, "_load_registered_legacy_adoption", lambda: adoption)
monkeypatch.setattr(deploy, "_registered_legacy_receipt_key", lambda: b"k" * 32)
monkeypatch.setattr(
deploy, "restore_live_source",
lambda *_args: source_changes.append("source-restored"),
)
with pytest.raises(adoption.RegisteredLegacyAdoptionError):
deploy.restore_compose_snapshot(
project, forged_snapshot, REPOSITORY, failed_revision,
)
assert source_changes == []
def test_candidate_pre_mutation_rechecks_latest_canonical_main_run(
monkeypatch, tmp_path
):
"""A newly failed full run blocks a previously sealed candidate before Compose."""
support_path = ROOT / "backend" / "tests" / "test_registered_legacy_runtime.py"
support_spec = importlib.util.spec_from_file_location(
"registered_legacy_runtime_followup_review", support_path
)
assert support_spec and support_spec.loader
support = importlib.util.module_from_spec(support_spec)
support_spec.loader.exec_module(support)
module = support._load_module()
source = tmp_path / "source"
source.mkdir()
(source / "docker-compose.yml").write_text("services: {}\n", encoding="utf-8")
project = support._project(source)
model = support._compose_model()
boundary = support._DockerComposeBoundary(module, project, model)
boundary.install(monkeypatch)
runtime, _registry, _read_state, _state_bytes, _snapshot = (
support._adopt_with_production_runtime(
module, boundary, project, tmp_path, monkeypatch
)
)
bound_project = support._prepare_candidate_overlay(
module, boundary, project, tmp_path
)
module.prepare_candidate_deployment(
REPOSITORY,
bound_project,
support.REQUEST_ID,
support.TARGET_SHA,
receipt_key=support.KEY,
state_root=tmp_path,
runtime=runtime,
)
older_success = {
"id": 7,
"run_number": 19,
"run_attempt": 1,
"event": "push",
"head_sha": support.TARGET_SHA,
"head_branch": "main",
"path": "managed-validation.yml@refs/heads/main",
"created_at": "2026-10-07T12:00:00Z",
"status": "completed",
"conclusion": "success",
"repository": {"full_name": REPOSITORY},
"head_repository": {"full_name": REPOSITORY},
"pull_requests": [],
}
newer_failure = {
**older_success,
"id": 8,
"run_number": 20,
"created_at": "2026-10-07T12:01:00Z",
"status": "completed",
"conclusion": "failure",
}
queried = []
def failed_latest_api(path, *, query):
queried.append(path)
if path == f"/repos/{REPOSITORY}/actions/runs":
return {
"total_count": 2,
"workflow_runs": [older_success, newer_failure],
}
return support._validation_api(path, query=query)
with pytest.raises(module.RegisteredLegacyAdoptionError):
module.verify_candidate_pre_mutation(
REPOSITORY,
bound_project,
support.REQUEST_ID,
support.TARGET_SHA,
receipt_key=support.KEY,
state_root=tmp_path,
runtime=runtime,
api_json=failed_latest_api,
)
assert queried == [f"/repos/{REPOSITORY}/branches/main", f"/repos/{REPOSITORY}/actions/runs"]
assert boundary.compose_ups == 0
assert not any(
command[:3] == ["docker", "compose", "--project-name"]
and command[-1:] == ["--force-recreate"]
for command in boundary.commands
)
verified = module.verify_candidate_pre_mutation(
REPOSITORY,
bound_project,
support.REQUEST_ID,
support.TARGET_SHA,
receipt_key=support.KEY,
state_root=tmp_path,
runtime=runtime,
api_json=support._validation_api,
)
assert verified["status"] == "pre-mutation-ok"
assert verified["validationEvidenceSha256"]
assert boundary.compose_ups == 0