From a059e76742e4604baf22fc7fb7bc32f73e4b01b3 Mon Sep 17 00:00:00 2001 From: hy Date: Thu, 23 Apr 2026 16:52:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20CLB=20SDK=20=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E4=BD=93=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- monitors/yumi/tencent_clb.py | 8 ++++++- tests/test_gateway_release_clb.py | 38 ++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/monitors/yumi/tencent_clb.py b/monitors/yumi/tencent_clb.py index 1276b8c..aa1e979 100644 --- a/monitors/yumi/tencent_clb.py +++ b/monitors/yumi/tencent_clb.py @@ -101,7 +101,13 @@ def call_clb(method_name: str, payload: dict[str, Any]) -> dict[str, Any]: request = request_class() request.from_json_string(json.dumps(payload)) response = invoke_clb(lambda: getattr(client, method_name)(request), method_name) - return dict(json.loads(response.to_json_string()).get("Response") or {}) + payload = json.loads(response.to_json_string()) + # 腾讯云 Python SDK 在不同产品/版本里有两种常见结构: + # 1. 顶层直接就是业务字段;2. 顶层包一层 Response。 + # 这里同时兼容两种形态,避免把真实返回体误吃成空字典。 + if isinstance(payload, dict) and isinstance(payload.get("Response"), dict): + return dict(payload.get("Response") or {}) + return dict(payload or {}) def wait_clb_task(task_id: str, *, timeout_seconds: int = GATEWAY_CLB_TASK_TIMEOUT_SECONDS) -> dict[str, Any]: diff --git a/tests/test_gateway_release_clb.py b/tests/test_gateway_release_clb.py index 4f0c542..fef8f34 100644 --- a/tests/test_gateway_release_clb.py +++ b/tests/test_gateway_release_clb.py @@ -1,10 +1,46 @@ from __future__ import annotations +import json import unittest from unittest.mock import patch from monitors.yumi.service_release import deploy_runtime_services_payload -from monitors.yumi.tencent_clb import collect_service_bindings, prepare_gateway_release_clb_snapshot +from monitors.yumi.tencent_clb import call_clb, collect_service_bindings, prepare_gateway_release_clb_snapshot + + +class GatewayClbClientTests(unittest.TestCase): + @patch("monitors.yumi.tencent_clb.invoke_clb") + @patch("monitors.yumi.tencent_clb.build_clb_client") + def test_call_clb_accepts_top_level_payload_shape( + self, + build_clb_client_mock, + invoke_clb_mock, + ) -> None: + class DummyRequest: + def from_json_string(self, text: str) -> None: + self.text = text + + class DummyModels: + DescribeTargetsRequest = DummyRequest + + class DummyResponse: + def to_json_string(self) -> str: + return json.dumps({"Listeners": [{"ListenerId": "lbl-http"}], "RequestId": "req-1"}) + + class DummyClient: + def DescribeTargets(self, request): + return request + + build_clb_client_mock.return_value = (DummyClient(), "") + invoke_clb_mock.return_value = DummyResponse() + + import tencentcloud.clb.v20180317 as clb_pkg + + with patch.object(clb_pkg, "models", DummyModels(), create=True): + payload = call_clb("DescribeTargets", {"LoadBalancerId": "lb-test"}) + + self.assertEqual(payload["RequestId"], "req-1") + self.assertEqual(payload["Listeners"][0]["ListenerId"], "lbl-http") class GatewayClbDiscoveryTests(unittest.TestCase):