1
+ import asyncio
1
2
import os
2
3
import tempfile
3
4
7
8
from mcp_shell_server .server import call_tool , list_tools
8
9
9
10
11
+ # Mock process class
12
+ class MockProcess :
13
+ def __init__ (self , stdout = None , stderr = None , returncode = 0 ):
14
+ self .stdout = stdout
15
+ self .stderr = stderr
16
+ self .returncode = returncode
17
+ self ._input = None
18
+
19
+ async def communicate (self , input = None ):
20
+ self ._input = input
21
+ if self ._input and not isinstance (self ._input , bytes ):
22
+ self ._input = self ._input .encode ("utf-8" )
23
+
24
+ # For cat command, echo back the input
25
+ if self .stdout is None and self ._input :
26
+ return self ._input , self .stderr
27
+
28
+ if isinstance (self .stdout , int ):
29
+ self .stdout = str (self .stdout ).encode ("utf-8" )
30
+ if self .stdout is None :
31
+ self .stdout = b""
32
+ if self .stderr is None :
33
+ self .stderr = b""
34
+ return self .stdout , self .stderr
35
+
36
+ async def wait (self ):
37
+ return self .returncode
38
+
39
+ def kill (self ):
40
+ pass
41
+
42
+
43
+ def setup_mock_subprocess (monkeypatch ):
44
+ """Set up mock subprocess to avoid interactive shell warnings"""
45
+
46
+ async def mock_create_subprocess_shell (
47
+ cmd , stdin = None , stdout = None , stderr = None , env = None , cwd = None
48
+ ):
49
+ # Return appropriate output based on command
50
+ if "echo" in cmd :
51
+ return MockProcess (stdout = b"hello world\n " , stderr = b"" , returncode = 0 )
52
+ elif "pwd" in cmd :
53
+ return MockProcess (stdout = cwd .encode () + b"\n " , stderr = b"" , returncode = 0 )
54
+ elif "cat" in cmd :
55
+ return MockProcess (
56
+ stdout = None , stderr = b"" , returncode = 0
57
+ ) # Will echo back stdin
58
+ elif "ps" in cmd :
59
+ return MockProcess (stdout = b"bash\n " , stderr = b"" , returncode = 0 )
60
+ elif "env" in cmd :
61
+ return MockProcess (stdout = b"TEST_ENV=value\n " , stderr = b"" , returncode = 0 )
62
+ elif "sleep" in cmd :
63
+ return MockProcess (stdout = b"" , stderr = b"" , returncode = 0 )
64
+ else :
65
+ return MockProcess (stdout = b"" , stderr = b"" , returncode = 0 )
66
+
67
+ monkeypatch .setattr (
68
+ asyncio , "create_subprocess_shell" , mock_create_subprocess_shell
69
+ )
70
+
71
+
10
72
@pytest .fixture
11
73
def temp_test_dir ():
12
74
"""Create a temporary directory for testing"""
@@ -62,6 +124,7 @@ async def test_call_tool_valid_command(monkeypatch, temp_test_dir):
62
124
@pytest .mark .asyncio
63
125
async def test_call_tool_with_stdin (monkeypatch , temp_test_dir ):
64
126
"""Test command execution with stdin"""
127
+ setup_mock_subprocess (monkeypatch )
65
128
monkeypatch .setenv ("ALLOW_COMMANDS" , "cat" )
66
129
result = await call_tool (
67
130
"shell_execute" ,
@@ -237,6 +300,22 @@ async def test_disallowed_command(monkeypatch):
237
300
@pytest .mark .asyncio
238
301
async def test_call_tool_with_stderr (monkeypatch ):
239
302
"""Test command execution with stderr output"""
303
+
304
+ async def mock_create_subprocess_shell (
305
+ cmd , stdin = None , stdout = None , stderr = None , env = None , cwd = None
306
+ ):
307
+ # Return mock process with stderr for ls command
308
+ if "ls" in cmd :
309
+ return MockProcess (
310
+ stdout = b"" ,
311
+ stderr = b"ls: cannot access '/nonexistent/directory': No such file or directory\n " ,
312
+ returncode = 2 ,
313
+ )
314
+ return MockProcess (stdout = b"" , stderr = b"" , returncode = 0 )
315
+
316
+ monkeypatch .setattr (
317
+ asyncio , "create_subprocess_shell" , mock_create_subprocess_shell
318
+ )
240
319
monkeypatch .setenv ("ALLOW_COMMANDS" , "ls" )
241
320
result = await call_tool (
242
321
"shell_execute" ,
@@ -327,6 +406,7 @@ def stdio_server_impl():
327
406
@pytest .mark .asyncio
328
407
async def test_shell_startup (monkeypatch , temp_test_dir ):
329
408
"""Test shell startup and environment"""
409
+ setup_mock_subprocess (monkeypatch )
330
410
monkeypatch .setenv ("ALLOW_COMMANDS" , "ps" )
331
411
result = await call_tool (
332
412
"shell_execute" ,
@@ -339,6 +419,7 @@ async def test_shell_startup(monkeypatch, temp_test_dir):
339
419
@pytest .mark .asyncio
340
420
async def test_environment_variables (monkeypatch , temp_test_dir ):
341
421
"""Test to check environment variables during test execution"""
422
+ setup_mock_subprocess (monkeypatch )
342
423
monkeypatch .setenv ("ALLOW_COMMANDS" , "env" )
343
424
result = await call_tool (
344
425
"shell_execute" ,
0 commit comments