Skip to content

Commit c2b3360

Browse files
committed
test: Rework test_hook_skips_when_pyproject_toml_missing to use tmp_path
1 parent ca05f18 commit c2b3360

File tree

2 files changed

+97
-75
lines changed

2 files changed

+97
-75
lines changed

src/mxdev/uv.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import logging
2-
import re
1+
from mxdev.hooks import Hook
2+
from mxdev.state import State
33
from pathlib import Path
44
from typing import Any
55

6+
import logging
7+
import re
68
import tomlkit
7-
from mxdev.hooks import Hook
8-
from mxdev.state import State
9+
910

1011
logger = logging.getLogger("mxdev")
1112

@@ -16,9 +17,7 @@ def normalize_name(name: str) -> str:
1617

1718

1819
class UvPyprojectUpdater(Hook):
19-
"""
20-
An mxdev hook that updates pyproject.toml during the write phase for uv-managed projects.
21-
"""
20+
"""An mxdev hook that updates pyproject.toml during the write phase for uv-managed projects."""
2221

2322
namespace = "uv"
2423

tests/test_uv.py

Lines changed: 91 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,136 +1,159 @@
1-
from pathlib import Path
2-
3-
import pytest
4-
import tomlkit
5-
1+
from mxdev.config import Configuration
62
from mxdev.state import State
73
from mxdev.uv import UvPyprojectUpdater
84

9-
10-
class MockConfig:
11-
def __init__(self, packages=None, settings=None):
12-
self.packages = packages or {}
13-
self.settings = settings or {}
5+
import tomlkit
146

157

16-
def test_hook_skips_when_pyproject_toml_missing(mocker):
8+
def test_hook_skips_when_pyproject_toml_missing(mocker, tmp_path, monkeypatch):
9+
monkeypatch.chdir(tmp_path)
1710
hook = UvPyprojectUpdater()
18-
state = State(MockConfig())
19-
mocker.patch("mxdev.uv.Path.exists", return_value=False)
11+
(tmp_path / "mx.ini").write_text("[settings]")
12+
config = Configuration("mx.ini")
13+
state = State(config)
2014
mock_logger = mocker.patch("mxdev.uv.logger")
2115
hook.write(state)
2216
mock_logger.debug.assert_called_with("[%s] pyproject.toml not found, skipping.", "uv")
2317

2418

25-
def test_hook_skips_when_uv_managed_is_false_or_missing(mocker, tmp_path):
19+
def test_hook_skips_when_uv_managed_is_false_or_missing(mocker, tmp_path, monkeypatch):
2620
# Test skipping logic when [tool.uv] is missing or managed != true
21+
monkeypatch.chdir(tmp_path)
2722
hook = UvPyprojectUpdater()
28-
state = State(MockConfig())
23+
(tmp_path / "mx.ini").write_text("[settings]")
24+
config = Configuration("mx.ini")
25+
state = State(config)
2926

3027
# Mock pyproject.toml without tool.uv.managed
3128
doc = tomlkit.document()
3229
doc.add("project", tomlkit.table())
30+
(tmp_path / "pyproject.toml").write_text(tomlkit.dumps(doc))
3331

34-
mocker.patch("mxdev.uv.Path.exists", return_value=True)
35-
mocker.patch("mxdev.uv.Path.open", mocker.mock_open(read_data=tomlkit.dumps(doc)))
3632
mock_logger = mocker.patch("mxdev.uv.logger")
3733

34+
# Store initial content
35+
initial_content = (tmp_path / "pyproject.toml").read_text()
36+
3837
hook.write(state)
3938
mock_logger.debug.assert_called_with(
4039
"[%s] Project not explicitly managed by uv ([tool.uv] managed=true missing), skipping.", "uv"
4140
)
4241

42+
# Verify the file was not modified
43+
assert (tmp_path / "pyproject.toml").read_text() == initial_content
44+
4345

44-
def test_hook_skips_when_uv_managed_is_false(mocker, tmp_path):
46+
def test_hook_skips_when_uv_managed_is_false(mocker, tmp_path, monkeypatch):
4547
# Test skipping logic when [tool.uv] managed is explicitly false
48+
monkeypatch.chdir(tmp_path)
4649
hook = UvPyprojectUpdater()
47-
state = State(MockConfig())
50+
(tmp_path / "mx.ini").write_text("[settings]")
51+
config = Configuration("mx.ini")
52+
state = State(config)
4853

4954
# Mock pyproject.toml with tool.uv.managed = false
5055
initial_toml = """
5156
[tool.uv]
5257
managed = false
5358
"""
54-
doc = tomlkit.parse(initial_toml)
59+
(tmp_path / "pyproject.toml").write_text(initial_toml.strip())
5560

56-
mocker.patch("mxdev.uv.Path.exists", return_value=True)
57-
mocker.patch("mxdev.uv.Path.open", mocker.mock_open(read_data=initial_toml))
5861
mock_logger = mocker.patch("mxdev.uv.logger")
5962

63+
# Store initial content
64+
initial_content = (tmp_path / "pyproject.toml").read_text()
65+
6066
hook.write(state)
6167
mock_logger.debug.assert_called_with(
6268
"[%s] Project not explicitly managed by uv ([tool.uv] managed=true missing), skipping.", "uv"
6369
)
6470

71+
# Verify the file was not modified
72+
assert (tmp_path / "pyproject.toml").read_text() == initial_content
6573

