33import os
44from unittest .mock import patch , MagicMock , AsyncMock
55import json
6+ import asyncio
7+ import importlib
68
79# Ensure src/ is in sys.path for imports
810sys .path .insert (0 , os .path .abspath (os .path .join (os .path .dirname (__file__ ), '../src' )))
1113@pytest .mark .asyncio
1214async def test_process_tool_calls_empty_tool_name ():
1315 """Test process_tool_calls with an empty tool name."""
16+ # Import agent inside test to avoid importing it with the graceful_exit decorator
17+ # already active on methods
1418 import agent
1519
1620 # Create a response with empty tool name
@@ -40,52 +44,60 @@ async def test_process_tool_calls_empty_tool_name():
4044@pytest .mark .asyncio
4145async def test_process_tool_calls_tool_error ():
4246 """Test process_tool_calls with a tool that raises an error."""
43- import agent
44-
45- # Create a mock tool that raises an exception
46- mock_tool = MagicMock ()
47- mock_tool .run = AsyncMock (side_effect = ValueError ("Tool execution failed" ))
48-
49- # Create a response using the mock tool
50- response = {
51- "tool_calls" : [
52- {
53- "id" : "call_123" ,
54- "function" : {
55- "name" : "error_tool" ,
56- "arguments" : '{"param": "value"}'
57- }
58- }
59- ]
60- }
61-
62- # Mock callback function
63- callback_mock = MagicMock ()
47+ # First patch the graceful_exit decorator before any imports
48+ identity_decorator = lambda f : f
6449
65- # Save original tool_map and restore it later
66- original_tool_map = agent .tool_map
67- try :
68- # Set up our mock tool
69- agent .tool_map = {"error_tool" : mock_tool }
50+ with patch ('src.utils.graceful_exit' , identity_decorator ):
51+ # Now we can safely import agent, as it won't use the problematic decorator
52+ import agent
7053
71- # Process calls with the error-raising tool
72- with patch ('builtins.print' ): # Suppress print output
73- await agent .process_tool_calls (response , callback_mock )
54+ # Make sure we reimport agent to avoid cached module
55+ importlib .reload (agent )
7456
75- # Verify the callback was called with an error response
76- callback_mock .assert_called_once ()
77- call_args = callback_mock .call_args [0 ][0 ]
78- assert call_args ["role" ] == "tool"
79- assert call_args ["tool_call_id" ] == "call_123"
57+ # Create a mock tool with an async mock that will raise an error
58+ mock_tool = MagicMock ()
59+ mock_tool .run = AsyncMock (side_effect = ValueError ("Tool execution failed" ))
8060
81- # Parse the content to verify the error message
82- content = json .loads (call_args ["content" ])
83- assert "error" in content
84- assert "Tool execution failed" in content ["error" ]
61+ # Create a response using the mock tool
62+ response = {
63+ "tool_calls" : [
64+ {
65+ "id" : "call_123" ,
66+ "function" : {
67+ "name" : "error_tool" ,
68+ "arguments" : '{"param": "value"}'
69+ }
70+ }
71+ ]
72+ }
8573
86- finally :
87- # Restore original tool_map
88- agent .tool_map = original_tool_map
74+ # Mock callback function
75+ callback_mock = MagicMock ()
76+
77+ # Save original tool_map and restore it later
78+ original_tool_map = agent .tool_map .copy () if hasattr (agent , 'tool_map' ) else {}
79+ try :
80+ # Set up our mock tool
81+ agent .tool_map = {"error_tool" : mock_tool }
82+
83+ # Process calls with the error-raising tool
84+ with patch ('builtins.print' ): # Suppress print output
85+ await agent .process_tool_calls (response , callback_mock )
86+
87+ # Verify the callback was called with an error response
88+ callback_mock .assert_called_once ()
89+ call_args = callback_mock .call_args [0 ][0 ]
90+ assert call_args ["role" ] == "tool"
91+ assert call_args ["tool_call_id" ] == "call_123"
92+
93+ # Parse the content to verify the error message
94+ content = json .loads (call_args ["content" ])
95+ assert "error" in content
96+ assert "Tool execution failed" in content ["error" ]
97+
98+ finally :
99+ # Restore original tool_map
100+ agent .tool_map = original_tool_map
89101
90102
91103@pytest .mark .asyncio
@@ -97,10 +109,10 @@ async def test_run_conversation_empty_choices():
97109 # Create a test function that simulates the run_conversation without chatutil decorator
98110 async def test_run_conv (prompt ):
99111 # Save original messages and chat
100- original_messages = agent .messages .copy ()
112+ original_messages = agent .messages .copy () if hasattr ( agent , 'messages' ) else []
101113 original_chat = agent .chat
102-
103- # Mock objects for testing
114+
115+ # Mock objects for testing using proper async function
104116 mock_chat = MagicMock ()
105117 mock_chat .send_messages = AsyncMock (return_value = {"choices" : []})
106118
@@ -154,17 +166,26 @@ async def test_process_tool_calls_invalid_json():
154166 # Mock callback function
155167 callback_mock = MagicMock ()
156168
157- # Process calls with invalid JSON should handle the error gracefully
158- with patch ('builtins.print' ): # Suppress print output
159- await agent .process_tool_calls (response , callback_mock )
160-
161- # Verify the callback was called with the appropriate tool response
162- callback_mock .assert_called_once ()
163- call_args = callback_mock .call_args [0 ][0 ]
164- assert call_args ["role" ] == "tool"
165- assert call_args ["tool_call_id" ] == "call_123"
166-
167- # The content should contain an error message about the tool not being found
168- content = json .loads (call_args ["content" ])
169- assert "error" in content
170- assert "not found" in content ["error" ]
169+ # Save original tool_map and restore it later
170+ original_tool_map = agent .tool_map .copy () if hasattr (agent , 'tool_map' ) else {}
171+ try :
172+ # Make sure tool_map doesn't have the tool to trigger specific error path
173+ agent .tool_map = {}
174+
175+ # Process calls with invalid JSON should handle the error gracefully
176+ with patch ('builtins.print' ): # Suppress print output
177+ await agent .process_tool_calls (response , callback_mock )
178+
179+ # Verify the callback was called with the appropriate tool response
180+ callback_mock .assert_called_once ()
181+ call_args = callback_mock .call_args [0 ][0 ]
182+ assert call_args ["role" ] == "tool"
183+ assert call_args ["tool_call_id" ] == "call_123"
184+
185+ # The content should contain an error message about the tool not being found
186+ content = json .loads (call_args ["content" ])
187+ assert "error" in content
188+ assert "not found" in content ["error" ]
189+ finally :
190+ # Restore original tool_map
191+ agent .tool_map = original_tool_map
0 commit comments