Coverage for tests/test_prepare_release.py: 100%

99 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2026-10-15 15:22 +0000

1import importlib.util 1ab

2from datetime import date 1ab

3from pathlib import Path 1ab

4 

5import pytest 1ab

6from typer.testing import CliRunner 1ab

7 

8prepare_release_path = Path(__file__).parents[1] / "scripts" / "prepare_release.py" 1ab

9spec = importlib.util.spec_from_file_location("prepare_release", prepare_release_path) 1ab

10assert spec is not None 1ab

11assert spec.loader is not None 1ab

12prepare_release = importlib.util.module_from_spec(spec) 1ab

13spec.loader.exec_module(prepare_release) 1ab

14 

15BumpType = prepare_release.BumpType 1ab

16app = prepare_release.app 1ab

17bump_version = prepare_release.bump_version 1ab

18get_release_notes_body = prepare_release.get_release_notes_body 1ab

19update_release_notes = prepare_release.update_release_notes 1ab

20update_version_file = prepare_release.update_version_file 1ab

21 

22runner = CliRunner() 1ab

23 

24 

25@pytest.mark.parametrize( 1ab

26 ("current_version", "bump", "new_version"), 

27 [ 

28 ("0.0.8", BumpType.major, "1.0.0"), 

29 ("0.0.8", BumpType.minor, "0.1.0"), 

30 ("0.0.8", BumpType.patch, "0.0.9"), 

31 ], 

32) 

33def test_bump_version(current_version: str, bump: BumpType, new_version: str) -> None: 1ab

34 assert bump_version(current_version, bump) == new_version 1CD

35 

36 

37def test_update_version_file() -> None: 1ab

38 content = '__version__ = "0.0.8"\n' 1kl

39 

40 new_content = update_version_file( 1kl

41 content, "0.0.9", Path("src/markdown_include_variants/_version.py") 

42 ) 

43 

44 assert new_content == '__version__ = "0.0.9"\n' 1kl

45 

46 

47def test_update_version_file_requires_newer_version() -> None: 1ab

48 content = '__version__ = "0.0.8"\n' 1mn

49 

50 with pytest.raises(RuntimeError, match="must be greater"): 1mn

51 update_version_file( 1mn

52 content, "0.0.8", Path("src/markdown_include_variants/_version.py") 

53 ) 

54 

55 

56def test_update_release_notes() -> None: 1ab

57 content = """# Release Notes 1op

58 

59## Latest Changes 

60 

61### Fixes 

62 

63* Fix something. 

64 

65## 0.0.8 (2025-12-12) 

66 

67### Fixes 

68 

69* Previous fix. 

70""" 

71 

72 new_content = update_release_notes( 1op

73 content, "0.0.9", date(2026, 5, 30), Path("release-notes.md") 

74 ) 

75 

76 assert ( 1op

77 new_content 

78 == """# Release Notes 

79 

80## Latest Changes 

81 

82## 0.0.9 (2026-05-30) 

83 

84### Fixes 

85 

86* Fix something. 

87 

88## 0.0.8 (2025-12-12) 

89 

90### Fixes 

91 

92* Previous fix. 

93""" 

94 ) 

95 

96 

97def test_update_release_notes_rejects_existing_version() -> None: 1ab

98 content = """# Release Notes 1qr

99 

100## Latest Changes 

101 

102## 0.0.9 (2026-05-30) 

103""" 

104 

105 with pytest.raises(RuntimeError, match="already contain"): 1qr

106 update_release_notes( 1qr

107 content, "0.0.9", date(2026, 5, 30), Path("release-notes.md") 

108 ) 

109 

110 

111def test_get_release_notes_body_with_dated_heading() -> None: 1ab

112 content = """# Release Notes 1st

113 

114## Latest Changes 

115 

116## 0.0.9 (2026-05-30) 

117 

118### Fixes 

119 

120* Fix something. 

121 

122## 0.0.8 (2025-12-12) 

123 

124### Fixes 

125 

126* Previous fix. 

127""" 

128 

129 body = get_release_notes_body(content, "0.0.9", Path("release-notes.md")) 1st

130 

131 assert ( 1st

132 body 

133 == """### Fixes 

134 

135* Fix something. 

136""" 

137 ) 

138 

139 

140def test_get_release_notes_body_with_plain_heading() -> None: 1ab

141 content = """# Release Notes 1uv

142 

143## Latest Changes 

144 

145## 0.0.9 

146 

147### Fixes 

148 

149* Fix something. 

150""" 

151 

152 body = get_release_notes_body(content, "0.0.9", Path("release-notes.md")) 1uv

153 

154 assert body == "### Fixes\n\n* Fix something.\n" 1uv

155 

156 

157def test_get_release_notes_body_allows_non_version_h2_content() -> None: 1ab

158 content = """# Release Notes 1wx

159 

160## Latest Changes 

161 

162## 0.0.9 

163 

164## Highlights 

165 

166* Fix something. 

167 

168## 0.0.8 

169 

170* Previous fix. 

171""" 

172 

173 body = get_release_notes_body(content, "0.0.9", Path("release-notes.md")) 1wx

