@@ -1292,7 +1292,7 @@ def boom(_client, _number, _authorized, _skip):
12921292 assert not cancel_event .is_set ()
12931293
12941294
1295- def test_rate_limit_abandons_remaining_fingerprints (make_config , monkeypatch ):
1295+ def test_rate_limit_abandons_remaining_fingerprints (make_config , monkeypatch , caplog ):
12961296 from github import RateLimitExceededException
12971297
12981298 # Pin one worker for a deterministic FIFO order: PR1 runs first and trips the cancel event before
@@ -1310,7 +1310,7 @@ def fingerprint(_client, number, _authorized, _skip):
13101310 def fake_dispatch (_client , number , _head_sha , _eval_hash , _ref ):
13111311 dispatched .append (number )
13121312
1313- with pytest . raises ( RuntimeError , match = r"1 PR\(s\) failed during scan: \[1\]" ) :
1313+ with caplog . at_level ( logging . WARNING , logger = "greenlight" ), pytest . raises ( RuntimeError ) as excinfo :
13141314 review .run (
13151315 make_config (github_token = "t" ),
13161316 build_github = lambda _token , ** _kwargs : _CLIENT ,
@@ -1323,13 +1323,20 @@ def fake_dispatch(_client, number, _head_sha, _eval_hash, _ref):
13231323 )
13241324
13251325 # PR1's rate limit sets the shared cancel event, so the queued PR2/PR3 short-circuit to _CANCELLED
1326- # without ever calling fingerprint (only PR1 appears). A cancelled task is not a failure, so only
1327- # PR1 lands in `failed`, and nothing dispatches.
1326+ # without ever calling fingerprint (only PR1 appears). A cancelled task is not a failure: PR1 lands
1327+ # in `failed`, PR2/PR3 are counted as abandoned (not failed) , and nothing dispatches.
13281328 assert fingerprinted == [1 ]
13291329 assert dispatched == []
1330+ # The abandonment is surfaced, never silently dropped: a warning names the count, and the
1331+ # end-of-scan RuntimeError carries both the failed PR and the abandoned ones distinctly so the
1332+ # scan still fails closed on an incomplete pass.
1333+ message = str (excinfo .value )
1334+ assert "1 PR(s) failed during scan: [1]" in message
1335+ assert "2 PR(s) abandoned due to rate limit: [2, 3]" in message
1336+ assert "abandoned 2 of 3" in caplog .text
13301337
13311338
1332- def test_rate_limit_still_dispatches_earlier_completed_candidate (make_config , monkeypatch ):
1339+ def test_rate_limit_still_dispatches_earlier_completed_candidate (make_config , monkeypatch , caplog ):
13331340 from github import RateLimitExceededException
13341341
13351342 monkeypatch .setattr (review , "_FINGERPRINT_WORKERS" , 1 )
@@ -1345,7 +1352,7 @@ def fingerprint(_client, number, _authorized, _skip):
13451352 def fake_dispatch (_client , number , _head_sha , _eval_hash , _ref ):
13461353 dispatched .append (number )
13471354
1348- with pytest . raises ( RuntimeError , match = r"1 PR\(s\) failed during scan: \[2\]" ) :
1355+ with caplog . at_level ( logging . WARNING , logger = "greenlight" ), pytest . raises ( RuntimeError ) as excinfo :
13491356 review .run (
13501357 make_config (github_token = "t" ),
13511358 build_github = lambda _token , ** _kwargs : _CLIENT ,
@@ -1359,9 +1366,15 @@ def fake_dispatch(_client, number, _head_sha, _eval_hash, _ref):
13591366
13601367 # PR1 completes as a candidate before PR2 hits the rate limit, so the partial success is preserved
13611368 # -- PR1 still dispatches. PR2 trips the cancel event, so PR3 short-circuits without being
1362- # fingerprinted, and only PR2 lands in `failed`.
1369+ # fingerprinted: PR2 lands in `failed`, PR3 is counted as abandoned (not failed) .
13631370 assert fingerprinted == [1 , 2 ]
13641371 assert dispatched == [1 ]
1372+ # The failure and the abandonment are surfaced distinctly in the fail-closed signal, and the
1373+ # abandonment is logged as its own warning rather than folded into the failure count.
1374+ message = str (excinfo .value )
1375+ assert "1 PR(s) failed during scan: [2]" in message
1376+ assert "1 PR(s) abandoned due to rate limit: [3]" in message
1377+ assert "abandoned 1 of 3" in caplog .text
13651378
13661379
13671380def test_rate_limit_abandonment_breaks_max_dispatch_batches (make_config , monkeypatch ):
0 commit comments