hatch-xclam 0.7.1.dev3__py3-none-any.whl → 0.8.0.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. hatch/__init__.py +1 -1
  2. hatch/cli/__init__.py +71 -0
  3. hatch/cli/__main__.py +1035 -0
  4. hatch/cli/cli_env.py +865 -0
  5. hatch/cli/cli_mcp.py +1965 -0
  6. hatch/cli/cli_package.py +566 -0
  7. hatch/cli/cli_system.py +136 -0
  8. hatch/cli/cli_utils.py +1289 -0
  9. hatch/cli_hatch.py +160 -2838
  10. hatch/mcp_host_config/__init__.py +10 -10
  11. hatch/mcp_host_config/adapters/__init__.py +34 -0
  12. hatch/mcp_host_config/adapters/base.py +170 -0
  13. hatch/mcp_host_config/adapters/claude.py +105 -0
  14. hatch/mcp_host_config/adapters/codex.py +104 -0
  15. hatch/mcp_host_config/adapters/cursor.py +83 -0
  16. hatch/mcp_host_config/adapters/gemini.py +75 -0
  17. hatch/mcp_host_config/adapters/kiro.py +78 -0
  18. hatch/mcp_host_config/adapters/lmstudio.py +79 -0
  19. hatch/mcp_host_config/adapters/registry.py +149 -0
  20. hatch/mcp_host_config/adapters/vscode.py +83 -0
  21. hatch/mcp_host_config/backup.py +5 -3
  22. hatch/mcp_host_config/fields.py +126 -0
  23. hatch/mcp_host_config/models.py +161 -456
  24. hatch/mcp_host_config/reporting.py +57 -16
  25. hatch/mcp_host_config/strategies.py +155 -87
  26. hatch/template_generator.py +1 -1
  27. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/METADATA +3 -2
  28. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/RECORD +52 -43
  29. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/WHEEL +1 -1
  30. hatch_xclam-0.8.0.dev1.dist-info/entry_points.txt +2 -0
  31. tests/cli_test_utils.py +280 -0
  32. tests/integration/cli/__init__.py +14 -0
  33. tests/integration/cli/test_cli_reporter_integration.py +2439 -0
  34. tests/integration/mcp/__init__.py +0 -0
  35. tests/integration/mcp/test_adapter_serialization.py +173 -0
  36. tests/regression/cli/__init__.py +16 -0
  37. tests/regression/cli/test_color_logic.py +268 -0
  38. tests/regression/cli/test_consequence_type.py +298 -0
  39. tests/regression/cli/test_error_formatting.py +328 -0
  40. tests/regression/cli/test_result_reporter.py +586 -0
  41. tests/regression/cli/test_table_formatter.py +211 -0
  42. tests/regression/mcp/__init__.py +0 -0
  43. tests/regression/mcp/test_field_filtering.py +162 -0
  44. tests/test_cli_version.py +7 -5
  45. tests/test_data/fixtures/cli_reporter_fixtures.py +184 -0
  46. tests/unit/__init__.py +0 -0
  47. tests/unit/mcp/__init__.py +0 -0
  48. tests/unit/mcp/test_adapter_protocol.py +138 -0
  49. tests/unit/mcp/test_adapter_registry.py +158 -0
  50. tests/unit/mcp/test_config_model.py +146 -0
  51. hatch_xclam-0.7.1.dev3.dist-info/entry_points.txt +0 -2
  52. tests/integration/test_mcp_kiro_integration.py +0 -153
  53. tests/regression/test_mcp_codex_backup_integration.py +0 -162
  54. tests/regression/test_mcp_codex_host_strategy.py +0 -163
  55. tests/regression/test_mcp_codex_model_validation.py +0 -117
  56. tests/regression/test_mcp_kiro_backup_integration.py +0 -241
  57. tests/regression/test_mcp_kiro_cli_integration.py +0 -141
  58. tests/regression/test_mcp_kiro_decorator_registration.py +0 -71
  59. tests/regression/test_mcp_kiro_host_strategy.py +0 -214
  60. tests/regression/test_mcp_kiro_model_validation.py +0 -116
  61. tests/regression/test_mcp_kiro_omni_conversion.py +0 -104
  62. tests/test_mcp_atomic_operations.py +0 -276
  63. tests/test_mcp_backup_integration.py +0 -308
  64. tests/test_mcp_cli_all_host_specific_args.py +0 -496
  65. tests/test_mcp_cli_backup_management.py +0 -295
  66. tests/test_mcp_cli_direct_management.py +0 -456
  67. tests/test_mcp_cli_discovery_listing.py +0 -582
  68. tests/test_mcp_cli_host_config_integration.py +0 -823
  69. tests/test_mcp_cli_package_management.py +0 -360
  70. tests/test_mcp_cli_partial_updates.py +0 -859
  71. tests/test_mcp_environment_integration.py +0 -520
  72. tests/test_mcp_host_config_backup.py +0 -257
  73. tests/test_mcp_host_configuration_manager.py +0 -331
  74. tests/test_mcp_host_registry_decorator.py +0 -348
  75. tests/test_mcp_pydantic_architecture_v4.py +0 -603
  76. tests/test_mcp_server_config_models.py +0 -242
  77. tests/test_mcp_server_config_type_field.py +0 -221
  78. tests/test_mcp_sync_functionality.py +0 -316
  79. tests/test_mcp_user_feedback_reporting.py +0 -359
  80. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/licenses/LICENSE +0 -0
  81. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/top_level.txt +0 -0