174 

175 assert body == "## Highlights\n\n* Fix something.\n" 1wx

176 

177 

178def test_get_release_notes_body_requires_version_section() -> None: 1ab

179 content = "# Release Notes\n\n## Latest Changes\n" 1yz

180 

181 with pytest.raises(RuntimeError, match="Could not find"): 1yz

182 get_release_notes_body(content, "0.0.9", Path("release-notes.md")) 1yz

183 

184 

185def test_get_release_notes_body_requires_non_empty_section() -> None: 1ab

186 content = """# Release Notes 1AB

187 

188## Latest Changes 

189 

190## 0.0.9 

191 

192## 0.0.8 

193 

194* Previous fix. 

195""" 

196 

197 with pytest.raises(RuntimeError, match="is empty"): 1AB

198 get_release_notes_body(content, "0.0.9", Path("release-notes.md")) 1AB

199 

200 

201def test_cli_updates_configured_files(tmp_path: Path) -> None: 1ab

202 version_file = tmp_path / "src" / "markdown_include_variants" / "_version.py" 1ef

203 version_file.parent.mkdir(parents=True) 1ef

204 version_file.write_text('__version__ = "0.0.8"\n') 1ef

205 release_notes_file = tmp_path / "release-notes.md" 1ef

206 release_notes_file.write_text( 1ef

207 """# Release Notes 

208 

209## Latest Changes 

210 

211### Fixes 

212 

213* Fix something. 

214""" 

215 ) 

216 

217 result = runner.invoke( 1ef

218 app, 

219 [ 

220 "prepare", 

221 "patch", 

222 "--version-file", 

223 str(version_file), 

224 "--release-notes-file", 

225 str(release_notes_file), 

226 "--date", 

227 "2026-05-30", 

228 ], 

229 ) 

230 

231 assert result.exit_code == 0, result.output 1ef

232 assert "Prepared release 0.0.9 (2026-05-30)" in result.output 1ef

233 assert version_file.read_text() == '__version__ = "0.0.9"\n' 1ef

234 assert "## 0.0.9 (2026-05-30)" in release_notes_file.read_text() 1ef

235 

236 

237def test_cli_accepts_env_vars(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: 1ab

238 version_file = tmp_path / "src" / "markdown_include_variants" / "_version.py" 1cd

239 version_file.parent.mkdir(parents=True) 1cd

240 version_file.write_text('__version__ = "0.0.8"\n') 1cd

241 release_notes_file = tmp_path / "release-notes.md" 1cd

242 release_notes_file.write_text("# Release Notes\n\n## Latest Changes\n") 1cd

243 monkeypatch.setenv("PREPARE_RELEASE_BUMP", "minor") 1cd

244 monkeypatch.setenv("PREPARE_RELEASE_VERSION_FILE", str(version_file)) 1cd

245 monkeypatch.setenv("PREPARE_RELEASE_RELEASE_NOTES_FILE", str(release_notes_file)) 1cd

246 monkeypatch.setenv("PREPARE_RELEASE_DATE", "2026-05-30") 1cd

247 

248 result = runner.invoke(app, ["prepare"]) 1cd

249 

250 assert result.exit_code == 0, result.output 1cd

251 assert "Prepared release 0.1.0 (2026-05-30)" in result.output 1cd

252 assert version_file.read_text() == '__version__ = "0.1.0"\n' 1cd

253 assert "## 0.1.0 (2026-05-30)" in release_notes_file.read_text() 1cd

254 

255 

256def test_cli_prints_current_version(tmp_path: Path) -> None: 1ab

257 version_file = tmp_path / "src" / "markdown_include_variants" / "_version.py" 1ij

258 version_file.parent.mkdir(parents=True) 1ij

259 version_file.write_text('__version__ = "0.0.8"\n') 1ij

260 

261 result = runner.invoke( 1ij

262 app, 

263 [ 

264 "current-version", 

265 "--version-file", 

266 str(version_file), 

267 ], 

268 ) 

269 

270 assert result.exit_code == 0, result.output 1ij

271 assert result.output == "0.0.8\n" 1ij

272 

273 

274def test_cli_prints_release_notes(tmp_path: Path) -> None: 1ab

275 version_file = tmp_path / "src" / "markdown_include_variants" / "_version.py" 1gh

276 version_file.parent.mkdir(parents=True) 1gh

277 version_file.write_text('__version__ = "0.0.9"\n') 1gh

278 release_notes_file = tmp_path / "release-notes.md" 1gh

279 release_notes_file.write_text( 1gh

280 """# Release Notes 

281 

282## Latest Changes 

283 

284## 0.0.9 (2026-05-30) 

285 

286### Fixes 

287 

288* Fix something. 

289""" 

290 ) 

291 

292 result = runner.invoke( 1gh

293 app, 

294 [ 

295 "release-notes", 

296 "--version-file", 

297 str(version_file), 

298 "--release-notes-file", 

299 str(release_notes_file), 

300 ], 

301 ) 

302 

303 assert result.exit_code == 0, result.output 1gh

304 assert result.output == "### Fixes\n\n* Fix something.\n" 1gh