feat: add ops center and node onboarding flow

This commit is contained in:
Your Name
2026-04-18 23:52:51 +08:00
parent 246838ae4c
commit b9c29481b5
142 changed files with 89727 additions and 186 deletions

View File

@@ -0,0 +1,169 @@
from __future__ import annotations
import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
from app.services.ops_service import get_ops_go_live_review
class OpsServiceGoLiveReviewTests(unittest.TestCase):
@patch("app.services.ops_service.get_ops_contract_registry")
def test_go_live_review_marks_missing_when_no_manifest_exists(self, mock_get_ops_contract_registry) -> None:
mock_get_ops_contract_registry.return_value = {
"contracts_by_key": {
"ops_go_live_review_contract": {"version": "v1"},
}
}
with patch("app.services.ops_service._find_latest_go_live_bundle_manifest", return_value=(None, Path("/tmp/go-live-bundles"))):
payload = get_ops_go_live_review(base_url="http://127.0.0.1:8100")
self.assertEqual("missing", payload["status"])
self.assertEqual("待导出", payload["status_label"])
self.assertIn("go-live-export", payload["recommended_commands"]["go_live_export"])
@patch("app.services.ops_service.get_ops_contract_registry")
def test_go_live_review_reads_manifest_and_alignment(self, mock_get_ops_contract_registry) -> None:
mock_get_ops_contract_registry.return_value = {
"contracts_by_key": {
"ops_go_live_review_contract": {"version": "v1"},
}
}
with tempfile.TemporaryDirectory() as temp_dir:
report_dir = Path(temp_dir) / "go-live-bundle-001"
report_dir.mkdir(parents=True, exist_ok=True)
env_audit_path = report_dir / "00_env_audit.txt"
env_audit_path.write_text(
"[8/8] condensed env audit summary\n"
+ json.dumps(
{
"status": "attention",
"headline": "环境仍有轻微缺口",
"missing_items": ["missing_tool:jq"],
"recommended_actions": ["bash runtime-refresh-recover"],
},
ensure_ascii=False,
),
encoding="utf-8",
)
go_live_summary_path = report_dir / "02_go_live_summary.json"
go_live_summary_path.write_text(
json.dumps(
{
"go_live_status": "attention",
"publish_status": "attention",
"operator_title": "先补接入缺口",
"next_step_action_code": "fix_managed_nodes",
"launchpad_recommended_target_node_code": "mainland-worker-01",
"launchpad_recommended_recovery_label": "先补接入缺口",
"launchpad_recommended_recovery_summary": "mainland-worker-01 还缺接入收口。",
"launchpad_onboarding_bootstrap_pending_nodes": 1,
"launchpad_onboarding_acceptance_ready_nodes": 0,
},
ensure_ascii=False,
),
encoding="utf-8",
)
stack_diagnosis_path = report_dir / "03_stack_diagnosis.json"
stack_diagnosis_path.write_text(
json.dumps(
{
"diagnosis": {
"stack_status": "attention",
"operator_decision": {"title": "先补接入缺口"},
"next_step": {"action_code": "fix_managed_nodes"},
"launchpad_recommended_target_node_code": "mainland-worker-02",
"launchpad_recommended_recovery_label": "先看节点接管",
"launchpad_recommended_recovery_summary": "stack 与 summary 存在漂移。",
"launchpad_onboarding_bootstrap_pending_nodes": 1,
"launchpad_onboarding_acceptance_ready_nodes": 0,
}
},
ensure_ascii=False,
),
encoding="utf-8",
)
driver_feed_path = report_dir / "04_driver_feed.json"
driver_feed_path.write_text(
json.dumps(
{
"headline": "driver ok",
"summary": {
"launchpad_recommended_target_node_code": "mainland-worker-01",
"launchpad_recommended_recovery_label": "先补接入缺口",
"launchpad_recommended_recovery_summary": "mainland-worker-01 还缺接入收口。",
"launchpad_onboarding_bootstrap_pending_nodes": 1,
"launchpad_onboarding_acceptance_ready_nodes": 0,
},
"automation_coverage": {"launch_status": "attention", "launch_ready": False},
},
ensure_ascii=False,
),
encoding="utf-8",
)
codex_brief_path = report_dir / "05_codex_brief.json"
codex_brief_path.write_text(
json.dumps(
{
"headline": "codex ok",
"summary": {
"launchpad_recommended_target_node_code": "mainland-worker-01",
"launchpad_recommended_recovery_label": "先补接入缺口",
"launchpad_recommended_recovery_summary": "mainland-worker-01 还缺接入收口。",
"launchpad_onboarding_bootstrap_pending_nodes": 1,
"launchpad_onboarding_acceptance_ready_nodes": 0,
},
"automation_coverage": {"launch_status": "attention", "launch_ready": False},
"focus": {"entry_key": "fix_managed_nodes"},
},
ensure_ascii=False,
),
encoding="utf-8",
)
manifest_path = report_dir / "manifest.json"
manifest_path.write_text(
json.dumps(
{
"report_dir": str(report_dir),
"artifacts": [
{"key": "env_audit", "path": str(env_audit_path), "ok": True},
{"key": "go_live_summary", "path": str(go_live_summary_path), "ok": True},
{"key": "stack_diagnosis", "path": str(stack_diagnosis_path), "ok": True},
{"key": "driver_feed", "path": str(driver_feed_path), "ok": True},
{"key": "codex_brief", "path": str(codex_brief_path), "ok": True},
{"key": "release_launchpad", "path": str(report_dir / "06_release_launchpad.json"), "ok": True},
],
"failures": ["doctor_export"],
"summary": {
"artifact_total": 9,
"failure_total": 1,
"recommended_reading_order": ["00_env_audit.txt", "02_go_live_summary.json"],
},
},
ensure_ascii=False,
indent=2,
),
encoding="utf-8",
)
with patch(
"app.services.ops_service._find_latest_go_live_bundle_manifest",
return_value=(manifest_path, report_dir),
):
payload = get_ops_go_live_review(base_url="http://127.0.0.1:8100")
self.assertEqual("attention", payload["status"])
self.assertEqual(9, payload["artifact_total"])
self.assertEqual(["doctor_export"], payload["noncritical_failures"])
self.assertFalse(payload["launchpad_alignment"]["consistent"])
self.assertIn("go_live_summary", payload["launchpad_alignment"]["available_sources"])
self.assertIn("mainland-worker-01", payload["recommended_next_steps"][0] + "".join(payload["recommended_next_steps"]))
self.assertIn(str(manifest_path), payload["recommended_commands"]["go_live_review"])
if __name__ == "__main__":
unittest.main()