@@ -211,3 +211,93 @@ async def test_disallowed_command(monkeypatch):
211
211
},
212
212
)
213
213
assert "Command not allowed: sudo" in str (exc .value )
214
+
215
+
216
+ @pytest .mark .asyncio
217
+ async def test_call_tool_with_stderr (monkeypatch ):
218
+ """Test command execution with stderr output"""
219
+ monkeypatch .setenv ("ALLOW_COMMANDS" , "ls" )
220
+ result = await call_tool (
221
+ "shell_execute" ,
222
+ {"command" : ["ls" , "/nonexistent/directory" ]},
223
+ )
224
+ assert len (result ) >= 1
225
+ stderr_content = next (
226
+ (c for c in result if isinstance (c , TextContent ) and "No such file" in c .text ),
227
+ None ,
228
+ )
229
+ assert stderr_content is not None
230
+ assert stderr_content .type == "text"
231
+
232
+
233
+ @pytest .mark .asyncio
234
+ async def test_main_server (mocker ):
235
+ """Test the main server function"""
236
+ # Mock the stdio_server
237
+ mock_read_stream = mocker .AsyncMock ()
238
+ mock_write_stream = mocker .AsyncMock ()
239
+
240
+ # Create an async context manager mock
241
+ context_manager = mocker .AsyncMock ()
242
+ context_manager .__aenter__ = mocker .AsyncMock (
243
+ return_value = (mock_read_stream , mock_write_stream )
244
+ )
245
+ context_manager .__aexit__ = mocker .AsyncMock (return_value = None )
246
+
247
+ # Set up stdio_server mock to return a regular function that returns the context manager
248
+ def stdio_server_impl ():
249
+ return context_manager
250
+
251
+ mock_stdio_server = mocker .Mock (side_effect = stdio_server_impl )
252
+
253
+ # Mock app.run and create_initialization_options
254
+ mock_server_run = mocker .patch ("mcp_shell_server.server.app.run" )
255
+ mock_create_init_options = mocker .patch (
256
+ "mcp_shell_server.server.app.create_initialization_options"
257
+ )
258
+
259
+ # Import main after setting up mocks
260
+ from mcp_shell_server .server import main
261
+
262
+ # Execute main function
263
+ mocker .patch ("mcp.server.stdio.stdio_server" , mock_stdio_server )
264
+ await main ()
265
+
266
+ # Verify interactions
267
+ mock_stdio_server .assert_called_once ()
268
+ context_manager .__aenter__ .assert_awaited_once ()
269
+ context_manager .__aexit__ .assert_awaited_once ()
270
+ mock_server_run .assert_called_once_with (
271
+ mock_read_stream , mock_write_stream , mock_create_init_options .return_value
272
+ )
273
+
274
+
275
+ @pytest .mark .asyncio
276
+ async def test_main_server_error_handling (mocker ):
277
+ """Test error handling in the main server function"""
278
+ # Mock app.run to raise an exception
279
+ mocker .patch (
280
+ "mcp_shell_server.server.app.run" , side_effect = RuntimeError ("Test error" )
281
+ )
282
+
283
+ # Mock the stdio_server
284
+ context_manager = mocker .AsyncMock ()
285
+ context_manager .__aenter__ = mocker .AsyncMock (
286
+ return_value = (mocker .AsyncMock (), mocker .AsyncMock ())
287
+ )
288
+ context_manager .__aexit__ = mocker .AsyncMock (return_value = None )
289
+
290
+ def stdio_server_impl ():
291
+ return context_manager
292
+
293
+ mock_stdio_server = mocker .Mock (side_effect = stdio_server_impl )
294
+
295
+ # Import main after setting up mocks
296
+ from mcp_shell_server .server import main
297
+
298
+ # Execute main function and expect it to raise the error
299
+ mocker .patch ("mcp.server.stdio.stdio_server" , mock_stdio_server )
300
+ with pytest .raises (RuntimeError ) as exc :
301
+ await main ()
302
+
303
+ assert str (exc .value ) == "Test error"
0 commit comments