Coverage for tests / test_prepare_release.py: 100%
122 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-03 20:19 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-03 20:19 +0000
1import importlib.util 1abcdefghi
2import sys 1abcdefghi
3from datetime import date 1abcdefghi
4from pathlib import Path 1abcdefghi
6import pytest 1abcdefghi
7from typer.testing import CliRunner 1abcdefghi
9prepare_release_path = Path(__file__).parents[1] / "scripts" / "prepare_release.py" 1abcdefghi
10spec = importlib.util.spec_from_file_location("prepare_release", prepare_release_path) 1abcdefghi
11assert spec is not None 1abcdefghi
12prepare_release = importlib.util.module_from_spec(spec) 1abcdefghi
13sys.modules["prepare_release"] = prepare_release 1abcdefghi
14assert spec.loader is not None 1abcdefghi
15spec.loader.exec_module(prepare_release) 1abcdefghi
17BumpType = prepare_release.BumpType 1abcdefghi
18app = prepare_release.app 1abcdefghi
19bump_version = prepare_release.bump_version 1abcdefghi
20get_release_notes_body = prepare_release.get_release_notes_body 1abcdefghi
21update_json_version_file = prepare_release.update_json_version_file 1abcdefghi
22update_package_lock_file = prepare_release.update_package_lock_file 1abcdefghi
23update_release_notes = prepare_release.update_release_notes 1abcdefghi
24update_version_file = prepare_release.update_version_file 1abcdefghi
26runner = CliRunner() 1abcdefghi
29@pytest.mark.parametrize( 1abcdefghi
30 ("current_version", "bump", "new_version"),
31 [
32 ("0.0.5", "major", "1.0.0"),
33 ("0.0.5", "minor", "0.1.0"),
34 ("0.0.5", "patch", "0.0.6"),
35 ],
36)
37def test_bump_version(current_version: str, bump: BumpType, new_version: str) -> None: 1abcdefghi
38 assert bump_version(current_version, bump) == new_version 23b4b5b6b7b8b9b!b#b$b%b'b(b)b*b
41def test_update_version_file() -> None: 1abcdefghi
42 content = '[project]\nname = "library-skills"\nversion = "0.0.5"\n' 1TUVWXYZ01
44 new_content = update_version_file(content, "0.0.6", Path("pyproject.toml")) 1TUVWXYZ01
46 assert new_content == '[project]\nname = "library-skills"\nversion = "0.0.6"\n' 1TUVWXYZ01
49def test_update_version_file_requires_newer_version() -> None: 1abcdefghi
50 content = '[project]\nversion = "0.0.5"\n' 123456789!
52 with pytest.raises(RuntimeError, match="must be greater"): 123456789!
53 update_version_file(content, "0.0.5", Path("pyproject.toml")) 123456789!
56def test_update_json_version_file() -> None: 1abcdefghi
57 content = '{\n "name": "library-skills",\n "version": "0.0.5"\n}\n' 1#$%'()*+,
59 new_content = update_json_version_file(content, "0.0.6", Path("package.json")) 1#$%'()*+,
61 assert new_content == '{\n "name": "library-skills",\n "version": "0.0.6"\n}\n' 1#$%'()*+,
64def test_update_package_lock_file() -> None: 1abcdefghi
65 content = ( 1-./:;=?@[
66 "{\n"
67 ' "name": "library-skills",\n'
68 ' "version": "0.0.5",\n'
69 ' "packages": {\n'
70 ' "": {\n'
71 ' "name": "library-skills",\n'
72 ' "version": "0.0.5"\n'
73 " }\n"
74 " }\n"
75 "}\n"
76 )
78 new_content = update_package_lock_file(content, "0.0.6", Path("package-lock.json")) 1-./:;=?@[
80 assert ( 1-./:;=?@[
81 new_content == "{\n"
82 ' "name": "library-skills",\n'
83 ' "version": "0.0.6",\n'
84 ' "packages": {\n'
85 ' "": {\n'
86 ' "name": "library-skills",\n'
87 ' "version": "0.0.6"\n'
88 " }\n"
89 " }\n"
90 "}\n"
91 )
94def test_update_release_notes() -> None: 1abcdefghi
95 content = """# Release Notes 2] ^ _ ` { | } ~ ab
97## Latest Changes
99### Fixes
101* Fix something.
103## 0.0.5 (2026-05-01)
105### Fixes
107* Previous fix.
108"""
110 new_content = update_release_notes( 2] ^ _ ` { | } ~ ab
111 content, "0.0.6", date(2026, 6, 20), Path("docs/release-notes.md")
112 )
114 assert ( 2] ^ _ ` { | } ~ ab
115 new_content
116 == """# Release Notes
118## Latest Changes
120## 0.0.6 (2026-06-20)
122### Fixes
124* Fix something.
126## 0.0.5 (2026-05-01)
128### Fixes
130* Previous fix.
131"""
132 )
135def test_update_release_notes_rejects_existing_version() -> None: 1abcdefghi
136 content = """# Release Notes 2bbcbdbebfbgbhbibjb
138## Latest Changes
140## 0.0.6 (2026-06-20)
141"""
143 with pytest.raises(RuntimeError, match="already contain"): 2bbcbdbebfbgbhbibjb
144 update_release_notes( 2bbcbdbebfbgbhbibjb
145 content, "0.0.6", date(2026, 6, 20), Path("docs/release-notes.md")
146 )
149def test_get_release_notes_body_with_dated_heading() -> None: 1abcdefghi
150 content = """# Release Notes 2kblbmbnbobpbqbrbsb
152## Latest Changes
154## 0.0.6 (2026-06-20)
156### Fixes
158* Fix something.
160## 0.0.5 (2026-05-01)
162### Fixes
164* Previous fix.
165"""
167 body = get_release_notes_body(content, "0.0.6", Path("docs/release-notes.md")) 2kblbmbnbobpbqbrbsb
169 assert ( 2kblbmbnbobpbqbrbsb
170 body
171 == """### Fixes
173* Fix something.
174"""
175 )
178def test_get_release_notes_body_with_plain_heading() -> None: 1abcdefghi
179 content = """# Release Notes 2tbubvbwbxbybzbAbBb
181## Latest Changes
183## 0.0.6
185### Fixes
187* Fix something.
188"""
190 body = get_release_notes_body(content, "0.0.6", Path("docs/release-notes.md")) 2tbubvbwbxbybzbAbBb
192 assert body == "### Fixes\n\n* Fix something.\n" 2tbubvbwbxbybzbAbBb
195def test_get_release_notes_body_allows_non_version_h2_content() -> None: 1abcdefghi
196 content = """# Release Notes 2CbDbEbFbGbHbIbJbKb
198## Latest Changes
200## 0.0.6
202## Highlights
204* Fix something.
206## 0.0.5
208* Previous fix.
209"""
211 body = get_release_notes_body(content, "0.0.6", Path("docs/release-notes.md")) 2CbDbEbFbGbHbIbJbKb
213 assert body == "## Highlights\n\n* Fix something.\n" 2CbDbEbFbGbHbIbJbKb
216def test_get_release_notes_body_requires_version_section() -> None: 1abcdefghi
217 content = "# Release Notes\n\n## Latest Changes\n" 2LbMbNbObPbQbRbSbTb
219 with pytest.raises(RuntimeError, match="Could not find"): 2LbMbNbObPbQbRbSbTb
220 get_release_notes_body(content, "0.0.6", Path("docs/release-notes.md")) 2LbMbNbObPbQbRbSbTb
223def test_get_release_notes_body_requires_non_empty_section() -> None: 1abcdefghi
224 content = """# Release Notes 2UbVbWbXbYbZb0b1b2b
226## Latest Changes
228## 0.0.6
230## 0.0.5
232* Previous fix.
233"""
235 with pytest.raises(RuntimeError, match="is empty"): 2UbVbWbXbYbZb0b1b2b
236 get_release_notes_body(content, "0.0.6", Path("docs/release-notes.md")) 2UbVbWbXbYbZb0b1b2b
239def test_cli_updates_configured_files(tmp_path: Path) -> None: 1abcdefghi
240 version_file = tmp_path / "pyproject.toml" 1stuvwxyzA
241 version_file.write_text('[project]\nversion = "0.0.5"\n', encoding="utf-8") 1stuvwxyzA
242 package_json_file = tmp_path / "package.json" 1stuvwxyzA
243 package_json_file.write_text('{"version": "0.0.5"}\n', encoding="utf-8") 1stuvwxyzA
244 package_lock_file = tmp_path / "package-lock.json" 1stuvwxyzA
245 package_lock_file.write_text( 1stuvwxyzA
246 '{"version": "0.0.5", "packages": {"": {"version": "0.0.5"}}}\n',
247 encoding="utf-8",
248 )
249 release_notes_file = tmp_path / "release-notes.md" 1stuvwxyzA
250 release_notes_file.write_text( 1stuvwxyzA
251 """# Release Notes
253## Latest Changes
255### Fixes
257* Fix something.
258""",
259 encoding="utf-8",
260 )
262 result = runner.invoke( 1stuvwxyzA
263 app,
264 [
265 "prepare",
266 "patch",
267 "--version-file",
268 str(version_file),
269 "--package-json-file",
270 str(package_json_file),
271 "--package-lock-file",
272 str(package_lock_file),
273 "--release-notes-file",
274 str(release_notes_file),
275 "--date",
276 "2026-06-20",
277 ],
278 )
280 assert result.exit_code == 0, result.output 1stuvwxyzA
281 assert "Prepared release 0.0.6 (2026-06-20)" in result.output 1stuvwxyzA
282 assert version_file.read_text(encoding="utf-8") == '[project]\nversion = "0.0.6"\n' 1stuvwxyzA
283 assert '"version": "0.0.6"' in package_json_file.read_text(encoding="utf-8") 1stuvwxyzA
284 assert '"version": "0.0.6"' in package_lock_file.read_text(encoding="utf-8") 1stuvwxyzA
285 assert "## 0.0.6 (2026-06-20)" in release_notes_file.read_text(encoding="utf-8") 1stuvwxyzA
288def test_cli_accepts_env_vars(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: 1abcdefghi
289 version_file = tmp_path / "pyproject.toml" 1jklmnopqr
290 version_file.write_text('[project]\nversion = "0.0.5"\n', encoding="utf-8") 1jklmnopqr
291 package_json_file = tmp_path / "package.json" 1jklmnopqr
292 package_json_file.write_text('{"version": "0.0.5"}\n', encoding="utf-8") 1jklmnopqr
293 package_lock_file = tmp_path / "package-lock.json" 1jklmnopqr
294 package_lock_file.write_text( 1jklmnopqr
295 '{"version": "0.0.5", "packages": {"": {"version": "0.0.5"}}}\n',
296 encoding="utf-8",
297 )
298 release_notes_file = tmp_path / "docs" / "release-notes.md" 1jklmnopqr
299 release_notes_file.parent.mkdir() 1jklmnopqr
300 release_notes_file.write_text( 1jklmnopqr
301 "# Release Notes\n\n## Latest Changes\n", encoding="utf-8"
302 )
303 monkeypatch.setenv("PREPARE_RELEASE_BUMP", "minor") 1jklmnopqr
304 monkeypatch.setenv("PREPARE_RELEASE_VERSION_FILE", str(version_file)) 1jklmnopqr
305 monkeypatch.setenv("PREPARE_RELEASE_PACKAGE_JSON_FILE", str(package_json_file)) 1jklmnopqr
306 monkeypatch.setenv("PREPARE_RELEASE_PACKAGE_LOCK_FILE", str(package_lock_file)) 1jklmnopqr
307 monkeypatch.setenv("PREPARE_RELEASE_RELEASE_NOTES_FILE", str(release_notes_file)) 1jklmnopqr
308 monkeypatch.setenv("PREPARE_RELEASE_DATE", "2026-06-20") 1jklmnopqr
310 result = runner.invoke(app, ["prepare"]) 1jklmnopqr
312 assert result.exit_code == 0, result.output 1jklmnopqr
313 assert "Prepared release 0.1.0 (2026-06-20)" in result.output 1jklmnopqr
314 assert version_file.read_text(encoding="utf-8") == '[project]\nversion = "0.1.0"\n' 1jklmnopqr
315 assert "0.1.0" in package_json_file.read_text(encoding="utf-8") 1jklmnopqr
316 assert "0.1.0" in package_lock_file.read_text(encoding="utf-8") 1jklmnopqr
317 assert "## 0.1.0 (2026-06-20)" in release_notes_file.read_text(encoding="utf-8") 1jklmnopqr
320def test_cli_prints_current_version(tmp_path: Path) -> None: 1abcdefghi
321 version_file = tmp_path / "pyproject.toml" 1KLMNOPQRS
322 version_file.write_text('[project]\nversion = "0.0.5"\n', encoding="utf-8") 1KLMNOPQRS
324 result = runner.invoke( 1KLMNOPQRS
325 app,
326 [
327 "current-version",
328 "--version-file",
329 str(version_file),
330 ],
331 )
333 assert result.exit_code == 0, result.output 1KLMNOPQRS
334 assert result.output == "0.0.5\n" 1KLMNOPQRS
337def test_cli_prints_release_notes(tmp_path: Path) -> None: 1abcdefghi
338 version_file = tmp_path / "pyproject.toml" 1BCDEFGHIJ
339 version_file.write_text('[project]\nversion = "0.0.6"\n', encoding="utf-8") 1BCDEFGHIJ
340 release_notes_file = tmp_path / "release-notes.md" 1BCDEFGHIJ
341 release_notes_file.write_text( 1BCDEFGHIJ
342 """# Release Notes
344## Latest Changes
346## 0.0.6 (2026-06-20)
348### Fixes
350* Fix something.
351""",
352 encoding="utf-8",
353 )
355 result = runner.invoke( 1BCDEFGHIJ
356 app,
357 [
358 "release-notes",
359 "--version-file",
360 str(version_file),
361 "--release-notes-file",
362 str(release_notes_file),
363 ],
364 )
366 assert result.exit_code == 0, result.output 1BCDEFGHIJ
367 assert result.output == "### Fixes\n\n* Fix something.\n" 1BCDEFGHIJ