Skip to content

Commit 9bd6f91

Browse files
Merge pull request #862 from microsoft/psl-latestversinov2
test: updated the unit testcases to update the latest agent framework version
2 parents 649ac4f + aae6a2a commit 9bd6f91

7 files changed

Lines changed: 245 additions & 143 deletions

File tree

src/backend/requirements.txt

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,42 @@
1-
fastapi
2-
uvicorn
3-
autogen-agentchat==0.7.5
4-
azure-cosmos
5-
azure-monitor-opentelemetry
6-
azure-monitor-events-extension
7-
azure-identity
8-
python-dotenv
9-
python-multipart
10-
opentelemetry-api
11-
opentelemetry-sdk
12-
opentelemetry-exporter-otlp-proto-grpc
13-
opentelemetry-instrumentation-fastapi
14-
opentelemetry-instrumentation-openai
15-
opentelemetry-exporter-otlp-proto-http
1+
fastapi==0.116.1
2+
uvicorn==0.35.0
3+
azure-cosmos==4.9.0
4+
azure-monitor-opentelemetry==1.8.5
5+
azure-monitor-events-extension==0.1.0
6+
azure-identity==1.24.0
7+
python-dotenv==1.1.1
8+
python-multipart==0.0.22
9+
opentelemetry-api==1.39.0
10+
opentelemetry-sdk==1.39.0
11+
opentelemetry-exporter-otlp-proto-grpc==1.39.0
12+
opentelemetry-exporter-otlp-proto-http==1.39.0
13+
opentelemetry-instrumentation-fastapi==0.60b0
14+
opentelemetry-instrumentation-openai==0.46.2
1615

17-
semantic-kernel[azure]==1.39.4
18-
azure-ai-projects==1.0.0
19-
openai==1.105.0
20-
azure-ai-inference==1.0.0b9
21-
azure-search-documents
22-
azure-ai-evaluation
16+
azure-ai-projects==2.0.0
17+
openai==2.16.0
18+
azure-ai-inference==1.0.0b9
19+
azure-search-documents==11.5.3
20+
azure-ai-evaluation==1.11.0
21+
azure-core==1.38.0
2322

24-
opentelemetry-exporter-otlp-proto-grpc
23+
agent-framework-azure-ai==1.0.0rc4
24+
agent-framework-core==1.0.0rc4
25+
agent-framework-orchestrations==1.0.0b260311
2526

26-
# Date and internationalization
27-
babel>=2.9.0
27+
mcp==1.26.0
28+
werkzeug==3.1.5
29+
pylint-pydantic==0.3.5
30+
pexpect==4.9.0
31+
urllib3==2.6.3
32+
protobuf==5.29.6
33+
cryptography==46.0.5
34+
aiohttp==3.13.3
35+
pyasn1==0.6.2
36+
nltk==3.9.3
2837

2938
# Testing tools
30-
pytest>=8.2,<9 # Compatible version for pytest-asyncio
39+
pytest==8.4.1
3140
pytest-asyncio==0.24.0
3241
pytest-cov==5.0.0
3342

src/tests/backend/conftest.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,26 @@ def _setup_agent_framework_mock():
5151
# Names used as base classes or in Union type hints MUST be real classes
5252
# to avoid SyntaxError from typing module's forward reference evaluation.
5353
_class_names = [
54-
'AgentResponse', 'AgentResponseUpdate', 'AgentRunUpdateEvent',
55-
'AgentThread', 'BaseAgent', 'ChatAgent', 'ChatMessage',
54+
'Agent', 'AgentResponse', 'AgentResponseUpdate', 'AgentRunUpdateEvent',
55+
'AgentSession', 'AgentThread', 'BaseAgent', 'ChatAgent', 'ChatMessage',
5656
'ChatOptions', 'Content', 'ExecutorCompletedEvent',
5757
'GroupChatRequestSentEvent', 'GroupChatResponseReceivedEvent',
5858
'HostedCodeInterpreterTool', 'HostedMCPTool',
5959
'InMemoryCheckpointStorage', 'MCPStreamableHTTPTool',
6060
'MagenticBuilder', 'MagenticOrchestratorEvent',
61-
'MagenticProgressLedger', 'Role', 'UsageDetails',
61+
'MagenticProgressLedger', 'Message', 'Role', 'UsageDetails',
6262
'WorkflowOutputEvent',
6363
]
6464
for name in _class_names:
65-
setattr(mock_af, name, type(name, (), {}))
65+
setattr(mock_af, name, type(name, (), {
66+
'__init__': lambda self, *args, **kwargs: None,
67+
}))
68+
69+
# Sub-module: agent_framework._types
70+
mock_af_types = ModuleType('agent_framework._types')
71+
mock_af_types.ResponseStream = type('ResponseStream', (), {})
72+
mock_af._types = mock_af_types
73+
sys.modules['agent_framework._types'] = mock_af_types
6674

