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,520 +0,0 @@
1
- """
2
- Test suite for MCP environment integration.
3
-
4
- This module tests the integration between environment data and MCP host configuration
5
- with the corrected data structure.
6
- """
7
-
8
- import unittest
9
- import sys
10
- from pathlib import Path
11
- from datetime import datetime
12
- from unittest.mock import MagicMock, patch
13
- import json
14
-
15
- # Add the parent directory to the path to import wobble
16
- sys.path.insert(0, str(Path(__file__).parent.parent))
17
-
18
- try:
19
- from wobble.decorators import regression_test, integration_test
20
- except ImportError:
21
- # Fallback decorators if wobble is not available
22
- def regression_test(func):
23
- return func
24
-
25
- def integration_test(scope="component"):
26
- def decorator(func):
27
- return func
28
- return decorator
29
-
30
- from test_data_utils import MCPHostConfigTestDataLoader
31
- from hatch.mcp_host_config.models import (
32
- MCPServerConfig, EnvironmentData, EnvironmentPackageEntry,
33
- PackageHostConfiguration, MCPHostType
34
- )
35
- from hatch.environment_manager import HatchEnvironmentManager
36
-
37
-
38
- class TestMCPEnvironmentIntegration(unittest.TestCase):
39
- """Test suite for MCP environment integration with corrected structure."""
40
-
41
- def setUp(self):
42
- """Set up test environment."""
43
- self.test_data_loader = MCPHostConfigTestDataLoader()
44
-
45
- @regression_test
46
- def test_environment_data_validation_success(self):
47
- """Test successful environment data validation."""
48
- env_data = self.test_data_loader.load_corrected_environment_data("simple")
49
- environment = EnvironmentData(**env_data)
50
-
51
- self.assertEqual(environment.name, "test_environment")
52
- self.assertEqual(len(environment.packages), 1)
53
-
54
- package = environment.packages[0]
55
- self.assertEqual(package.name, "weather-toolkit")
56
- self.assertEqual(package.version, "1.0.0")
57
- self.assertIn("claude-desktop", package.configured_hosts)
58
-
59
- host_config = package.configured_hosts["claude-desktop"]
60
- self.assertIsInstance(host_config, PackageHostConfiguration)
61
- self.assertIsInstance(host_config.server_config, MCPServerConfig)
62
-
63
- @regression_test
64
- def test_environment_data_multi_host_validation(self):
65
- """Test environment data validation with multiple hosts."""
66
- env_data = self.test_data_loader.load_corrected_environment_data("multi_host")
67
- environment = EnvironmentData(**env_data)
68
-
69
- self.assertEqual(environment.name, "multi_host_environment")
70
- self.assertEqual(len(environment.packages), 1)
71
-
72
- package = environment.packages[0]
73
- self.assertEqual(package.name, "file-manager")
74
- self.assertEqual(len(package.configured_hosts), 2)
75
- self.assertIn("claude-desktop", package.configured_hosts)
76
- self.assertIn("cursor", package.configured_hosts)
77
-
78
- # Verify both host configurations
79
- claude_config = package.configured_hosts["claude-desktop"]
80
- cursor_config = package.configured_hosts["cursor"]
81
-
82
- self.assertIsInstance(claude_config, PackageHostConfiguration)
83
- self.assertIsInstance(cursor_config, PackageHostConfiguration)
84
-
85
- # Verify server configurations are different for different hosts
86
- self.assertEqual(claude_config.server_config.command, "/usr/local/bin/python")
87
- self.assertEqual(cursor_config.server_config.command, "python")
88
-
89
- @regression_test
90
- def test_package_host_configuration_validation(self):
91
- """Test package host configuration validation."""
92
- server_config_data = self.test_data_loader.load_mcp_server_config("local")
93
- server_config = MCPServerConfig(**server_config_data)
94
-
95
- host_config = PackageHostConfiguration(
96
- config_path="~/test/config.json",
97
- configured_at=datetime.fromisoformat("2025-09-21T10:00:00.000000"),
98
- last_synced=datetime.fromisoformat("2025-09-21T10:00:00.000000"),
99
- server_config=server_config
100
- )
101
-
102
- self.assertEqual(host_config.config_path, "~/test/config.json")
103
- self.assertIsInstance(host_config.server_config, MCPServerConfig)
104
- self.assertEqual(host_config.server_config.command, "python")
105
- self.assertEqual(len(host_config.server_config.args), 3)
106
-
107
- @regression_test
108
- def test_environment_package_entry_validation_success(self):
109
- """Test successful environment package entry validation."""
110
- server_config_data = self.test_data_loader.load_mcp_server_config("local")
111
- server_config = MCPServerConfig(**server_config_data)
112
-
113
- host_config = PackageHostConfiguration(
114
- config_path="~/test/config.json",
115
- configured_at=datetime.fromisoformat("2025-09-21T10:00:00.000000"),
116
- last_synced=datetime.fromisoformat("2025-09-21T10:00:00.000000"),
117
- server_config=server_config
118
- )
119
-
120
- package = EnvironmentPackageEntry(
121
- name="test-package",
122
- version="1.0.0",
123
- type="hatch",
124
- source="github:user/test-package",
125
- installed_at=datetime.fromisoformat("2025-09-21T10:00:00.000000"),
126
- configured_hosts={"claude-desktop": host_config}
127
- )
128
-
129
- self.assertEqual(package.name, "test-package")
130
- self.assertEqual(package.version, "1.0.0")
131
- self.assertEqual(package.type, "hatch")
132
- self.assertEqual(len(package.configured_hosts), 1)
133
- self.assertIn("claude-desktop", package.configured_hosts)
134
-
135
- @regression_test
136
- def test_environment_package_entry_invalid_host_name(self):
137
- """Test environment package entry validation with invalid host name."""
138
- server_config_data = self.test_data_loader.load_mcp_server_config("local")
139
- server_config = MCPServerConfig(**server_config_data)
140
-
141
- host_config = PackageHostConfiguration(
142
- config_path="~/test/config.json",
143
- configured_at=datetime.fromisoformat("2025-09-21T10:00:00.000000"),
144
- last_synced=datetime.fromisoformat("2025-09-21T10:00:00.000000"),
145
- server_config=server_config
146
- )
147
-
148
- with self.assertRaises(Exception) as context:
149
- EnvironmentPackageEntry(
150
- name="test-package",
151
- version="1.0.0",
152
- type="hatch",
153
- source="github:user/test-package",
154
- installed_at=datetime.fromisoformat("2025-09-21T10:00:00.000000"),
155
- configured_hosts={"invalid-host": host_config} # Invalid host name
156
- )
157
-
158
- self.assertIn("Unsupported host", str(context.exception))
159
-
160
- @regression_test
161
- def test_environment_package_entry_invalid_package_name(self):
162
- """Test environment package entry validation with invalid package name."""
163
- server_config_data = self.test_data_loader.load_mcp_server_config("local")
164
- server_config = MCPServerConfig(**server_config_data)
165
-
166
- host_config = PackageHostConfiguration(
167
- config_path="~/test/config.json",
168
- configured_at=datetime.fromisoformat("2025-09-21T10:00:00.000000"),
169
- last_synced=datetime.fromisoformat("2025-09-21T10:00:00.000000"),
170
- server_config=server_config
171
- )
172
-
173
- with self.assertRaises(Exception) as context:
174
- EnvironmentPackageEntry(
175
- name="invalid@package!name", # Invalid characters
176
- version="1.0.0",
177
- type="hatch",
178
- source="github:user/test-package",
179
- installed_at=datetime.fromisoformat("2025-09-21T10:00:00.000000"),
180
- configured_hosts={"claude-desktop": host_config}
181
- )
182
-
183
- self.assertIn("Invalid package name format", str(context.exception))
184
-
185
- @regression_test
186
- def test_environment_data_get_mcp_packages(self):
187
- """Test getting MCP packages from environment data."""
188
- env_data = self.test_data_loader.load_corrected_environment_data("multi_host")
189
- environment = EnvironmentData(**env_data)
190
-
191
- mcp_packages = environment.get_mcp_packages()
192
-
193
- self.assertEqual(len(mcp_packages), 1)
194
- self.assertEqual(mcp_packages[0].name, "file-manager")
195
- self.assertEqual(len(mcp_packages[0].configured_hosts), 2)
196
-
197
- @regression_test
198
- def test_environment_data_serialization_roundtrip(self):
199
- """Test environment data serialization and deserialization."""
200
- env_data = self.test_data_loader.load_corrected_environment_data("simple")
201
- environment = EnvironmentData(**env_data)
202
-
203
- # Serialize and deserialize
204
- serialized = environment.model_dump()
205
- roundtrip_environment = EnvironmentData(**serialized)
206
-
207
- self.assertEqual(environment.name, roundtrip_environment.name)
208
- self.assertEqual(len(environment.packages), len(roundtrip_environment.packages))
209
-
210
- original_package = environment.packages[0]
211
- roundtrip_package = roundtrip_environment.packages[0]
212
-
213
- self.assertEqual(original_package.name, roundtrip_package.name)
214
- self.assertEqual(original_package.version, roundtrip_package.version)
215
- self.assertEqual(len(original_package.configured_hosts), len(roundtrip_package.configured_hosts))
216
-
217
- # Verify host configuration roundtrip
218
- original_host_config = original_package.configured_hosts["claude-desktop"]
219
- roundtrip_host_config = roundtrip_package.configured_hosts["claude-desktop"]
220
-
221
- self.assertEqual(original_host_config.config_path, roundtrip_host_config.config_path)
222
- self.assertEqual(original_host_config.server_config.command, roundtrip_host_config.server_config.command)
223
-
224
- @regression_test
225
- def test_corrected_environment_structure_single_server_per_package(self):
226
- """Test that corrected environment structure enforces single server per package."""
227
- env_data = self.test_data_loader.load_corrected_environment_data("simple")
228
- environment = EnvironmentData(**env_data)
229
-
230
- # Verify single server per package constraint
231
- for package in environment.packages:
232
- # Each package should have one server configuration per host
233
- for host_name, host_config in package.configured_hosts.items():
234
- self.assertIsInstance(host_config, PackageHostConfiguration)
235
- self.assertIsInstance(host_config.server_config, MCPServerConfig)
236
-
237
- # The server configuration should be for this specific package
238
- # (In real usage, the server would be the package's MCP server)
239
-
240
- @regression_test
241
- def test_environment_data_json_serialization(self):
242
- """Test JSON serialization compatibility."""
243
- import json
244
-
245
- env_data = self.test_data_loader.load_corrected_environment_data("simple")
246
- environment = EnvironmentData(**env_data)
247
-
248
- # Test JSON serialization
249
- json_str = environment.model_dump_json()
250
- self.assertIsInstance(json_str, str)
251
-
252
- # Test JSON deserialization
253
- parsed_data = json.loads(json_str)
254
- roundtrip_environment = EnvironmentData(**parsed_data)
255
-
256
- self.assertEqual(environment.name, roundtrip_environment.name)
257
- self.assertEqual(len(environment.packages), len(roundtrip_environment.packages))
258
-
259
-
260
- class TestMCPHostTypeIntegration(unittest.TestCase):
261
- """Test suite for MCP host type integration."""
262
-
263
- @regression_test
264
- def test_mcp_host_type_enum_values(self):
265
- """Test MCP host type enum values."""
266
- # Verify all expected host types are available
267
- expected_hosts = [
268
- "claude-desktop", "claude-code", "vscode",
269
- "cursor", "lmstudio", "gemini"
270
- ]
271
-
272
- for host_name in expected_hosts:
273
- host_type = MCPHostType(host_name)
274
- self.assertEqual(host_type.value, host_name)
275
-
276
- @regression_test
277
- def test_mcp_host_type_invalid_value(self):
278
- """Test MCP host type with invalid value."""
279
- with self.assertRaises(ValueError):
280
- MCPHostType("invalid-host")
281
-
282
-
283
- class TestEnvironmentManagerHostSync(unittest.TestCase):
284
- """Test suite for EnvironmentManager host synchronization methods."""
285
-
286
- def setUp(self):
287
- """Set up test fixtures."""
288
- self.mock_env_manager = MagicMock(spec=HatchEnvironmentManager)
289
-
290
- # Load test fixture data
291
- fixture_path = Path(__file__).parent / "test_data" / "fixtures" / "host_sync_scenarios.json"
292
- with open(fixture_path, 'r') as f:
293
- self.test_data = json.load(f)
294
-
295
- @regression_test
296
- def test_remove_package_host_configuration_success(self):
297
- """Test successful removal of host from package tracking.
298
-
299
- Validates:
300
- - Removes specified host from package's configured_hosts
301
- - Updates environments.json file via _save_environments()
302
- - Returns True when removal occurs
303
- - Logs successful removal with package/host details
304
- """
305
- # Setup: Environment with package having configured_hosts for multiple hosts
306
- env_manager = HatchEnvironmentManager()
307
- env_manager._environments = {
308
- "test-env": self.test_data["remove_server_scenario"]["before"]
309
- }
310
-
311
- with patch.object(env_manager, '_save_environments') as mock_save:
312
- with patch.object(env_manager, 'logger') as mock_logger:
313
- # Action: remove_package_host_configuration(env_name, package_name, hostname)
314
- result = env_manager.remove_package_host_configuration("test-env", "weather-toolkit", "cursor")
315
-
316
- # Assert: Host removed from package, environments.json updated, returns True
317
- self.assertTrue(result)
318
- mock_save.assert_called_once()
319
- mock_logger.info.assert_called_with("Removed host cursor from package weather-toolkit in env test-env")
320
-
321
- # Verify host was actually removed
322
- packages = env_manager._environments["test-env"]["packages"]
323
- weather_pkg = next(pkg for pkg in packages if pkg["name"] == "weather-toolkit")
324
- self.assertNotIn("cursor", weather_pkg["configured_hosts"])
325
- self.assertIn("claude-desktop", weather_pkg["configured_hosts"])
326
-
327
- @regression_test
328
- def test_remove_package_host_configuration_not_found(self):
329
- """Test removal when package or host not found.
330
-
331
- Validates:
332
- - Returns False when environment doesn't exist
333
- - Returns False when package not found in environment
334
- - Returns False when host not in package's configured_hosts
335
- - No changes to environments.json when nothing to remove
336
- """
337
- env_manager = HatchEnvironmentManager()
338
- env_manager._environments = {
339
- "test-env": self.test_data["remove_server_scenario"]["before"]
340
- }
341
-
342
- with patch.object(env_manager, '_save_environments') as mock_save:
343
- # Test scenarios: missing env, missing package, missing host
344
-
345
- # Missing environment
346
- result = env_manager.remove_package_host_configuration("missing-env", "weather-toolkit", "cursor")
347
- self.assertFalse(result)
348
-
349
- # Missing package
350
- result = env_manager.remove_package_host_configuration("test-env", "missing-package", "cursor")
351
- self.assertFalse(result)
352
-
353
- # Missing host
354
- result = env_manager.remove_package_host_configuration("test-env", "weather-toolkit", "missing-host")
355
- self.assertFalse(result)
356
-
357
- # Assert: No file changes when nothing to remove
358
- mock_save.assert_not_called()
359
-
360
- @regression_test
361
- def test_clear_host_from_all_packages_all_envs(self):
362
- """Test host removal across multiple environments.
363
-
364
- Validates:
365
- - Iterates through all environments in _environments
366
- - Removes hostname from all packages' configured_hosts
367
- - Returns correct count of updated package entries
368
- - Calls _save_environments() only once after all updates
369
- """
370
- # Setup: Multiple environments with packages using same host
371
- env_manager = HatchEnvironmentManager()
372
- env_manager._environments = self.test_data["remove_host_scenario"]["multi_environment_before"]
373
-
374
- with patch.object(env_manager, '_save_environments') as mock_save:
375
- with patch.object(env_manager, 'logger') as mock_logger:
376
- # Action: clear_host_from_all_packages_all_envs(hostname)
377
- updates_count = env_manager.clear_host_from_all_packages_all_envs("cursor")
378
-
379
- # Assert: Host removed from all packages, correct count returned
380
- self.assertEqual(updates_count, 2) # 2 packages had cursor configured
381
- mock_save.assert_called_once()
382
-
383
- # Verify cursor was removed from all packages
384
- for env_name, env_data in env_manager._environments.items():
385
- for pkg in env_data["packages"]:
386
- configured_hosts = pkg.get("configured_hosts", {})
387
- self.assertNotIn("cursor", configured_hosts)
388
-
389
-
390
- class TestEnvironmentManagerHostSyncErrorHandling(unittest.TestCase):
391
- """Test suite for error handling and edge cases."""
392
-
393
- def setUp(self):
394
- """Set up test fixtures."""
395
- self.env_manager = HatchEnvironmentManager()
396
-
397
- @regression_test
398
- def test_remove_operations_exception_handling(self):
399
- """Test exception handling in remove operations.
400
-
401
- Validates:
402
- - Catches and logs exceptions during removal operations
403
- - Returns False/0 on exceptions rather than crashing
404
- - Provides meaningful error messages in logs
405
- - Maintains environment file integrity on errors
406
- """
407
- # Setup: Mock scenarios that raise exceptions
408
- # Create environment with package that has the host, so _save_environments will be called
409
- self.env_manager._environments = {
410
- "test-env": {
411
- "packages": [
412
- {
413
- "name": "test-pkg",
414
- "configured_hosts": {
415
- "test-host": {"config_path": "test"}
416
- }
417
- }
418
- ]
419
- }
420
- }
421
-
422
- with patch.object(self.env_manager, '_save_environments', side_effect=Exception("File error")):
423
- with patch.object(self.env_manager, 'logger') as mock_logger:
424
- # Action: Call remove methods with exception-inducing conditions
425
- result = self.env_manager.remove_package_host_configuration("test-env", "test-pkg", "test-host")
426
-
427
- # Assert: Graceful error handling, no crashes, appropriate returns
428
- self.assertFalse(result)
429
- mock_logger.error.assert_called()
430
-
431
-
432
- class TestCLIHostMutationSync(unittest.TestCase):
433
- """Test suite for CLI integration with environment tracking."""
434
-
435
- def setUp(self):
436
- """Set up test fixtures."""
437
- self.mock_env_manager = MagicMock(spec=HatchEnvironmentManager)
438
-
439
- @integration_test(scope="component")
440
- def test_remove_server_updates_environment(self):
441
- """Test that remove server updates current environment tracking.
442
-
443
- Validates:
444
- - CLI remove server calls environment manager update method
445
- - Updates only current environment (not all environments)
446
- - Passes correct parameters (env_name, server_name, hostname)
447
- - Maintains existing CLI behavior and exit codes
448
- """
449
- from hatch.cli_hatch import handle_mcp_remove_server
450
- from hatch.mcp_host_config import MCPHostConfigurationManager
451
-
452
- # Setup: Environment with server configured on host
453
- self.mock_env_manager.get_current_environment.return_value = "test-env"
454
-
455
- with patch.object(MCPHostConfigurationManager, 'remove_server') as mock_remove:
456
- mock_result = MagicMock()
457
- mock_result.success = True
458
- mock_result.backup_path = None
459
- mock_remove.return_value = mock_result
460
-
461
- with patch('hatch.cli_hatch.request_confirmation', return_value=True):
462
- with patch('builtins.print'):
463
- # Action: hatch mcp remove server <server> --host <host>
464
- result = handle_mcp_remove_server(
465
- self.mock_env_manager, "test-server", "claude-desktop",
466
- None, False, False, True
467
- )
468
-
469
- # Assert: Environment manager method called with correct parameters
470
- self.mock_env_manager.get_current_environment.assert_called_once()
471
- self.mock_env_manager.remove_package_host_configuration.assert_called_with(
472
- "test-env", "test-server", "claude-desktop"
473
- )
474
-
475
- # Assert: Success exit code
476
- self.assertEqual(result, 0)
477
-
478
- @integration_test(scope="component")
479
- def test_remove_host_updates_all_environments(self):
480
- """Test that remove host updates all environment tracking.
481
-
482
- Validates:
483
- - CLI remove host calls global environment update method
484
- - Updates ALL environments (not just current)
485
- - Passes correct hostname parameter
486
- - Reports number of updates performed to user
487
- """
488
- from hatch.cli_hatch import handle_mcp_remove_host
489
- from hatch.mcp_host_config import MCPHostConfigurationManager
490
-
491
- # Setup: Multiple environments with packages using the host
492
- with patch.object(MCPHostConfigurationManager, 'remove_host_configuration') as mock_remove:
493
- mock_result = MagicMock()
494
- mock_result.success = True
495
- mock_result.backup_path = None
496
- mock_remove.return_value = mock_result
497
-
498
- self.mock_env_manager.clear_host_from_all_packages_all_envs.return_value = 3
499
-
500
- with patch('hatch.cli_hatch.request_confirmation', return_value=True):
501
- with patch('builtins.print') as mock_print:
502
- # Action: hatch mcp remove host <host>
503
- result = handle_mcp_remove_host(
504
- self.mock_env_manager, "cursor", False, False, True
505
- )
506
-
507
- # Assert: Global environment update method called
508
- self.mock_env_manager.clear_host_from_all_packages_all_envs.assert_called_with("cursor")
509
-
510
- # Assert: User informed of update count
511
- print_calls = [call[0][0] for call in mock_print.call_args_list]
512
- output = ' '.join(print_calls)
513
- self.assertIn("Updated 3 package entries across environments", output)
514
-
515
- # Assert: Success exit code
516
- self.assertEqual(result, 0)
517
-
518
-
519
- if __name__ == '__main__':
520
- unittest.main()