feat: add ops center and node onboarding flow
This commit is contained in:
317
domain-api/tests/test_ops_service_go_live_signoff.py
Normal file
317
domain-api/tests/test_ops_service_go_live_signoff.py
Normal file
@@ -0,0 +1,317 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user