import unittest from unittest.mock import patch from app.services.ops_service import execute_ops_runbook_sequence, resolve_ops_runbook_sequence class OpsServiceRunbookSequenceTests(unittest.TestCase): @patch("app.services.ops_service.get_ops_runbook") def test_resolve_ops_runbook_sequence_uses_primary_action(self, mock_get_ops_runbook) -> None: mock_get_ops_runbook.return_value = { "control_sequences": [ { "key": "release_progression", "title": "版本发布与放量", "status": "ready", "status_label": "可发 Worker 灰度", "summary": "可以直接发布。", "reason": "launchpad 已就绪", "target_scope_label": "Release / Launchpad / 2026.04.18", "step_titles": ["确认发布包", "发灰度"], "target_node_codes": ["mainland-worker-01"], "focus_ref": { "kind": "release_hub", "release_id": 12, "section": "release_launchpad", }, "primary_label": "直接发 Worker 灰度", "primary_action_code": "publish_latest_worker", "primary_action_payload": { "channel": "stable", "release_id": 12, }, "primary_focus_ref": { "kind": "release_hub", "release_id": 12, "section": "release_launchpad", "mode": "worker", }, } ] } ok, message, data = resolve_ops_runbook_sequence( "release_progression", { "requested_by": "test", }, ) self.assertTrue(ok, message) self.assertEqual("标准作业路径解析成功", message) self.assertEqual("publish_latest_worker", data["driver_action_code"]) self.assertEqual("直接发 Worker 灰度", data["driver_action_label"]) self.assertEqual(["mainland-worker-01"], data["driver_node_codes"]) self.assertEqual("test/runbook/release_progression", data["driver_requested_by"]) self.assertEqual("Release / Launchpad / 2026.04.18", data["sequence"]["target_scope_label"]) self.assertEqual(["确认发布包", "发灰度"], data["sequence"]["step_titles"]) self.assertEqual("release_launchpad", data["sequence"]["focus_ref"]["section"]) self.assertEqual("worker", data["sequence"]["action_focus_ref"]["mode"]) self.assertEqual("worker", data["driver_action_payload"]["focus_ref"]["mode"]) @patch("app.services.ops_service.get_ops_runbook") def test_resolve_ops_runbook_sequence_uses_secondary_action_and_allows_payload_override( self, mock_get_ops_runbook, ) -> None: mock_get_ops_runbook.return_value = { "control_sequences": [ { "key": "node_onboarding", "title": "纳管新节点", "status": "warning", "status_label": "有待接管节点", "summary": "先补接入。", "reason": "新增节点需要先纳管。", "target_node_codes": ["mainland-worker-01"], "secondary_label": "跑接管验收", "secondary_action_code": "run_acceptance", "secondary_action_payload": { "node_code": "mainland-worker-01", }, } ] } ok, message, data = resolve_ops_runbook_sequence( "node_onboarding", { "secondary": True, "requested_by": "test", "node_codes": ["mainland-worker-02"], "action_payload": { "auto_approve": True, }, }, ) self.assertTrue(ok, message) self.assertEqual("标准作业路径解析成功", message) self.assertEqual("run_acceptance", data["driver_action_code"]) self.assertEqual("跑接管验收", data["driver_action_label"]) self.assertEqual(["mainland-worker-02"], data["driver_node_codes"]) self.assertTrue(data["sequence"]["secondary"]) self.assertEqual( { "node_code": "mainland-worker-01", "auto_approve": True, }, data["driver_action_payload"], ) @patch("app.services.ops_service.execute_driver_action") @patch("app.services.ops_service.get_ops_runbook") def test_execute_ops_runbook_sequence_uses_primary_action( self, mock_get_ops_runbook, mock_execute_driver_action, ) -> None: mock_get_ops_runbook.return_value = { "control_sequences": [ { "key": "release_progression", "title": "版本发布与放量", "status": "ready", "status_label": "可发 Worker 灰度", "summary": "可以直接发布。", "reason": "launchpad 已就绪", "target_node_codes": ["mainland-worker-01"], "primary_action_code": "publish_latest_worker", "primary_action_payload": { "channel": "stable", "release_id": 12, }, "secondary_action_code": "focus_release_hub", "secondary_action_payload": { "release_id": 12, }, } ] } mock_execute_driver_action.return_value = ( True, "Release rollout 已创建", { "handled": True, "mode": "smart-release-rollout", }, ) ok, message, data = execute_ops_runbook_sequence( "release_progression", { "requested_by": "test", }, ) self.assertTrue(ok, message) self.assertEqual("Release rollout 已创建", message) self.assertTrue(data["handled"]) self.assertEqual("publish_latest_worker", data["driver_action_code"]) self.assertEqual(["mainland-worker-01"], data["driver_node_codes"]) self.assertEqual("release_progression", data["sequence_key"]) self.assertEqual("版本发布与放量", data["sequence"]["title"]) mock_execute_driver_action.assert_called_once_with( { "action_code": "publish_latest_worker", "node_codes": ["mainland-worker-01"], "action_payload": { "channel": "stable", "release_id": 12, }, "requested_by": "test/runbook/release_progression", } ) @patch("app.services.ops_service.execute_driver_action") @patch("app.services.ops_service.get_ops_runbook") def test_execute_ops_runbook_sequence_uses_secondary_action_and_allows_payload_override( self, mock_get_ops_runbook, mock_execute_driver_action, ) -> None: mock_get_ops_runbook.return_value = { "control_sequences": [ { "key": "node_onboarding", "title": "纳管新节点", "status": "warning", "status_label": "有待接管节点", "summary": "先补接入。", "reason": "新增节点需要先纳管。", "target_node_codes": ["mainland-worker-01"], "primary_action_code": "bootstrap_run", "primary_action_payload": { "node_code": "mainland-worker-01", }, "secondary_action_code": "run_acceptance", "secondary_action_payload": { "node_code": "mainland-worker-01", }, } ] } mock_execute_driver_action.return_value = ( True, "", { "handled": True, "mode": "ui-intent", }, ) ok, message, data = execute_ops_runbook_sequence( "node_onboarding", { "secondary": True, "requested_by": "test", "node_codes": ["mainland-worker-02"], "action_payload": { "auto_approve": True, }, }, ) self.assertTrue(ok, message) self.assertEqual("", message) self.assertTrue(data["handled"]) self.assertEqual("run_acceptance", data["driver_action_code"]) self.assertEqual(["mainland-worker-02"], data["driver_node_codes"]) self.assertTrue(data["sequence"]["secondary"]) mock_execute_driver_action.assert_called_once_with( { "action_code": "run_acceptance", "node_codes": ["mainland-worker-02"], "action_payload": { "node_code": "mainland-worker-01", "auto_approve": True, }, "requested_by": "test/runbook/node_onboarding", } ) @patch("app.services.ops_service.get_ops_runbook") def test_execute_ops_runbook_sequence_returns_error_when_sequence_missing(self, mock_get_ops_runbook) -> None: mock_get_ops_runbook.return_value = {"control_sequences": []} ok, message, data = execute_ops_runbook_sequence("missing-sequence", {}) self.assertFalse(ok) self.assertEqual("标准作业路径不存在", message) self.assertEqual("missing-sequence", data["sequence_key"]) if __name__ == "__main__": unittest.main()