-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_main.py
More file actions
326 lines (245 loc) · 10.8 KB
/
test_main.py
File metadata and controls
326 lines (245 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
import pytest
import asyncio
from unittest.mock import Mock, patch, AsyncMock, MagicMock
from fastapi.responses import JSONResponse
from fastmcp import FastMCP
class TestMainMCPServer:
"""Test cases for main.py MCP server setup"""
def test_mcp_server_creation(self):
"""Test that MCP server is created with correct name"""
import main
# Verify mcp object exists and is properly initialized
assert hasattr(main, 'mcp')
assert isinstance(main.mcp, FastMCP)
assert main.mcp.name == "mcp-server"
@pytest.mark.asyncio
async def test_health_check_endpoint_response(self):
"""Test health check endpoint returns correct response"""
import main
# Create a mock request
mock_request = Mock()
# Call the health check endpoint
response = await main.health_check(mock_request)
# Verify response
assert isinstance(response, JSONResponse)
assert response.status_code == 200
# Parse the response body
import json
body = json.loads(response.body.decode())
assert body["status"] == "healthy"
assert body["service"] == "mcp-server"
@pytest.mark.asyncio
async def test_tools_list_endpoint_response(self):
"""Test tools list endpoint returns expected metadata structure."""
import json
import main
class MockClientContext:
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb):
return False
async def list_tools(self):
return [
{
"name": "get_booking_time_slots",
"description": "Get available slots",
"inputSchema": {
"type": "object",
"required": ["calendar_id"],
"properties": {
"calendar_id": {
"type": "string",
"description": "Calendar id",
},
"start_time": {
"type": "string",
"description": "Start time",
},
},
},
}
]
with patch("main.Client", return_value=MockClientContext()):
mock_request = Mock()
response = await main.tools_list(mock_request)
assert isinstance(response, JSONResponse)
assert response.status_code == 200
body = json.loads(response.body.decode())
assert body["success"] is True
assert body["total_tools"] == 1
assert body["tools"][0]["name"] == "get_booking_time_slots"
assert body["tools"][0]["required_parameters"] == ["calendar_id"]
assert len(body["tools"][0]["parameters"]) == 2
@pytest.mark.asyncio
async def test_tools_list_endpoint_error(self):
"""Test tools list endpoint error handling."""
import json
import main
class ErrorClientContext:
async def __aenter__(self):
raise RuntimeError("Connection failed")
async def __aexit__(self, exc_type, exc, tb):
return False
with patch("main.Client", return_value=ErrorClientContext()):
mock_request = Mock()
response = await main.tools_list(mock_request)
assert isinstance(response, JSONResponse)
assert response.status_code == 500
body = json.loads(response.body.decode())
assert body["success"] is False
assert "Failed to list tools" in body["error"]
def test_tools_are_registered(self):
"""Test that tools are registered with the MCP server"""
import main
# Verify the mcp object exists and is a FastMCP instance
assert hasattr(main, 'mcp')
assert isinstance(main.mcp, FastMCP)
# The tools should be registered via decorators
# We can't directly test decorator application, but we can verify
# the mcp object has the tool method
assert hasattr(main.mcp, 'tool')
def test_logging_configuration(self):
"""Test that logging is properly configured"""
import logging
import main
# Verify logger exists
assert hasattr(main, 'logger')
assert isinstance(main.logger, logging.Logger)
# Note: logging.basicConfig sets root logger level to INFO
# but other tests may have changed it, so we just verify the logger exists
assert main.logger.name == 'main'
class TestHealthCheckEndpoint:
"""Test cases for health check endpoint functionality"""
@pytest.mark.asyncio
async def test_health_check_success(self):
"""Test successful health check response"""
import main
mock_request = Mock()
response = await main.health_check(mock_request)
assert response.status_code == 200
assert response.media_type == "application/json"
@pytest.mark.asyncio
async def test_health_check_json_structure(self):
"""Test health check returns proper JSON structure"""
import main
mock_request = Mock()
response = await main.health_check(mock_request)
import json
body = json.loads(response.body.decode())
# Verify required fields
assert "status" in body
assert "service" in body
assert body["status"] == "healthy"
assert body["service"] == "mcp-server"
@pytest.mark.asyncio
async def test_health_check_multiple_calls(self):
"""Test health check endpoint can be called multiple times"""
import main
mock_request = Mock()
# Call health check multiple times
for _ in range(5):
response = await main.health_check(mock_request)
assert response.status_code == 200
class TestMCPServerIntegration:
"""Integration tests for the MCP server"""
def test_server_has_custom_routes(self):
"""Test that custom routes are registered"""
import main
# Verify mcp object has the custom_route decorator method
assert hasattr(main.mcp, 'custom_route')
def test_imported_tools_exist(self):
"""Test that imported tools from tools module are available"""
from tools import get_booking_time_slots, schedule_meeting
# Verify tools are callable
assert callable(get_booking_time_slots)
assert callable(schedule_meeting)
def test_module_has_main_guard(self):
"""Test that module can be imported without running main"""
import main
# If we can import main without error, the if __name__ == "__main__" guard works
assert hasattr(main, 'mcp')
assert hasattr(main, 'health_check')
assert hasattr(main, 'tools_list')
class TestServerConfiguration:
"""Test server configuration and setup"""
def test_mcp_server_name(self):
"""Test MCP server has correct name"""
import main
assert main.mcp.name == "mcp-server"
@patch('main.asyncio.run')
@patch('main.mcp.run_async')
def test_main_execution(self, mock_run_async, mock_asyncio_run):
"""Test main execution with correct parameters"""
# This test verifies the structure, not actual execution
# since if __name__ == "__main__" won't be true during imports
import main
# Verify that run_async method exists
assert hasattr(main.mcp, 'run_async')
def test_module_level_imports(self):
"""Test that all required modules are imported"""
import main
# Verify required imports
assert hasattr(main, 'asyncio')
assert hasattr(main, 'logging')
assert hasattr(main, 'JSONResponse')
assert hasattr(main, 'FastMCP')
assert hasattr(main, 'get_booking_time_slots')
assert hasattr(main, 'schedule_meeting')
class TestLoggingConfiguration:
"""Test logging setup and configuration"""
def test_logger_name(self):
"""Test logger has correct name"""
import main
assert main.logger.name == 'main'
def test_logging_format(self):
"""Test logging is configured with proper format"""
import logging
# Get root logger
root_logger = logging.getLogger()
# Verify handlers exist (logging.basicConfig creates at least one handler)
assert len(root_logger.handlers) > 0
# Verify that logging configuration was called (by checking if handlers exist)
# Note: The actual level may vary depending on test execution order
assert root_logger.level in [logging.INFO, logging.WARNING, logging.DEBUG]
def test_logger_can_log(self):
"""Test that logger can write log messages"""
import main
import logging
# Capture log output
with patch.object(main.logger, 'info') as mock_log:
main.logger.info("Test message")
mock_log.assert_called_once_with("Test message")
class TestHealthCheckDecorators:
"""Test health check endpoint decorators"""
def test_health_check_has_docstring(self):
"""Test health check function has documentation"""
import main
assert main.health_check.__doc__ is not None
assert "Health check" in main.health_check.__doc__
@pytest.mark.asyncio
async def test_health_check_is_async(self):
"""Test health check is an async function"""
import main
import inspect
assert inspect.iscoroutinefunction(main.health_check)
class TestToolsListHelpers:
"""Test helper functions used by tools listing endpoint."""
def test_to_dict_with_dict(self):
"""_to_dict should return dictionaries as-is."""
import main
data = {"name": "x"}
assert main._to_dict(data) == data
def test_to_dict_with_object_attrs(self):
"""_to_dict should map common object attributes."""
import main
tool_obj = Mock()
tool_obj.name = "tool_a"
tool_obj.description = "desc"
tool_obj.inputSchema = {"type": "object"}
tool_obj.outputSchema = None
result = main._to_dict(tool_obj)
assert result["name"] == "tool_a"
assert result["description"] == "desc"
assert result["inputSchema"]["type"] == "object"
if __name__ == "__main__":
pytest.main([__file__, "-v", "--tb=short"])