Skip to main content
Glama
test_cli_flow.py9.29 kB
import pytest import time import json from unittest.mock import MagicMock, patch from build_unblocker_mcp.server import mcp, main from build_unblocker_mcp.config import DEFAULT_PROCESSES # Mock psutil.Process object for testing class MockProcess: def __init__(self, pid, name, create_time): self.info = {'pid': pid, 'name': name, 'create_time': create_time} self._cpu_percent = 0.0 self._killed = False def cpu_percent(self, interval=None): return self._cpu_percent def set_cpu_percent(self, percent): self._cpu_percent = percent def create_time(self): return self.info['create_time'] def kill(self): self._killed = True def is_killed(self): return self._killed @pytest.fixture def mock_psutil_process_iter(mocker): """Fixture to mock psutil.process_iter.""" mock_procs = [] mock_iter = mocker.patch("psutil.process_iter") mock_iter.return_value = mock_procs return mock_procs import pytest # Import pytest for async test functions def test_unblock_build_no_processes_killed(mock_psutil_process_iter): """Test that no processes are killed when none meet the criteria.""" current_time = time.time() # Add a process that is too young mock_psutil_process_iter.append(MockProcess(1, "cl.exe", current_time - 10)) # idle_seconds default is 90 # Add a process with high CPU high_cpu_proc = MockProcess(2, "link.exe", current_time - 100) high_cpu_proc.set_cpu_percent(10.0) mock_psutil_process_iter.append(high_cpu_proc) # Add a process not in the default list mock_psutil_process_iter.append(MockProcess(3, "notepad.exe", current_time - 100)) @pytest.mark.asyncio async def test_unblock_build_no_processes_killed(mock_psutil_process_iter): """Test that no processes are killed when none meet the criteria.""" current_time = time.time() # Add a process that is too young mock_psutil_process_iter.append(MockProcess(1, "cl.exe", current_time - 10)) # idle_seconds default is 90 # Add a process with high CPU high_cpu_proc = MockProcess(2, "link.exe", current_time - 100) high_cpu_proc.set_cpu_percent(10.0) mock_psutil_process_iter.append(high_cpu_proc) # Add a process not in the default list mock_psutil_process_iter.append(MockProcess(3, "notepad.exe", current_time - 100)) result = await mcp.call_tool(name="unblock_build", arguments={}) # Call the tool using call_tool # The call_tool method returns a list of content objects, so we need to extract the result dictionary result_dict = result[0].text if result and result[0].type == "text" else "{}" result = json.loads(result_dict) assert result['examined'] == 2 # cl.exe and link.exe assert result['killed'] == 0 assert result['killed_processes'] == [] assert result['dry_run'] is False assert result['idle_seconds_threshold'] == 90 assert result['process_names_monitored'] == DEFAULT_PROCESSES @pytest.mark.asyncio async def test_unblock_build_processes_killed(mock_psutil_process_iter): """Test that processes meeting the criteria are killed.""" current_time = time.time() # Add a process that should be killed killable_proc_1 = MockProcess(1, "cl.exe", current_time - 100) killable_proc_1.set_cpu_percent(0.5) mock_psutil_process_iter.append(killable_proc_1) # Add another process that should be killed killable_proc_2 = MockProcess(2, "link.exe", current_time - 120) killable_proc_2.set_cpu_percent(0.1) mock_psutil_process_iter.append(killable_proc_2) # Add a process that should not be killed (high CPU) high_cpu_proc = MockProcess(3, "msbuild.exe", current_time - 100) high_cpu_proc.set_cpu_percent(5.0) mock_psutil_process_iter.append(high_cpu_proc) result = await mcp.call_tool(name="unblock_build", arguments={}) result_dict = result[0].text if result and result[0].type == "text" else "{}" result = json.loads(result_dict) current_time = time.time() # Add a process that should be killed killable_proc_1 = MockProcess(1, "cl.exe", current_time - 100) killable_proc_1.set_cpu_percent(0.5) mock_psutil_process_iter.append(killable_proc_1) # Add another process that should be killed killable_proc_2 = MockProcess(2, "link.exe", current_time - 120) killable_proc_2.set_cpu_percent(0.1) mock_psutil_process_iter.append(killable_proc_2) # Add a process that should not be killed (high CPU) high_cpu_proc = MockProcess(3, "msbuild.exe", current_time - 100) high_cpu_proc.set_cpu_percent(5.0) mock_psutil_process_iter.append(high_cpu_proc) result = mcp.call_tool(name="unblock_build", arguments={}) result_dict = result[0].text if result and result[0].type == "text" else "{}" result = json.loads(result_dict) assert result['examined'] == 3 assert result['killed'] == 2 assert len(result['killed_processes']) == 2 killed_pids = [p['pid'] for p in result['killed_processes']] assert 1 in killed_pids assert 2 in killed_pids assert killable_proc_1.is_killed() is True assert killable_proc_2.is_killed() is True assert high_cpu_proc.is_killed() is False @pytest.mark.asyncio async def test_unblock_build_dry_run(mock_psutil_process_iter, capsys): """Test that dry_run prevents killing processes but logs.""" current_time = time.time() # Add a process that would be killed killable_proc = MockProcess(1, "cl.exe", current_time - 100) killable_proc.set_cpu_percent(0.5) mock_psutil_process_iter.append(killable_proc) result = await mcp.call_tool(name="unblock_build", arguments={"dry_run": True}) result_dict = result[0].text if result and result[0].type == "text" else "{}" result = json.loads(result_dict) current_time = time.time() # Add a process that would be killed killable_proc = MockProcess(1, "cl.exe", current_time - 100) killable_proc.set_cpu_percent(0.5) mock_psutil_process_iter.append(killable_proc) result = mcp.call_tool(name="unblock_build", arguments={"dry_run": True}) result_dict = result[0].text if result and result[0].type == "text" else "{}" result = json.loads(result_dict) assert result['examined'] == 1 assert result['killed'] == 0 assert result['dry_run'] is True assert killable_proc.is_killed() is False captured = capsys.readouterr() assert "Dry run: Would kill process cl.exe" in captured.stderr @pytest.mark.asyncio async def test_unblock_build_custom_process_names(mock_psutil_process_iter): """Test using custom process names.""" current_time = time.time() custom_processes = ["mybuild.exe", "anotherproc.exe"] # Add a process from the custom list that should be killed killable_proc = MockProcess(1, "mybuild.exe", current_time - 100) killable_proc.set_cpu_percent(0.5) mock_psutil_process_iter.append(killable_proc) # Add a process from the default list (should not be examined) mock_psutil_process_iter.append(MockProcess(2, "cl.exe", current_time - 100)) result = await mcp.call_tool(name="unblock_build", arguments={"process_names": custom_processes}) result_dict = result[0].text if result and result[0].type == "text" else "{}" result = json.loads(result_dict) current_time = time.time() custom_processes = ["mybuild.exe", "anotherproc.exe"] # Add a process from the custom list that should be killed killable_proc = MockProcess(1, "mybuild.exe", current_time - 100) killable_proc.set_cpu_percent(0.5) mock_psutil_process_iter.append(killable_proc) # Add a process from the default list (should not be examined) mock_psutil_process_iter.append(MockProcess(2, "cl.exe", current_time - 100)) result = mcp.call_tool(name="unblock_build", arguments={"process_names": custom_processes}) result_dict = result[0].text if result and result[0].type == "text" else "{}" result = json.loads(result_dict) assert result['examined'] == 1 assert result['killed'] == 1 assert len(result['killed_processes']) == 1 assert result['killed_processes'][0]['pid'] == 1 assert result['process_names_monitored'] == custom_processes assert killable_proc.is_killed() is True @patch('sys.argv', ['unblock-build-mcp', '--help']) @patch('sys.exit') @pytest.mark.asyncio # Mark as async test async def test_cli_help(mock_exit, capsys): """Test that the CLI --help command runs without error.""" # This test primarily checks that the FastMCP CLI setup doesn't crash on --help # The actual help output content is handled by FastMCP main() captured = capsys.readouterr() # We expect sys.exit(0) to be called by FastMCP's help handler mock_exit.assert_called_once_with(0) # Basic check for some output, not the full help text assert captured.stdout or captured.stderr # This test primarily checks that the FastMCP CLI setup doesn't crash on --help # The actual help output content is handled by FastMCP main() captured = capsys.readouterr() # We expect sys.exit(0) to be called by FastMCP's help handler mock_exit.assert_called_once_with(0) # Basic check for some output, not the full help text assert captured.stdout or captured.stderr

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Jordan-Jarvis/Cpp-build-unlock-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server