6775
# Sub-module: agent_framework.azure
6876
mock_af_azure = ModuleType('agent_framework.azure')
@@ -91,6 +99,37 @@ def _setup_agent_framework_mock():
9199
sys.modules['agent_framework._workflows'] = mock_af_workflows
92100
sys.modules['agent_framework._workflows._magentic'] = mock_af_magentic
93101

102+
if 'agent_framework_orchestrations' not in sys.modules:
103+
mock_af_orch = ModuleType('agent_framework_orchestrations')
104+
mock_af_orch.MagenticBuilder = type('MagenticBuilder', (), {
105+
'__init__': lambda self, *args, **kwargs: None,
106+
'build': lambda self: Mock(),
107+
})
108+
sys.modules['agent_framework_orchestrations'] = mock_af_orch
109+
110+
mock_af_orch_base = ModuleType('agent_framework_orchestrations._base_group_chat_orchestrator')
111+
for name in ['GroupChatRequestSentEvent', 'GroupChatResponseReceivedEvent']:
112+
setattr(mock_af_orch_base, name, type(name, (), {}))
113+
sys.modules['agent_framework_orchestrations._base_group_chat_orchestrator'] = mock_af_orch_base
114+
115+
mock_af_orch_mag = ModuleType('agent_framework_orchestrations._magentic')
116+
for name in ['MagenticContext', 'MagenticProgressLedger']:
117+
setattr(mock_af_orch_mag, name, type(name, (), {}))
118+
# StandardMagenticManager needs a proper __init__ that accepts args/kwargs
119+
# because HumanApprovalMagenticManager calls super().__init__(agent, *args, **kwargs)
120+
setattr(mock_af_orch_mag, 'StandardMagenticManager',
121+
type('StandardMagenticManager', (), {
122+
'__init__': lambda self, *args, **kwargs: None
123+
}))
124+
for name in [
125+
'ORCHESTRATOR_FINAL_ANSWER_PROMPT',
126+
'ORCHESTRATOR_PROGRESS_LEDGER_PROMPT',
127+
'ORCHESTRATOR_TASK_LEDGER_PLAN_PROMPT',
128+
'ORCHESTRATOR_TASK_LEDGER_PLAN_UPDATE_PROMPT',
129+
]:
130+
setattr(mock_af_orch_mag, name, 'mock_prompt_string')
131+
sys.modules['agent_framework_orchestrations._magentic'] = mock_af_orch_mag
132+
94133
if 'agent_framework_azure_ai' not in sys.modules:
95134
mock_af_ai = ModuleType('agent_framework_azure_ai')
96135
mock_af_ai.AzureAIClient = type('AzureAIClient', (), {})

src/tests/backend/v4/callbacks/test_response_handlers.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,19 @@ def __init__(self):
6161
self.author_name = "TestAgent"
6262
self.role = "assistant"
6363

64+
class MockMessage:
65+
"""Mock Message class for isinstance checks."""
66+
def __init__(self, text="", role="assistant", author_name=""):
67+
self.text = text
68+
self.author_name = author_name
69+
self.role = role
70+
6471
mock_chat_message = MockChatMessage
6572
mock_agent_response_update = Mock()
6673
mock_agent_response_update.text = "Sample update text"
6774
mock_agent_response_update.contents = []
6875

69-
sys.modules['agent_framework'] = Mock(ChatMessage=mock_chat_message)
76+
sys.modules['agent_framework'] = Mock(ChatMessage=mock_chat_message, Message=MockMessage)
7077
sys.modules['agent_framework._workflows'] = Mock()
7178
sys.modules['agent_framework._workflows._magentic'] = Mock(AgentRunResponseUpdate=mock_agent_response_update)
7279
sys.modules['agent_framework.azure'] = Mock(AzureOpenAIChatClient=Mock())
@@ -388,14 +395,15 @@ def test_agent_response_callback_no_user_id(self):
388395
@patch('backend.v4.callbacks.response_handlers.asyncio.create_task')
389396
@patch('backend.v4.callbacks.response_handlers.time.time')
390397
def test_agent_response_callback_with_chat_message(self, mock_time, mock_create_task):
391-
"""Test agent_response_callback with ChatMessage object."""
398+
"""Test agent_response_callback with Message object."""
392399
mock_time.return_value = 1234567890.0
393400