66-
def test_hook_executes_when_uv_managed_is_true(mocker, tmp_path):
74+
75+
def test_hook_executes_when_uv_managed_is_true(mocker, tmp_path, monkeypatch):
6776
# Test that updates proceed when managed = true is present
77+
monkeypatch.chdir(tmp_path)
6878
hook = UvPyprojectUpdater()
6979

70-
packages = {"pkg1": {"target": "sources", "install-mode": "editable"}}
71-
state = State(MockConfig(packages=packages))
80+
mx_ini = """
81+
[settings]
82+
[pkg1]
83+
url = https://example.com/pkg1.git
84+
target = sources
85+
install-mode = editable
86+
"""
87+
(tmp_path / "mx.ini").write_text(mx_ini.strip())
88+
config = Configuration("mx.ini")
89+
state = State(config)
7290

7391
# Mock pyproject.toml with tool.uv.managed = true
7492
initial_toml = """
93+
[project]
94+
name = "test"
95+
dependencies = []
96+
7597
[tool.uv]
7698
managed = true
7799
"""
78-
doc = tomlkit.parse(initial_toml)
79-
80-
mocker.patch("mxdev.uv.Path.exists", return_value=True)
81-
82-
# We need a proper mock for pathlib.Path.open that returns our doc and captures the write
83-
mock_file = mocker.mock_open(read_data=initial_toml)
84-
mocker.patch("mxdev.uv.Path.open", mock_file)
100+
(tmp_path / "pyproject.toml").write_text(initial_toml.strip())
85101

86102
mock_logger = mocker.patch("mxdev.uv.logger")
87-
88103
hook.write(state)
89104
mock_logger.info.assert_any_call("[%s] Updating pyproject.toml...", "uv")
90105
mock_logger.info.assert_any_call("[%s] Successfully updated pyproject.toml", "uv")
91106

92-
93-
# Additional test cases to migrate from the old tests
94-
def test_update_pyproject_creates_tool_uv_sources():
95-
hook = UvPyprojectUpdater()
96-
doc = tomlkit.document()
97-
packages = {"pkg1": {"target": "sources", "install-mode": "editable"}}
98-
state = State(MockConfig(packages=packages))
99-
100-
hook._update_pyproject(doc, state)
101-
107+
# Verify the file was actually written correctly
108+
doc = tomlkit.parse((tmp_path / "pyproject.toml").read_text())
102109
assert "tool" in doc
103110
assert "uv" in doc["tool"]
104111
assert "sources" in doc["tool"]["uv"]
105-
sources = doc["tool"]["uv"]["sources"]
106-
assert "pkg1" in sources
107-
assert sources["pkg1"]["path"] == "sources/pkg1"
108-
assert sources["pkg1"]["editable"] is True
112+
assert "pkg1" in doc["tool"]["uv"]["sources"]
113+
assert doc["tool"]["uv"]["sources"]["pkg1"]["path"] == "sources/pkg1"
114+
assert doc["tool"]["uv"]["sources"]["pkg1"]["editable"] is True
115+
assert "pkg1" in doc["project"]["dependencies"]
109116

110117

111-
def test_update_pyproject_respects_install_modes():
118+
def test_update_pyproject_respects_install_modes(tmp_path, monkeypatch):
119+
monkeypatch.chdir(tmp_path)
112120
hook = UvPyprojectUpdater()
113-
doc = tomlkit.document()
114-
packages = {
115-
"editable-pkg": {"target": "sources", "install-mode": "editable"},
116-
"fixed-pkg": {"target": "sources", "install-mode": "fixed"},
117-
"skip-pkg": {"target": "sources", "install-mode": "skip"},
118-
}
119-
state = State(MockConfig(packages=packages))
120-
121-
hook._update_pyproject(doc, state)
121+
122+
mx_ini = """
123+
[settings]
124+
[editable-pkg]
125+
url = https://example.com/e.git
126+
target = sources
127+
install-mode = editable
128+
129+
[fixed-pkg]
130+
url = https://example.com/f.git
131+
target = sources
132+
install-mode = fixed
133+
134+
[skip-pkg]
135+
url = https://example.com/s.git
136+
target = sources
137+
install-mode = skip
138+
"""
139+
(tmp_path / "mx.ini").write_text(mx_ini.strip())
140+
config = Configuration("mx.ini")
141+
state = State(config)
142+
143+
initial_toml = """
144+
[project]
145+
name = "test"
146+
dependencies = []
147+
148+
[tool.uv]
149+
managed = true
150+
"""
151+
(tmp_path / "pyproject.toml").write_text(initial_toml.strip())
152+
153+
hook.write(state)
154+
155+
doc = tomlkit.parse((tmp_path / "pyproject.toml").read_text())
122156
sources = doc["tool"]["uv"]["sources"]
123157
assert sources["editable-pkg"]["editable"] is True
124158
assert sources["fixed-pkg"]["editable"] is False
125159
assert "skip-pkg" not in sources
126-
127-
128-
def test_update_pyproject_adds_dependencies():
129-
hook = UvPyprojectUpdater()
130-
doc = tomlkit.document()
131-
packages = {"pkg1": {"target": "sources", "install-mode": "editable"}}
132-
state = State(MockConfig(packages=packages))
133-
134-
hook._update_pyproject(doc, state)
135-
deps = doc["project"]["dependencies"]
136-
assert "pkg1" in deps

0 commit comments

Comments
 (0)