Files
getDomain/domain-api/tests/test_ops_release_service_launchpad.py
2026-04-18 23:52:51 +08:00

213 lines
11 KiB
Python

import unittest
from app.services.ops_release_service import (
_build_release_launchpad_status,
_compact_latest_smart_rollout_preview,
)
class OpsReleaseServiceLaunchpadTests(unittest.TestCase):
def test_launchpad_status_blocks_when_package_missing(self) -> None:
status = _build_release_launchpad_status(
package={"available": False},
latest_package_preview={},
worker_preview={},
control_preview={},
)
self.assertEqual("blocked", status["status"])
self.assertEqual("release_package", status["recommended_action_code"])
self.assertEqual(status["summary"], status["summary_text"])
self.assertEqual("release_hub", status["focus_ref"]["kind"])
self.assertEqual("release_launchpad", status["focus_ref"]["section"])
def test_launchpad_status_blocks_when_rollout_preview_has_blockers(self) -> None:
status = _build_release_launchpad_status(
package={"available": True},
latest_package_preview={},
worker_preview={"policy_preview": {"blocked": True}},
control_preview={"policy_preview": {"blocked": False}},
)
self.assertEqual("blocked", status["status"])
self.assertEqual("fix_rollout_blockers", status["recommended_action_code"])
self.assertEqual(status["summary"], status["summary_text"])
def test_launchpad_status_blocks_when_final_signoff_missing(self) -> None:
status = _build_release_launchpad_status(
package={
"available": True,
"final_release_ok": False,
"final_release_report_available": True,
"final_release_report_ok": True,
"final_release_report_matches_latest": False,
"final_release_report_stale_reason": "final_release_report does not match latest_release: package_name",
"final_release_gate_decision": "ready",
"final_release_gate_blocked_reasons": [],
},
latest_package_preview={},
worker_preview={"smart_rollout": {"available": True}, "policy_preview": {}},
control_preview={"smart_rollout": {"available": True}, "policy_preview": {}},
)
self.assertEqual("blocked", status["status"])
self.assertEqual("release_prepare", status["recommended_action_code"])
self.assertIn("final_release_report does not match latest_release: package_name", status["summary"])
self.assertIn("release-prepare", status["recommended_command"])
self.assertEqual(status["summary"], status["summary_text"])
def test_launchpad_status_prefers_runtime_refresh_when_runtime_schema_is_stale(self) -> None:
status = _build_release_launchpad_status(
package={"available": True, "final_release_ok": True},
latest_package_preview={},
worker_preview={"smart_rollout": {"available": True}, "policy_preview": {}},
control_preview={"smart_rollout": {"available": True}, "policy_preview": {}},
runtime_build_info={
"runtime_schema_stale": True,
"route_surface_complete": True,
"route_surface_missing_keys": ["ops_node_handover_bootstrap_plan"],
},
)
self.assertEqual("attention", status["status"])
self.assertEqual("api-restart", status["recommended_action_code"])
self.assertEqual("待刷新运行时", status["status_label"])
self.assertTrue(status["runtime_refresh_required"])
self.assertTrue(status["runtime_schema_stale"])
self.assertIn("runtime-refresh-recover", status["recommended_command"])
self.assertEqual(status["summary"], status["summary_text"])
def test_launchpad_status_marks_attention_when_confirmation_required(self) -> None:
status = _build_release_launchpad_status(
package={"available": True},
latest_package_preview={},
worker_preview={"requires_confirmation": True, "policy_preview": {}},
control_preview={"policy_preview": {}},
)
self.assertEqual("attention", status["status"])
self.assertEqual("review_smart_rollout_preview", status["recommended_action_code"])
self.assertEqual("release_launchpad", status["focus_ref"]["section"])
def test_launchpad_status_ready_when_worker_rollout_available(self) -> None:
status = _build_release_launchpad_status(
package={"available": True},
latest_package_preview={},
worker_preview={"smart_rollout": {"available": True, "execution_mode": "ssh", "execution_mode_label": "SSH"}, "policy_preview": {}},
control_preview={"smart_rollout": {"available": False}, "policy_preview": {}},
)
self.assertEqual("ready", status["status"])
self.assertEqual("publish_latest_worker", status["recommended_action_code"])
self.assertEqual("ssh", status["recommended_execution_mode"])
self.assertEqual("SSH", status["recommended_execution_mode_label"])
self.assertEqual(status["summary"], status["summary_text"])
def test_launchpad_status_requires_managed_nodes_when_no_targets_available(self) -> None:
status = _build_release_launchpad_status(
package={"available": True},
latest_package_preview={},
worker_preview={"smart_rollout": {"available": False}, "policy_preview": {}},
control_preview={"smart_rollout": {"available": False}, "policy_preview": {}},
)
self.assertEqual("attention", status["status"])
self.assertEqual("fix_managed_nodes", status["recommended_action_code"])
self.assertEqual("release_hub", status["focus_ref"]["kind"])
def test_launchpad_status_prefers_bootstrap_recovery_when_gap_row_has_recovery_action(self) -> None:
status = _build_release_launchpad_status(
package={"available": True},
latest_package_preview={},
worker_preview={
"smart_rollout": {"available": False},
"policy_preview": {
"blocked": True,
"release_gate": {
"operational_readiness": {
"rows": [
{
"node_code": "mainland-worker-02",
"recovery_action": "bootstrap_run",
"recovery_label": "签发接入工单",
"recovery_summary": "当前节点仍处于接入阶段,建议先签发 onboarding.bootstrap。",
}
]
}
},
},
},
control_preview={"smart_rollout": {"available": False}, "policy_preview": {}},
)
self.assertEqual("blocked", status["status"])
self.assertEqual("bootstrap_run", status["recommended_action_code"])
self.assertEqual("mainland-worker-02", status["recommended_target_node_code"])
self.assertEqual("签发接入工单", status["recommended_recovery_label"])
self.assertEqual("当前节点仍处于接入阶段,建议先签发 onboarding.bootstrap。", status["recommended_recovery_summary"])
self.assertEqual(1, status["onboarding_bootstrap_pending_nodes"])
self.assertEqual(0, status["onboarding_acceptance_ready_nodes"])
self.assertEqual(1, status["onboarding_gap_nodes_total"])
self.assertIn("node-bootstrap-run", status["recommended_command"])
def test_compact_latest_smart_rollout_preview_adds_rollout_preview_alias(self) -> None:
preview = _compact_latest_smart_rollout_preview(
"智能 Rollout 预检通过",
{
"release": {
"id": 18,
"release_version": "2026.04.18+bd6bcb2",
"channel": "stable",
"status": "ready",
"artifact_url": "https://example.com/domaincheck.tar.gz",
},
"package": {
"available": True,
"package_name": "domaincheck-2026.04.18+bd6bcb2",
"package_type": "tar.gz",
"sha256": "sha256:test",
"metadata": {
"manifest": {
"included_services": ["domaincheck-api", "domaincheck-worker"],
}
},
},
"smart_rollout": {
"available": True,
"execution_mode": "remote-agent",
"execution_mode_label": "远端 Agent",
"summary": "已按 Worker 预选 2 台节点。",
"target_selector": {"node_codes": ["mainland-worker-01", "mainland-worker-02"]},
"target_nodes": [
{
"node_code": "mainland-worker-01",
"region": "mainland",
"role": "worker",
"delivery_queue_state": "healthy",
},
{
"node_code": "mainland-worker-02",
"region": "mainland",
"role": "worker",
"delivery_queue_state": "retrying",
},
],
},
"preview": {
"target_summary": {"nodes_total": 2},
"batch_plan": {"batches_total": 2, "first_batch_size": 1},
"release_gate": {"status": "ready", "summary": "当前可推进。"},
},
},
)
self.assertEqual("remote-agent", preview["rollout_preview"]["execution_mode"])
self.assertEqual(2, preview["rollout_preview"]["target_nodes_total"])
self.assertEqual(2, preview["rollout_preview"]["expected_batches_total"])
self.assertEqual(2, preview["rollout_preview"]["expected_jobs_total"])
self.assertEqual("release_hub", preview["rollout_preview"]["focus_ref"]["kind"])
self.assertEqual("healthy", preview["smart_rollout"]["target_nodes"][0]["delivery_queue_state"])
self.assertEqual("retrying", preview["smart_rollout"]["target_nodes"][1]["delivery_queue_state"])
self.assertEqual("rollout_preview", preview["rollout_preview"]["focus_ref"]["section"])
self.assertEqual(["domaincheck-api", "domaincheck-worker"], preview["package"]["manifest"]["included_services"])