Coverage for tests/test_prepare_release.py: 100%
87 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-11 09:18 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-11 09:18 +0000
1from datetime import date 1abcdefghijklmn
2from pathlib import Path 1abcdefghijklmn
4import pytest 1abcdefghijklmn
5from typer.testing import CliRunner 1abcdefghijklmn
7from scripts.prepare_release import ( 1abcdefghijklmn
8 BumpType,
9 app,
10 bump_version,
11 get_release_notes_body,
12 update_release_notes,
13 update_version_file,
14)
16runner = CliRunner() 1abcdefghijklmn
19@pytest.mark.parametrize( 1abcdefghijklmn
20 ("current_version", "bump", "new_version"),
21 [
22 ("0.0.24", BumpType.major, "1.0.0"),
23 ("0.0.24", BumpType.minor, "0.1.0"),
24 ("0.0.24", BumpType.patch, "0.0.25"),
25 ],
26)
27def test_bump_version(current_version: str, bump: BumpType, new_version: str) -> None: 1abcdefghijklmn
28 assert bump_version(current_version, bump) == new_version 2sctcucvcwcxcyczcAcBcCcDcEcFc
31def test_update_version_file() -> None: 1abcdefghijklmn
32 content = '__version__ = "0.0.24"\n' 1+,-./:;=?@[]^_
34 new_content = update_version_file( 1+,-./:;=?@[]^_
35 content, "0.0.25", Path("src/fastapi_cli/__init__.py")
36 )
38 assert new_content == '__version__ = "0.0.25"\n' 1+,-./:;=?@[]^_
41def test_update_version_file_requires_newer_version() -> None: 1abcdefghijklmn
42 content = '__version__ = "0.0.24"\n' 2` { | } ~ abbbcbdbebfbgbhbib
44 with pytest.raises(RuntimeError, match="must be greater"): 2` { | } ~ abbbcbdbebfbgbhbib
45 update_version_file(content, "0.0.24", Path("src/fastapi_cli/__init__.py")) 2` { | } ~ abbbcbdbebfbgbhbib
48def test_update_release_notes() -> None: 1abcdefghijklmn
49 content = """# Release Notes 2jbkblbmbnbobpbqbrbsbtbubvbwb
51## Latest Changes
53### Fixes
55* Fix something.
57## 0.0.24 (2026-02-24)
59### Internal
61* Previous fix.
62"""
64 new_content = update_release_notes( 2jbkblbmbnbobpbqbrbsbtbubvbwb
65 content, "0.0.25", date(2026, 5, 30), Path("release-notes.md")
66 )
68 assert ( 2jbkblbmbnbobpbqbrbsbtbubvbwb
69 new_content
70 == """# Release Notes
72## Latest Changes
74## 0.0.25 (2026-05-30)
76### Fixes
78* Fix something.
80## 0.0.24 (2026-02-24)
82### Internal
84* Previous fix.
85"""
86 )
89def test_update_release_notes_rejects_existing_version() -> None: 1abcdefghijklmn
90 content = """# Release Notes 2xbybzbAbBbCbDbEbFbGbHbIbJbKb
92## Latest Changes
94## 0.0.25 (2026-05-30)
95"""
97 with pytest.raises(RuntimeError, match="already contain"): 2xbybzbAbBbCbDbEbFbGbHbIbJbKb
98 update_release_notes( 2xbybzbAbBbCbDbEbFbGbHbIbJbKb
99 content, "0.0.25", date(2026, 5, 30), Path("release-notes.md")
100 )
103def test_get_release_notes_body_with_dated_heading() -> None: 1abcdefghijklmn
104 content = """# Release Notes 2LbMbNbObPbQbRbSbTbUbVbWbXbYb
106## Latest Changes
108## 0.0.25 (2026-05-30)
110### Fixes
112* Fix something.
114## 0.0.24 (2026-02-24)
116### Internal
118* Previous fix.
119"""
121 body = get_release_notes_body(content, "0.0.25", Path("release-notes.md")) 2LbMbNbObPbQbRbSbTbUbVbWbXbYb
123 assert ( 2LbMbNbObPbQbRbSbTbUbVbWbXbYb
124 body
125 == """### Fixes
127* Fix something.
128"""
129 )
132def test_get_release_notes_body_with_plain_heading() -> None: 1abcdefghijklmn
133 content = """# Release Notes 2Zb0b1b2b3b4b5b6b7b8b9b!b#b$b
135## Latest Changes
137## 0.0.25
139### Fixes
141* Fix something.
142"""
144 body = get_release_notes_body(content, "0.0.25", Path("release-notes.md")) 2Zb0b1b2b3b4b5b6b7b8b9b!b#b$b
146 assert body == "### Fixes\n\n* Fix something.\n" 2Zb0b1b2b3b4b5b6b7b8b9b!b#b$b
149def test_get_release_notes_body_allows_non_version_h2_content() -> None: 1abcdefghijklmn
150 content = """# Release Notes 2%b'b(b)b*b+b,b-b.b/b:b;b=b?b
152## Latest Changes
154## 0.0.25
156## Highlights
158* Fix something.
160## 0.0.24
162* Previous fix.
163"""
165 body = get_release_notes_body(content, "0.0.25", Path("release-notes.md")) 2%b'b(b)b*b+b,b-b.b/b:b;b=b?b
167 assert body == "## Highlights\n\n* Fix something.\n" 2%b'b(b)b*b+b,b-b.b/b:b;b=b?b
170def test_get_release_notes_body_requires_version_section() -> None: 1abcdefghijklmn
171 content = "# Release Notes\n\n## Latest Changes\n" 2@b[b]b^b_b`b{b|b}b~bacbcccdc
173 with pytest.raises(RuntimeError, match="Could not find"): 2@b[b]b^b_b`b{b|b}b~bacbcccdc
174 get_release_notes_body(content, "0.0.25", Path("release-notes.md")) 2@b[b]b^b_b`b{b|b}b~bacbcccdc
177def test_get_release_notes_body_requires_non_empty_section() -> None: 1abcdefghijklmn
178 content = """# Release Notes 2ecfcgchcicjckclcmcncocpcqcrc
180## Latest Changes
182## 0.0.25
184## 0.0.24
186* Previous fix.
187"""
189 with pytest.raises(RuntimeError, match="is empty"): 2ecfcgchcicjckclcmcncocpcqcrc
190 get_release_notes_body(content, "0.0.25", Path("release-notes.md")) 2ecfcgchcicjckclcmcncocpcqcrc
193def test_cli_updates_configured_files(tmp_path: Path) -> None: 1abcdefghijklmn
194 version_file = tmp_path / "src" / "fastapi_cli" / "__init__.py" 1CDEFGHIJKLMNOP
195 version_file.parent.mkdir(parents=True) 1CDEFGHIJKLMNOP
196 version_file.write_text('__version__ = "0.0.24"\n') 1CDEFGHIJKLMNOP
197 release_notes_file = tmp_path / "release-notes.md" 1CDEFGHIJKLMNOP
198 release_notes_file.write_text( 1CDEFGHIJKLMNOP
199 """# Release Notes
201## Latest Changes
203### Fixes
205* Fix something.
206"""
207 )
209 result = runner.invoke( 1CDEFGHIJKLMNOP
210 app,
211 [
212 "prepare",
213 "patch",
214 "--version-file",
215 str(version_file),
216 "--release-notes-file",
217 str(release_notes_file),
218 "--date",
219 "2026-05-30",
220 ],
221 )
223 assert result.exit_code == 0, result.output 1CDEFGHIJKLMNOP
224 assert "Prepared release 0.0.25 (2026-05-30)" in result.output 1CDEFGHIJKLMNOP
225 assert version_file.read_text() == '__version__ = "0.0.25"\n' 1CDEFGHIJKLMNOP
226 assert "## 0.0.25 (2026-05-30)" in release_notes_file.read_text() 1CDEFGHIJKLMNOP
229def test_cli_accepts_env_vars(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: 1abcdefghijklmn
230 version_file = tmp_path / "src" / "fastapi_cli" / "__init__.py" 1opqrstuvwxyzAB
231 version_file.parent.mkdir(parents=True) 1opqrstuvwxyzAB
232 version_file.write_text('__version__ = "0.0.24"\n') 1opqrstuvwxyzAB
233 release_notes_file = tmp_path / "release-notes.md" 1opqrstuvwxyzAB
234 release_notes_file.write_text("# Release Notes\n\n## Latest Changes\n") 1opqrstuvwxyzAB
235 monkeypatch.setenv("PREPARE_RELEASE_BUMP", "minor") 1opqrstuvwxyzAB
236 monkeypatch.setenv("PREPARE_RELEASE_VERSION_FILE", str(version_file)) 1opqrstuvwxyzAB
237 monkeypatch.setenv("PREPARE_RELEASE_RELEASE_NOTES_FILE", str(release_notes_file)) 1opqrstuvwxyzAB
238 monkeypatch.setenv("PREPARE_RELEASE_DATE", "2026-05-30") 1opqrstuvwxyzAB
240 result = runner.invoke(app, ["prepare"]) 1opqrstuvwxyzAB
242 assert result.exit_code == 0, result.output 1opqrstuvwxyzAB
243 assert "Prepared release 0.1.0 (2026-05-30)" in result.output 1opqrstuvwxyzAB
244 assert version_file.read_text() == '__version__ = "0.1.0"\n' 1opqrstuvwxyzAB
245 assert "## 0.1.0 (2026-05-30)" in release_notes_file.read_text() 1opqrstuvwxyzAB
248def test_cli_prints_current_version(tmp_path: Path) -> None: 1abcdefghijklmn
249 version_file = tmp_path / "src" / "fastapi_cli" / "__init__.py" 1456789!#$%'()*
250 version_file.parent.mkdir(parents=True) 1456789!#$%'()*
251 version_file.write_text('__version__ = "0.0.24"\n') 1456789!#$%'()*
253 result = runner.invoke( 1456789!#$%'()*
254 app,
255 [
256 "current-version",
257 "--version-file",
258 str(version_file),
259 ],
260 )
262 assert result.exit_code == 0, result.output 1456789!#$%'()*
263 assert result.output == "0.0.24\n" 1456789!#$%'()*
266def test_cli_prints_release_notes(tmp_path: Path) -> None: 1abcdefghijklmn
267 version_file = tmp_path / "src" / "fastapi_cli" / "__init__.py" 1QRSTUVWXYZ0123
268 version_file.parent.mkdir(parents=True) 1QRSTUVWXYZ0123
269 version_file.write_text('__version__ = "0.0.25"\n') 1QRSTUVWXYZ0123
270 release_notes_file = tmp_path / "release-notes.md" 1QRSTUVWXYZ0123
271 release_notes_file.write_text( 1QRSTUVWXYZ0123
272 """# Release Notes
274## Latest Changes
276## 0.0.25 (2026-05-30)
278### Fixes
280* Fix something.
281"""
282 )
284 result = runner.invoke( 1QRSTUVWXYZ0123
285 app,
286 [
287 "release-notes",
288 "--version-file",
289 str(version_file),
290 "--release-notes-file",
291 str(release_notes_file),
292 ],
293 )
295 assert result.exit_code == 0, result.output 1QRSTUVWXYZ0123
296 assert result.output == "### Fixes\n\n* Fix something.\n" 1QRSTUVWXYZ0123