Coverage for tests/test_prepare_release.py: 100%
88 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-10-19 03:36 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-10-19 03:36 +0000
1from __future__ import annotations 1abcdefghi
3from datetime import date 1abcdefghi
4from pathlib import Path 1abcdefghi
6import pytest 1abcdefghi
7from typer.testing import CliRunner 1abcdefghi
9from scripts.prepare_release import ( 1abcdefghi
10 BumpType,
11 app,
12 bump_version,
13 get_release_notes_body,
14 update_release_notes,
15 update_version_file,
16)
18runner = CliRunner() 1abcdefghi
21@pytest.mark.parametrize( 1abcdefghi
22 ("current_version", "bump", "new_version"),
23 [
24 ("0.0.4", "major", "1.0.0"),
25 ("0.0.4", "minor", "0.1.0"),
26 ("0.0.4", "patch", "0.0.5"),
27 ],
28)
29def test_bump_version(current_version: str, bump: BumpType, new_version: str) -> None: 1abcdefghi
30 assert bump_version(current_version, bump) == new_version 2LbMbNbObPbQbRbSbTb
33def test_update_version_file() -> None: 1abcdefghi
34 content = 'from .main import Doc as Doc\n\n__version__ = "0.0.4"\n' 1TUVWXYZ01
36 new_content = update_version_file( 1TUVWXYZ01
37 content, "0.0.5", Path("src/annotated_doc/__init__.py")
38 )
40 assert new_content == 'from .main import Doc as Doc\n\n__version__ = "0.0.5"\n' 1TUVWXYZ01
43def test_update_version_file_requires_newer_version() -> None: 1abcdefghi
44 content = '__version__ = "0.0.4"\n' 123456789!
46 with pytest.raises(RuntimeError, match="must be greater"): 123456789!
47 update_version_file(content, "0.0.4", Path("src/annotated_doc/__init__.py")) 123456789!
50def test_update_release_notes() -> None: 1abcdefghi
51 content = """# Release Notes 1#$%'()*+,
53## Latest Changes
55### Fixes
57* Fix something.
59## 0.0.4 (2025-11-10)
61### Fixes
63* Previous fix.
64"""
66 new_content = update_release_notes( 1#$%'()*+,
67 content, "0.0.5", date(2026, 5, 30), Path("release-notes.md")
68 )
70 assert ( 1#$%'()*+,
71 new_content
72 == """# Release Notes
74## Latest Changes
76## 0.0.5 (2026-05-30)
78### Fixes
80* Fix something.
82## 0.0.4 (2025-11-10)
84### Fixes
86* Previous fix.
87"""
88 )
91def test_update_release_notes_rejects_existing_version() -> None: 1abcdefghi
92 content = """# Release Notes 1-./:;=?@[
94## Latest Changes
96## 0.0.5 (2026-05-30)
97"""
99 with pytest.raises(RuntimeError, match="already contain"): 1-./:;=?@[
100 update_release_notes( 1-./:;=?@[
101 content, "0.0.5", date(2026, 5, 30), Path("release-notes.md")
102 )
105def test_get_release_notes_body_with_dated_heading() -> None: 1abcdefghi
106 content = """# Release Notes 2] ^ _ ` { | } ~ ab
108## Latest Changes
110## 0.0.5 (2026-05-30)
112### Fixes
114* Fix something.
116## 0.0.4 (2025-11-10)
118### Fixes
120* Previous fix.
121"""
123 body = get_release_notes_body(content, "0.0.5", Path("release-notes.md")) 2] ^ _ ` { | } ~ ab
125 assert ( 2] ^ _ ` { | } ~ ab
126 body
127 == """### Fixes
129* Fix something.
130"""
131 )
134def test_get_release_notes_body_with_plain_heading() -> None: 1abcdefghi
135 content = """# Release Notes 2bbcbdbebfbgbhbibjb
137## Latest Changes
139## 0.0.5
141### Fixes
143* Fix something.
144"""
146 body = get_release_notes_body(content, "0.0.5", Path("release-notes.md")) 2bbcbdbebfbgbhbibjb
148 assert body == "### Fixes\n\n* Fix something.\n" 2bbcbdbebfbgbhbibjb
151def test_get_release_notes_body_allows_non_version_h2_content() -> None: 1abcdefghi
152 content = """# Release Notes 2kblbmbnbobpbqbrbsb
154## Latest Changes
156## 0.0.5
158## Highlights
160* Fix something.
162## 0.0.4
164* Previous fix.
165"""
167 body = get_release_notes_body(content, "0.0.5", Path("release-notes.md")) 2kblbmbnbobpbqbrbsb
169 assert body == "## Highlights\n\n* Fix something.\n" 2kblbmbnbobpbqbrbsb
172def test_get_release_notes_body_requires_version_section() -> None: 1abcdefghi
173 content = "# Release Notes\n\n## Latest Changes\n" 2tbubvbwbxbybzbAbBb
175 with pytest.raises(RuntimeError, match="Could not find"): 2tbubvbwbxbybzbAbBb
176 get_release_notes_body(content, "0.0.5", Path("release-notes.md")) 2tbubvbwbxbybzbAbBb
179def test_get_release_notes_body_requires_non_empty_section() -> None: 1abcdefghi
180 content = """# Release Notes 2CbDbEbFbGbHbIbJbKb
182## Latest Changes
184## 0.0.5
186## 0.0.4
188* Previous fix.
189"""
191 with pytest.raises(RuntimeError, match="is empty"): 2CbDbEbFbGbHbIbJbKb
192 get_release_notes_body(content, "0.0.5", Path("release-notes.md")) 2CbDbEbFbGbHbIbJbKb
195def test_cli_updates_configured_files(tmp_path: Path) -> None: 1abcdefghi
196 version_file = tmp_path / "src" / "annotated_doc" / "__init__.py" 1stuvwxyzA
197 version_file.parent.mkdir(parents=True) 1stuvwxyzA
198 version_file.write_text('from .main import Doc as Doc\n\n__version__ = "0.0.4"\n') 1stuvwxyzA
199 release_notes_file = tmp_path / "release-notes.md" 1stuvwxyzA
200 release_notes_file.write_text( 1stuvwxyzA
201 """# Release Notes
203## Latest Changes
205### Fixes
207* Fix something.
208"""
209 )
211 result = runner.invoke( 1stuvwxyzA
212 app,
213 [
214 "prepare",
215 "patch",
216 "--version-file",
217 str(version_file),
218 "--release-notes-file",
219 str(release_notes_file),
220 "--date",
221 "2026-05-30",
222 ],
223 )
225 assert result.exit_code == 0, result.output 1stuvwxyzA
226 assert "Prepared release 0.0.5 (2026-05-30)" in result.output 1stuvwxyzA
227 assert version_file.read_text() == ( 1stuvwxyzA
228 'from .main import Doc as Doc\n\n__version__ = "0.0.5"\n'
229 )
230 assert "## 0.0.5 (2026-05-30)" in release_notes_file.read_text() 1stuvwxyzA
233def test_cli_accepts_env_vars(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: 1abcdefghi
234 version_file = tmp_path / "src" / "annotated_doc" / "__init__.py" 1jklmnopqr
235 version_file.parent.mkdir(parents=True) 1jklmnopqr
236 version_file.write_text('__version__ = "0.0.4"\n') 1jklmnopqr
237 release_notes_file = tmp_path / "release-notes.md" 1jklmnopqr
238 release_notes_file.write_text("# Release Notes\n\n## Latest Changes\n") 1jklmnopqr
239 monkeypatch.setenv("PREPARE_RELEASE_BUMP", "minor") 1jklmnopqr
240 monkeypatch.setenv("PREPARE_RELEASE_VERSION_FILE", str(version_file)) 1jklmnopqr
241 monkeypatch.setenv("PREPARE_RELEASE_RELEASE_NOTES_FILE", str(release_notes_file)) 1jklmnopqr
242 monkeypatch.setenv("PREPARE_RELEASE_DATE", "2026-05-30") 1jklmnopqr
244 result = runner.invoke(app, ["prepare"]) 1jklmnopqr
246 assert result.exit_code == 0, result.output 1jklmnopqr
247 assert "Prepared release 0.1.0 (2026-05-30)" in result.output 1jklmnopqr
248 assert version_file.read_text() == '__version__ = "0.1.0"\n' 1jklmnopqr
249 assert "## 0.1.0 (2026-05-30)" in release_notes_file.read_text() 1jklmnopqr
252def test_cli_prints_current_version(tmp_path: Path) -> None: 1abcdefghi
253 version_file = tmp_path / "src" / "annotated_doc" / "__init__.py" 1KLMNOPQRS
254 version_file.parent.mkdir(parents=True) 1KLMNOPQRS
255 version_file.write_text('__version__ = "0.0.4"\n') 1KLMNOPQRS
257 result = runner.invoke( 1KLMNOPQRS
258 app,
259 [
260 "current-version",
261 "--version-file",
262 str(version_file),
263 ],
264 )
266 assert result.exit_code == 0, result.output 1KLMNOPQRS
267 assert result.output == "0.0.4\n" 1KLMNOPQRS
270def test_cli_prints_release_notes(tmp_path: Path) -> None: 1abcdefghi
271 version_file = tmp_path / "src" / "annotated_doc" / "__init__.py" 1BCDEFGHIJ
272 version_file.parent.mkdir(parents=True) 1BCDEFGHIJ
273 version_file.write_text('__version__ = "0.0.5"\n') 1BCDEFGHIJ
274 release_notes_file = tmp_path / "release-notes.md" 1BCDEFGHIJ
275 release_notes_file.write_text( 1BCDEFGHIJ
276 """# Release Notes
278## Latest Changes
280## 0.0.5 (2026-05-30)
282### Fixes
284* Fix something.
285"""
286 )
288 result = runner.invoke( 1BCDEFGHIJ
289 app,
290 [
291 "release-notes",
292 "--version-file",
293 str(version_file),
294 "--release-notes-file",
295 str(release_notes_file),
296 ],
297 )
299 assert result.exit_code == 0, result.output 1BCDEFGHIJ
300 assert result.output == "### Fixes\n\n* Fix something.\n" 1BCDEFGHIJ