import unittest from app.services.ops_template_service import build_ops_template_payload, get_ops_action_template class OpsTemplateServiceTests(unittest.TestCase): def test_node_bootstrap_defaults_and_clamps_number(self) -> None: ok, message, data = build_ops_template_payload( "node.bootstrap", { "expires_in_hours": 9999, }, ) self.assertTrue(ok, message) self.assertEqual("ok", message) self.assertEqual("", data["control_plane_base_url"]) self.assertEqual("/opt/domaincheck", data["root_dir"]) self.assertEqual(720, data["expires_in_hours"]) self.assertEqual("", data["node_region"]) self.assertEqual("", data["node_role"]) def test_deploy_release_custom_normalizes_lists_and_booleans(self) -> None: ok, message, data = build_ops_template_payload( "deploy.release.custom", { "release_version": "2026.04.18-rc1", "artifact_url": "https://example.com/release.tar.gz", "restart_services": "domaincheck-api\ndomaincheck-worker", "health_check_urls": "", "health_check_services": "", "rollback_on_failure": "false", "switch_current": "true", }, ) self.assertTrue(ok, message) self.assertEqual("ok", message) self.assertEqual(["domaincheck-api", "domaincheck-worker"], data["restart_services"]) self.assertEqual([], data["health_check_urls"]) self.assertEqual([], data["health_check_services"]) self.assertFalse(data["rollback_on_failure"]) self.assertTrue(data["switch_current"]) self.assertEqual("/opt/domaincheck", data["install_root"]) def test_deploy_release_control_applies_defaults(self) -> None: ok, message, data = build_ops_template_payload( "deploy.release.control", { "release_version": "2026.04.18-rc1", "artifact_url": "https://example.com/release.tar.gz", }, ) self.assertTrue(ok, message) self.assertEqual( ["domaincheck-api", "domaincheck-worker", "domaincheck-sync-agent"], data["restart_services"], ) self.assertEqual(["http://127.0.0.1:8100/health"], data["health_check_urls"]) self.assertEqual( ["domaincheck-api", "domaincheck-worker", "domaincheck-sync-agent"], data["health_check_services"], ) self.assertTrue(data["rollback_on_failure"]) self.assertTrue(data["switch_current"]) def test_deploy_release_missing_required_field_fails(self) -> None: ok, message, data = build_ops_template_payload( "deploy.release.custom", { "release_version": "2026.04.18-rc1", "artifact_url": "", }, ) self.assertFalse(ok) self.assertEqual("artifact_url 不能为空", message) self.assertEqual({}, data) def test_logs_collect_lines_are_clamped(self) -> None: ok, message, data = build_ops_template_payload( "logs.collect", { "service_name": "domaincheck-worker", "lines": 9999, }, ) self.assertTrue(ok, message) self.assertEqual(500, data["lines"]) def test_delivery_queue_templates_normalize_and_require_expected_fields(self) -> None: replay_ok, replay_message, replay_data = build_ops_template_payload( "delivery.queue.replay", { "record_id": "evt-001", "limit": 999, "flush_after_replay": "false", "reason": "确认状态已修复", }, ) discard_ok, discard_message, discard_data = build_ops_template_payload( "delivery.queue.discard", { "record_id": "evt-002", "limit": 0, "reason": "确认这条死信不再需要补发", }, ) flush_ok, flush_message, flush_data = build_ops_template_payload( "delivery.queue.flush", { "limit": 999, }, ) discard_fail_ok, discard_fail_message, discard_fail_data = build_ops_template_payload( "delivery.queue.discard", { "record_id": "evt-003", "reason": "", }, ) self.assertTrue(replay_ok, replay_message) self.assertEqual(200, replay_data["limit"]) self.assertFalse(replay_data["flush_after_replay"]) self.assertEqual("evt-001", replay_data["record_id"]) self.assertTrue(discard_ok, discard_message) self.assertEqual(1, discard_data["limit"]) self.assertEqual("确认这条死信不再需要补发", discard_data["reason"]) self.assertTrue(flush_ok, flush_message) self.assertEqual(200, flush_data["limit"]) self.assertFalse(discard_fail_ok) self.assertEqual("reason 不能为空", discard_fail_message) self.assertEqual({}, discard_fail_data) def test_diagnostics_and_service_control_templates_expose_ssh(self) -> None: health_template = get_ops_action_template("health.snapshot") logs_template = get_ops_action_template("logs.collect") diagnostics_template = get_ops_action_template("diagnostics.collect") service_status_template = get_ops_action_template("service.status") restart_template = get_ops_action_template("service.restart") restart_api_template = get_ops_action_template("runtime.restart_api") stop_worker_template = get_ops_action_template("runtime.stop_worker") deploy_control_template = get_ops_action_template("deploy.release.control") deploy_worker_template = get_ops_action_template("deploy.release.worker") deploy_custom_template = get_ops_action_template("deploy.release.custom") queue_flush_template = get_ops_action_template("delivery.queue.flush") queue_replay_template = get_ops_action_template("delivery.queue.replay") queue_discard_template = get_ops_action_template("delivery.queue.discard") self.assertIn("ssh", health_template["execution_modes"]) self.assertEqual("远端 Agent", health_template["default_execution_mode_label"]) self.assertEqual("SSH", next(item["label"] for item in health_template["execution_mode_options"] if item["value"] == "ssh")) self.assertIn("ssh", logs_template["execution_modes"]) self.assertIn("ssh", diagnostics_template["execution_modes"]) self.assertIn("ssh", service_status_template["execution_modes"]) self.assertIn("ssh", restart_template["execution_modes"]) self.assertIn("ssh", restart_api_template["execution_modes"]) self.assertIn("ssh", stop_worker_template["execution_modes"]) self.assertEqual("控制面", get_ops_action_template("node.bootstrap")["default_execution_mode_label"]) self.assertIn("ssh", deploy_control_template["execution_modes"]) self.assertIn("ssh", deploy_worker_template["execution_modes"]) self.assertIn("ssh", deploy_custom_template["execution_modes"]) self.assertEqual(["remote-agent"], queue_flush_template["execution_modes"]) self.assertEqual(["remote-agent"], queue_replay_template["execution_modes"]) self.assertEqual(["remote-agent"], queue_discard_template["execution_modes"]) self.assertEqual("远端 Agent", queue_flush_template["default_execution_mode_label"]) if __name__ == "__main__": unittest.main()