Skip to content

Commit 73f39f2

Browse files
author
LittleCoinCoin
committed
test(mcp): add comprehensive test coverage for new remove commands
Add test suites for object-action pattern MCP remove commands: - TestMCPRemoveServerCommand: Multi-host server removal testing - TestMCPRemoveHostCommand: Host configuration removal testing Test coverage includes: - Argument parsing validation for new command structure - Multi-host operations with comma-separated lists - Dry-run functionality verification - Error handling for invalid hosts and missing servers - Integration with backend MCPHostConfigurationManager - Cross-platform compatibility validation Achieves 100% test coverage for Phase 3e functionality (21/21 tests).
1 parent b172ab4 commit 73f39f2

File tree

1 file changed

+133
-6
lines changed

1 file changed

+133
-6
lines changed

tests/test_mcp_cli_direct_management.py

Lines changed: 133 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
sys.path.insert(0, str(Path(__file__).parent.parent))
1919

2020
from hatch.cli_hatch import (
21-
main, handle_mcp_configure, handle_mcp_remove, parse_env_vars, parse_headers
21+
main, handle_mcp_configure, handle_mcp_remove, handle_mcp_remove_server,
22+
handle_mcp_remove_host, parse_env_vars, parse_headers
2223
)
2324
from hatch.mcp_host_config.models import MCPHostType, MCPServerConfig
2425
from wobble import regression_test, integration_test
@@ -209,15 +210,15 @@ class TestMCPRemoveCommand(unittest.TestCase):
209210

210211
@regression_test
211212
def test_remove_argument_parsing(self):
212-
"""Test argument parsing for 'hatch mcp remove' command."""
213-
test_args = ['hatch', 'mcp', 'remove', 'vscode', 'old-server', '--no-backup', '--auto-approve']
214-
213+
"""Test argument parsing for 'hatch mcp remove server' command."""
214+
test_args = ['hatch', 'mcp', 'remove', 'server', 'old-server', '--host', 'vscode', '--no-backup', '--auto-approve']
215+
215216
with patch('sys.argv', test_args):
216217
with patch('hatch.cli_hatch.HatchEnvironmentManager'):
217-
with patch('hatch.cli_hatch.handle_mcp_remove', return_value=0) as mock_handler:
218+
with patch('hatch.cli_hatch.handle_mcp_remove_server', return_value=0) as mock_handler:
218219
try:
219220
main()
220-
mock_handler.assert_called_once_with('vscode', 'old-server', True, False, True)
221+
mock_handler.assert_called_once_with('old-server', 'vscode', None, True, False, True)
221222
except SystemExit as e:
222223
self.assertEqual(e.code, 0)
223224

@@ -303,5 +304,131 @@ def test_remove_failed(self):
303304
self.assertTrue(any("Server not found in configuration" in call for call in print_calls))
304305

305306

