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

1from datetime import date 1abcdefghijklmn

2from pathlib import Path 1abcdefghijklmn

3 

4import pytest 1abcdefghijklmn

5from typer.testing import CliRunner 1abcdefghijklmn

6 

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) 

15 

16runner = CliRunner() 1abcdefghijklmn

17 

18 

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

29 

30 

31def test_update_version_file() -> None: 1abcdefghijklmn

32 content = '__version__ = "0.0.24"\n' 1+,-./:;=?@[]^_

33 

34 new_content = update_version_file( 1+,-./:;=?@[]^_

35 content, "0.0.25", Path("src/fastapi_cli/__init__.py") 

36 ) 

37 

38 assert new_content == '__version__ = "0.0.25"\n' 1+,-./:;=?@[]^_

39 

40 

41def test_update_version_file_requires_newer_version() -> None: 1abcdefghijklmn

42 content = '__version__ = "0.0.24"\n' 2` { | } ~ abbbcbdbebfbgbhbib

43 

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

46 

47 

48def test_update_release_notes() -> None: 1abcdefghijklmn

49 content = """# Release Notes 2jbkblbmbnbobpbqbrbsbtbubvbwb

50 

51## Latest Changes 

52 

53### Fixes 

54 

55* Fix something. 

56 

57## 0.0.24 (2026-02-24) 

58 

59### Internal 

60 

61* Previous fix. 

62""" 

63 

64 new_content = update_release_notes( 2jbkblbmbnbobpbqbrbsbtbubvbwb

65 content, "0.0.25", date(2026, 5, 30), Path("release-notes.md") 

66 ) 

67 

68 assert ( 2jbkblbmbnbobpbqbrbsbtbubvbwb

69 new_content 

70 == """# Release Notes 

71 

72## Latest Changes 

73 

74## 0.0.25 (2026-05-30) 

75 

76### Fixes 

77 

78* Fix something. 

79 

80## 0.0.24 (2026-02-24) 

81 

82### Internal 

83 

84* Previous fix. 

85""" 

86 ) 

87 

88 

89def test_update_release_notes_rejects_existing_version() -> None: 1abcdefghijklmn

90 content = """# Release Notes 2xbybzbAbBbCbDbEbFbGbHbIbJbKb

91 

92## Latest Changes 

93 

94## 0.0.25 (2026-05-30) 

95""" 

96 

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 ) 

101 

102 

103def test_get_release_notes_body_with_dated_heading() -> None: 1abcdefghijklmn

104 content = """# Release Notes 2LbMbNbObPbQbRbSbTbUbVbWbXbYb

105 

106## Latest Changes 

107 

108## 0.0.25 (2026-05-30) 

109 

110### Fixes 

111 

112* Fix something. 

113 

114## 0.0.24 (2026-02-24) 

115 

116### Internal 

117 

118* Previous fix. 

119""" 

120 

121 body = get_release_notes_body(content, "0.0.25", Path("release-notes.md")) 2LbMbNbObPbQbRbSbTbUbVbWbXbYb

122 

123 assert ( 2LbMbNbObPbQbRbSbTbUbVbWbXbYb

124 body 

125 == """### Fixes 

126 

127* Fix something. 

128""" 

129 ) 

130 

131 

132def test_get_release_notes_body_with_plain_heading() -> None: 1abcdefghijklmn

133 content = """# Release Notes 2Zb0b1b2b3b4b5b6b7b8b9b!b#b$b

134 

135## Latest Changes 

136 

137## 0.0.25 

138 

139### Fixes 

140 

141* Fix something. 

142""" 

143 

144 body = get_release_notes_body(content, "0.0.25", Path("release-notes.md")) 2Zb0b1b2b3b4b5b6b7b8b9b!b#b$b

145 

146 assert body == "### Fixes\n\n* Fix something.\n" 2Zb0b1b2b3b4b5b6b7b8b9b!b#b$b

147 

148 

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

151 

152## Latest Changes 

153 

154## 0.0.25 

155 

156## Highlights 

157 

158* Fix something. 

159 

160## 0.0.24 

161 

162* Previous fix. 

163""" 

164 

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

166 

167 assert body == "## Highlights\n\n* Fix something.\n" 2%b'b(b)b*b+b,b-b.b/b:b;b=b?b

168 

169 

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

172 

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

175 

176 

177def test_get_release_notes_body_requires_non_empty_section() -> None: 1abcdefghijklmn

178 content = """# Release Notes 2ecfcgchcicjckclcmcncocpcqcrc

179 

180## Latest Changes 

181 

182## 0.0.25 

183 

184## 0.0.24 

185 

186* Previous fix. 

187""" 

188 

189 with pytest.raises(RuntimeError, match="is empty"): 2ecfcgchcicjckclcmcncocpcqcrc

190 get_release_notes_body(content, "0.0.25", Path("release-notes.md")) 2ecfcgchcicjckclcmcncocpcqcrc

191 

192 

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 

200 

201## Latest Changes 

202 

203### Fixes 

204 

205* Fix something. 

206""" 

207 ) 

208 

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 ) 

222 

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

227 

228 

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

239 

240 result = runner.invoke(app, ["prepare"]) 1opqrstuvwxyzAB

241 

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

246 

247 

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!#$%'()*

252 

253 result = runner.invoke( 1456789!#$%'()*

254 app, 

255 [ 

256 "current-version", 

257 "--version-file", 

258 str(version_file), 

259 ], 

260 ) 

261 

262 assert result.exit_code == 0, result.output 1456789!#$%'()*

263 assert result.output == "0.0.24\n" 1456789!#$%'()*

264 

265 

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 

273 

274## Latest Changes 

275 

276## 0.0.25 (2026-05-30) 

277 

278### Fixes 

279 

280* Fix something. 

281""" 

282 ) 

283 

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 ) 

294 

295 assert result.exit_code == 0, result.output 1QRSTUVWXYZ0123

296 assert result.output == "### Fixes\n\n* Fix something.\n" 1QRSTUVWXYZ0123