GuestOps/tests/test_pilot_approval.py
wolf-demon 4dd33c90e1
Some checks are pending
Build and verify web migration / verify (push) Waiting to run
milestone 19 &20
2026-09-29 20:50:40 +01:00

71 lines
3.4 KiB
Python

import importlib.util
from pathlib import Path
import unittest
spec = importlib.util.spec_from_file_location("pilot_approval", Path(__file__).resolve().parents[1] / "deploy" / "pilot_approval.py")
approval = importlib.util.module_from_spec(spec)
spec.loader.exec_module(approval)
def valid_record(gate="B"):
ids = approval.GATE_C if gate == "C" else approval.GATE_B
return {
"schemaVersion": 1, "targetGate": gate, "decision": "approved",
"releaseCommit": "a" * 40, "releaseRecordSha256": "b" * 64,
"decidedAt": "2026-09-29T12:00:00Z",
"approvals": {
"hotelOwner": {"name": "Hotel owner", "approvedAt": "2026-09-29T11:00:00Z"},
"technicalOwner": {"name": "Technical owner", "approvedAt": "2026-09-29T11:30:00Z"},
},
"capacity": {"reportSha256": "c" * 64, "targetConcurrency": 10, "observedConcurrency": 10, "targetP95Ms": 500, "observedP95Ms": 250, "targetErrorRate": 0.01, "observedErrorRate": 0},
"evidence": [{"id": item, "status": "pass", "references": ["restricted-ticket-" + item]} for item in sorted(ids)],
}
class PilotApprovalTests(unittest.TestCase):
def test_gate_b_and_c_complete_records_pass(self):
approval.validate(valid_record("B"))
approval.validate(valid_record("C"))
def test_pending_or_missing_evidence_fails(self):
record = valid_record();record["decision"] = "pending"
with self.assertRaisesRegex(ValueError, "explicit approved"):
approval.validate(record)
record = valid_record();record["evidence"].pop()
with self.assertRaisesRegex(ValueError, "exact evidence"):
approval.validate(record)
def test_capacity_must_meet_preapproved_targets(self):
record = valid_record();record["capacity"]["observedP95Ms"] = 501
with self.assertRaisesRegex(ValueError, "observedP95Ms"):
approval.validate(record)
record = valid_record();record["capacity"]["observedConcurrency"] = 9
with self.assertRaisesRegex(ValueError, "Observed concurrency"):
approval.validate(record)
record = valid_record();record["capacity"]["targetConcurrency"] = True
with self.assertRaisesRegex(ValueError, "Observed concurrency"):
approval.validate(record)
record = valid_record();record["capacity"]["targetErrorRate"] = 2
with self.assertRaisesRegex(ValueError, "ratio"):
approval.validate(record)
def test_approvers_must_be_separate_people_without_email_addresses(self):
record = valid_record();record["approvals"]["technicalOwner"]["name"] = "Hotel owner"
with self.assertRaisesRegex(ValueError, "different people"):
approval.validate(record)
record = valid_record();record["approvals"]["hotelOwner"]["name"] = "owner@example.invalid"
with self.assertRaisesRegex(ValueError, "without an email"):
approval.validate(record)
def test_containment_requires_owner_future_expiry_and_trigger(self):
record = valid_record();item = record["evidence"][0];item["status"] = "contained"
with self.assertRaisesRegex(ValueError, "containment details"):
approval.validate(record)
item["containment"] = {"owner": "Release owner", "expiresAt": "2026-10-10T12:00:00Z", "rollbackTrigger": "Rollback if the contained condition occurs."}
approval.validate(record)
if __name__ == "__main__":
unittest.main()