394-
# Create an instance of our MockChatMessage
395-
mock_message = MockChatMessage()
396-
mock_message.text = "Test message with citations [1:2|source]"
397-
mock_message.author_name = "TestAgent"
398-
mock_message.role = "assistant"
401+
# Create an instance of our MockMessage (source checks isinstance(message, Message))
402+
mock_message = MockMessage(
403+
text="Test message with citations [1:2|source]",
404+
author_name="TestAgent",
405+
role="assistant",
406+
)
399407

400408
with patch('backend.v4.callbacks.response_handlers.AgentMessage') as mock_agent_message:
401409
mock_agent_msg = Mock()

src/tests/backend/v4/magentic_agents/common/test_lifecycle.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
sys.modules['azure.identity'] = Mock()
1616
sys.modules['azure.identity.aio'] = Mock()
1717
sys.modules['common'] = Mock()
18+
sys.modules['common.config'] = Mock()
19+
sys.modules['common.config.app_config'] = Mock(config=Mock())
1820
sys.modules['common.database'] = Mock()
1921
sys.modules['common.database.database_base'] = Mock()
2022
sys.modules['common.models'] = Mock()
@@ -327,7 +329,7 @@ def test_get_chat_client_with_existing_client(self):
327329
base = MCPEnabledBase()
328330
mock_agent = Mock()
329331
mock_chat_client = Mock()
330-
mock_agent.chat_client = mock_chat_client
332+
mock_agent.client = mock_chat_client
331333
base._agent = mock_agent
332334

333335
result = base.get_chat_client()
@@ -339,7 +341,7 @@ def test_get_chat_client_from_agent(self):
339341
base = MCPEnabledBase()
340342
mock_agent = Mock()
341343
mock_chat_client = Mock()
342-
mock_agent.chat_client = mock_chat_client
344+
mock_agent.client = mock_chat_client
343345
base._agent = mock_agent
344346

345347
result = base.get_chat_client()

src/tests/backend/v4/magentic_agents/test_foundry_agent.py

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@
4545
sys.modules['azure.core'] = Mock()
4646
sys.modules['azure.core.exceptions'] = Mock()
4747
sys.modules['azure.identity'] = Mock()
48+
sys.modules['azure.identity.aio'] = Mock()
4849
sys.modules['azure.cosmos'] = Mock(CosmosClient=Mock)
49-
sys.modules['agent_framework'] = Mock(ChatAgent=Mock, ChatMessage=Mock, HostedCodeInterpreterTool=Mock, Role=Mock)
50+
sys.modules['agent_framework'] = Mock(Agent=Mock, Message=Mock, ChatOptions=Mock, ChatMessage=Mock, Role=Mock)
5051
sys.modules['agent_framework_azure_ai'] = Mock(AzureAIClient=Mock)
5152

