test(autonomous-release): specify governed release gates
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
from dataclasses import replace
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import pytest
|
||||
|
||||
from aios_autonomous_release_candidate.approval import ApprovalVerificationResult
|
||||
from aios_autonomous_release_candidate.errors import ReleaseBlocked
|
||||
from aios_autonomous_release_candidate.models import ReleasePlan
|
||||
from aios_autonomous_release_candidate.release import ReleaseController, ReleaseState
|
||||
from aios_autonomous_release_candidate.release_gate import ReleaseEvidence, ReleaseGate
|
||||
from aios_autonomous_release_candidate.tag import TagReference, TagVerifier
|
||||
|
||||
|
||||
OID = "a" * 40
|
||||
TAG_OID = "b" * 40
|
||||
DIGEST = "c" * 64
|
||||
|
||||
|
||||
def plan() -> ReleasePlan:
|
||||
return ReleasePlan(
|
||||
task_id="release-task",
|
||||
repository="aios/aios",
|
||||
branch="integration/autonomous-release",
|
||||
version="V2R1C4",
|
||||
target_oid=OID,
|
||||
tree_oid="d" * 40,
|
||||
artifact_sha256=DIGEST,
|
||||
manifest_sha256="e" * 64,
|
||||
evidence_closure_oid="f" * 40,
|
||||
approval_plan_id="owner-release-v2r1c4",
|
||||
rollback_approval_plan_id="owner-rollback-v2r1c4",
|
||||
rollback_artifact_sha256="1" * 64,
|
||||
expected_tag_oid=TAG_OID,
|
||||
expected_target_oid=OID,
|
||||
production_config_sha256="2" * 64,
|
||||
)
|
||||
|
||||
|
||||
def approval(plan_id: str) -> ApprovalVerificationResult:
|
||||
return ApprovalVerificationResult(
|
||||
approval_plan_id=plan_id,
|
||||
task_id="release-task",
|
||||
signing_key_id="owner-kms-1",
|
||||
verified_at=datetime(2026, 7, 26, tzinfo=timezone.utc),
|
||||
)
|
||||
|
||||
|
||||
def test_deploy_and_rollback_require_distinct_verified_approvals() -> None:
|
||||
controller = ReleaseController()
|
||||
controller.attach_plan(plan())
|
||||
controller.mark_built()
|
||||
controller.verify(artifact_ok=True, evidence_ok=True)
|
||||
controller.request_owner_approval()
|
||||
with pytest.raises(ReleaseBlocked, match="OWNER_APPROVAL_PLAN_MISMATCH"):
|
||||
controller.approve(approval("wrong-plan"))
|
||||
controller.approve(approval("owner-release-v2r1c4"))
|
||||
controller.start_deployment()
|
||||
controller.mark_deployed()
|
||||
controller.request_rollback("candidate acceptance regression")
|
||||
with pytest.raises(ReleaseBlocked, match="ROLLBACK_APPROVAL_PLAN_MISMATCH"):
|
||||
controller.approve_rollback(approval("owner-release-v2r1c4"))
|
||||
controller.approve_rollback(approval("owner-rollback-v2r1c4"))
|
||||
controller.start_rollback()
|
||||
assert controller.mark_rolled_back().state == ReleaseState.ROLLED_BACK
|
||||
|
||||
|
||||
def test_annotated_tag_uses_object_and_peeled_oids_separately() -> None:
|
||||
tag = TagReference("refs/tags/V2R1C4", "tag", TAG_OID, OID)
|
||||
TagVerifier().verify(tag, expected_tag_oid=TAG_OID, expected_target_oid=OID)
|
||||
with pytest.raises(ReleaseBlocked, match="TAG_PEELED_TARGET_MISMATCH"):
|
||||
TagVerifier().verify(tag, expected_tag_oid=TAG_OID, expected_target_oid="3" * 40)
|
||||
|
||||
|
||||
def test_release_gate_requires_four_source_tag_and_artifact_closure() -> None:
|
||||
evidence = ReleaseEvidence(
|
||||
expected_version="V2R1C4",
|
||||
registry_version="V2R1C4",
|
||||
baseline_version="V2R1C4",
|
||||
local_tag_oid=TAG_OID,
|
||||
remote_tag_oid=TAG_OID,
|
||||
gitea_tag_oid=TAG_OID,
|
||||
expected_tag_oid=TAG_OID,
|
||||
local_peeled_target_oid=OID,
|
||||
remote_peeled_target_oid=OID,
|
||||
gitea_peeled_target_oid=OID,
|
||||
expected_target_oid=OID,
|
||||
artifact_sha256=DIGEST,
|
||||
manifest_sha256="e" * 64,
|
||||
evidence_sha256="4" * 64,
|
||||
rollback_artifact_sha256="1" * 64,
|
||||
)
|
||||
ReleaseGate().verify(evidence)
|
||||
with pytest.raises(ReleaseBlocked, match="TAG_GITEA_MISMATCH"):
|
||||
ReleaseGate().verify(replace(evidence, gitea_tag_oid="5" * 40))
|
||||
Reference in New Issue
Block a user