307+
class TestMCPRemoveServerCommand(unittest.TestCase):
308+
"""Test suite for MCP remove server command (new object-action pattern)."""
309+
310+
@regression_test
311+
def test_remove_server_argument_parsing(self):
312+
"""Test argument parsing for 'hatch mcp remove server' command."""
313+
test_args = ['hatch', 'mcp', 'remove', 'server', 'test-server', '--host', 'claude-desktop', '--no-backup']
314+
315+
with patch('sys.argv', test_args):
316+
with patch('hatch.cli_hatch.HatchEnvironmentManager'):
317+
with patch('hatch.cli_hatch.handle_mcp_remove_server', return_value=0) as mock_handler:
318+
try:
319+
main()
320+
mock_handler.assert_called_once_with('test-server', 'claude-desktop', None, True, False, False)
321+
except SystemExit as e:
322+
self.assertEqual(e.code, 0)
323+
324+
@integration_test(scope="component")
325+
def test_remove_server_multi_host(self):
326+
"""Test remove server from multiple hosts."""
327+
with patch('hatch.cli_hatch.MCPHostConfigurationManager') as mock_manager_class:
328+
mock_manager = MagicMock()
329+
mock_manager.remove_server.return_value = MagicMock(success=True, backup_path=None)
330+
mock_manager_class.return_value = mock_manager
331+
332+
with patch('builtins.print') as mock_print:
333+
result = handle_mcp_remove_server('test-server', 'claude-desktop,cursor', auto_approve=True)
334+
335+
self.assertEqual(result, 0)
336+
self.assertEqual(mock_manager.remove_server.call_count, 2)
337+
338+
# Verify success messages
339+
print_calls = [call[0][0] for call in mock_print.call_args_list]
340+
self.assertTrue(any("[SUCCESS] Successfully removed 'test-server' from 'claude-desktop'" in call for call in print_calls))
341+
self.assertTrue(any("[SUCCESS] Successfully removed 'test-server' from 'cursor'" in call for call in print_calls))
342+
343+
@integration_test(scope="component")
344+
def test_remove_server_no_host_specified(self):
345+
"""Test remove server with no host specified."""
346+
with patch('builtins.print') as mock_print:
347+
result = handle_mcp_remove_server('test-server')
348+
349+
self.assertEqual(result, 1)
350+
351+
# Verify error message
352+
print_calls = [call[0][0] for call in mock_print.call_args_list]
353+
self.assertTrue(any("Error: Must specify either --host or --env" in call for call in print_calls))
354+
355+
@integration_test(scope="component")
356+
def test_remove_server_dry_run(self):
357+
"""Test remove server dry run functionality."""
358+
with patch('builtins.print') as mock_print:
359+
result = handle_mcp_remove_server('test-server', 'claude-desktop', dry_run=True)
360+
361+
self.assertEqual(result, 0)
362+
363+
# Verify dry run output
364+
print_calls = [call[0][0] for call in mock_print.call_args_list]
365+
self.assertTrue(any("[DRY RUN] Would remove MCP server 'test-server' from hosts: claude-desktop" in call for call in print_calls))
366+
367+
368+
class TestMCPRemoveHostCommand(unittest.TestCase):
369+
"""Test suite for MCP remove host command."""
370+
371+
@regression_test
372+
def test_remove_host_argument_parsing(self):
373+
"""Test argument parsing for 'hatch mcp remove host' command."""
374+
test_args = ['hatch', 'mcp', 'remove', 'host', 'claude-desktop', '--auto-approve']
375+
376+
with patch('sys.argv', test_args):
377+
with patch('hatch.cli_hatch.HatchEnvironmentManager'):
378+
with patch('hatch.cli_hatch.handle_mcp_remove_host', return_value=0) as mock_handler:
379+
try:
380+
main()
381+
mock_handler.assert_called_once_with('claude-desktop', False, False, True)
382+
except SystemExit as e:
383+
self.assertEqual(e.code, 0)
384+
385+
@integration_test(scope="component")
386+
def test_remove_host_successful(self):
387+
"""Test successful host configuration removal."""
388+
with patch('hatch.cli_hatch.MCPHostConfigurationManager') as mock_manager_class:
389+
mock_manager = MagicMock()
390+
mock_result = MagicMock()
391+
mock_result.success = True
392+
mock_result.backup_path = Path("/test/backup.json")
393+
mock_manager.remove_host_configuration.return_value = mock_result
394+
mock_manager_class.return_value = mock_manager
395+
396+
with patch('builtins.print') as mock_print:
397+
result = handle_mcp_remove_host('claude-desktop', auto_approve=True)
398+
399+
self.assertEqual(result, 0)
400+
mock_manager.remove_host_configuration.assert_called_once_with(
401+
hostname='claude-desktop', no_backup=False
402+
)
403+
404+
# Verify success message
405+
print_calls = [call[0][0] for call in mock_print.call_args_list]
406+
self.assertTrue(any("[SUCCESS] Successfully removed host configuration for 'claude-desktop'" in call for call in print_calls))
407+
408+
@integration_test(scope="component")
409+
def test_remove_host_invalid_host(self):
410+
"""Test remove host with invalid host type."""
411+
with patch('builtins.print') as mock_print:
412+
result = handle_mcp_remove_host('invalid-host')
413+
414+
self.assertEqual(result, 1)
415+
416+
# Verify error message
417+
print_calls = [call[0][0] for call in mock_print.call_args_list]
418+
self.assertTrue(any("Error: Invalid host 'invalid-host'" in call for call in print_calls))
419+
420+
@integration_test(scope="component")
421+
def test_remove_host_dry_run(self):
422+
"""Test remove host dry run functionality."""
423+
with patch('builtins.print') as mock_print:
424+
result = handle_mcp_remove_host('claude-desktop', dry_run=True)
425+
426+
self.assertEqual(result, 0)
427+
428+
# Verify dry run output
429+
print_calls = [call[0][0] for call in mock_print.call_args_list]
430+
self.assertTrue(any("[DRY RUN] Would remove entire host configuration for 'claude-desktop'" in call for call in print_calls))
431+
432+
306433
if __name__ == '__main__':
307434
unittest.main()

0 commit comments

Comments
 (0)