|
1 | | -from pathlib import Path |
2 | | - |
3 | | -import pytest |
4 | | -import tomlkit |
5 | | - |
| 1 | +from mxdev.config import Configuration |
6 | 2 | from mxdev.state import State |
7 | 3 | from mxdev.uv import UvPyprojectUpdater |
8 | 4 |
|
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 |
14 | 6 |
|
15 | 7 |
|
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) |
17 | 10 | 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) |
20 | 14 | mock_logger = mocker.patch("mxdev.uv.logger") |
21 | 15 | hook.write(state) |
22 | 16 | mock_logger.debug.assert_called_with("[%s] pyproject.toml not found, skipping.", "uv") |
23 | 17 |
|
24 | 18 |
|
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): |
26 | 20 | # Test skipping logic when [tool.uv] is missing or managed != true |
| 21 | + monkeypatch.chdir(tmp_path) |
27 | 22 | hook = UvPyprojectUpdater() |
28 | | - state = State(MockConfig()) |
| 23 | + (tmp_path / "mx.ini").write_text("[settings]") |
| 24 | + config = Configuration("mx.ini") |
| 25 | + state = State(config) |
29 | 26 |
|
30 | 27 | # Mock pyproject.toml without tool.uv.managed |
31 | 28 | doc = tomlkit.document() |
32 | 29 | doc.add("project", tomlkit.table()) |
| 30 | + (tmp_path / "pyproject.toml").write_text(tomlkit.dumps(doc)) |
33 | 31 |
|
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))) |
36 | 32 | mock_logger = mocker.patch("mxdev.uv.logger") |
37 | 33 |
|
| 34 | + # Store initial content |
| 35 | + initial_content = (tmp_path / "pyproject.toml").read_text() |
| 36 | + |
38 | 37 | hook.write(state) |
39 | 38 | mock_logger.debug.assert_called_with( |
40 | 39 | "[%s] Project not explicitly managed by uv ([tool.uv] managed=true missing), skipping.", "uv" |
41 | 40 | ) |
42 | 41 |
|
| 42 | + # Verify the file was not modified |
| 43 | + assert (tmp_path / "pyproject.toml").read_text() == initial_content |
| 44 | + |
43 | 45 |
|
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): |
45 | 47 | # Test skipping logic when [tool.uv] managed is explicitly false |
| 48 | + monkeypatch.chdir(tmp_path) |
46 | 49 | hook = UvPyprojectUpdater() |
47 | | - state = State(MockConfig()) |
| 50 | + (tmp_path / "mx.ini").write_text("[settings]") |
| 51 | + config = Configuration("mx.ini") |
| 52 | + state = State(config) |
48 | 53 |
|
49 | 54 | # Mock pyproject.toml with tool.uv.managed = false |
50 | 55 | initial_toml = """ |
51 | 56 | [tool.uv] |
52 | 57 | managed = false |
53 | 58 | """ |
54 | | - doc = tomlkit.parse(initial_toml) |
| 59 | + (tmp_path / "pyproject.toml").write_text(initial_toml.strip()) |
55 | 60 |
|
56 | | - mocker.patch("mxdev.uv.Path.exists", return_value=True) |
57 | | - mocker.patch("mxdev.uv.Path.open", mocker.mock_open(read_data=initial_toml)) |
58 | 61 | mock_logger = mocker.patch("mxdev.uv.logger") |
59 | 62 |
|
| 63 | + # Store initial content |
| 64 | + initial_content = (tmp_path / "pyproject.toml").read_text() |
| 65 | + |
60 | 66 | hook.write(state) |
61 | 67 | mock_logger.debug.assert_called_with( |
62 | 68 | "[%s] Project not explicitly managed by uv ([tool.uv] managed=true missing), skipping.", "uv" |
63 | 69 | ) |
64 | 70 |
|
| 71 | + # Verify the file was not modified |
| 72 | + assert (tmp_path / "pyproject.toml").read_text() == initial_content |
65 | 73 |
|
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): |
67 | 76 | # Test that updates proceed when managed = true is present |
| 77 | + monkeypatch.chdir(tmp_path) |
68 | 78 | hook = UvPyprojectUpdater() |
69 | 79 |
|
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) |
72 | 90 |
|
73 | 91 | # Mock pyproject.toml with tool.uv.managed = true |
74 | 92 | initial_toml = """ |
| 93 | +[project] |
| 94 | +name = "test" |
| 95 | +dependencies = [] |
| 96 | +
|
75 | 97 | [tool.uv] |
76 | 98 | managed = true |
77 | 99 | """ |
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()) |
85 | 101 |
|
86 | 102 | mock_logger = mocker.patch("mxdev.uv.logger") |
87 | | - |
88 | 103 | hook.write(state) |
89 | 104 | mock_logger.info.assert_any_call("[%s] Updating pyproject.toml...", "uv") |
90 | 105 | mock_logger.info.assert_any_call("[%s] Successfully updated pyproject.toml", "uv") |
91 | 106 |
|
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()) |
102 | 109 | assert "tool" in doc |
103 | 110 | assert "uv" in doc["tool"] |
104 | 111 | 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"] |
109 | 116 |
|
110 | 117 |
|
111 | | -def test_update_pyproject_respects_install_modes(): |
| 118 | +def test_update_pyproject_respects_install_modes(tmp_path, monkeypatch): |
| 119 | + monkeypatch.chdir(tmp_path) |
112 | 120 | 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()) |
122 | 156 | sources = doc["tool"]["uv"]["sources"] |
123 | 157 | assert sources["editable-pkg"]["editable"] is True |
124 | 158 | assert sources["fixed-pkg"]["editable"] is False |
125 | 159 | 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