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": 2, "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, "hostMetricsSha256": "d" * 64, "targetConcurrency": 10, "observedConcurrency": 10, "targetP95Ms": 500, "observedP95Ms": 250, "targetErrorRate": 0.01, "observedErrorRate": 0, "targetCpuHeadroomPercent": 25, "observedCpuHeadroomPercent": 40, "targetMemoryHeadroomPercent": 25, "observedMemoryHeadroomPercent": 35}, "pilot": {"recordSha256": "e" * 64, "businessDaysObserved": 5, "hotelsObserved": 1, "stopConditionsObserved": 0, "unresolvedFindings": 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) record = valid_record();record["capacity"]["observedCpuHeadroomPercent"] = 24 with self.assertRaisesRegex(ValueError, "cpu headroom"): approval.validate(record) def test_completed_five_day_single_hotel_pilot_is_required(self): record = valid_record();record["pilot"]["businessDaysObserved"] = 4 with self.assertRaisesRegex(ValueError, "five business days"): approval.validate(record) record = valid_record();record["pilot"]["stopConditionsObserved"] = 1 with self.assertRaisesRegex(ValueError, "stop condition"): 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()