5253
# Mock additional Azure modules that may be needed
@@ -303,17 +304,13 @@ def test_is_azure_search_requested_no_index_name(self, mock_get_logger, mock_con
303304
assert result is False
304305

305306
@pytest.mark.asyncio
306-
@patch('backend.v4.magentic_agents.foundry_agent.HostedCodeInterpreterTool')
307307
@patch('backend.v4.magentic_agents.foundry_agent.config')
308308
@patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger')
309-
async def test_collect_tools_with_code_interpreter(self, mock_get_logger, mock_config, mock_code_tool_class):
310-
"""Test _collect_tools with code interpreter enabled."""
309+
async def test_collect_tools_with_code_interpreter(self, mock_get_logger, mock_config):
310+
"""Test _collect_tools with code interpreter enabled - now handled server-side."""
311311
mock_logger = Mock()
312312
mock_get_logger.return_value = mock_logger
313313

314-
mock_code_tool = Mock()
315-
mock_code_tool_class.return_value = mock_code_tool
316-
317314
agent = FoundryAgentTemplate(
318315
agent_name="TestAgent",
319316
agent_description="Test Description",
@@ -329,23 +326,19 @@ async def test_collect_tools_with_code_interpreter(self, mock_get_logger, mock_c
329326

330327
tools = await agent._collect_tools()
331328

332-
assert len(tools) == 1
333-
assert tools[0] == mock_code_tool
334-
mock_code_tool_class.assert_called_once()
335-
mock_logger.info.assert_any_call("Added Code Interpreter tool.")
336-
mock_logger.info.assert_any_call("Total tools collected (MCP path): %d", 1)
329+
# HostedCodeInterpreterTool was removed in rc4; code interpreter is now server-side
330+
assert len(tools) == 0
331+
mock_logger.info.assert_any_call("Code Interpreter requested \u2014 handled server-side by AzureAIClient.")
332+
mock_logger.info.assert_any_call("Total tools collected (MCP path): %d", 0)
337333

338334
@pytest.mark.asyncio
339-
@patch('backend.v4.magentic_agents.foundry_agent.HostedCodeInterpreterTool')
340335
@patch('backend.v4.magentic_agents.foundry_agent.config')
341336
@patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger')
342-
async def test_collect_tools_code_interpreter_exception(self, mock_get_logger, mock_config, mock_code_tool_class):
343-
"""Test _collect_tools when code interpreter creation fails."""
337+
async def test_collect_tools_code_interpreter_server_side(self, mock_get_logger, mock_config):
338+
"""Test _collect_tools when code interpreter is enabled - handled server-side in rc4."""
344339
mock_logger = Mock()
345340
mock_get_logger.return_value = mock_logger
346341

347-
mock_code_tool_class.side_effect = Exception("Code interpreter failed")
348-
349342
agent = FoundryAgentTemplate(
350343
agent_name="TestAgent",
351344
agent_description="Test Description",
@@ -361,8 +354,9 @@ async def test_collect_tools_code_interpreter_exception(self, mock_get_logger, m
361354

362355
tools = await agent._collect_tools()
363356

357+
# No tools created locally; code interpreter is handled server-side
364358
assert len(tools) == 0
365-
mock_logger.error.assert_called_with("Code Interpreter tool creation failed: %s", mock_code_tool_class.side_effect)
359+
mock_logger.info.assert_any_call("Code Interpreter requested \u2014 handled server-side by AzureAIClient.")
366360

367361
@pytest.mark.asyncio
368362
@patch('backend.v4.magentic_agents.foundry_agent.config')
@@ -639,7 +633,7 @@ class SimpleCreds:
639633
# Verify error was logged (removed specific assertion due to mock corruption issues)
640634

641635
@pytest.mark.asyncio
642-
@patch('backend.v4.magentic_agents.foundry_agent.ChatAgent')
636+
@patch('backend.v4.magentic_agents.foundry_agent.Agent')
643637
@patch('backend.v4.magentic_agents.foundry_agent.agent_registry')
644638
@patch('backend.v4.magentic_agents.foundry_agent.config')
645639
@patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger')
@@ -676,11 +670,11 @@ async def test_after_open_reasoning_mode_azure_search(self, mock_get_logger, moc
676670
"TestAgent",
677671
"test-index"
678672
)
679-
mock_logger.info.assert_any_call("Initialized ChatAgent '%s'", "TestAgent")
673+
mock_logger.info.assert_any_call("Initialized Agent '%s'", "TestAgent")
680674
mock_registry.register_agent.assert_called_once_with(agent)
681675

682676
@pytest.mark.asyncio
683-
@patch('backend.v4.magentic_agents.foundry_agent.ChatAgent')
677+
@patch('backend.v4.magentic_agents.foundry_agent.Agent')
684678
@patch('backend.v4.magentic_agents.foundry_agent.agent_registry')
685679
@patch('backend.v4.magentic_agents.foundry_agent.config')
686680
@patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger')
@@ -712,11 +706,11 @@ async def test_after_open_foundry_mode_mcp(self, mock_get_logger, mock_config, m
712706

713707
mock_logger.info.assert_any_call("Initializing agent in Foundry mode.")
714708
mock_logger.info.assert_any_call("Initializing agent in MCP mode.")
715-
mock_logger.info.assert_any_call("Initialized ChatAgent '%s'", "TestAgent")
709+
mock_logger.info.assert_any_call("Initialized Agent '%s'", "TestAgent")
716710
mock_registry.register_agent.assert_called_once_with(agent)
717711

718712
@pytest.mark.asyncio
719-
@patch('backend.v4.magentic_agents.foundry_agent.ChatAgent')
713+
@patch('backend.v4.magentic_agents.foundry_agent.Agent')
720714
@patch('backend.v4.magentic_agents.foundry_agent.agent_registry')
721715
@patch('backend.v4.magentic_agents.foundry_agent.config')
722716
@patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger')
@@ -745,16 +739,16 @@ async def test_after_open_azure_search_setup_failure(self, mock_get_logger, mock
745739
assert "Azure AI Search mode requested but setup failed." in str(exc_info.value)
746740

747741
@pytest.mark.asyncio
748-
@patch('backend.v4.magentic_agents.foundry_agent.ChatAgent')
742+
@patch('backend.v4.magentic_agents.foundry_agent.Agent')
749743
@patch('backend.v4.magentic_agents.foundry_agent.agent_registry')
750744
@patch('backend.v4.magentic_agents.foundry_agent.config')
751745
@patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger')
752746
async def test_after_open_chat_agent_creation_error(self, mock_get_logger, mock_config, mock_registry, mock_chat_agent_class):
753-
"""Test _after_open when ChatAgent creation fails."""
747+
"""Test _after_open when Agent creation fails."""
754748
mock_logger = Mock()
755749
mock_get_logger.return_value = mock_logger
756750

757-
mock_chat_agent_class.side_effect = Exception("ChatAgent creation failed")
751+
mock_chat_agent_class.side_effect = Exception("Agent creation failed")
758752

759753
agent = FoundryAgentTemplate(
760754
agent_name="TestAgent",
@@ -774,11 +768,11 @@ async def test_after_open_chat_agent_creation_error(self, mock_get_logger, mock_
774768
with pytest.raises(Exception) as exc_info:
775769
await agent._after_open()
776770

777-
assert "ChatAgent creation failed" in str(exc_info.value)
778-
mock_logger.error.assert_called_with("Failed to initialize ChatAgent: %s", mock_chat_agent_class.side_effect)
771+
assert "Agent creation failed" in str(exc_info.value)
772+
mock_logger.error.assert_called_with("Failed to initialize Agent: %s", mock_chat_agent_class.side_effect)
779773

780774
@pytest.mark.asyncio
781-
@patch('backend.v4.magentic_agents.foundry_agent.ChatAgent')
775+
@patch('backend.v4.magentic_agents.foundry_agent.Agent')
782776
@patch('backend.v4.magentic_agents.foundry_agent.agent_registry')
783777
@patch('backend.v4.magentic_agents.foundry_agent.config')
784778
@patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger')
@@ -817,11 +811,10 @@ async def test_after_open_registry_failure(self, mock_get_logger, mock_config, m
817811
)
818812

819813
@pytest.mark.asyncio
820-
@patch('backend.v4.magentic_agents.foundry_agent.ChatMessage')
821-
@patch('backend.v4.magentic_agents.foundry_agent.Role')
814+
@patch('backend.v4.magentic_agents.foundry_agent.Message')
822815
@patch('backend.v4.magentic_agents.foundry_agent.config')
823816
@patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger')
824-
async def test_invoke_success(self, mock_get_logger, mock_config, mock_role, mock_chat_message_class):
817+
async def test_invoke_success(self, mock_get_logger, mock_config, mock_message_class):
825818
"""Test invoke method successfully streams responses."""
826819
mock_logger = Mock()
827820
mock_get_logger.return_value = mock_logger
@@ -830,15 +823,14 @@ async def test_invoke_success(self, mock_get_logger, mock_config, mock_role, moc
830823
mock_update1 = Mock()
831824
mock_update2 = Mock()
832825

833-
# Mock run_stream to return an async iterator
834-
async def mock_run_stream(messages):
826+
# Mock run to return an async iterator (source uses self._agent.run, not run_stream)
827+
async def mock_run(messages, stream=True):
835828
yield mock_update1
836829
yield mock_update2
837-
mock_agent.run_stream = mock_run_stream
830+
mock_agent.run = mock_run
838831

839832
mock_message = Mock()
840-
mock_chat_message_class.return_value = mock_message
841-
mock_role.USER = "user"
833+
mock_message_class.return_value = mock_message
842834

843835
agent = FoundryAgentTemplate(
844836
agent_name="TestAgent",
@@ -857,7 +849,7 @@ async def mock_run_stream(messages):
857849
updates.append(update)
858850

859851
assert updates == [mock_update1, mock_update2]
860-
mock_chat_message_class.assert_called_once_with(role=mock_role.USER, text="Test prompt")
852+
mock_message_class.assert_called_once_with(role="user", text="Test prompt")
861853

862854
@pytest.mark.asyncio
863855
@patch('backend.v4.magentic_agents.foundry_agent.config')

0 commit comments

Comments
 (0)