@@ -359,3 +359,97 @@ def mock_factory(agent_id=None):
359359 assert calls [1 ] == "test-agent-123"
360360 # Third call retrieves by ID
361361 assert calls [2 ] == "test-agent-123"
362+
363+
364+ @pytest .mark .asyncio
365+ @patch .dict (
366+ "os.environ" ,
367+ {
368+ "BING_SUBSCRIPTION_KEY" : "test_api_key" ,
369+ "AZURE_OPENAI_ENDPOINT" : "https://test.openai.azure.com" ,
370+ "AZURE_OPENAI_API_KEY" : "test_key" ,
371+ },
372+ clear = False ,
373+ )
374+ async def test_web_search_full_lifecycle ():
375+ """
376+ Integration test verifying complete agent lifecycle.
377+
378+ Tests:
379+ - Agent created on first call with proper context manager cleanup
380+ - Agent ID stored at module level
381+ - Subsequent calls retrieve agent by ID with new context managers
382+ - Each context manager properly cleaned up (enters/exits balanced)
383+ """
384+ import spec_to_agents .tools .bing_search as bing_search_module
385+ from spec_to_agents .tools .bing_search import web_search
386+
387+ # Verify starting with no agent ID
388+ assert bing_search_module ._web_search_agent_id is None
389+
390+ with (
391+ patch ("spec_to_agents.tools.bing_search.create_agent_client" ) as mock_client_factory ,
392+ patch ("spec_to_agents.tools.bing_search.HostedWebSearchTool" ),
393+ ):
394+ mock_client = Mock ()
395+ mock_agent = Mock ()
396+
397+ # Track context manager calls to verify cleanup
398+ enter_count = 0
399+ exit_count = 0
400+
401+ async def mock_enter (self ):
402+ nonlocal enter_count
403+ enter_count += 1
404+ return mock_client
405+
406+ async def mock_exit (self , * args ):
407+ nonlocal exit_count
408+ exit_count += 1
409+ return
410+
411+ mock_client .__aenter__ = mock_enter
412+ mock_client .__aexit__ = mock_exit
413+
414+ # Mock agent with ID
415+ mock_agent .id = "persistent-agent-456"
416+
417+ response1 = Mock ()
418+ response1 .text = "First result"
419+ response2 = Mock ()
420+ response2 .text = "Second result"
421+ response3 = Mock ()
422+ response3 .text = "Third result"
423+
424+ mock_agent .run = AsyncMock (side_effect = [response1 , response2 , response3 ])
425+ mock_client .create_agent .return_value = mock_agent
426+ mock_client .run = AsyncMock (side_effect = [response1 , response2 , response3 ])
427+ mock_client_factory .return_value = mock_client
428+
429+ # First call - creates agent
430+ result1 = await web_search ("query 1" )
431+ assert result1 == "First result"
432+ assert bing_search_module ._web_search_agent_id == "persistent-agent-456"
433+ # One context manager used for creation, one for run
434+ assert enter_count == 2 # Once for creation, once for run
435+ assert exit_count == 2
436+
437+ # Second call - retrieves by ID
438+ result2 = await web_search ("query 2" )
439+ assert result2 == "Second result"
440+ # Still same agent ID
441+ assert bing_search_module ._web_search_agent_id == "persistent-agent-456"
442+ # Additional context manager for retrieval
443+ assert enter_count == 3 # One more for run
444+ assert exit_count == 3
445+
446+ # Third call - retrieves by ID again
447+ result3 = await web_search ("query 3" )
448+ assert result3 == "Third result"
449+ assert bing_search_module ._web_search_agent_id == "persistent-agent-456"
450+ # Another context manager for retrieval
451+ assert enter_count == 4
452+ assert exit_count == 4
453+
454+ # Verify context managers are balanced (proper cleanup)
455+ assert enter_count == exit_count
0 commit comments