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()