Skip to content

Commit da10744

Browse files
committed
test: Add missing test coverage and remove obsolete tests
1 parent 0a07ad3 commit da10744

File tree

1 file changed

+152
-1
lines changed

1 file changed

+152
-1
lines changed

tests/test_uv.py

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ def test_hook_executes_when_uv_managed_is_true(mocker, tmp_path, monkeypatch):
112112
assert "pkg1" in doc["tool"]["uv"]["sources"]
113113
assert doc["tool"]["uv"]["sources"]["pkg1"]["path"] == "sources/pkg1"
114114
assert doc["tool"]["uv"]["sources"]["pkg1"]["editable"] is True
115-
assert "pkg1" in doc["project"]["dependencies"]
116115

117116

118117
def test_update_pyproject_respects_install_modes(tmp_path, monkeypatch):
@@ -157,3 +156,155 @@ def test_update_pyproject_respects_install_modes(tmp_path, monkeypatch):
157156
assert sources["editable-pkg"]["editable"] is True
158157
assert sources["fixed-pkg"]["editable"] is False
159158
assert "skip-pkg" not in sources
159+
160+
161+
def test_update_pyproject_idempotency(tmp_path, monkeypatch):
162+
monkeypatch.chdir(tmp_path)
163+
hook = UvPyprojectUpdater()
164+
165+
mx_ini = """
166+
[settings]
167+
[pkg1]
168+
url = https://example.com/pkg1.git
169+
target = sources
170+
install-mode = editable
171+
"""
172+
(tmp_path / "mx.ini").write_text(mx_ini.strip())
173+
config = Configuration("mx.ini")
174+
state = State(config)
175+
176+
initial_toml = """
177+
[project]
178+
name = "test"
179+
dependencies = []
180+
181+
[tool.uv]
182+
managed = true
183+
"""
184+
(tmp_path / "pyproject.toml").write_text(initial_toml.strip())
185+
186+
# Run first time
187+
hook.write(state)
188+
content_after_first = (tmp_path / "pyproject.toml").read_text()
189+
190+
# Run second time
191+
hook.write(state)
192+
content_after_second = (tmp_path / "pyproject.toml").read_text()
193+
194+
assert content_after_first == content_after_second
195+
196+
197+
def test_update_pyproject_with_subdirectory(tmp_path, monkeypatch):
198+
monkeypatch.chdir(tmp_path)
199+
hook = UvPyprojectUpdater()
200+
201+
mx_ini = """
202+
[settings]
203+
[pkg1]
204+
url = https://example.com/pkg1.git
205+
target = sources
206+
subdirectory = sub/dir
207+
install-mode = editable
208+
"""
209+
(tmp_path / "mx.ini").write_text(mx_ini.strip())
210+
config = Configuration("mx.ini")
211+
state = State(config)
212+
213+
initial_toml = """
214+
[project]
215+
name = "test"
216+
dependencies = []
217+
218+
[tool.uv]
219+
managed = true
220+
"""
221+
(tmp_path / "pyproject.toml").write_text(initial_toml.strip())
222+
223+
hook.write(state)
224+
225+
doc = tomlkit.parse((tmp_path / "pyproject.toml").read_text())
226+
assert doc["tool"]["uv"]["sources"]["pkg1"]["path"] == "sources/pkg1/sub/dir"
227+
228+
229+
def test_hook_handles_oserror_on_read(mocker, tmp_path, monkeypatch):
230+
monkeypatch.chdir(tmp_path)
231+
hook = UvPyprojectUpdater()
232+
233+
(tmp_path / "mx.ini").write_text("[settings]")
234+
config = Configuration("mx.ini")
235+
state = State(config)
236+
237+
# Mock pyproject.toml with tool.uv.managed = true
238+
initial_toml = """
239+
[project]
240+
name = "test"
241+
242+
[tool.uv]
243+
managed = true
244+
"""
245+
(tmp_path / "pyproject.toml").write_text(initial_toml.strip())
246+
247+
mock_logger = mocker.patch("mxdev.uv.logger")
248+
mocker.patch("pathlib.Path.open", side_effect=OSError("denied"))
249+
250+
hook.write(state)
251+
252+
mock_logger.error.assert_called_with("[%s] Failed to read pyproject.toml: %s", "uv", mocker.ANY)
253+
254+
255+
def test_hook_handles_oserror_on_write(mocker, tmp_path, monkeypatch):
256+
monkeypatch.chdir(tmp_path)
257+
hook = UvPyprojectUpdater()
258+
259+
(tmp_path / "mx.ini").write_text("[settings]")
260+
config = Configuration("mx.ini")
261+
state = State(config)
262+
263+
initial_toml = """
264+
[project]
265+
name = "test"
266+
267+
[tool.uv]
268+
managed = true
269+
"""
270+
(tmp_path / "pyproject.toml").write_text(initial_toml.strip())
271+
272+
mock_logger = mocker.patch("mxdev.uv.logger")
273+
mocker.patch("os.replace", side_effect=OSError("write denied"))
274+
275+
hook.write(state)
276+
277+
mock_logger.error.assert_called_with("[%s] Failed to write pyproject.toml: %s", "uv", mocker.ANY)
278+
279+
280+
import pytest
281+
import sys
282+
283+
284+
def test_hook_raises_runtime_error_if_tomlkit_missing(mocker, tmp_path, monkeypatch):
285+
monkeypatch.chdir(tmp_path)
286+
hook = UvPyprojectUpdater()
287+
288+
(tmp_path / "mx.ini").write_text("[settings]")
289+
config = Configuration("mx.ini")
290+
state = State(config)
291+
292+
(tmp_path / "pyproject.toml").write_text("[tool.uv]\\nmanaged = true\\n")
293+
294+
mocker.patch.dict(sys.modules, {"tomlkit": None})
295+
# Also need to make the import fail
296+
import builtins
297+
298+
orig_import = builtins.__import__
299+
300+
def fake_import(name, *args, **kw):
301+
if name == "tomlkit":
302+
raise ImportError("No module named 'tomlkit'")
303+
return orig_import(name, *args, **kw)
304+
305+
mocker.patch("builtins.__import__", side_effect=fake_import)
306+
307+
with pytest.raises(RuntimeError) as excinfo:
308+
hook.write(state)
309+
310+
assert "tomlkit is required for the uv hook" in str(excinfo.value)

0 commit comments

Comments
 (0)