@@ -1,456 +0,0 @@
1
- """
2
- Test suite for MCP CLI direct management commands (Phase 3e).
3
-
4
- This module tests the new MCP direct management functionality:
5
- - hatch mcp configure
6
- - hatch mcp remove
7
-
8
- Tests cover argument parsing, server configuration, output formatting,
9
- and error handling scenarios.
10
- """
11
-
12
- import unittest
13
- from unittest.mock import patch, MagicMock, ANY
14
- import sys
15
- from pathlib import Path
16
-
17
- # Add the parent directory to the path to import hatch modules
18
- sys.path.insert(0, str(Path(__file__).parent.parent))
19
-
20
- from hatch.cli_hatch import (
21
- main, handle_mcp_configure, handle_mcp_remove, handle_mcp_remove_server,
22
- handle_mcp_remove_host, parse_env_vars, parse_header
23
- )
24
- from hatch.mcp_host_config.models import MCPHostType, MCPServerConfig
25
- from wobble import regression_test, integration_test
26
-
27
-
28
- class TestMCPConfigureCommand(unittest.TestCase):
29
- """Test suite for MCP configure command."""
30
-
31
- @regression_test
32
- def test_configure_argument_parsing_basic(self):
33
- """Test basic argument parsing for 'hatch mcp configure' command."""
34
- # Updated to match current CLI: server_name is positional, --host is required, --command/--url are mutually exclusive
35
- test_args = ['hatch', 'mcp', 'configure', 'weather-server', '--host', 'claude-desktop', '--command', 'python', '--args', 'weather.py']
36
-
37
- with patch('sys.argv', test_args):
38
- with patch('hatch.cli_hatch.HatchEnvironmentManager'):
39
- with patch('hatch.cli_hatch.handle_mcp_configure', return_value=0) as mock_handler:
40
- try:
41
- result = main()
42
- # If main() returns without SystemExit, check the handler was called
43
- # Updated to include ALL host-specific parameters (27 total)
44
- mock_handler.assert_called_once_with(
45
- 'claude-desktop', 'weather-server', 'python', ['weather.py'],
46
- None, None, None, None, False, None, None, None, None, None, None,
47
- False, None, None, None, None, None, False, None, None, False, False, False
48
- )
49
- except SystemExit as e:
50
- # If SystemExit is raised, it should be 0 (success) and handler should have been called
51
- if e.code == 0:
52
- mock_handler.assert_called_once_with(
53
- 'claude-desktop', 'weather-server', 'python', ['weather.py'],
54
- None, None, None, None, False, None, None, None, None, None, None,
55
- False, None, None, None, None, None, False, None, None, False, False, False
56
- )
57
- else:
58
- self.fail(f"main() exited with code {e.code}, expected 0")
59
-
60
- @regression_test
61
- def test_configure_argument_parsing_with_options(self):
62
- """Test argument parsing with environment variables and options."""
63
- test_args = [
64
- 'hatch', 'mcp', 'configure', 'file-server', '--host', 'cursor', '--url', 'http://localhost:8080',
65
- '--env-var', 'API_KEY=secret', '--env-var', 'DEBUG=true',
66
- '--header', 'Authorization=Bearer token',
67
- '--no-backup', '--dry-run', '--auto-approve'
68
- ]
69
-
70
- with patch('sys.argv', test_args):
71
- with patch('hatch.cli_hatch.HatchEnvironmentManager'):
72
- with patch('hatch.cli_hatch.handle_mcp_configure', return_value=0) as mock_handler:
73
- try:
74
- main()
75
- # Updated to include ALL host-specific parameters (27 total)
76
- mock_handler.assert_called_once_with(
77
- 'cursor', 'file-server', None, None,
78
- ['API_KEY=secret', 'DEBUG=true'], 'http://localhost:8080',
79
- ['Authorization=Bearer token'], None, False, None, None, None, None, None, None,
80
- False, None, None, None, None, None, False, None, None, True, True, True
81
- )
82
- except SystemExit as e:
83
- self.assertEqual(e.code, 0)
84
-
85
- @regression_test
86
- def test_parse_env_vars(self):
87
- """Test environment variable parsing utility."""
88
- # Valid environment variables
89
- env_list = ['API_KEY=secret', 'DEBUG=true', 'PORT=8080']
90
- result = parse_env_vars(env_list)
91
-
92
- expected = {
93
- 'API_KEY': 'secret',
94
- 'DEBUG': 'true',
95
- 'PORT': '8080'
96
- }
97
- self.assertEqual(result, expected)
98
-
99
- # Empty list
100
- self.assertEqual(parse_env_vars(None), {})
101
- self.assertEqual(parse_env_vars([]), {})
102
-
103
- # Invalid format (should be skipped with warning)
104
- with patch('builtins.print') as mock_print:
105
- result = parse_env_vars(['INVALID_FORMAT', 'VALID=value'])
106
- self.assertEqual(result, {'VALID': 'value'})
107
- mock_print.assert_called()
108
-
109
- @regression_test
110
- def test_parse_header(self):
111
- """Test HTTP headers parsing utility."""
112
- # Valid headers
113
- headers_list = ['Authorization=Bearer token', 'Content-Type=application/json']
114
- result = parse_header(headers_list)
115
-
116
- expected = {
117
- 'Authorization': 'Bearer token',
118
- 'Content-Type': 'application/json'
119
- }
120
- self.assertEqual(result, expected)
121
-
122
- # Empty list
123
- self.assertEqual(parse_header(None), {})
124
- self.assertEqual(parse_header([]), {})
125
-
126
- @integration_test(scope="component")
127
- def test_configure_invalid_host(self):
128
- """Test configure command with invalid host type."""
129
- with patch('builtins.print') as mock_print:
130
- result = handle_mcp_configure('invalid-host', 'test-server', 'python', ['test.py'])
131
-
132
- self.assertEqual(result, 1)
133
-
134
- # Verify error message
135
- print_calls = [call[0][0] for call in mock_print.call_args_list]
136
- self.assertTrue(any("Error: Invalid host 'invalid-host'" in call for call in print_calls))
137
-
138
- @integration_test(scope="component")
139
- def test_configure_dry_run(self):
140
- """Test configure command dry run functionality."""
141
- with patch('builtins.print') as mock_print:
142
- result = handle_mcp_configure(
143
- 'claude-desktop', 'weather-server', 'python', ['weather.py'],
144
- env=['API_KEY=secret'], url=None,
145
- dry_run=True
146
- )
147
-
148
- self.assertEqual(result, 0)
149
-
150
- # Verify dry run output
151
- print_calls = [call[0][0] for call in mock_print.call_args_list]
152
- self.assertTrue(any("[DRY RUN] Would configure MCP server 'weather-server'" in call for call in print_calls))
153
- self.assertTrue(any("[DRY RUN] Command: python" in call for call in print_calls))
154
- self.assertTrue(any("[DRY RUN] Environment:" in call for call in print_calls))
155
- # URL should not be present for local server configuration
156
-
157
- @integration_test(scope="component")
158
- def test_configure_successful(self):
159
- """Test successful MCP server configuration."""
160
- from hatch.mcp_host_config.host_management import ConfigurationResult
161
-
162
- mock_result = ConfigurationResult(
163
- success=True,
164
- hostname='claude-desktop',
165
- server_name='weather-server',
166
- backup_path=Path('/test/backup.json')
167
- )
168
-
169
- with patch('hatch.cli_hatch.MCPHostConfigurationManager') as mock_manager_class:
170
- mock_manager = MagicMock()
171
- mock_manager.configure_server.return_value = mock_result
172
- mock_manager_class.return_value = mock_manager
173
-
174
- with patch('hatch.cli_hatch.request_confirmation', return_value=True):
175
- with patch('builtins.print') as mock_print:
176
- result = handle_mcp_configure(
177
- 'claude-desktop', 'weather-server', 'python', ['weather.py'],
178
- auto_approve=True
179
- )
180
-
181
- self.assertEqual(result, 0)
182
- mock_manager.configure_server.assert_called_once()
183
-
184
- # Verify success message
185
- print_calls = [call[0][0] for call in mock_print.call_args_list]
186
- self.assertTrue(any("[SUCCESS] Successfully configured MCP server 'weather-server'" in call for call in print_calls))
187
- self.assertTrue(any("Backup created:" in call for call in print_calls))
188
-
189
- @integration_test(scope="component")
190
- def test_configure_failed(self):
191
- """Test failed MCP server configuration."""
192
- from hatch.mcp_host_config.host_management import ConfigurationResult
193
-
194
- mock_result = ConfigurationResult(
195
- success=False,
196
- hostname='claude-desktop',
197
- server_name='weather-server',
198
- error_message='Configuration validation failed'
199
- )
200
-
201
- with patch('hatch.cli_hatch.MCPHostConfigurationManager') as mock_manager_class:
202
- mock_manager = MagicMock()
203
- mock_manager.configure_server.return_value = mock_result
204
- mock_manager_class.return_value = mock_manager
205
-
206
- with patch('hatch.cli_hatch.request_confirmation', return_value=True):
207
- with patch('builtins.print') as mock_print:
208
- result = handle_mcp_configure(
209
- 'claude-desktop', 'weather-server', 'python', ['weather.py'],
210
- auto_approve=True
211
- )
212
-
213
- self.assertEqual(result, 1)
214
-
215
- # Verify error message
216
- print_calls = [call[0][0] for call in mock_print.call_args_list]
217
- self.assertTrue(any("[ERROR] Failed to configure MCP server 'weather-server'" in call for call in print_calls))
218
- self.assertTrue(any("Configuration validation failed" in call for call in print_calls))
219
-
220
-
221
- class TestMCPRemoveCommand(unittest.TestCase):
222
- """Test suite for MCP remove command."""
223
-
224
- @regression_test
225
- def test_remove_argument_parsing(self):
226
- """Test argument parsing for 'hatch mcp remove server' command."""
227
- test_args = ['hatch', 'mcp', 'remove', 'server', 'old-server', '--host', 'vscode', '--no-backup', '--auto-approve']
228
-
229
- with patch('sys.argv', test_args):
230
- with patch('hatch.cli_hatch.HatchEnvironmentManager'):
231
- with patch('hatch.cli_hatch.handle_mcp_remove_server', return_value=0) as mock_handler:
232
- try:
233
- main()
234
- mock_handler.assert_called_once_with(ANY, 'old-server', 'vscode', None, True, False, True)
235
- except SystemExit as e:
236
- self.assertEqual(e.code, 0)
237
-
238
- @integration_test(scope="component")
239
- def test_remove_invalid_host(self):
240
- """Test remove command with invalid host type."""
241
- with patch('builtins.print') as mock_print:
242
- result = handle_mcp_remove('invalid-host', 'test-server')
243
-
244
- self.assertEqual(result, 1)
245
-
246
- # Verify error message
247
- print_calls = [call[0][0] for call in mock_print.call_args_list]
248
- self.assertTrue(any("Error: Invalid host 'invalid-host'" in call for call in print_calls))
249
-
250
- @integration_test(scope="component")
251
- def test_remove_dry_run(self):
252
- """Test remove command dry run functionality."""
253
- with patch('builtins.print') as mock_print:
254
- result = handle_mcp_remove('claude-desktop', 'old-server', no_backup=True, dry_run=True)
255
-
256
- self.assertEqual(result, 0)
257
-
258
- # Verify dry run output
259
- print_calls = [call[0][0] for call in mock_print.call_args_list]
260
- self.assertTrue(any("[DRY RUN] Would remove MCP server 'old-server'" in call for call in print_calls))
261
- self.assertTrue(any("[DRY RUN] Backup: Disabled" in call for call in print_calls))
262
-
263
- @integration_test(scope="component")
264
- def test_remove_successful(self):
265
- """Test successful MCP server removal."""
266
- from hatch.mcp_host_config.host_management import ConfigurationResult
267
-
268
- mock_result = ConfigurationResult(
269
- success=True,
270
- hostname='claude-desktop',
271
- server_name='old-server',
272
- backup_path=Path('/test/backup.json')
273
- )
274
-
275
- with patch('hatch.cli_hatch.MCPHostConfigurationManager') as mock_manager_class:
276
- mock_manager = MagicMock()
277
- mock_manager.remove_server.return_value = mock_result
278
- mock_manager_class.return_value = mock_manager
279
-
280
- with patch('hatch.cli_hatch.request_confirmation', return_value=True):
281
- with patch('builtins.print') as mock_print:
282
- result = handle_mcp_remove('claude-desktop', 'old-server', auto_approve=True)
283
-
284
- self.assertEqual(result, 0)
285
- mock_manager.remove_server.assert_called_once()
286
-
287
- # Verify success message
288
- print_calls = [call[0][0] for call in mock_print.call_args_list]
289
- self.assertTrue(any("[SUCCESS] Successfully removed MCP server 'old-server'" in call for call in print_calls))
290
-
291
- @integration_test(scope="component")
292
- def test_remove_failed(self):
293
- """Test failed MCP server removal."""
294
- from hatch.mcp_host_config.host_management import ConfigurationResult
295
-
296
- mock_result = ConfigurationResult(
297
- success=False,
298
- hostname='claude-desktop',
299
- server_name='old-server',
300
- error_message='Server not found in configuration'
301
- )
302
-
303
- with patch('hatch.cli_hatch.MCPHostConfigurationManager') as mock_manager_class:
304
- mock_manager = MagicMock()
305
- mock_manager.remove_server.return_value = mock_result
306
- mock_manager_class.return_value = mock_manager
307
-
308
- with patch('hatch.cli_hatch.request_confirmation', return_value=True):
309
- with patch('builtins.print') as mock_print:
310
- result = handle_mcp_remove('claude-desktop', 'old-server', auto_approve=True)
311
-
312
- self.assertEqual(result, 1)
313
-
314
- # Verify error message
315
- print_calls = [call[0][0] for call in mock_print.call_args_list]
316
- self.assertTrue(any("[ERROR] Failed to remove MCP server 'old-server'" in call for call in print_calls))
317
- self.assertTrue(any("Server not found in configuration" in call for call in print_calls))
318
-
319
-
320
- class TestMCPRemoveServerCommand(unittest.TestCase):
321
- """Test suite for MCP remove server command (new object-action pattern)."""
322
-
323
- @regression_test
324
- def test_remove_server_argument_parsing(self):
325
- """Test argument parsing for 'hatch mcp remove server' command."""
326
- test_args = ['hatch', 'mcp', 'remove', 'server', 'test-server', '--host', 'claude-desktop', '--no-backup']
327
-
328
- with patch('sys.argv', test_args):
329
- with patch('hatch.cli_hatch.HatchEnvironmentManager'):
330
- with patch('hatch.cli_hatch.handle_mcp_remove_server', return_value=0) as mock_handler:
331
- try:
332
- main()
333
- mock_handler.assert_called_once_with(ANY, 'test-server', 'claude-desktop', None, True, False, False)
334
- except SystemExit as e:
335
- self.assertEqual(e.code, 0)
336
-
337
- @integration_test(scope="component")
338
- def test_remove_server_multi_host(self):
339
- """Test remove server from multiple hosts."""
340
- with patch('hatch.cli_hatch.MCPHostConfigurationManager') as mock_manager_class:
341
- mock_manager = MagicMock()
342
- mock_manager.remove_server.return_value = MagicMock(success=True, backup_path=None)
343
- mock_manager_class.return_value = mock_manager
344
-
345
- with patch('hatch.cli_hatch.HatchEnvironmentManager') as mock_env_manager:
346
- with patch('builtins.print') as mock_print:
347
- result = handle_mcp_remove_server(mock_env_manager.return_value, 'test-server', 'claude-desktop,cursor', auto_approve=True)
348
-
349
- self.assertEqual(result, 0)
350
- self.assertEqual(mock_manager.remove_server.call_count, 2)
351
-
352
- # Verify success messages
353
- print_calls = [call[0][0] for call in mock_print.call_args_list]
354
- self.assertTrue(any("[SUCCESS] Successfully removed 'test-server' from 'claude-desktop'" in call for call in print_calls))
355
- self.assertTrue(any("[SUCCESS] Successfully removed 'test-server' from 'cursor'" in call for call in print_calls))
356
-
357
- @integration_test(scope="component")
358
- def test_remove_server_no_host_specified(self):
359
- """Test remove server with no host specified."""
360
- with patch('hatch.cli_hatch.HatchEnvironmentManager') as mock_env_manager:
361
- with patch('builtins.print') as mock_print:
362
- result = handle_mcp_remove_server(mock_env_manager.return_value, 'test-server')
363
-
364
- self.assertEqual(result, 1)
365
-
366
- # Verify error message
367
- print_calls = [call[0][0] for call in mock_print.call_args_list]
368
- self.assertTrue(any("Error: Must specify either --host or --env" in call for call in print_calls))
369
-
370
- @integration_test(scope="component")
371
- def test_remove_server_dry_run(self):
372
- """Test remove server dry run functionality."""
373
- with patch('hatch.cli_hatch.HatchEnvironmentManager') as mock_env_manager:
374
- with patch('builtins.print') as mock_print:
375
- result = handle_mcp_remove_server(mock_env_manager.return_value, 'test-server', 'claude-desktop', dry_run=True)
376
-
377
- self.assertEqual(result, 0)
378
-
379
- # Verify dry run output
380
- print_calls = [call[0][0] for call in mock_print.call_args_list]
381
- self.assertTrue(any("[DRY RUN] Would remove MCP server 'test-server' from hosts: claude-desktop" in call for call in print_calls))
382
-
383
-
384
- class TestMCPRemoveHostCommand(unittest.TestCase):
385
- """Test suite for MCP remove host command."""
386
-
387
- @regression_test
388
- def test_remove_host_argument_parsing(self):
389
- """Test argument parsing for 'hatch mcp remove host' command."""
390
- test_args = ['hatch', 'mcp', 'remove', 'host', 'claude-desktop', '--auto-approve']
391
-
392
- with patch('sys.argv', test_args):
393
- with patch('hatch.cli_hatch.HatchEnvironmentManager'):
394
- with patch('hatch.cli_hatch.handle_mcp_remove_host', return_value=0) as mock_handler:
395
- try:
396
- main()
397
- mock_handler.assert_called_once_with(ANY, 'claude-desktop', False, False, True)
398
- except SystemExit as e:
399
- self.assertEqual(e.code, 0)
400
-
401
- @integration_test(scope="component")
402
- def test_remove_host_successful(self):
403
- """Test successful host configuration removal."""
404
- with patch('hatch.cli_hatch.MCPHostConfigurationManager') as mock_manager_class:
405
- mock_manager = MagicMock()
406
- mock_result = MagicMock()
407
- mock_result.success = True
408
- mock_result.backup_path = Path("/test/backup.json")
409
- mock_manager.remove_host_configuration.return_value = mock_result
410
- mock_manager_class.return_value = mock_manager
411
-
412
- with patch('hatch.cli_hatch.HatchEnvironmentManager') as mock_env_manager:
413
- # Mock the clear_host_from_all_packages_all_envs method
414
- mock_env_manager.return_value.clear_host_from_all_packages_all_envs.return_value = 2
415
-
416
- with patch('builtins.print') as mock_print:
417
- result = handle_mcp_remove_host(mock_env_manager.return_value, 'claude-desktop', auto_approve=True)
418
-
419
- self.assertEqual(result, 0)
420
- mock_manager.remove_host_configuration.assert_called_once_with(
421
- hostname='claude-desktop', no_backup=False
422
- )
423
-
424
- # Verify success message
425
- print_calls = [call[0][0] for call in mock_print.call_args_list]
426
- self.assertTrue(any("[SUCCESS] Successfully removed host configuration for 'claude-desktop'" in call for call in print_calls))
427
-
428
- @integration_test(scope="component")
429
- def test_remove_host_invalid_host(self):
430
- """Test remove host with invalid host type."""
431
- with patch('hatch.cli_hatch.HatchEnvironmentManager') as mock_env_manager:
432
- with patch('builtins.print') as mock_print:
433
- result = handle_mcp_remove_host(mock_env_manager.return_value, 'invalid-host')
434
-
435
- self.assertEqual(result, 1)
436
-
437
- # Verify error message
438
- print_calls = [call[0][0] for call in mock_print.call_args_list]
439
- self.assertTrue(any("Error: Invalid host 'invalid-host'" in call for call in print_calls))
440
-
441
- @integration_test(scope="component")
442
- def test_remove_host_dry_run(self):
443
- """Test remove host dry run functionality."""
444
- with patch('hatch.cli_hatch.HatchEnvironmentManager') as mock_env_manager:
445
- with patch('builtins.print') as mock_print:
446
- result = handle_mcp_remove_host(mock_env_manager.return_value, 'claude-desktop', dry_run=True)
447
-
448
- self.assertEqual(result, 0)
449
-
450
- # Verify dry run output
451
- print_calls = [call[0][0] for call in mock_print.call_args_list]
452
- self.assertTrue(any("[DRY RUN] Would remove entire host configuration for 'claude-desktop'" in call for call in print_calls))
453
-
454
-
455
- if __name__ == '__main__':
456
- unittest.main()