hatch-xclam 0.7.0__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.
- hatch/__init__.py +21 -0
- hatch/cli_hatch.py +2748 -0
- hatch/environment_manager.py +1375 -0
- hatch/installers/__init__.py +25 -0
- hatch/installers/dependency_installation_orchestrator.py +636 -0
- hatch/installers/docker_installer.py +545 -0
- hatch/installers/hatch_installer.py +198 -0
- hatch/installers/installation_context.py +109 -0
- hatch/installers/installer_base.py +195 -0
- hatch/installers/python_installer.py +342 -0
- hatch/installers/registry.py +179 -0
- hatch/installers/system_installer.py +588 -0
- hatch/mcp_host_config/__init__.py +38 -0
- hatch/mcp_host_config/backup.py +458 -0
- hatch/mcp_host_config/host_management.py +572 -0
- hatch/mcp_host_config/models.py +602 -0
- hatch/mcp_host_config/reporting.py +181 -0
- hatch/mcp_host_config/strategies.py +513 -0
- hatch/package_loader.py +263 -0
- hatch/python_environment_manager.py +734 -0
- hatch/registry_explorer.py +171 -0
- hatch/registry_retriever.py +335 -0
- hatch/template_generator.py +179 -0
- hatch_xclam-0.7.0.dist-info/METADATA +150 -0
- hatch_xclam-0.7.0.dist-info/RECORD +93 -0
- hatch_xclam-0.7.0.dist-info/WHEEL +5 -0
- hatch_xclam-0.7.0.dist-info/entry_points.txt +2 -0
- hatch_xclam-0.7.0.dist-info/licenses/LICENSE +661 -0
- hatch_xclam-0.7.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +1 -0
- tests/run_environment_tests.py +124 -0
- tests/test_cli_version.py +122 -0
- tests/test_data/packages/basic/base_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/basic/base_pkg/mcp_server.py +21 -0
- tests/test_data/packages/basic/base_pkg_v2/hatch_mcp_server.py +18 -0
- tests/test_data/packages/basic/base_pkg_v2/mcp_server.py +21 -0
- tests/test_data/packages/basic/utility_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/basic/utility_pkg/mcp_server.py +21 -0
- tests/test_data/packages/dependencies/complex_dep_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/dependencies/complex_dep_pkg/mcp_server.py +21 -0
- tests/test_data/packages/dependencies/docker_dep_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/dependencies/docker_dep_pkg/mcp_server.py +21 -0
- tests/test_data/packages/dependencies/mixed_dep_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/dependencies/mixed_dep_pkg/mcp_server.py +21 -0
- tests/test_data/packages/dependencies/python_dep_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/dependencies/python_dep_pkg/mcp_server.py +21 -0
- tests/test_data/packages/dependencies/simple_dep_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/dependencies/simple_dep_pkg/mcp_server.py +21 -0
- tests/test_data/packages/dependencies/system_dep_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/dependencies/system_dep_pkg/mcp_server.py +21 -0
- tests/test_data/packages/error_scenarios/circular_dep_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/error_scenarios/circular_dep_pkg/mcp_server.py +21 -0
- tests/test_data/packages/error_scenarios/circular_dep_pkg_b/hatch_mcp_server.py +18 -0
- tests/test_data/packages/error_scenarios/circular_dep_pkg_b/mcp_server.py +21 -0
- tests/test_data/packages/error_scenarios/invalid_dep_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/error_scenarios/invalid_dep_pkg/mcp_server.py +21 -0
- tests/test_data/packages/error_scenarios/version_conflict_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/error_scenarios/version_conflict_pkg/mcp_server.py +21 -0
- tests/test_data/packages/schema_versions/schema_v1_1_0_pkg/main.py +11 -0
- tests/test_data/packages/schema_versions/schema_v1_2_0_pkg/main.py +11 -0
- tests/test_data/packages/schema_versions/schema_v1_2_1_pkg/hatch_mcp_server.py +18 -0
- tests/test_data/packages/schema_versions/schema_v1_2_1_pkg/mcp_server.py +21 -0
- tests/test_data_utils.py +472 -0
- tests/test_dependency_orchestrator_consent.py +266 -0
- tests/test_docker_installer.py +524 -0
- tests/test_env_manip.py +991 -0
- tests/test_hatch_installer.py +179 -0
- tests/test_installer_base.py +221 -0
- tests/test_mcp_atomic_operations.py +276 -0
- tests/test_mcp_backup_integration.py +308 -0
- tests/test_mcp_cli_all_host_specific_args.py +303 -0
- tests/test_mcp_cli_backup_management.py +295 -0
- tests/test_mcp_cli_direct_management.py +453 -0
- tests/test_mcp_cli_discovery_listing.py +582 -0
- tests/test_mcp_cli_host_config_integration.py +823 -0
- tests/test_mcp_cli_package_management.py +360 -0
- tests/test_mcp_cli_partial_updates.py +859 -0
- tests/test_mcp_environment_integration.py +520 -0
- tests/test_mcp_host_config_backup.py +257 -0
- tests/test_mcp_host_configuration_manager.py +331 -0
- tests/test_mcp_host_registry_decorator.py +348 -0
- tests/test_mcp_pydantic_architecture_v4.py +603 -0
- tests/test_mcp_server_config_models.py +242 -0
- tests/test_mcp_server_config_type_field.py +221 -0
- tests/test_mcp_sync_functionality.py +316 -0
- tests/test_mcp_user_feedback_reporting.py +359 -0
- tests/test_non_tty_integration.py +281 -0
- tests/test_online_package_loader.py +202 -0
- tests/test_python_environment_manager.py +882 -0
- tests/test_python_installer.py +327 -0
- tests/test_registry.py +51 -0
- tests/test_registry_retriever.py +250 -0
- tests/test_system_installer.py +733 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test suite for MCP CLI package management enhancements.
|
|
3
|
+
|
|
4
|
+
This module tests the enhanced package management commands with MCP host
|
|
5
|
+
configuration integration following CrackingShells testing standards.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import unittest
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from unittest.mock import MagicMock, mock_open, patch
|
|
12
|
+
|
|
13
|
+
# Add the parent directory to the path to import wobble
|
|
14
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
from wobble.decorators import integration_test, regression_test
|
|
18
|
+
except ImportError:
|
|
19
|
+
# Fallback decorators if wobble is not available
|
|
20
|
+
def regression_test(func):
|
|
21
|
+
return func
|
|
22
|
+
|
|
23
|
+
def integration_test(scope="component"):
|
|
24
|
+
def decorator(func):
|
|
25
|
+
return func
|
|
26
|
+
|
|
27
|
+
return decorator
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
from hatch.cli_hatch import (
|
|
31
|
+
get_package_mcp_server_config,
|
|
32
|
+
parse_host_list,
|
|
33
|
+
request_confirmation,
|
|
34
|
+
)
|
|
35
|
+
from hatch.mcp_host_config import MCPHostType, MCPServerConfig
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TestMCPCLIPackageManagement(unittest.TestCase):
|
|
39
|
+
"""Test suite for MCP CLI package management enhancements."""
|
|
40
|
+
|
|
41
|
+
@regression_test
|
|
42
|
+
def test_parse_host_list_comma_separated(self):
|
|
43
|
+
"""Test parsing comma-separated host list."""
|
|
44
|
+
hosts = parse_host_list("claude-desktop,cursor,vscode")
|
|
45
|
+
expected = [MCPHostType.CLAUDE_DESKTOP, MCPHostType.CURSOR, MCPHostType.VSCODE]
|
|
46
|
+
self.assertEqual(hosts, expected)
|
|
47
|
+
|
|
48
|
+
@regression_test
|
|
49
|
+
def test_parse_host_list_single_host(self):
|
|
50
|
+
"""Test parsing single host."""
|
|
51
|
+
hosts = parse_host_list("claude-desktop")
|
|
52
|
+
expected = [MCPHostType.CLAUDE_DESKTOP]
|
|
53
|
+
self.assertEqual(hosts, expected)
|
|
54
|
+
|
|
55
|
+
@regression_test
|
|
56
|
+
def test_parse_host_list_empty(self):
|
|
57
|
+
"""Test parsing empty host list."""
|
|
58
|
+
hosts = parse_host_list("")
|
|
59
|
+
self.assertEqual(hosts, [])
|
|
60
|
+
|
|
61
|
+
@regression_test
|
|
62
|
+
def test_parse_host_list_none(self):
|
|
63
|
+
"""Test parsing None host list."""
|
|
64
|
+
hosts = parse_host_list(None)
|
|
65
|
+
self.assertEqual(hosts, [])
|
|
66
|
+
|
|
67
|
+
@regression_test
|
|
68
|
+
def test_parse_host_list_all(self):
|
|
69
|
+
"""Test parsing 'all' host list."""
|
|
70
|
+
with patch(
|
|
71
|
+
"hatch.cli_hatch.MCPHostRegistry.detect_available_hosts"
|
|
72
|
+
) as mock_detect:
|
|
73
|
+
mock_detect.return_value = [MCPHostType.CLAUDE_DESKTOP, MCPHostType.CURSOR]
|
|
74
|
+
hosts = parse_host_list("all")
|
|
75
|
+
expected = [MCPHostType.CLAUDE_DESKTOP, MCPHostType.CURSOR]
|
|
76
|
+
self.assertEqual(hosts, expected)
|
|
77
|
+
mock_detect.assert_called_once()
|
|
78
|
+
|
|
79
|
+
@regression_test
|
|
80
|
+
def test_parse_host_list_invalid_host(self):
|
|
81
|
+
"""Test parsing invalid host raises ValueError."""
|
|
82
|
+
with self.assertRaises(ValueError) as context:
|
|
83
|
+
parse_host_list("invalid-host")
|
|
84
|
+
|
|
85
|
+
self.assertIn("Unknown host 'invalid-host'", str(context.exception))
|
|
86
|
+
self.assertIn("Available:", str(context.exception))
|
|
87
|
+
|
|
88
|
+
@regression_test
|
|
89
|
+
def test_parse_host_list_mixed_valid_invalid(self):
|
|
90
|
+
"""Test parsing mixed valid and invalid hosts."""
|
|
91
|
+
with self.assertRaises(ValueError) as context:
|
|
92
|
+
parse_host_list("claude-desktop,invalid-host,cursor")
|
|
93
|
+
|
|
94
|
+
self.assertIn("Unknown host 'invalid-host'", str(context.exception))
|
|
95
|
+
|
|
96
|
+
@regression_test
|
|
97
|
+
def test_parse_host_list_whitespace_handling(self):
|
|
98
|
+
"""Test parsing host list with whitespace."""
|
|
99
|
+
hosts = parse_host_list(" claude-desktop , cursor , vscode ")
|
|
100
|
+
expected = [MCPHostType.CLAUDE_DESKTOP, MCPHostType.CURSOR, MCPHostType.VSCODE]
|
|
101
|
+
self.assertEqual(hosts, expected)
|
|
102
|
+
|
|
103
|
+
@regression_test
|
|
104
|
+
def test_request_confirmation_auto_approve(self):
|
|
105
|
+
"""Test confirmation with auto-approve flag."""
|
|
106
|
+
result = request_confirmation("Test message?", auto_approve=True)
|
|
107
|
+
self.assertTrue(result)
|
|
108
|
+
|
|
109
|
+
@regression_test
|
|
110
|
+
def test_request_confirmation_user_yes(self):
|
|
111
|
+
"""Test confirmation with user saying yes."""
|
|
112
|
+
with patch("builtins.input", return_value="y"):
|
|
113
|
+
result = request_confirmation("Test message?", auto_approve=False)
|
|
114
|
+
self.assertTrue(result)
|
|
115
|
+
|
|
116
|
+
@regression_test
|
|
117
|
+
def test_request_confirmation_user_yes_full(self):
|
|
118
|
+
"""Test confirmation with user saying 'yes'."""
|
|
119
|
+
with patch("builtins.input", return_value="yes"):
|
|
120
|
+
result = request_confirmation("Test message?", auto_approve=False)
|
|
121
|
+
self.assertTrue(result)
|
|
122
|
+
|
|
123
|
+
@regression_test
|
|
124
|
+
def test_request_confirmation_user_no(self):
|
|
125
|
+
"""Test confirmation with user saying no."""
|
|
126
|
+
with patch.dict("os.environ", {"HATCH_AUTO_APPROVE": ""}, clear=False):
|
|
127
|
+
with patch("builtins.input", return_value="n"):
|
|
128
|
+
result = request_confirmation("Test message?", auto_approve=False)
|
|
129
|
+
self.assertFalse(result)
|
|
130
|
+
|
|
131
|
+
@regression_test
|
|
132
|
+
def test_request_confirmation_user_no_full(self):
|
|
133
|
+
"""Test confirmation with user saying 'no'."""
|
|
134
|
+
with patch.dict("os.environ", {"HATCH_AUTO_APPROVE": ""}, clear=False):
|
|
135
|
+
with patch("builtins.input", return_value="no"):
|
|
136
|
+
result = request_confirmation("Test message?", auto_approve=False)
|
|
137
|
+
self.assertFalse(result)
|
|
138
|
+
|
|
139
|
+
@regression_test
|
|
140
|
+
def test_request_confirmation_user_empty(self):
|
|
141
|
+
"""Test confirmation with user pressing enter (default no)."""
|
|
142
|
+
with patch.dict("os.environ", {"HATCH_AUTO_APPROVE": ""}, clear=False):
|
|
143
|
+
with patch("builtins.input", return_value=""):
|
|
144
|
+
result = request_confirmation("Test message?", auto_approve=False)
|
|
145
|
+
self.assertFalse(result)
|
|
146
|
+
|
|
147
|
+
@integration_test(scope="component")
|
|
148
|
+
def test_package_add_argument_parsing(self):
|
|
149
|
+
"""Test package add command argument parsing with MCP flags."""
|
|
150
|
+
import argparse
|
|
151
|
+
|
|
152
|
+
from hatch.cli_hatch import main
|
|
153
|
+
|
|
154
|
+
# Mock argparse to capture parsed arguments
|
|
155
|
+
with patch("argparse.ArgumentParser.parse_args") as mock_parse:
|
|
156
|
+
mock_args = MagicMock()
|
|
157
|
+
mock_args.command = "package"
|
|
158
|
+
mock_args.pkg_command = "add"
|
|
159
|
+
mock_args.package_path_or_name = "test-package"
|
|
160
|
+
mock_args.host = "claude-desktop,cursor"
|
|
161
|
+
mock_args.env = None
|
|
162
|
+
mock_args.version = None
|
|
163
|
+
mock_args.force_download = False
|
|
164
|
+
mock_args.refresh_registry = False
|
|
165
|
+
mock_args.auto_approve = False
|
|
166
|
+
mock_parse.return_value = mock_args
|
|
167
|
+
|
|
168
|
+
# Mock environment manager to avoid actual operations
|
|
169
|
+
with patch("hatch.cli_hatch.HatchEnvironmentManager") as mock_env_manager:
|
|
170
|
+
mock_env_manager.return_value.add_package_to_environment.return_value = True
|
|
171
|
+
mock_env_manager.return_value.get_current_environment.return_value = (
|
|
172
|
+
"default"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Mock MCP manager
|
|
176
|
+
with patch("hatch.cli_hatch.MCPHostConfigurationManager"):
|
|
177
|
+
with patch("builtins.print") as mock_print:
|
|
178
|
+
result = main()
|
|
179
|
+
|
|
180
|
+
# Should succeed
|
|
181
|
+
self.assertEqual(result, 0)
|
|
182
|
+
|
|
183
|
+
# Should print success message
|
|
184
|
+
mock_print.assert_any_call(
|
|
185
|
+
"Successfully added package: test-package"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
@integration_test(scope="component")
|
|
189
|
+
def test_package_sync_argument_parsing(self):
|
|
190
|
+
"""Test package sync command argument parsing."""
|
|
191
|
+
import argparse
|
|
192
|
+
|
|
193
|
+
from hatch.cli_hatch import main
|
|
194
|
+
|
|
195
|
+
# Mock argparse to capture parsed arguments
|
|
196
|
+
with patch("argparse.ArgumentParser.parse_args") as mock_parse:
|
|
197
|
+
mock_args = MagicMock()
|
|
198
|
+
mock_args.command = "package"
|
|
199
|
+
mock_args.pkg_command = "sync"
|
|
200
|
+
mock_args.package_name = "test-package"
|
|
201
|
+
mock_args.host = "claude-desktop,cursor"
|
|
202
|
+
mock_args.env = None
|
|
203
|
+
mock_args.dry_run = True # Use dry run to avoid actual configuration
|
|
204
|
+
mock_args.auto_approve = False
|
|
205
|
+
mock_args.no_backup = False
|
|
206
|
+
mock_parse.return_value = mock_args
|
|
207
|
+
|
|
208
|
+
# Mock the get_package_mcp_server_config function
|
|
209
|
+
with patch(
|
|
210
|
+
"hatch.cli_hatch.get_package_mcp_server_config"
|
|
211
|
+
) as mock_get_config:
|
|
212
|
+
mock_server_config = MagicMock()
|
|
213
|
+
mock_server_config.name = "test-package"
|
|
214
|
+
mock_server_config.args = ["/path/to/server.py"]
|
|
215
|
+
mock_get_config.return_value = mock_server_config
|
|
216
|
+
|
|
217
|
+
# Mock environment manager
|
|
218
|
+
with patch(
|
|
219
|
+
"hatch.cli_hatch.HatchEnvironmentManager"
|
|
220
|
+
) as mock_env_manager:
|
|
221
|
+
mock_env_manager.return_value.get_current_environment.return_value = "default"
|
|
222
|
+
|
|
223
|
+
# Mock MCP manager
|
|
224
|
+
with patch("hatch.cli_hatch.MCPHostConfigurationManager"):
|
|
225
|
+
with patch("builtins.print") as mock_print:
|
|
226
|
+
result = main()
|
|
227
|
+
|
|
228
|
+
# Should succeed
|
|
229
|
+
self.assertEqual(result, 0)
|
|
230
|
+
|
|
231
|
+
# Should print dry run message (new format includes dependency info)
|
|
232
|
+
mock_print.assert_any_call(
|
|
233
|
+
"[DRY RUN] Would synchronize MCP servers for 1 package(s) to hosts: ['claude-desktop', 'cursor']"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
@integration_test(scope="component")
|
|
237
|
+
def test_package_sync_package_not_found(self):
|
|
238
|
+
"""Test package sync when package doesn't exist."""
|
|
239
|
+
import argparse
|
|
240
|
+
|
|
241
|
+
from hatch.cli_hatch import main
|
|
242
|
+
|
|
243
|
+
# Mock argparse to capture parsed arguments
|
|
244
|
+
with patch("argparse.ArgumentParser.parse_args") as mock_parse:
|
|
245
|
+
mock_args = MagicMock()
|
|
246
|
+
mock_args.command = "package"
|
|
247
|
+
mock_args.pkg_command = "sync"
|
|
248
|
+
mock_args.package_name = "nonexistent-package"
|
|
249
|
+
mock_args.host = "claude-desktop"
|
|
250
|
+
mock_args.env = None
|
|
251
|
+
mock_args.dry_run = False
|
|
252
|
+
mock_args.auto_approve = False
|
|
253
|
+
mock_args.no_backup = False
|
|
254
|
+
mock_parse.return_value = mock_args
|
|
255
|
+
|
|
256
|
+
# Mock the get_package_mcp_server_config function to raise ValueError
|
|
257
|
+
with patch(
|
|
258
|
+
"hatch.cli_hatch.get_package_mcp_server_config"
|
|
259
|
+
) as mock_get_config:
|
|
260
|
+
mock_get_config.side_effect = ValueError(
|
|
261
|
+
"Package 'nonexistent-package' not found in environment 'default'"
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Mock environment manager
|
|
265
|
+
with patch(
|
|
266
|
+
"hatch.cli_hatch.HatchEnvironmentManager"
|
|
267
|
+
) as mock_env_manager:
|
|
268
|
+
mock_env_manager.return_value.get_current_environment.return_value = "default"
|
|
269
|
+
|
|
270
|
+
with patch("builtins.print") as mock_print:
|
|
271
|
+
result = main()
|
|
272
|
+
|
|
273
|
+
# Should fail
|
|
274
|
+
self.assertEqual(result, 1)
|
|
275
|
+
|
|
276
|
+
# Should print error message (new format)
|
|
277
|
+
mock_print.assert_any_call(
|
|
278
|
+
"Error: No MCP server configurations found for package 'nonexistent-package' or its dependencies"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
@regression_test
|
|
282
|
+
def test_get_package_mcp_server_config_success(self):
|
|
283
|
+
"""Test successful MCP server config retrieval."""
|
|
284
|
+
# Mock environment manager
|
|
285
|
+
mock_env_manager = MagicMock()
|
|
286
|
+
mock_env_manager.list_packages.return_value = [
|
|
287
|
+
{
|
|
288
|
+
"name": "test-package",
|
|
289
|
+
"version": "1.0.0",
|
|
290
|
+
"source": {"path": "/path/to/package"},
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
# Mock the Python executable method to return a proper string
|
|
294
|
+
mock_env_manager.get_current_python_executable.return_value = "/path/to/python"
|
|
295
|
+
|
|
296
|
+
# Mock file system and metadata
|
|
297
|
+
with patch("pathlib.Path.exists", return_value=True):
|
|
298
|
+
with patch(
|
|
299
|
+
"builtins.open",
|
|
300
|
+
mock_open(
|
|
301
|
+
read_data='{"package_schema_version": "1.2.1", "name": "test-package"}'
|
|
302
|
+
),
|
|
303
|
+
):
|
|
304
|
+
with patch(
|
|
305
|
+
"hatch_validator.package.package_service.PackageService"
|
|
306
|
+
) as mock_service_class:
|
|
307
|
+
mock_service = MagicMock()
|
|
308
|
+
mock_service.get_mcp_entry_point.return_value = "mcp_server.py"
|
|
309
|
+
mock_service_class.return_value = mock_service
|
|
310
|
+
|
|
311
|
+
config = get_package_mcp_server_config(
|
|
312
|
+
mock_env_manager, "test-env", "test-package"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
self.assertIsInstance(config, MCPServerConfig)
|
|
316
|
+
self.assertEqual(config.name, "test-package")
|
|
317
|
+
self.assertEqual(
|
|
318
|
+
config.command, "/path/to/python"
|
|
319
|
+
) # Now uses environment-specific Python
|
|
320
|
+
self.assertTrue(config.args[0].endswith("mcp_server.py"))
|
|
321
|
+
|
|
322
|
+
@regression_test
|
|
323
|
+
def test_get_package_mcp_server_config_package_not_found(self):
|
|
324
|
+
"""Test MCP server config retrieval when package not found."""
|
|
325
|
+
# Mock environment manager with empty package list
|
|
326
|
+
mock_env_manager = MagicMock()
|
|
327
|
+
mock_env_manager.list_packages.return_value = []
|
|
328
|
+
|
|
329
|
+
with self.assertRaises(ValueError) as context:
|
|
330
|
+
get_package_mcp_server_config(
|
|
331
|
+
mock_env_manager, "test-env", "nonexistent-package"
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
self.assertIn("Package 'nonexistent-package' not found", str(context.exception))
|
|
335
|
+
|
|
336
|
+
@regression_test
|
|
337
|
+
def test_get_package_mcp_server_config_no_metadata(self):
|
|
338
|
+
"""Test MCP server config retrieval when package has no metadata."""
|
|
339
|
+
# Mock environment manager
|
|
340
|
+
mock_env_manager = MagicMock()
|
|
341
|
+
mock_env_manager.list_packages.return_value = [
|
|
342
|
+
{
|
|
343
|
+
"name": "test-package",
|
|
344
|
+
"version": "1.0.0",
|
|
345
|
+
"source": {"path": "/path/to/package"},
|
|
346
|
+
}
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
# Mock file system - metadata file doesn't exist
|
|
350
|
+
with patch("pathlib.Path.exists", return_value=False):
|
|
351
|
+
with self.assertRaises(ValueError) as context:
|
|
352
|
+
get_package_mcp_server_config(
|
|
353
|
+
mock_env_manager, "test-env", "test-package"
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
self.assertIn("not a Hatch package", str(context.exception))
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
if __name__ == "__main__":
|
|
360
|
+
unittest.main()
|