Skip to content

Commit 92b623a

Browse files
authored
test: add basic tests (#1)
1 parent a7e746d commit 92b623a

File tree

1 file changed

+168
-3
lines changed

1 file changed

+168
-3
lines changed

tests/test_mcpx.py

Lines changed: 168 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,170 @@
11
import unittest
2+
from unittest.mock import Mock, patch
3+
from mcpx_pydantic_ai import Agent, _convert_type
4+
from typing import Dict, Any
25

3-
class TestMcpxPydanticAi(unittest.TestCase):
4-
def test_basic(self):
5-
pass
6+
7+
class MockTool:
8+
def __init__(self, name: str, description: str, input_schema: Dict[str, Any]):
9+
self.name = name
10+
self.description = description
11+
self.input_schema = input_schema
12+
13+
14+
class MockResponse:
15+
def __init__(self, content):
16+
self.content = [Mock(text=content)]
17+
18+
19+
class MockClient:
20+
def __init__(self):
21+
self.tools = {
22+
"test_tool": MockTool(
23+
"test_tool",
24+
"A test tool",
25+
{
26+
"properties": {
27+
"param1": {"type": "string"},
28+
"param2": {"type": "integer"},
29+
}
30+
},
31+
)
32+
}
33+
self.called_tool = None
34+
self.called_params = None
35+
36+
def call_tool(self, tool: str, params: Dict[str, Any]) -> MockResponse:
37+
self.called_tool = tool
38+
self.called_params = params
39+
return MockResponse("mock response")
40+
41+
def set_profile(self, profile: str):
42+
self.profile = profile
43+
44+
45+
class TestTypeConversion(unittest.TestCase):
46+
def test_convert_basic_types(self):
47+
self.assertEqual(_convert_type("string"), str)
48+
self.assertEqual(_convert_type("boolean"), bool)
49+
self.assertEqual(_convert_type("number"), float)
50+
self.assertEqual(_convert_type("integer"), int)
51+
self.assertEqual(_convert_type("object"), dict)
52+
self.assertEqual(_convert_type("array"), list)
53+
54+
def test_convert_invalid_type(self):
55+
with self.assertRaises(TypeError):
56+
_convert_type("invalid_type")
57+
58+
59+
class TestAgent(unittest.TestCase):
60+
def setUp(self):
61+
self.mock_client = MockClient()
62+
self.agent = Agent(
63+
model="claude-3-5-sonnet-latest",
64+
client=self.mock_client,
65+
system_prompt="test prompt",
66+
)
67+
68+
def test_init_with_custom_client(self):
69+
"""Test agent initialization with custom client"""
70+
self.assertEqual(self.agent.client, self.mock_client)
71+
self.assertEqual(
72+
len(self.agent._function_tools), 1
73+
) # Should have our mock tool
74+
75+
def test_init_with_ignore_tools(self):
76+
"""Test agent initialization with ignored tools"""
77+
agent = Agent(
78+
model="claude-3-5-sonnet-latest",
79+
client=self.mock_client,
80+
ignore_tools=["test_tool"],
81+
system_prompt="test prompt",
82+
)
83+
self.assertEqual(
84+
len(agent._function_tools), 0
85+
) # Should have no tools due to ignore
86+
87+
def test_set_profile(self):
88+
"""Test setting profile updates client profile"""
89+
self.agent.set_profile("test_profile")
90+
self.assertEqual(self.mock_client.profile, "test_profile")
91+
92+
def test_register_custom_tool(self):
93+
"""Test registering a custom tool with custom function"""
94+
custom_mock = Mock(return_value="custom response")
95+
96+
self.agent.register_tool(
97+
MockTool(
98+
"custom_tool",
99+
"A custom tool",
100+
{"properties": {"param": {"type": "string"}}},
101+
),
102+
custom_mock,
103+
)
104+
105+
# Verify tool was registered
106+
self.assertIn("custom_tool", self.agent._function_tools)
107+
108+
# Test tool execution
109+
tool_func = self.agent._function_tools["custom_tool"].function
110+
result = tool_func({"param": "test"})
111+
112+
custom_mock.assert_called_once_with({"param": "test"})
113+
self.assertEqual(result, "custom response")
114+
115+
def test_tool_execution(self):
116+
"""Test executing a registered tool"""
117+
# Our mock tool should be registered automatically
118+
tool_func = self.agent._function_tools["test_tool"].function
119+
120+
result = tool_func({"param1": "test", "param2": 123})
121+
122+
self.assertEqual(self.mock_client.called_tool, "test_tool")
123+
self.assertEqual(
124+
self.mock_client.called_params, {"param1": "test", "param2": 123}
125+
)
126+
self.assertEqual(result, "mock response")
127+
128+
def test_reset_tools(self):
129+
"""Test resetting tools"""
130+
# Add a custom tool
131+
self.agent.register_tool(
132+
MockTool(
133+
"custom_tool",
134+
"A custom tool",
135+
{"properties": {"param": {"type": "string"}}},
136+
),
137+
Mock(),
138+
)
139+
140+
# Reset tools
141+
self.agent.reset_tools()
142+
143+
# Only custom tool should remain
144+
self.assertEqual(len(self.agent._function_tools), 1)
145+
self.assertIn("custom_tool", self.agent._function_tools)
146+
self.assertNotIn("test_tool", self.agent._function_tools)
147+
148+
@patch("mcpx_pydantic_ai.pydantic_ai.Agent.run_sync")
149+
def test_run_sync_updates_tools(self, mock_run_sync):
150+
"""Test that run_sync updates tools by default"""
151+
mock_run_sync.return_value = "test response"
152+
153+
result = self.agent.run_sync("test prompt")
154+
155+
self.assertEqual(result, "test response")
156+
mock_run_sync.assert_called_once()
157+
158+
@patch("mcpx_pydantic_ai.pydantic_ai.Agent.run_sync")
159+
def test_run_sync_without_tool_update(self, mock_run_sync):
160+
"""Test that run_sync can skip tool updates"""
161+
mock_run_sync.return_value = "test response"
162+
163+
result = self.agent.run_sync("test prompt", update_tools=False)
164+
165+
self.assertEqual(result, "test response")
166+
mock_run_sync.assert_called_once()
167+
168+
169+
if __name__ == "__main__":
170+
unittest.main()

0 commit comments

Comments
 (0)