mirror of
https://github.com/wassname/openrouter-python-sdk-retry-errors.git
synced 2026-09-09 11:27:54 +08:00
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>
This commit is contained in:
+16
-10
@@ -21,9 +21,11 @@ def response(status_code, json_body=None):
|
|||||||
|
|
||||||
|
|
||||||
def fast_retries():
|
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(
|
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"])
|
return Retries(RetryConfig("backoff", backoff, retry_connection_errors=True), ["5XX"])
|
||||||
|
|
||||||
@@ -46,17 +48,19 @@ class WrapsTransientInnerCodeTests(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
class RetryIntegrationTests(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
|
calls = 0
|
||||||
|
|
||||||
def func():
|
def func():
|
||||||
nonlocal calls
|
nonlocal calls
|
||||||
calls += 1
|
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())
|
result = retry(func, fast_retries())
|
||||||
self.assertEqual(result.status_code, 400)
|
self.assertEqual(result.status_code, 200)
|
||||||
self.assertGreater(calls, 1) # it retried
|
self.assertEqual(calls, 3) # retried twice, then succeeded
|
||||||
|
|
||||||
def test_does_not_retry_plain_400(self):
|
def test_does_not_retry_plain_400(self):
|
||||||
calls = 0
|
calls = 0
|
||||||
@@ -70,17 +74,19 @@ class RetryIntegrationTests(unittest.TestCase):
|
|||||||
self.assertEqual(result.status_code, 400)
|
self.assertEqual(result.status_code, 400)
|
||||||
self.assertEqual(calls, 1) # not retried
|
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
|
calls = 0
|
||||||
|
|
||||||
async def func():
|
async def func():
|
||||||
nonlocal calls
|
nonlocal calls
|
||||||
calls += 1
|
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()))
|
result = asyncio.run(retry_async(func, fast_retries()))
|
||||||
self.assertEqual(result.status_code, 400)
|
self.assertEqual(result.status_code, 200)
|
||||||
self.assertGreater(calls, 1)
|
self.assertEqual(calls, 3)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user