hud-python 0.4.11__py3-none-any.whl → 0.4.13__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.
Potentially problematic release.
This version of hud-python might be problematic. Click here for more details.
- hud/__main__.py +8 -0
- hud/agents/base.py +7 -8
- hud/agents/langchain.py +2 -2
- hud/agents/tests/test_openai.py +3 -1
- hud/cli/__init__.py +114 -52
- hud/cli/build.py +121 -71
- hud/cli/debug.py +2 -2
- hud/cli/{mcp_server.py → dev.py} +101 -38
- hud/cli/eval.py +175 -90
- hud/cli/init.py +442 -64
- hud/cli/list_func.py +72 -71
- hud/cli/pull.py +1 -2
- hud/cli/push.py +35 -23
- hud/cli/remove.py +35 -41
- hud/cli/tests/test_analyze.py +2 -1
- hud/cli/tests/test_analyze_metadata.py +42 -49
- hud/cli/tests/test_build.py +28 -52
- hud/cli/tests/test_cursor.py +1 -1
- hud/cli/tests/test_debug.py +1 -1
- hud/cli/tests/test_list_func.py +75 -64
- hud/cli/tests/test_main_module.py +30 -0
- hud/cli/tests/test_mcp_server.py +3 -3
- hud/cli/tests/test_pull.py +30 -61
- hud/cli/tests/test_push.py +70 -89
- hud/cli/tests/test_registry.py +36 -38
- hud/cli/tests/test_utils.py +1 -1
- hud/cli/utils/__init__.py +1 -0
- hud/cli/{docker_utils.py → utils/docker.py} +36 -0
- hud/cli/{env_utils.py → utils/environment.py} +7 -7
- hud/cli/{interactive.py → utils/interactive.py} +91 -19
- hud/cli/{analyze_metadata.py → utils/metadata.py} +12 -8
- hud/cli/{registry.py → utils/registry.py} +28 -30
- hud/cli/{remote_runner.py → utils/remote_runner.py} +1 -1
- hud/cli/utils/runner.py +134 -0
- hud/cli/utils/server.py +250 -0
- hud/clients/base.py +1 -1
- hud/clients/fastmcp.py +5 -13
- hud/clients/mcp_use.py +6 -10
- hud/server/server.py +35 -5
- hud/shared/exceptions.py +11 -0
- hud/shared/tests/test_exceptions.py +22 -0
- hud/telemetry/tests/__init__.py +0 -0
- hud/telemetry/tests/test_replay.py +40 -0
- hud/telemetry/tests/test_trace.py +63 -0
- hud/tools/base.py +20 -3
- hud/tools/computer/hud.py +15 -6
- hud/tools/executors/tests/test_base_executor.py +27 -0
- hud/tools/response.py +12 -8
- hud/tools/tests/test_response.py +60 -0
- hud/tools/tests/test_tools_init.py +49 -0
- hud/utils/design.py +19 -8
- hud/utils/mcp.py +17 -5
- hud/utils/tests/test_mcp.py +112 -0
- hud/utils/tests/test_version.py +1 -1
- hud/version.py +1 -1
- {hud_python-0.4.11.dist-info → hud_python-0.4.13.dist-info}/METADATA +16 -13
- {hud_python-0.4.11.dist-info → hud_python-0.4.13.dist-info}/RECORD +62 -52
- hud/cli/runner.py +0 -160
- /hud/cli/{cursor.py → utils/cursor.py} +0 -0
- /hud/cli/{utils.py → utils/logging.py} +0 -0
- {hud_python-0.4.11.dist-info → hud_python-0.4.13.dist-info}/WHEEL +0 -0
- {hud_python-0.4.11.dist-info → hud_python-0.4.13.dist-info}/entry_points.txt +0 -0
- {hud_python-0.4.11.dist-info → hud_python-0.4.13.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,20 +3,17 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
-
import os
|
|
7
|
-
import tempfile
|
|
8
|
-
from pathlib import Path
|
|
9
6
|
from unittest import mock
|
|
10
7
|
|
|
11
8
|
import pytest
|
|
12
9
|
import yaml
|
|
13
10
|
|
|
14
|
-
from hud.cli.
|
|
11
|
+
from hud.cli.utils.metadata import (
|
|
15
12
|
analyze_from_metadata,
|
|
16
13
|
check_local_cache,
|
|
17
14
|
fetch_lock_from_registry,
|
|
18
15
|
)
|
|
19
|
-
from hud.cli.registry import save_to_registry
|
|
16
|
+
from hud.cli.utils.registry import save_to_registry
|
|
20
17
|
|
|
21
18
|
|
|
22
19
|
@pytest.fixture
|
|
@@ -79,9 +76,7 @@ class TestFetchLockFromRegistry:
|
|
|
79
76
|
"""Test successful fetch from registry."""
|
|
80
77
|
mock_response = mock.Mock()
|
|
81
78
|
mock_response.status_code = 200
|
|
82
|
-
mock_response.json.return_value = {
|
|
83
|
-
"lock": yaml.dump({"test": "data"})
|
|
84
|
-
}
|
|
79
|
+
mock_response.json.return_value = {"lock": yaml.dump({"test": "data"})}
|
|
85
80
|
mock_get.return_value = mock_response
|
|
86
81
|
|
|
87
82
|
result = fetch_lock_from_registry("test/env:latest")
|
|
@@ -93,9 +88,7 @@ class TestFetchLockFromRegistry:
|
|
|
93
88
|
"""Test fetch when response has lock_data key."""
|
|
94
89
|
mock_response = mock.Mock()
|
|
95
90
|
mock_response.status_code = 200
|
|
96
|
-
mock_response.json.return_value = {
|
|
97
|
-
"lock_data": {"test": "data"}
|
|
98
|
-
}
|
|
91
|
+
mock_response.json.return_value = {"lock_data": {"test": "data"}}
|
|
99
92
|
mock_get.return_value = mock_response
|
|
100
93
|
|
|
101
94
|
result = fetch_lock_from_registry("test/env:latest")
|
|
@@ -120,10 +113,10 @@ class TestFetchLockFromRegistry:
|
|
|
120
113
|
mock_get.return_value = mock_response
|
|
121
114
|
|
|
122
115
|
fetch_lock_from_registry("test/env")
|
|
123
|
-
|
|
124
|
-
# Check that the URL includes :latest
|
|
116
|
+
|
|
117
|
+
# Check that the URL includes :latest (URL-encoded)
|
|
125
118
|
call_args = mock_get.call_args
|
|
126
|
-
assert "test/env
|
|
119
|
+
assert "test/env%3Alatest" in call_args[0][0]
|
|
127
120
|
|
|
128
121
|
@mock.patch("requests.get")
|
|
129
122
|
def test_fetch_lock_failure(self, mock_get):
|
|
@@ -150,11 +143,11 @@ class TestCheckLocalCache:
|
|
|
150
143
|
def test_check_local_cache_found(self, mock_registry_dir, sample_lock_data, monkeypatch):
|
|
151
144
|
"""Test finding lock data in local cache."""
|
|
152
145
|
# Mock registry directory
|
|
153
|
-
monkeypatch.setattr("hud.cli.registry.get_registry_dir", lambda: mock_registry_dir)
|
|
154
|
-
|
|
146
|
+
monkeypatch.setattr("hud.cli.utils.registry.get_registry_dir", lambda: mock_registry_dir)
|
|
147
|
+
|
|
155
148
|
# Save sample data to registry
|
|
156
149
|
save_to_registry(sample_lock_data, "test/environment:latest", verbose=False)
|
|
157
|
-
|
|
150
|
+
|
|
158
151
|
# Check cache
|
|
159
152
|
result = check_local_cache("test/environment:latest")
|
|
160
153
|
assert result is not None
|
|
@@ -162,21 +155,21 @@ class TestCheckLocalCache:
|
|
|
162
155
|
|
|
163
156
|
def test_check_local_cache_not_found(self, mock_registry_dir, monkeypatch):
|
|
164
157
|
"""Test when lock data not in local cache."""
|
|
165
|
-
monkeypatch.setattr("hud.cli.registry.get_registry_dir", lambda: mock_registry_dir)
|
|
166
|
-
|
|
158
|
+
monkeypatch.setattr("hud.cli.utils.registry.get_registry_dir", lambda: mock_registry_dir)
|
|
159
|
+
|
|
167
160
|
result = check_local_cache("nonexistent/env:latest")
|
|
168
161
|
assert result is None
|
|
169
162
|
|
|
170
163
|
def test_check_local_cache_invalid_yaml(self, mock_registry_dir, monkeypatch):
|
|
171
164
|
"""Test when lock file has invalid YAML."""
|
|
172
|
-
monkeypatch.setattr("hud.cli.registry.get_registry_dir", lambda: mock_registry_dir)
|
|
173
|
-
|
|
165
|
+
monkeypatch.setattr("hud.cli.utils.registry.get_registry_dir", lambda: mock_registry_dir)
|
|
166
|
+
|
|
174
167
|
# Create invalid lock file
|
|
175
|
-
digest = "
|
|
168
|
+
digest = "invalid"
|
|
176
169
|
lock_file = mock_registry_dir / digest / "hud.lock.yaml"
|
|
177
170
|
lock_file.parent.mkdir(parents=True)
|
|
178
171
|
lock_file.write_text("invalid: yaml: content:")
|
|
179
|
-
|
|
172
|
+
|
|
180
173
|
result = check_local_cache("test/invalid:latest")
|
|
181
174
|
assert result is None
|
|
182
175
|
|
|
@@ -189,60 +182,60 @@ class TestCheckLocalCache:
|
|
|
189
182
|
class TestAnalyzeFromMetadata:
|
|
190
183
|
"""Test the main analyze_from_metadata function."""
|
|
191
184
|
|
|
192
|
-
@mock.patch("hud.cli.
|
|
193
|
-
@mock.patch("hud.cli.
|
|
185
|
+
@mock.patch("hud.cli.utils.metadata.check_local_cache")
|
|
186
|
+
@mock.patch("hud.cli.utils.metadata.console")
|
|
194
187
|
async def test_analyze_from_local_cache(self, mock_console, mock_check, sample_lock_data):
|
|
195
188
|
"""Test analyzing from local cache."""
|
|
196
189
|
mock_check.return_value = sample_lock_data
|
|
197
|
-
|
|
190
|
+
|
|
198
191
|
await analyze_from_metadata("test/env:latest", "json", verbose=False)
|
|
199
|
-
|
|
192
|
+
|
|
200
193
|
mock_check.assert_called_once_with("test/env:latest")
|
|
201
194
|
# Should output JSON
|
|
202
195
|
mock_console.print_json.assert_called_once()
|
|
203
196
|
|
|
204
|
-
@mock.patch("hud.cli.
|
|
205
|
-
@mock.patch("hud.cli.
|
|
206
|
-
@mock.patch("hud.cli.
|
|
207
|
-
@mock.patch("hud.cli.
|
|
197
|
+
@mock.patch("hud.cli.utils.metadata.check_local_cache")
|
|
198
|
+
@mock.patch("hud.cli.utils.metadata.fetch_lock_from_registry")
|
|
199
|
+
@mock.patch("hud.cli.utils.registry.save_to_registry")
|
|
200
|
+
@mock.patch("hud.cli.utils.metadata.console")
|
|
208
201
|
async def test_analyze_from_registry(
|
|
209
202
|
self, mock_console, mock_save, mock_fetch, mock_check, sample_lock_data
|
|
210
203
|
):
|
|
211
204
|
"""Test analyzing from registry when not in cache."""
|
|
212
205
|
mock_check.return_value = None
|
|
213
206
|
mock_fetch.return_value = sample_lock_data
|
|
214
|
-
|
|
207
|
+
|
|
215
208
|
await analyze_from_metadata("test/env:latest", "json", verbose=False)
|
|
216
|
-
|
|
209
|
+
|
|
217
210
|
mock_check.assert_called_once()
|
|
218
211
|
mock_fetch.assert_called_once()
|
|
219
212
|
mock_save.assert_called_once() # Should save to cache
|
|
220
213
|
mock_console.print_json.assert_called_once()
|
|
221
214
|
|
|
222
|
-
@mock.patch("hud.cli.
|
|
223
|
-
@mock.patch("hud.cli.
|
|
224
|
-
@mock.patch("hud.cli.
|
|
225
|
-
@mock.patch("hud.cli.
|
|
215
|
+
@mock.patch("hud.cli.utils.metadata.check_local_cache")
|
|
216
|
+
@mock.patch("hud.cli.utils.metadata.fetch_lock_from_registry")
|
|
217
|
+
@mock.patch("hud.cli.utils.metadata.design")
|
|
218
|
+
@mock.patch("hud.cli.utils.metadata.console")
|
|
226
219
|
async def test_analyze_not_found(self, mock_console, mock_design, mock_fetch, mock_check):
|
|
227
220
|
"""Test when environment not found anywhere."""
|
|
228
221
|
mock_check.return_value = None
|
|
229
222
|
mock_fetch.return_value = None
|
|
230
|
-
|
|
223
|
+
|
|
231
224
|
await analyze_from_metadata("test/notfound:latest", "json", verbose=False)
|
|
232
|
-
|
|
225
|
+
|
|
233
226
|
# Should show error
|
|
234
227
|
mock_design.error.assert_called_with("Environment metadata not found")
|
|
235
228
|
# Should print suggestions
|
|
236
229
|
mock_console.print.assert_called()
|
|
237
230
|
|
|
238
|
-
@mock.patch("hud.cli.
|
|
239
|
-
@mock.patch("hud.cli.
|
|
231
|
+
@mock.patch("hud.cli.utils.metadata.check_local_cache")
|
|
232
|
+
@mock.patch("hud.cli.utils.metadata.console")
|
|
240
233
|
async def test_analyze_verbose_mode(self, mock_console, mock_check, sample_lock_data):
|
|
241
234
|
"""Test verbose mode includes input schemas."""
|
|
242
235
|
mock_check.return_value = sample_lock_data
|
|
243
|
-
|
|
236
|
+
|
|
244
237
|
await analyze_from_metadata("test/env:latest", "json", verbose=True)
|
|
245
|
-
|
|
238
|
+
|
|
246
239
|
# In verbose mode, the JSON output should include input schemas
|
|
247
240
|
mock_console.print_json.assert_called_once()
|
|
248
241
|
# Get the JSON string that was printed
|
|
@@ -250,13 +243,13 @@ class TestAnalyzeFromMetadata:
|
|
|
250
243
|
output_data = json.loads(call_args)
|
|
251
244
|
assert "inputSchema" in output_data["tools"][0]
|
|
252
245
|
|
|
253
|
-
@mock.patch("hud.cli.
|
|
254
|
-
@mock.patch("hud.cli.
|
|
246
|
+
@mock.patch("hud.cli.utils.metadata.check_local_cache")
|
|
247
|
+
@mock.patch("hud.cli.utils.metadata.fetch_lock_from_registry")
|
|
255
248
|
async def test_analyze_registry_reference_parsing(self, mock_fetch, mock_check):
|
|
256
249
|
"""Test parsing of different registry reference formats."""
|
|
257
250
|
mock_check.return_value = None
|
|
258
251
|
mock_fetch.return_value = {"test": "data"}
|
|
259
|
-
|
|
252
|
+
|
|
260
253
|
# Test different reference formats
|
|
261
254
|
test_cases = [
|
|
262
255
|
("docker.io/org/name:tag", "org/name:tag"),
|
|
@@ -265,13 +258,13 @@ class TestAnalyzeFromMetadata:
|
|
|
265
258
|
("org/name", "org/name"),
|
|
266
259
|
("name:tag", "name:tag"),
|
|
267
260
|
]
|
|
268
|
-
|
|
261
|
+
|
|
269
262
|
for input_ref, expected_call in test_cases:
|
|
270
263
|
await analyze_from_metadata(input_ref, "json", verbose=False)
|
|
271
|
-
|
|
264
|
+
|
|
272
265
|
# Check what was passed to fetch_lock_from_registry
|
|
273
266
|
calls = mock_fetch.call_args_list
|
|
274
267
|
last_call = calls[-1][0][0]
|
|
275
|
-
|
|
268
|
+
|
|
276
269
|
# The function might add :latest, so check base name
|
|
277
270
|
assert expected_call.split(":")[0] in last_call
|
hud/cli/tests/test_build.py
CHANGED
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import subprocess
|
|
6
|
-
import tempfile
|
|
7
|
-
from datetime import datetime
|
|
8
|
-
from pathlib import Path
|
|
9
6
|
from unittest import mock
|
|
10
7
|
|
|
11
8
|
import pytest
|
|
@@ -81,11 +78,7 @@ class TestGetExistingVersion:
|
|
|
81
78
|
|
|
82
79
|
def test_get_version_from_lock(self, tmp_path):
|
|
83
80
|
"""Test extracting version from lock file."""
|
|
84
|
-
lock_data = {
|
|
85
|
-
"build": {
|
|
86
|
-
"version": "1.2.3"
|
|
87
|
-
}
|
|
88
|
-
}
|
|
81
|
+
lock_data = {"build": {"version": "1.2.3"}}
|
|
89
82
|
lock_path = tmp_path / "hud.lock.yaml"
|
|
90
83
|
lock_path.write_text(yaml.dump(lock_data))
|
|
91
84
|
|
|
@@ -119,8 +112,7 @@ class TestGetDockerImageDigest:
|
|
|
119
112
|
"""Test successfully getting image digest."""
|
|
120
113
|
# Note: The function expects to parse a list from the string representation
|
|
121
114
|
mock_run.return_value = mock.Mock(
|
|
122
|
-
stdout="['docker.io/library/test@sha256:abc123']",
|
|
123
|
-
returncode=0
|
|
115
|
+
stdout="['docker.io/library/test@sha256:abc123']", returncode=0
|
|
124
116
|
)
|
|
125
117
|
|
|
126
118
|
result = get_docker_image_digest("test:latest")
|
|
@@ -149,10 +141,7 @@ class TestGetDockerImageId:
|
|
|
149
141
|
@mock.patch("subprocess.run")
|
|
150
142
|
def test_get_id_success(self, mock_run):
|
|
151
143
|
"""Test successfully getting image ID."""
|
|
152
|
-
mock_run.return_value = mock.Mock(
|
|
153
|
-
stdout="sha256:abc123def456",
|
|
154
|
-
returncode=0
|
|
155
|
-
)
|
|
144
|
+
mock_run.return_value = mock.Mock(stdout="sha256:abc123def456", returncode=0)
|
|
156
145
|
|
|
157
146
|
result = get_docker_image_id("test:latest")
|
|
158
147
|
assert result == "sha256:abc123def456"
|
|
@@ -223,13 +212,13 @@ class TestAnalyzeMcpEnvironment:
|
|
|
223
212
|
# Setup mock client
|
|
224
213
|
mock_client = mock.AsyncMock()
|
|
225
214
|
mock_client_class.return_value = mock_client
|
|
226
|
-
|
|
215
|
+
|
|
227
216
|
# Mock tool
|
|
228
217
|
mock_tool = mock.Mock()
|
|
229
218
|
mock_tool.name = "test_tool"
|
|
230
219
|
mock_tool.description = "Test tool"
|
|
231
220
|
mock_tool.inputSchema = {"type": "object"}
|
|
232
|
-
|
|
221
|
+
|
|
233
222
|
mock_client.list_tools.return_value = [mock_tool]
|
|
234
223
|
|
|
235
224
|
result = await analyze_mcp_environment("test:latest")
|
|
@@ -264,7 +253,7 @@ class TestAnalyzeMcpEnvironment:
|
|
|
264
253
|
|
|
265
254
|
# Just test that it runs without error in verbose mode
|
|
266
255
|
result = await analyze_mcp_environment("test:latest", verbose=True)
|
|
267
|
-
|
|
256
|
+
|
|
268
257
|
assert result["success"] is True
|
|
269
258
|
assert "initializeMs" in result
|
|
270
259
|
|
|
@@ -272,35 +261,31 @@ class TestAnalyzeMcpEnvironment:
|
|
|
272
261
|
class TestBuildDockerImage:
|
|
273
262
|
"""Test building Docker images."""
|
|
274
263
|
|
|
275
|
-
@mock.patch("subprocess.
|
|
276
|
-
def test_build_success(self,
|
|
264
|
+
@mock.patch("subprocess.run")
|
|
265
|
+
def test_build_success(self, mock_run, tmp_path):
|
|
277
266
|
"""Test successful Docker build."""
|
|
278
267
|
# Create Dockerfile
|
|
279
268
|
dockerfile = tmp_path / "Dockerfile"
|
|
280
269
|
dockerfile.write_text("FROM python:3.11")
|
|
281
270
|
|
|
282
271
|
# Mock successful process
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
mock_process.returncode = 0
|
|
287
|
-
mock_popen.return_value = mock_process
|
|
272
|
+
mock_result = mock.Mock()
|
|
273
|
+
mock_result.returncode = 0
|
|
274
|
+
mock_run.return_value = mock_result
|
|
288
275
|
|
|
289
276
|
result = build_docker_image(tmp_path, "test:latest")
|
|
290
277
|
assert result is True
|
|
291
278
|
|
|
292
|
-
@mock.patch("subprocess.
|
|
293
|
-
def test_build_failure(self,
|
|
279
|
+
@mock.patch("subprocess.run")
|
|
280
|
+
def test_build_failure(self, mock_run, tmp_path):
|
|
294
281
|
"""Test failed Docker build."""
|
|
295
282
|
dockerfile = tmp_path / "Dockerfile"
|
|
296
283
|
dockerfile.write_text("FROM python:3.11")
|
|
297
284
|
|
|
298
285
|
# Mock failed process
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
mock_process.returncode = 1
|
|
303
|
-
mock_popen.return_value = mock_process
|
|
286
|
+
mock_result = mock.Mock()
|
|
287
|
+
mock_result.returncode = 1
|
|
288
|
+
mock_run.return_value = mock_result
|
|
304
289
|
|
|
305
290
|
result = build_docker_image(tmp_path, "test:latest")
|
|
306
291
|
assert result is False
|
|
@@ -310,22 +295,20 @@ class TestBuildDockerImage:
|
|
|
310
295
|
result = build_docker_image(tmp_path, "test:latest")
|
|
311
296
|
assert result is False
|
|
312
297
|
|
|
313
|
-
@mock.patch("subprocess.
|
|
314
|
-
def test_build_with_no_cache(self,
|
|
298
|
+
@mock.patch("subprocess.run")
|
|
299
|
+
def test_build_with_no_cache(self, mock_run, tmp_path):
|
|
315
300
|
"""Test build with --no-cache flag."""
|
|
316
301
|
dockerfile = tmp_path / "Dockerfile"
|
|
317
302
|
dockerfile.write_text("FROM python:3.11")
|
|
318
303
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
mock_process.returncode = 0
|
|
323
|
-
mock_popen.return_value = mock_process
|
|
304
|
+
mock_result = mock.Mock()
|
|
305
|
+
mock_result.returncode = 0
|
|
306
|
+
mock_run.return_value = mock_result
|
|
324
307
|
|
|
325
308
|
build_docker_image(tmp_path, "test:latest", no_cache=True)
|
|
326
309
|
|
|
327
310
|
# Check that --no-cache was included
|
|
328
|
-
call_args =
|
|
311
|
+
call_args = mock_run.call_args[0][0]
|
|
329
312
|
assert "--no-cache" in call_args
|
|
330
313
|
|
|
331
314
|
|
|
@@ -336,12 +319,10 @@ class TestBuildEnvironment:
|
|
|
336
319
|
@mock.patch("hud.cli.build.analyze_mcp_environment")
|
|
337
320
|
@mock.patch("hud.cli.build.save_to_registry")
|
|
338
321
|
@mock.patch("hud.cli.build.get_docker_image_id")
|
|
339
|
-
@mock.patch("subprocess.Popen")
|
|
340
322
|
@mock.patch("subprocess.run")
|
|
341
323
|
def test_build_environment_success(
|
|
342
324
|
self,
|
|
343
325
|
mock_run,
|
|
344
|
-
mock_popen,
|
|
345
326
|
mock_get_id,
|
|
346
327
|
mock_save_registry,
|
|
347
328
|
mock_analyze,
|
|
@@ -352,14 +333,14 @@ class TestBuildEnvironment:
|
|
|
352
333
|
# Setup directory structure
|
|
353
334
|
env_dir = tmp_path / "test-env"
|
|
354
335
|
env_dir.mkdir()
|
|
355
|
-
|
|
336
|
+
|
|
356
337
|
# Create pyproject.toml
|
|
357
338
|
pyproject = env_dir / "pyproject.toml"
|
|
358
339
|
pyproject.write_text("""
|
|
359
340
|
[tool.hud]
|
|
360
341
|
image = "test/env:dev"
|
|
361
342
|
""")
|
|
362
|
-
|
|
343
|
+
|
|
363
344
|
# Create Dockerfile
|
|
364
345
|
dockerfile = env_dir / "Dockerfile"
|
|
365
346
|
dockerfile.write_text("""
|
|
@@ -379,16 +360,11 @@ ENV API_KEY
|
|
|
379
360
|
],
|
|
380
361
|
}
|
|
381
362
|
mock_get_id.return_value = "sha256:abc123"
|
|
382
|
-
|
|
363
|
+
|
|
383
364
|
# Mock final rebuild
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
mock_stdout.read.return_value = ""
|
|
388
|
-
mock_process.stdout = mock_stdout
|
|
389
|
-
mock_process.wait.return_value = None
|
|
390
|
-
mock_process.returncode = 0
|
|
391
|
-
mock_popen.return_value = mock_process
|
|
365
|
+
mock_result = mock.Mock()
|
|
366
|
+
mock_result.returncode = 0
|
|
367
|
+
mock_run.return_value = mock_result
|
|
392
368
|
|
|
393
369
|
# Run build
|
|
394
370
|
build_environment(str(env_dir), "test/env:latest")
|
hud/cli/tests/test_cursor.py
CHANGED
|
@@ -9,7 +9,7 @@ from unittest.mock import mock_open, patch
|
|
|
9
9
|
|
|
10
10
|
import pytest
|
|
11
11
|
|
|
12
|
-
from hud.cli.cursor import get_cursor_config_path, list_cursor_servers, parse_cursor_config
|
|
12
|
+
from hud.cli.utils.cursor import get_cursor_config_path, list_cursor_servers, parse_cursor_config
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class TestParseCursorConfig:
|
hud/cli/tests/test_debug.py
CHANGED