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