318 lines
15 KiB
Python
318 lines
15 KiB
Python
import unittest
|
|
from unittest.mock import patch
|
|
|
|
from app.services.ops_service import get_ops_go_live_signoff
|
|
|
|
|
|
class OpsServiceGoLiveSignoffTests(unittest.TestCase):
|
|
@patch("app.services.ops_service.get_ops_contract_registry")
|
|
@patch("app.services.ops_service.get_ops_doctor_decision")
|
|
@patch("app.services.ops_service.get_ops_go_live_review")
|
|
@patch("app.services.ops_service.get_release_launchpad")
|
|
@patch("app.services.ops_service.get_ops_codex_brief")
|
|
@patch("app.services.ops_service.get_ops_driver_feed")
|
|
@patch("app.services.ops_service.get_ops_stack_diagnosis")
|
|
@patch("app.services.ops_service.get_ops_go_live_summary")
|
|
def test_go_live_signoff_marks_ready_when_all_surfaces_align(
|
|
self,
|
|
mock_get_ops_go_live_summary,
|
|
mock_get_ops_stack_diagnosis,
|
|
mock_get_ops_driver_feed,
|
|
mock_get_ops_codex_brief,
|
|
mock_get_release_launchpad,
|
|
mock_get_ops_go_live_review,
|
|
mock_get_ops_doctor_decision,
|
|
mock_get_ops_contract_registry,
|
|
) -> None:
|
|
mock_get_ops_go_live_summary.return_value = {
|
|
"go_live_status": "ready",
|
|
"publish_status": "ready",
|
|
"operator_title": "进入发布",
|
|
"next_step_action_code": "publish_latest_worker",
|
|
"next_step_reason": "当前门禁已收口。",
|
|
"launchpad_recommended_target_node_code": "mainland-worker-01",
|
|
"launchpad_recommended_recovery_label": "继续发布",
|
|
"launchpad_recommended_recovery_summary": "可以进入正式发布。",
|
|
"launchpad_onboarding_bootstrap_pending_nodes": 0,
|
|
"launchpad_onboarding_acceptance_ready_nodes": 0,
|
|
"recommended_commands": {"next_step": "bash next-step"},
|
|
}
|
|
mock_get_ops_stack_diagnosis.return_value = {
|
|
"diagnosis": {
|
|
"stack_status": "ready",
|
|
"operator_decision": {"title": "进入发布"},
|
|
"next_step": {"action_code": "publish_latest_worker"},
|
|
}
|
|
}
|
|
mock_get_ops_driver_feed.return_value = {
|
|
"headline": "主车道清晰",
|
|
"summary": {
|
|
"launch_status": "ready",
|
|
"launchpad_recommended_target_node_code": "mainland-worker-01",
|
|
"launchpad_recommended_recovery_label": "继续发布",
|
|
"launchpad_recommended_recovery_summary": "可以进入正式发布。",
|
|
"launchpad_onboarding_bootstrap_pending_nodes": 0,
|
|
"launchpad_onboarding_acceptance_ready_nodes": 0,
|
|
},
|
|
}
|
|
mock_get_ops_codex_brief.return_value = {
|
|
"headline": "可签字",
|
|
"summary": {
|
|
"launch_status": "ready",
|
|
"launchpad_recommended_target_node_code": "mainland-worker-01",
|
|
"launchpad_recommended_recovery_label": "继续发布",
|
|
"launchpad_recommended_recovery_summary": "可以进入正式发布。",
|
|
"launchpad_onboarding_bootstrap_pending_nodes": 0,
|
|
"launchpad_onboarding_acceptance_ready_nodes": 0,
|
|
},
|
|
"focus": {"entry_key": "publish_latest_worker"},
|
|
}
|
|
mock_get_release_launchpad.return_value = {
|
|
"launchpad_status": {
|
|
"status": "ready",
|
|
"status_label": "可发布",
|
|
"recommended_target_node_code": "mainland-worker-01",
|
|
"recommended_recovery_label": "继续发布",
|
|
"recommended_recovery_summary": "可以进入正式发布。",
|
|
"onboarding_bootstrap_pending_nodes": 0,
|
|
"onboarding_acceptance_ready_nodes": 0,
|
|
}
|
|
}
|
|
mock_get_ops_go_live_review.return_value = {
|
|
"status": "ready",
|
|
"status_label": "可复核发布",
|
|
"headline": "review ok",
|
|
"report_dir": "/tmp/review",
|
|
"manifest_path": "/tmp/review/manifest.json",
|
|
"recommended_commands": {"go_live_review": "bash review"},
|
|
}
|
|
mock_get_ops_doctor_decision.return_value = {
|
|
"status": "ready",
|
|
"headline": "doctor ok",
|
|
"report_dir": "/tmp/review",
|
|
"manifest_path": "/tmp/review/manifest.json",
|
|
"decision": {
|
|
"reason_code": "all_green",
|
|
"preferred_surface": "go-live-summary",
|
|
"next_action_code": "publish_latest_worker",
|
|
},
|
|
"recommended_commands": {"doctor_decision": "bash doctor"},
|
|
}
|
|
mock_get_ops_contract_registry.return_value = {
|
|
"contracts_by_key": {
|
|
"ops_go_live_signoff_contract": {"version": "v1"},
|
|
}
|
|
}
|
|
|
|
payload = get_ops_go_live_signoff(base_url="http://127.0.0.1:8100")
|
|
|
|
self.assertEqual("ready", payload["signoff_status"])
|
|
self.assertEqual("可签字上线", payload["status_label"])
|
|
self.assertEqual([], payload["blocked_reasons"])
|
|
self.assertEqual([], payload["attention_reasons"])
|
|
self.assertTrue(payload["launchpad_alignment"]["consistent"])
|
|
self.assertEqual("go-live-summary", payload["decision"]["preferred_surface"])
|
|
self.assertIn("go-live-signoff", payload["recommended_commands"]["go_live_signoff"])
|
|
self.assertEqual("ready", payload["release_gate"]["go_live_review_status"])
|
|
self.assertEqual("ready", payload["release_gate"]["doctor_decision_status"])
|
|
self.assertEqual("/tmp/review/manifest.json", payload["manifest_path"])
|
|
|
|
@patch("app.services.ops_service.get_ops_contract_registry")
|
|
@patch("app.services.ops_service.get_ops_doctor_decision")
|
|
@patch("app.services.ops_service.get_ops_go_live_review")
|
|
@patch("app.services.ops_service.get_release_launchpad")
|
|
@patch("app.services.ops_service.get_ops_codex_brief")
|
|
@patch("app.services.ops_service.get_ops_driver_feed")
|
|
@patch("app.services.ops_service.get_ops_stack_diagnosis")
|
|
@patch("app.services.ops_service.get_ops_go_live_summary")
|
|
def test_go_live_signoff_marks_attention_when_launchpad_alignment_drifts(
|
|
self,
|
|
mock_get_ops_go_live_summary,
|
|
mock_get_ops_stack_diagnosis,
|
|
mock_get_ops_driver_feed,
|
|
mock_get_ops_codex_brief,
|
|
mock_get_release_launchpad,
|
|
mock_get_ops_go_live_review,
|
|
mock_get_ops_doctor_decision,
|
|
mock_get_ops_contract_registry,
|
|
) -> None:
|
|
mock_get_ops_go_live_summary.return_value = {
|
|
"go_live_status": "ready",
|
|
"publish_status": "ready",
|
|
"operator_title": "进入发布",
|
|
"next_step_action_code": "publish_latest_worker",
|
|
"launchpad_recommended_target_node_code": "mainland-worker-01",
|
|
"launchpad_recommended_recovery_label": "继续发布",
|
|
"launchpad_recommended_recovery_summary": "可以进入正式发布。",
|
|
"launchpad_onboarding_bootstrap_pending_nodes": 0,
|
|
"launchpad_onboarding_acceptance_ready_nodes": 0,
|
|
"recommended_commands": {},
|
|
}
|
|
mock_get_ops_stack_diagnosis.return_value = {
|
|
"diagnosis": {"stack_status": "ready", "operator_decision": {}, "next_step": {}}
|
|
}
|
|
mock_get_ops_driver_feed.return_value = {
|
|
"headline": "主车道清晰",
|
|
"summary": {
|
|
"launch_status": "ready",
|
|
"launchpad_recommended_target_node_code": "mainland-worker-02",
|
|
"launchpad_recommended_recovery_label": "继续发布",
|
|
"launchpad_recommended_recovery_summary": "可以进入正式发布。",
|
|
"launchpad_onboarding_bootstrap_pending_nodes": 0,
|
|
"launchpad_onboarding_acceptance_ready_nodes": 0,
|
|
},
|
|
}
|
|
mock_get_ops_codex_brief.return_value = {
|
|
"headline": "可签字",
|
|
"summary": {
|
|
"launch_status": "ready",
|
|
"launchpad_recommended_target_node_code": "mainland-worker-01",
|
|
"launchpad_recommended_recovery_label": "继续发布",
|
|
"launchpad_recommended_recovery_summary": "可以进入正式发布。",
|
|
"launchpad_onboarding_bootstrap_pending_nodes": 0,
|
|
"launchpad_onboarding_acceptance_ready_nodes": 0,
|
|
},
|
|
"focus": {},
|
|
}
|
|
mock_get_release_launchpad.return_value = {
|
|
"launchpad_status": {
|
|
"status": "ready",
|
|
"recommended_target_node_code": "mainland-worker-01",
|
|
"recommended_recovery_label": "继续发布",
|
|
"recommended_recovery_summary": "可以进入正式发布。",
|
|
"onboarding_bootstrap_pending_nodes": 0,
|
|
"onboarding_acceptance_ready_nodes": 0,
|
|
}
|
|
}
|
|
mock_get_ops_go_live_review.return_value = {
|
|
"status": "attention",
|
|
"headline": "review drift",
|
|
"recommended_commands": {"go_live_review": "bash review"},
|
|
}
|
|
mock_get_ops_doctor_decision.return_value = {
|
|
"status": "ready",
|
|
"headline": "doctor ok",
|
|
"decision": {"preferred_surface": "go-live-summary"},
|
|
"recommended_commands": {"doctor_decision": "bash doctor"},
|
|
}
|
|
mock_get_ops_contract_registry.return_value = {
|
|
"contracts_by_key": {
|
|
"ops_go_live_signoff_contract": {"version": "v1"},
|
|
}
|
|
}
|
|
|
|
payload = get_ops_go_live_signoff()
|
|
|
|
self.assertEqual("attention", payload["signoff_status"])
|
|
self.assertIn("launchpad_alignment=inconsistent", payload["attention_reasons"])
|
|
self.assertIn("go_live_review=attention", payload["attention_reasons"])
|
|
self.assertFalse(payload["launchpad_alignment"]["consistent"])
|
|
self.assertFalse(payload["launchpad_alignment"]["target_node_code_consistent"])
|
|
self.assertEqual("go-live-review", payload["decision"]["preferred_surface"])
|
|
|
|
@patch("app.services.ops_service.get_ops_contract_registry")
|
|
@patch("app.services.ops_service.get_ops_doctor_decision")
|
|
@patch("app.services.ops_service.get_ops_go_live_review")
|
|
@patch("app.services.ops_service.get_release_launchpad")
|
|
@patch("app.services.ops_service.get_ops_codex_brief")
|
|
@patch("app.services.ops_service.get_ops_driver_feed")
|
|
@patch("app.services.ops_service.get_ops_stack_diagnosis")
|
|
@patch("app.services.ops_service.get_ops_go_live_summary")
|
|
def test_go_live_signoff_marks_blocked_when_publish_gate_is_blocked(
|
|
self,
|
|
mock_get_ops_go_live_summary,
|
|
mock_get_ops_stack_diagnosis,
|
|
mock_get_ops_driver_feed,
|
|
mock_get_ops_codex_brief,
|
|
mock_get_release_launchpad,
|
|
mock_get_ops_go_live_review,
|
|
mock_get_ops_doctor_decision,
|
|
mock_get_ops_contract_registry,
|
|
) -> None:
|
|
mock_get_ops_go_live_summary.return_value = {
|
|
"go_live_status": "attention",
|
|
"publish_status": "blocked",
|
|
"operator_title": "先处理发布阻断",
|
|
"next_step_action_code": "fix_rollout_blockers",
|
|
"launchpad_recommended_target_node_code": "mainland-worker-01",
|
|
"launchpad_recommended_recovery_label": "处理发布阻断",
|
|
"launchpad_recommended_recovery_summary": "当前 rollout gate 未通过。",
|
|
"launchpad_onboarding_bootstrap_pending_nodes": 0,
|
|
"launchpad_onboarding_acceptance_ready_nodes": 0,
|
|
"recommended_commands": {},
|
|
}
|
|
mock_get_ops_stack_diagnosis.return_value = {
|
|
"diagnosis": {
|
|
"stack_status": "attention",
|
|
"operator_decision": {"title": "先看发布门禁"},
|
|
"next_step": {"action_code": "fix_rollout_blockers"},
|
|
}
|
|
}
|
|
mock_get_ops_driver_feed.return_value = {
|
|
"headline": "发布门禁阻断",
|
|
"summary": {
|
|
"launch_status": "attention",
|
|
"launchpad_recommended_target_node_code": "mainland-worker-01",
|
|
"launchpad_recommended_recovery_label": "处理发布阻断",
|
|
"launchpad_recommended_recovery_summary": "当前 rollout gate 未通过。",
|
|
"launchpad_onboarding_bootstrap_pending_nodes": 0,
|
|
"launchpad_onboarding_acceptance_ready_nodes": 0,
|
|
},
|
|
}
|
|
mock_get_ops_codex_brief.return_value = {
|
|
"headline": "需先排阻断",
|
|
"summary": {
|
|
"launch_status": "attention",
|
|
"launchpad_recommended_target_node_code": "mainland-worker-01",
|
|
"launchpad_recommended_recovery_label": "处理发布阻断",
|
|
"launchpad_recommended_recovery_summary": "当前 rollout gate 未通过。",
|
|
"launchpad_onboarding_bootstrap_pending_nodes": 0,
|
|
"launchpad_onboarding_acceptance_ready_nodes": 0,
|
|
},
|
|
"focus": {"entry_key": "fix_rollout_blockers"},
|
|
}
|
|
mock_get_release_launchpad.return_value = {
|
|
"launchpad_status": {
|
|
"status": "blocked",
|
|
"status_label": "发布阻断",
|
|
"recommended_action_code": "fix_rollout_blockers",
|
|
"recommended_target_node_code": "mainland-worker-01",
|
|
"recommended_recovery_label": "处理发布阻断",
|
|
"recommended_recovery_summary": "当前 rollout gate 未通过。",
|
|
"onboarding_bootstrap_pending_nodes": 0,
|
|
"onboarding_acceptance_ready_nodes": 0,
|
|
}
|
|
}
|
|
mock_get_ops_go_live_review.return_value = {
|
|
"status": "ready",
|
|
"headline": "review ok",
|
|
"recommended_commands": {"go_live_review": "bash review"},
|
|
}
|
|
mock_get_ops_doctor_decision.return_value = {
|
|
"status": "attention",
|
|
"headline": "doctor focus",
|
|
"decision": {
|
|
"reason_code": "publish_blocked",
|
|
"preferred_surface": "release-launchpad",
|
|
"next_action_code": "fix_rollout_blockers",
|
|
},
|
|
"recommended_commands": {"doctor_decision": "bash doctor"},
|
|
}
|
|
mock_get_ops_contract_registry.return_value = {
|
|
"contracts_by_key": {
|
|
"ops_go_live_signoff_contract": {"version": "v1"},
|
|
}
|
|
}
|
|
|
|
payload = get_ops_go_live_signoff()
|
|
|
|
self.assertEqual("blocked", payload["signoff_status"])
|
|
self.assertIn("publish_status=blocked", payload["blocked_reasons"])
|
|
self.assertIn("release_launchpad=blocked", payload["blocked_reasons"])
|
|
self.assertEqual("release-launchpad", payload["decision"]["preferred_surface"])
|
|
self.assertEqual("publish_blocked", payload["decision"]["doctor_reason_code"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|