From 0a691a14806a4862bc005b31810c6ae10617cdb2 Mon Sep 17 00:00:00 2001 From: wassname Date: Sun, 5 Jul 2026 09:47:16 +0800 Subject: [PATCH] Make 400-retry integration tests deterministic (retry-then-succeed) Addresses Copilot review: the timing-based calls>1 assertion could be flaky on slow CI. Now the stub returns a transient 400 twice then a 200, so the retry count is exact regardless of scheduling. Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com> --- tests/test_retries.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/test_retries.py b/tests/test_retries.py index c5f1566..a418525 100644 --- a/tests/test_retries.py +++ b/tests/test_retries.py @@ -21,9 +21,11 @@ def response(status_code, json_body=None): def fast_retries(): - # Tiny, jitter-free budget so retry() exhausts in milliseconds during tests. + # Jitter-free, 1ms intervals. Budget is generous so these tests terminate on + # success / non-retryable status rather than on the elapsed-time timeout, which + # keeps `calls` counts deterministic regardless of CI scheduling overhead. backoff = BackoffStrategy( - initial_interval=1, max_interval=1, exponent=1.0, max_elapsed_time=20, jitter_ms=0 + initial_interval=1, max_interval=1, exponent=1.0, max_elapsed_time=10000, jitter_ms=0 ) return Retries(RetryConfig("backoff", backoff, retry_connection_errors=True), ["5XX"]) @@ -46,17 +48,19 @@ class WrapsTransientInnerCodeTests(unittest.TestCase): class RetryIntegrationTests(unittest.TestCase): - def test_retries_400_wrapping_transient_code(self): + def test_retries_400_wrapping_transient_code_then_succeeds(self): calls = 0 def func(): nonlocal calls calls += 1 - return response(400, {"error": {"message": "Provider returned error", "code": 502}}) + if calls < 3: + return response(400, {"error": {"message": "Provider returned error", "code": 502}}) + return response(200, {"ok": True}) result = retry(func, fast_retries()) - self.assertEqual(result.status_code, 400) - self.assertGreater(calls, 1) # it retried + self.assertEqual(result.status_code, 200) + self.assertEqual(calls, 3) # retried twice, then succeeded def test_does_not_retry_plain_400(self): calls = 0 @@ -70,17 +74,19 @@ class RetryIntegrationTests(unittest.TestCase): self.assertEqual(result.status_code, 400) self.assertEqual(calls, 1) # not retried - def test_async_retries_400_wrapping_transient_code(self): + def test_async_retries_400_wrapping_transient_code_then_succeeds(self): calls = 0 async def func(): nonlocal calls calls += 1 - return response(400, {"error": {"code": 503}}) + if calls < 3: + return response(400, {"error": {"code": 503}}) + return response(200, {"ok": True}) result = asyncio.run(retry_async(func, fast_retries())) - self.assertEqual(result.status_code, 400) - self.assertGreater(calls, 1) + self.assertEqual(result.status_code, 200) + self.assertEqual(calls, 3) if __name__ == "__main__":