@@ -131,6 +131,60 @@ async def test_load_toolset_success(aioresponses, test_tool_str, test_tool_int_b
131131 assert {t .__name__ for t in tools } == manifest .tools .keys ()
132132
133133
134+ @pytest .mark .asyncio
135+ async def test_invoke_tool_server_error (aioresponses , test_tool_str ):
136+ """Tests that invoking a tool raises an Exception when the server returns an
137+ error status."""
138+ TOOL_NAME = "server_error_tool"
139+ ERROR_MESSAGE = "Simulated Server Error"
140+ manifest = ManifestSchema (serverVersion = "0.0.0" , tools = {TOOL_NAME : test_tool_str })
141+
142+ aioresponses .get (
143+ f"{ TEST_BASE_URL } /api/tool/{ TOOL_NAME } " ,
144+ payload = manifest .model_dump (),
145+ status = 200 ,
146+ )
147+ aioresponses .post (
148+ f"{ TEST_BASE_URL } /api/tool/{ TOOL_NAME } /invoke" ,
149+ payload = {"error" : ERROR_MESSAGE },
150+ status = 500 ,
151+ )
152+
153+ async with ToolboxClient (TEST_BASE_URL ) as client :
154+ loaded_tool = await client .load_tool (TOOL_NAME )
155+
156+ with pytest .raises (Exception , match = ERROR_MESSAGE ):
157+ await loaded_tool (param1 = "some input" )
158+
159+
160+ @pytest .mark .asyncio
161+ async def test_load_tool_not_found_in_manifest (aioresponses , test_tool_str ):
162+ """
163+ Tests that load_tool raises an Exception when the requested tool name
164+ is not found in the manifest returned by the server, using existing fixtures.
165+ """
166+ ACTUAL_TOOL_IN_MANIFEST = "actual_tool_abc"
167+ REQUESTED_TOOL_NAME = "non_existent_tool_xyz"
168+
169+ manifest = ManifestSchema (
170+ serverVersion = "0.0.0" , tools = {ACTUAL_TOOL_IN_MANIFEST : test_tool_str }
171+ )
172+
173+ aioresponses .get (
174+ f"{ TEST_BASE_URL } /api/tool/{ REQUESTED_TOOL_NAME } " ,
175+ payload = manifest .model_dump (),
176+ status = 200 ,
177+ )
178+
179+ async with ToolboxClient (TEST_BASE_URL ) as client :
180+ with pytest .raises (Exception , match = f"Tool '{ REQUESTED_TOOL_NAME } ' not found!" ):
181+ await client .load_tool (REQUESTED_TOOL_NAME )
182+
183+ aioresponses .assert_called_once_with (
184+ f"{ TEST_BASE_URL } /api/tool/{ REQUESTED_TOOL_NAME } " , method = "GET"
185+ )
186+
187+
134188class TestAuth :
135189
136190 @pytest .fixture
@@ -183,7 +237,7 @@ def token_handler():
183237 tool = await client .load_tool (
184238 tool_name , auth_token_getters = {"my-auth-service" : token_handler }
185239 )
186- res = await tool (5 )
240+ await tool (5 )
187241
188242 @pytest .mark .asyncio
189243 async def test_auth_with_add_token_success (
@@ -196,20 +250,35 @@ def token_handler():
196250
197251 tool = await client .load_tool (tool_name )
198252 tool = tool .add_auth_token_getters ({"my-auth-service" : token_handler })
199- res = await tool (5 )
253+ await tool (5 )
200254
201255 @pytest .mark .asyncio
202256 async def test_auth_with_load_tool_fail_no_token (
203257 self , tool_name , expected_header , client
204258 ):
205259 """Tests 'load_tool' with auth token is specified."""
206260
207- def token_handler ():
208- return expected_header
209-
210261 tool = await client .load_tool (tool_name )
211262 with pytest .raises (Exception ):
212- res = await tool (5 )
263+ await tool (5 )
264+
265+ @pytest .mark .asyncio
266+ async def test_add_auth_token_getters_duplicate_fail (self , tool_name , client ):
267+ """
268+ Tests that adding a duplicate auth token getter raises ValueError.
269+ """
270+ AUTH_SERVICE = "my-auth-service"
271+
272+ tool = await client .load_tool (tool_name )
273+
274+ authed_tool = tool .add_auth_token_getters ({AUTH_SERVICE : {}})
275+ assert AUTH_SERVICE in authed_tool ._ToolboxTool__auth_service_token_getters
276+
277+ with pytest .raises (
278+ ValueError ,
279+ match = f"Authentication source\\ (s\\ ) `{ AUTH_SERVICE } ` already registered in tool `{ tool_name } `." ,
280+ ):
281+ authed_tool .add_auth_token_getters ({AUTH_SERVICE : {}})
213282
214283
215284class TestBoundParameter :
@@ -319,62 +388,15 @@ async def test_bind_param_fail(self, tool_name, client):
319388 with pytest .raises (Exception ):
320389 tool = tool .bind_parameters ({"argC" : lambda : 5 })
321390
391+ @pytest .mark .asyncio
392+ async def test_bind_param_static_value_success (self , tool_name , client ):
393+ """
394+ Tests bind_parameters method with a static value.
395+ """
322396
323- @pytest .mark .asyncio
324- async def test_invoke_tool_server_error (aioresponses , test_tool_str ):
325- """Tests that invoking a tool raises an Exception when the server returns an
326- error status."""
327- TOOL_NAME = "server_error_tool"
328- ERROR_MESSAGE = "Simulated Server Error"
329- manifest = ManifestSchema (serverVersion = "0.0.0" , tools = {TOOL_NAME : test_tool_str })
330-
331- aioresponses .get (
332- f"{ TEST_BASE_URL } /api/tool/{ TOOL_NAME } " ,
333- payload = manifest .model_dump (),
334- status = 200 ,
335- )
336- aioresponses .post (
337- f"{ TEST_BASE_URL } /api/tool/{ TOOL_NAME } /invoke" ,
338- payload = {"error" : ERROR_MESSAGE },
339- status = 500 ,
340- )
341-
342- async with ToolboxClient (TEST_BASE_URL ) as client :
343- loaded_tool = await client .load_tool (TOOL_NAME )
344-
345- with pytest .raises (Exception , match = ERROR_MESSAGE ):
346- await loaded_tool (param1 = "some input" )
347-
348-
349- @pytest .mark .asyncio
350- async def test_bind_param_static_value_success (aioresponses , test_tool_int_bool ):
351- """
352- Tests bind_parameters method with a static value.
353- """
354- TOOL_NAME = "async_bind_tool"
355- manifest = ManifestSchema (
356- serverVersion = "0.0.0" , tools = {TOOL_NAME : test_tool_int_bool }
357- )
358-
359- aioresponses .get (
360- f"{ TEST_BASE_URL } /api/tool/{ TOOL_NAME } " ,
361- payload = manifest .model_dump (),
362- status = 200 ,
363- )
364-
365- def reflect_parameters (url , ** kwargs ):
366- received_params = kwargs .get ("json" , {})
367- return CallbackResult (status = 200 , payload = {"result" : received_params })
368-
369- aioresponses .post (
370- f"{ TEST_BASE_URL } /api/tool/{ TOOL_NAME } /invoke" ,
371- callback = reflect_parameters ,
372- )
373-
374- bound_value = "Test value"
397+ bound_value = "Test value"
375398
376- async with ToolboxClient (TEST_BASE_URL ) as client :
377- tool = await client .load_tool (TOOL_NAME )
399+ tool = await client .load_tool (tool_name )
378400 bound_tool = tool .bind_parameters ({"argB" : bound_value })
379401
380402 assert bound_tool is not tool
@@ -386,37 +408,16 @@ def reflect_parameters(url, **kwargs):
386408
387409 assert res_payload == {"argA" : passed_value_a , "argB" : bound_value }
388410
411+ @pytest .mark .asyncio
412+ async def test_bind_param_sync_callable_value_success (self , tool_name , client ):
413+ """
414+ Tests bind_parameters method with a sync callable value.
415+ """
389416
390- @pytest .mark .asyncio
391- async def test_bind_param_sync_callable_value_success (aioresponses , test_tool_int_bool ):
392- """
393- Tests bind_parameters method with a sync callable value.
394- """
395- TOOL_NAME = "async_bind_tool"
396- manifest = ManifestSchema (
397- serverVersion = "0.0.0" , tools = {TOOL_NAME : test_tool_int_bool }
398- )
399-
400- aioresponses .get (
401- f"{ TEST_BASE_URL } /api/tool/{ TOOL_NAME } " ,
402- payload = manifest .model_dump (),
403- status = 200 ,
404- )
405-
406- def reflect_parameters (url , ** kwargs ):
407- received_params = kwargs .get ("json" , {})
408- return CallbackResult (status = 200 , payload = {"result" : received_params })
409-
410- aioresponses .post (
411- f"{ TEST_BASE_URL } /api/tool/{ TOOL_NAME } /invoke" ,
412- callback = reflect_parameters ,
413- )
414-
415- bound_value_result = True
416- bound_sync_callable = Mock (return_value = bound_value_result )
417+ bound_value_result = True
418+ bound_sync_callable = Mock (return_value = bound_value_result )
417419
418- async with ToolboxClient (TEST_BASE_URL ) as client :
419- tool = await client .load_tool (TOOL_NAME )
420+ tool = await client .load_tool (tool_name )
420421 bound_tool = tool .bind_parameters ({"argB" : bound_sync_callable })
421422
422423 assert bound_tool is not tool
@@ -429,39 +430,16 @@ def reflect_parameters(url, **kwargs):
429430 assert res_payload == {"argA" : passed_value_a , "argB" : bound_value_result }
430431 bound_sync_callable .assert_called_once ()
431432
433+ @pytest .mark .asyncio
434+ async def test_bind_param_async_callable_value_success (self , tool_name , client ):
435+ """
436+ Tests bind_parameters method with an async callable value.
437+ """
432438
433- @pytest .mark .asyncio
434- async def test_bind_param_async_callable_value_success (
435- aioresponses , test_tool_int_bool
436- ):
437- """
438- Tests bind_parameters method with an async callable value.
439- """
440- TOOL_NAME = "async_bind_tool"
441- manifest = ManifestSchema (
442- serverVersion = "0.0.0" , tools = {TOOL_NAME : test_tool_int_bool }
443- )
444-
445- aioresponses .get (
446- f"{ TEST_BASE_URL } /api/tool/{ TOOL_NAME } " ,
447- payload = manifest .model_dump (),
448- status = 200 ,
449- )
450-
451- def reflect_parameters (url , ** kwargs ):
452- received_params = kwargs .get ("json" , {})
453- return CallbackResult (status = 200 , payload = {"result" : received_params })
454-
455- aioresponses .post (
456- f"{ TEST_BASE_URL } /api/tool/{ TOOL_NAME } /invoke" ,
457- callback = reflect_parameters ,
458- )
459-
460- bound_value_result = True
461- bound_async_callable = AsyncMock (return_value = bound_value_result )
439+ bound_value_result = True
440+ bound_async_callable = AsyncMock (return_value = bound_value_result )
462441
463- async with ToolboxClient (TEST_BASE_URL ) as client :
464- tool = await client .load_tool (TOOL_NAME )
442+ tool = await client .load_tool (tool_name )
465443 bound_tool = tool .bind_parameters ({"argB" : bound_async_callable })
466444
467445 assert bound_tool is not tool
@@ -473,59 +451,3 @@ def reflect_parameters(url, **kwargs):
473451
474452 assert res_payload == {"argA" : passed_value_a , "argB" : bound_value_result }
475453 bound_async_callable .assert_awaited_once ()
476-
477-
478- @pytest .mark .asyncio
479- async def test_add_auth_token_getters_duplicate_fail (aioresponses , test_tool_auth ):
480- """
481- Tests that adding a duplicate auth token getter raises ValueError.
482- """
483- TOOL_NAME = "duplicate_auth_tool"
484- AUTH_SERVICE = "my-auth-service"
485- manifest = ManifestSchema (serverVersion = "0.0.0" , tools = {TOOL_NAME : test_tool_auth })
486-
487- aioresponses .get (
488- f"{ TEST_BASE_URL } /api/tool/{ TOOL_NAME } " ,
489- payload = manifest .model_dump (),
490- status = 200 ,
491- )
492-
493- async with ToolboxClient (TEST_BASE_URL ) as client :
494- tool = await client .load_tool (TOOL_NAME )
495-
496- authed_tool = tool .add_auth_token_getters ({AUTH_SERVICE : {}})
497- assert AUTH_SERVICE in authed_tool ._ToolboxTool__auth_service_token_getters
498-
499- with pytest .raises (
500- ValueError ,
501- match = f"Authentication source\\ (s\\ ) `{ AUTH_SERVICE } ` already registered in tool `{ TOOL_NAME } `." ,
502- ):
503- authed_tool .add_auth_token_getters ({AUTH_SERVICE : {}})
504-
505-
506- @pytest .mark .asyncio
507- async def test_load_tool_not_found_in_manifest (aioresponses , test_tool_str ):
508- """
509- Tests that load_tool raises an Exception when the requested tool name
510- is not found in the manifest returned by the server, using existing fixtures.
511- """
512- ACTUAL_TOOL_IN_MANIFEST = "actual_tool_abc"
513- REQUESTED_TOOL_NAME = "non_existent_tool_xyz"
514-
515- manifest = ManifestSchema (
516- serverVersion = "0.0.0" , tools = {ACTUAL_TOOL_IN_MANIFEST : test_tool_str }
517- )
518-
519- aioresponses .get (
520- f"{ TEST_BASE_URL } /api/tool/{ REQUESTED_TOOL_NAME } " ,
521- payload = manifest .model_dump (),
522- status = 200 ,
523- )
524-
525- async with ToolboxClient (TEST_BASE_URL ) as client :
526- with pytest .raises (Exception , match = f"Tool '{ REQUESTED_TOOL_NAME } ' not found!" ):
527- await client .load_tool (REQUESTED_TOOL_NAME )
528-
529- aioresponses .assert_called_once_with (
530- f"{ TEST_BASE_URL } /api/tool/{ REQUESTED_TOOL_NAME } " , method = "GET"
531- )
0 commit comments