Skip to content

Commit c177925

Browse files
committed
refactor(tool): 更新配置访问方式并改进测试覆盖
本提交更新了 `Tool` 类中的配置访问方法,将直接属性访问改为调用统一的方法接口,并相应地增强了单元测试以更好地模拟各种边界情况下的行为。 Co-developed-by: Aone Copilot <noreply@alibaba-inc.com> Signed-off-by: Sodawyx <sodawyx@126.com>
1 parent b9d9c6f commit c177925

File tree

3 files changed

+110
-28
lines changed

3 files changed

+110
-28
lines changed

agentrun/tool/__tool_async_template.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def _get_functioncall_server_url(
181181
data_endpoint = self.data_endpoint
182182
if not data_endpoint:
183183
cfg = Config.with_configs(config)
184-
data_endpoint = cfg._data_endpoint
184+
data_endpoint = cfg.get_data_endpoint()
185185
if not data_endpoint or not effective_name:
186186
return None
187187
return f"{data_endpoint}/tools/{effective_name}"
@@ -210,7 +210,7 @@ def _get_mcp_endpoint(
210210
data_endpoint = self.data_endpoint
211211
if not data_endpoint:
212212
cfg = Config.with_configs(config)
213-
data_endpoint = cfg._data_endpoint
213+
data_endpoint = cfg.get_data_endpoint()
214214
if not data_endpoint or not effective_name:
215215
return None
216216

@@ -451,7 +451,7 @@ def _get_skill_download_url(
451451
data_endpoint = self.data_endpoint
452452
if not data_endpoint:
453453
cfg = Config.with_configs(config)
454-
data_endpoint = cfg._data_endpoint
454+
data_endpoint = cfg.get_data_endpoint()
455455
if not data_endpoint or not effective_name:
456456
return None
457457
return f"{data_endpoint}/tools/{effective_name}/download"

agentrun/tool/tool.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def _get_functioncall_server_url(
206206
data_endpoint = self.data_endpoint
207207
if not data_endpoint:
208208
cfg = Config.with_configs(config)
209-
data_endpoint = cfg._data_endpoint
209+
data_endpoint = cfg.get_data_endpoint()
210210
if not data_endpoint or not effective_name:
211211
return None
212212
return f"{data_endpoint}/tools/{effective_name}"
@@ -235,7 +235,7 @@ def _get_mcp_endpoint(
235235
data_endpoint = self.data_endpoint
236236
if not data_endpoint:
237237
cfg = Config.with_configs(config)
238-
data_endpoint = cfg._data_endpoint
238+
data_endpoint = cfg.get_data_endpoint()
239239
if not data_endpoint or not effective_name:
240240
return None
241241

@@ -611,7 +611,7 @@ def _get_skill_download_url(
611611
data_endpoint = self.data_endpoint
612612
if not data_endpoint:
613613
cfg = Config.with_configs(config)
614-
data_endpoint = cfg._data_endpoint
614+
data_endpoint = cfg.get_data_endpoint()
615615
if not data_endpoint or not effective_name:
616616
return None
617617
return f"{data_endpoint}/tools/{effective_name}/download"

tests/unittests/tool/test_tool.py

Lines changed: 104 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,20 @@ def test_get_mcp_endpoint_no_name(self):
139139
endpoint = tool._get_mcp_endpoint()
140140
assert endpoint is None
141141

142-
def test_get_mcp_endpoint_no_data_endpoint(self):
143-
"""测试没有 data_endpoint 时获取 MCP endpoint"""
142+
@patch("agentrun.tool.tool.Config")
143+
def test_get_mcp_endpoint_no_data_endpoint(self, mock_config_class):
144+
"""测试没有 data_endpoint 时从 Config.get_data_endpoint() 兜底"""
145+
mock_config = Mock()
146+
mock_config.get_data_endpoint.return_value = (
147+
"https://fallback.example.com"
148+
)
149+
mock_config_class.with_configs.return_value = mock_config
150+
144151
tool = Tool(
145152
tool_name="my-tool",
146153
)
147154
endpoint = tool._get_mcp_endpoint()
148-
assert endpoint is None
155+
assert endpoint == "https://fallback.example.com/tools/my-tool/sse"
149156

150157
def test_from_inner_object(self):
151158
"""测试从内部对象创建 Tool"""
@@ -244,8 +251,11 @@ def test_list_tools_mcp(self, mock_config_class, mock_mcp_session_class):
244251
assert tools[0].name == "tool1"
245252
assert tools[1].name == "tool2"
246253

254+
@patch("agentrun.tool.tool.Config")
247255
@patch("agentrun.tool.api.openapi.ToolOpenAPIClient")
248-
def test_list_tools_functioncall(self, mock_openapi_client_class):
256+
def test_list_tools_functioncall(
257+
self, mock_openapi_client_class, mock_config_class
258+
):
249259
"""测试获取 FUNCTIONCALL 工具列表"""
250260
mock_client = Mock()
251261
mock_client.list_tools.return_value = [
@@ -254,6 +264,13 @@ def test_list_tools_functioncall(self, mock_openapi_client_class):
254264
]
255265
mock_openapi_client_class.return_value = mock_client
256266

267+
mock_config = Mock()
268+
mock_config.get_data_endpoint.return_value = (
269+
"https://fallback.example.com"
270+
)
271+
mock_config.get_headers.return_value = {}
272+
mock_config_class.with_configs.return_value = mock_config
273+
257274
tool = Tool(
258275
tool_type="FUNCTIONCALL",
259276
protocol_spec='{"openapi": "3.0.0"}',
@@ -295,7 +312,7 @@ def test_call_tool_mcp(self, mock_config_class, mock_mcp_session_class):
295312
assert result == {"result": "success"}
296313

297314
@patch("agentrun.tool.api.openapi.ToolOpenAPIClient")
298-
@patch("agentrun.utils.config.Config")
315+
@patch("agentrun.tool.tool.Config")
299316
def test_call_tool_functioncall(
300317
self, mock_config_class, mock_openapi_client_class
301318
):
@@ -306,6 +323,11 @@ def test_call_tool_functioncall(
306323

307324
mock_config = Mock()
308325
mock_config.get_headers.return_value = {}
326+
mock_config.get_data_endpoint.return_value = (
327+
"https://fallback.example.com"
328+
)
329+
mock_config.get_access_key_id.return_value = ""
330+
mock_config.get_access_key_secret.return_value = ""
309331
mock_config_class.with_configs.return_value = mock_config
310332

311333
tool = Tool(
@@ -417,9 +439,11 @@ def test_get_skill_download_url_tool_name_takes_priority(self):
417439

418440
@patch("agentrun.tool.tool.Config")
419441
def test_get_skill_download_url_config_fallback(self, mock_config_class):
420-
"""测试 data_endpoint 为空时从 Config 获取"""
442+
"""测试 data_endpoint 为空时从 Config.get_data_endpoint() 获取"""
421443
mock_config = Mock()
422-
mock_config._data_endpoint = "https://config-endpoint.com"
444+
mock_config.get_data_endpoint.return_value = (
445+
"https://config-endpoint.com"
446+
)
423447
mock_config_class.with_configs.return_value = mock_config
424448

425449
tool = Tool(tool_name="my-skill")
@@ -434,9 +458,9 @@ def test_get_skill_download_url_no_name(self):
434458

435459
@patch("agentrun.tool.tool.Config")
436460
def test_get_skill_download_url_no_endpoint(self, mock_config_class):
437-
"""测试没有 data_endpoint 且 Config 也没有时返回 None"""
461+
"""测试没有 data_endpoint 且 Config.get_data_endpoint() 返回空时返回 None"""
438462
mock_config = Mock()
439-
mock_config._data_endpoint = None
463+
mock_config.get_data_endpoint.return_value = ""
440464
mock_config_class.with_configs.return_value = mock_config
441465

442466
tool = Tool(tool_name="my-skill")
@@ -558,8 +582,13 @@ async def test_download_skill_async_wrong_type(self):
558582
with pytest.raises(ValueError, match="only available for SKILL"):
559583
await tool.download_skill_async()
560584

561-
async def test_download_skill_async_no_url(self):
562-
"""测试无法构造下载 URL 时抛出 ValueError"""
585+
@patch("agentrun.tool.tool.Config")
586+
async def test_download_skill_async_no_url(self, mock_config_class):
587+
"""测试无法构造下载 URL 时抛出 ValueError(无 name 且 get_data_endpoint 返回空)"""
588+
mock_config = Mock()
589+
mock_config.get_data_endpoint.return_value = ""
590+
mock_config_class.with_configs.return_value = mock_config
591+
563592
tool = Tool(tool_type="SKILL")
564593

565594
with pytest.raises(ValueError, match="Cannot construct download URL"):
@@ -875,7 +904,7 @@ def test_call_tool_mcp_bundle_always_uses_ram(
875904
assert call_kwargs["use_ram_auth"] is True
876905

877906
@patch("agentrun.tool.api.openapi.ToolOpenAPIClient")
878-
@patch("agentrun.utils.config.Config")
907+
@patch("agentrun.tool.tool.Config")
879908
def test_call_tool_functioncall_openapi_import_skips_ram(
880909
self, mock_config_class, mock_openapi_client_class
881910
):
@@ -886,6 +915,11 @@ def test_call_tool_functioncall_openapi_import_skips_ram(
886915

887916
mock_config = Mock()
888917
mock_config.get_headers.return_value = {}
918+
mock_config.get_data_endpoint.return_value = (
919+
"https://fallback.example.com"
920+
)
921+
mock_config.get_access_key_id.return_value = ""
922+
mock_config.get_access_key_secret.return_value = ""
889923
mock_config_class.with_configs.return_value = mock_config
890924

891925
tool = Tool(
@@ -961,7 +995,7 @@ async def test_call_tool_async_mcp_remote_without_proxy_skips_ram(
961995
assert call_kwargs["use_ram_auth"] is False
962996

963997
@patch("agentrun.tool.api.openapi.ToolOpenAPIClient")
964-
@patch("agentrun.utils.config.Config")
998+
@patch("agentrun.tool.tool.Config")
965999
async def test_call_tool_async_functioncall_openapi_import_skips_ram(
9661000
self, mock_config_class, mock_openapi_client_class
9671001
):
@@ -972,6 +1006,11 @@ async def test_call_tool_async_functioncall_openapi_import_skips_ram(
9721006

9731007
mock_config = Mock()
9741008
mock_config.get_headers.return_value = {}
1009+
mock_config.get_data_endpoint.return_value = (
1010+
"https://fallback.example.com"
1011+
)
1012+
mock_config.get_access_key_id.return_value = ""
1013+
mock_config.get_access_key_secret.return_value = ""
9751014
mock_config_class.with_configs.return_value = mock_config
9761015

9771016
tool = Tool(
@@ -1244,25 +1283,37 @@ def test_get_functioncall_server_url(self):
12441283

12451284
assert url == "https://example.com/data/tools/my-tool"
12461285

1247-
def test_get_functioncall_server_url_no_endpoint(self):
1286+
@patch("agentrun.tool.tool.Config")
1287+
def test_get_functioncall_server_url_no_endpoint(self, mock_config_class):
12481288
"""测试 _get_functioncall_server_url 没有 data_endpoint 和 name 时返回 None"""
1289+
mock_config = Mock()
1290+
mock_config.get_data_endpoint.return_value = (
1291+
"https://fallback.example.com"
1292+
)
1293+
mock_config_class.with_configs.return_value = mock_config
1294+
12491295
tool = Tool()
12501296
url = tool._get_functioncall_server_url()
12511297

12521298
assert url is None
12531299

1254-
@patch("agentrun.utils.config.Config")
1300+
@patch("agentrun.tool.tool.Config")
12551301
async def test_list_tools_async_mcp_no_endpoint(self, mock_config_class):
1256-
"""测试 MCP 类型但没有 endpoint 时返回空列表"""
1302+
"""测试 MCP 类型但没有 endpoint 时,使用 Config.get_data_endpoint() 兜底"""
1303+
mock_config = Mock()
1304+
mock_config.get_data_endpoint.return_value = ""
1305+
mock_config_class.with_configs.return_value = mock_config
1306+
12571307
tool = Tool(tool_name="my-tool", tool_type="MCP")
12581308

12591309
tools = await tool.list_tools_async()
12601310

12611311
assert tools == []
12621312

1313+
@patch("agentrun.tool.tool.Config")
12631314
@patch("agentrun.tool.api.openapi.ToolOpenAPIClient")
12641315
async def test_list_tools_async_functioncall(
1265-
self, mock_openapi_client_class
1316+
self, mock_openapi_client_class, mock_config_class
12661317
):
12671318
"""测试 FUNCTIONCALL 类型的 list_tools_async"""
12681319
mock_client = Mock()
@@ -1274,6 +1325,13 @@ async def test_list_tools_async_functioncall(
12741325
)
12751326
mock_openapi_client_class.return_value = mock_client
12761327

1328+
mock_config = Mock()
1329+
mock_config.get_data_endpoint.return_value = (
1330+
"https://fallback.example.com"
1331+
)
1332+
mock_config.get_headers.return_value = {}
1333+
mock_config_class.with_configs.return_value = mock_config
1334+
12771335
tool = Tool(
12781336
tool_type="FUNCTIONCALL",
12791337
protocol_spec='{"openapi": "3.0.0"}',
@@ -1292,7 +1350,7 @@ async def test_list_tools_async_no_type(self):
12921350
assert tools == []
12931351

12941352
@patch("agentrun.tool.api.openapi.ToolOpenAPIClient")
1295-
@patch("agentrun.utils.config.Config")
1353+
@patch("agentrun.tool.tool.Config")
12961354
async def test_call_tool_async_functioncall(
12971355
self, mock_config_class, mock_openapi_client_class
12981356
):
@@ -1305,6 +1363,11 @@ async def test_call_tool_async_functioncall(
13051363

13061364
mock_config = Mock()
13071365
mock_config.get_headers.return_value = {}
1366+
mock_config.get_data_endpoint.return_value = (
1367+
"https://fallback.example.com"
1368+
)
1369+
mock_config.get_access_key_id.return_value = ""
1370+
mock_config.get_access_key_secret.return_value = ""
13081371
mock_config_class.with_configs.return_value = mock_config
13091372

13101373
tool = Tool(
@@ -1316,15 +1379,20 @@ async def test_call_tool_async_functioncall(
13161379

13171380
assert result == {"result": "success"}
13181381

1319-
async def test_call_tool_async_mcp_no_endpoint(self):
1382+
@patch("agentrun.tool.tool.Config")
1383+
async def test_call_tool_async_mcp_no_endpoint(self, mock_config_class):
13201384
"""测试 MCP 类型但没有 endpoint 时 call_tool_async 抛出 ValueError"""
1385+
mock_config = Mock()
1386+
mock_config.get_data_endpoint.return_value = ""
1387+
mock_config_class.with_configs.return_value = mock_config
1388+
13211389
tool = Tool(tool_name="my-tool", tool_type="MCP")
13221390

13231391
with pytest.raises(ValueError, match="MCP endpoint not available"):
13241392
await tool.call_tool_async("tool1", {"param": "value"})
13251393

13261394
@patch("agentrun.tool.api.openapi.ToolOpenAPIClient")
1327-
@patch("agentrun.utils.config.Config")
1395+
@patch("agentrun.tool.tool.Config")
13281396
def test_call_tool_functioncall(
13291397
self, mock_config_class, mock_openapi_client_class
13301398
):
@@ -1335,6 +1403,11 @@ def test_call_tool_functioncall(
13351403

13361404
mock_config = Mock()
13371405
mock_config.get_headers.return_value = {}
1406+
mock_config.get_data_endpoint.return_value = (
1407+
"https://fallback.example.com"
1408+
)
1409+
mock_config.get_access_key_id.return_value = ""
1410+
mock_config.get_access_key_secret.return_value = ""
13381411
mock_config_class.with_configs.return_value = mock_config
13391412

13401413
tool = Tool(
@@ -1346,16 +1419,25 @@ def test_call_tool_functioncall(
13461419

13471420
assert result == {"result": "success"}
13481421

1349-
def test_call_tool_mcp_no_endpoint(self):
1422+
@patch("agentrun.tool.tool.Config")
1423+
def test_call_tool_mcp_no_endpoint(self, mock_config_class):
13501424
"""测试 MCP 类型但没有 endpoint 时 call_tool 抛出 ValueError"""
1425+
mock_config = Mock()
1426+
mock_config.get_data_endpoint.return_value = ""
1427+
mock_config_class.with_configs.return_value = mock_config
1428+
13511429
tool = Tool(tool_name="my-tool", tool_type="MCP")
13521430

13531431
with pytest.raises(ValueError, match="MCP endpoint not available"):
13541432
tool.call_tool("tool1", {"param": "value"})
13551433

1356-
@patch("agentrun.utils.config.Config")
1434+
@patch("agentrun.tool.tool.Config")
13571435
def test_list_tools_mcp_no_endpoint(self, mock_config_class):
13581436
"""测试 MCP 类型但没有 endpoint 时 list_tools 返回空列表"""
1437+
mock_config = Mock()
1438+
mock_config.get_data_endpoint.return_value = ""
1439+
mock_config_class.with_configs.return_value = mock_config
1440+
13591441
tool = Tool(tool_name="my-tool", tool_type="MCP")
13601442

13611443
tools = tool.list_tools()

0 commit comments

Comments
 (0)