44from __future__ import annotations
55
66import copy
7+ import contextlib
78import importlib .util
9+ import io
810from pathlib import Path
911from types import SimpleNamespace
1012
@@ -186,6 +188,57 @@ def test_publish_sequence_dry_runs_dependents_after_index_wait() -> None:
186188 ]
187189
188190
191+ def test_release_tag_check_requires_valid_signature () -> None :
192+ calls : list [tuple [str , ...]] = []
193+
194+ original_try_capture = release_crates .try_capture
195+ original_run = release_crates .subprocess .run
196+ try :
197+ release_crates .try_capture = lambda command : {
198+ ("git" , "rev-parse" , "HEAD" ): "abc" ,
199+ ("git" , "rev-list" , "-n" , "1" , "v1.0.10" ): "abc" ,
200+ }.get (tuple (command ))
201+
202+ def fake_run (command , ** kwargs ):
203+ calls .append (tuple (command ))
204+ return SimpleNamespace (returncode = 0 , stdout = "Good signature" , stderr = "" )
205+
206+ release_crates .subprocess .run = fake_run
207+ with contextlib .redirect_stdout (io .StringIO ()):
208+ release_crates .check_release_tag ("1.0.10" , require_tag = True )
209+ finally :
210+ release_crates .try_capture = original_try_capture
211+ release_crates .subprocess .run = original_run
212+
213+ assert ("git" , "tag" , "-v" , "v1.0.10" ) in calls
214+
215+
216+ def test_release_tag_check_rejects_unverified_required_tag () -> None :
217+ original_try_capture = release_crates .try_capture
218+ original_run = release_crates .subprocess .run
219+ try :
220+ release_crates .try_capture = lambda command : {
221+ ("git" , "rev-parse" , "HEAD" ): "abc" ,
222+ ("git" , "rev-list" , "-n" , "1" , "v1.0.10" ): "abc" ,
223+ }.get (tuple (command ))
224+
225+ def fake_run (command , ** kwargs ):
226+ return SimpleNamespace (returncode = 1 , stdout = "" , stderr = "no signature" )
227+
228+ release_crates .subprocess .run = fake_run
229+ try :
230+ with contextlib .redirect_stderr (io .StringIO ()):
231+ release_crates .check_release_tag ("1.0.10" , require_tag = True )
232+ except SystemExit as exc :
233+ assert exc .code == 1
234+ return
235+ finally :
236+ release_crates .try_capture = original_try_capture
237+ release_crates .subprocess .run = original_run
238+
239+ raise AssertionError ("expected release tag check to exit" )
240+
241+
189242def run_tests () -> None :
190243 tests = (
191244 test_current_plan_accepts_unchanged_crates ,
@@ -194,6 +247,8 @@ def run_tests() -> None:
194247 test_unchanged_crates_are_not_published ,
195248 test_publish_plan_skips_unchanged_crates ,
196249 test_publish_sequence_dry_runs_dependents_after_index_wait ,
250+ test_release_tag_check_requires_valid_signature ,
251+ test_release_tag_check_rejects_unverified_required_tag ,
197252 )
198253 for test in tests :
199254 test ()
0 commit comments