Skip to content

Commit 311d4ca

Browse files
moar tests
1 parent 9d2dae2 commit 311d4ca

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

tests/test_http_client_retry.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,83 @@ def mock_request(*args, **kwargs):
5353
# Verify sleep was called with exponential backoff
5454
assert mock_sleep.call_count == 2
5555

56+
def test_retries_on_408_error(self, sync_http_client, retry_config, monkeypatch):
57+
"""Test that 408 (Request Timeout) errors trigger retry."""
58+
call_count = 0
59+
60+
def mock_request(*args, **kwargs):
61+
nonlocal call_count
62+
call_count += 1
63+
if call_count < 2:
64+
return httpx.Response(status_code=408, json={"error": "Request timeout"})
65+
return httpx.Response(status_code=200, json={"success": True})
66+
67+
monkeypatch.setattr(sync_http_client._client, "request", MagicMock(side_effect=mock_request))
68+
69+
with patch("time.sleep") as mock_sleep:
70+
response = sync_http_client.request("test/path", retry_config=retry_config)
71+
72+
assert call_count == 2
73+
assert response == {"success": True}
74+
assert mock_sleep.call_count == 1
75+
76+
def test_retries_on_502_error(self, sync_http_client, retry_config, monkeypatch):
77+
"""Test that 502 (Bad Gateway) errors trigger retry."""
78+
call_count = 0
79+
80+
def mock_request(*args, **kwargs):
81+
nonlocal call_count
82+
call_count += 1
83+
if call_count < 3:
84+
return httpx.Response(status_code=502, json={"error": "Bad gateway"})
85+
return httpx.Response(status_code=200, json={"success": True})
86+
87+
monkeypatch.setattr(sync_http_client._client, "request", MagicMock(side_effect=mock_request))
88+
89+
with patch("time.sleep") as mock_sleep:
90+
response = sync_http_client.request("test/path", retry_config=retry_config)
91+
92+
assert call_count == 3
93+
assert response == {"success": True}
94+
assert mock_sleep.call_count == 2
95+
96+
def test_retries_on_504_error(self, sync_http_client, retry_config, monkeypatch):
97+
"""Test that 504 (Gateway Timeout) errors trigger retry."""
98+
call_count = 0
99+
100+
def mock_request(*args, **kwargs):
101+
nonlocal call_count
102+
call_count += 1
103+
if call_count < 2:
104+
return httpx.Response(status_code=504, json={"error": "Gateway timeout"})
105+
return httpx.Response(status_code=200, json={"success": True})
106+
107+
monkeypatch.setattr(sync_http_client._client, "request", MagicMock(side_effect=mock_request))
108+
109+
with patch("time.sleep") as mock_sleep:
110+
response = sync_http_client.request("test/path", retry_config=retry_config)
111+
112+
assert call_count == 2
113+
assert response == {"success": True}
114+
assert mock_sleep.call_count == 1
115+
116+
def test_no_retry_on_503_error(self, sync_http_client, retry_config, monkeypatch):
117+
"""Test that 503 (Service Unavailable) errors do NOT trigger retry (not in RETRY_STATUS_CODES)."""
118+
call_count = 0
119+
120+
def mock_request(*args, **kwargs):
121+
nonlocal call_count
122+
call_count += 1
123+
return httpx.Response(status_code=503, json={"error": "Service unavailable"})
124+
125+
monkeypatch.setattr(sync_http_client._client, "request", MagicMock(side_effect=mock_request))
126+
127+
with pytest.raises(ServerException):
128+
sync_http_client.request("test/path", retry_config=retry_config)
129+
130+
# Should only be called once (no retries on 503)
131+
assert call_count == 1
132+
56133
def test_retries_on_429_rate_limit(self, sync_http_client, retry_config, monkeypatch):
57134
"""Test that 429 errors do NOT trigger retry (consistent with workos-node)."""
58135
call_count = 0
@@ -339,6 +416,87 @@ async def mock_request(*args, **kwargs):
339416
# Verify sleep was called with exponential backoff
340417
assert mock_sleep.call_count == 2
341418

419+
@pytest.mark.asyncio
420+
async def test_retries_on_408_error(self, async_http_client, retry_config, monkeypatch):
421+
"""Test that 408 (Request Timeout) errors trigger retry."""
422+
call_count = 0
423+
424+
async def mock_request(*args, **kwargs):
425+
nonlocal call_count
426+
call_count += 1
427+
if call_count < 2:
428+
return httpx.Response(status_code=408, json={"error": "Request timeout"})
429+
return httpx.Response(status_code=200, json={"success": True})
430+
431+
monkeypatch.setattr(async_http_client._client, "request", AsyncMock(side_effect=mock_request))
432+
433+
with patch("asyncio.sleep") as mock_sleep:
434+
response = await async_http_client.request("test/path", retry_config=retry_config)
435+
436+
assert call_count == 2
437+
assert response == {"success": True}
438+
assert mock_sleep.call_count == 1
439+
440+
@pytest.mark.asyncio
441+
async def test_retries_on_502_error(self, async_http_client, retry_config, monkeypatch):
442+
"""Test that 502 (Bad Gateway) errors trigger retry."""
443+
call_count = 0
444+
445+
async def mock_request(*args, **kwargs):
446+
nonlocal call_count
447+
call_count += 1
448+
if call_count < 3:
449+
return httpx.Response(status_code=502, json={"error": "Bad gateway"})
450+
return httpx.Response(status_code=200, json={"success": True})
451+
452+
monkeypatch.setattr(async_http_client._client, "request", AsyncMock(side_effect=mock_request))
453+
454+
with patch("asyncio.sleep") as mock_sleep:
455+
response = await async_http_client.request("test/path", retry_config=retry_config)
456+
457+
assert call_count == 3
458+
assert response == {"success": True}
459+
assert mock_sleep.call_count == 2
460+
461+
@pytest.mark.asyncio
462+
async def test_retries_on_504_error(self, async_http_client, retry_config, monkeypatch):
463+
"""Test that 504 (Gateway Timeout) errors trigger retry."""
464+
call_count = 0
465+
466+
async def mock_request(*args, **kwargs):
467+
nonlocal call_count
468+
call_count += 1
469+
if call_count < 2:
470+
return httpx.Response(status_code=504, json={"error": "Gateway timeout"})
471+
return httpx.Response(status_code=200, json={"success": True})
472+
473+
monkeypatch.setattr(async_http_client._client, "request", AsyncMock(side_effect=mock_request))
474+
475+
with patch("asyncio.sleep") as mock_sleep:
476+
response = await async_http_client.request("test/path", retry_config=retry_config)
477+
478+
assert call_count == 2
479+
assert response == {"success": True}
480+
assert mock_sleep.call_count == 1
481+
482+
@pytest.mark.asyncio
483+
async def test_no_retry_on_503_error(self, async_http_client, retry_config, monkeypatch):
484+
"""Test that 503 (Service Unavailable) errors do NOT trigger retry (not in RETRY_STATUS_CODES)."""
485+
call_count = 0
486+
487+
async def mock_request(*args, **kwargs):
488+
nonlocal call_count
489+
call_count += 1
490+
return httpx.Response(status_code=503, json={"error": "Service unavailable"})
491+
492+
monkeypatch.setattr(async_http_client._client, "request", AsyncMock(side_effect=mock_request))
493+
494+
with pytest.raises(ServerException):
495+
await async_http_client.request("test/path", retry_config=retry_config)
496+
497+
# Should only be called once (no retries on 503)
498+
assert call_count == 1
499+
342500
@pytest.mark.asyncio
343501
async def test_retries_on_429_rate_limit(self, async_http_client, retry_config, monkeypatch):
344502
"""Test that 429 errors do NOT trigger retry (consistent with workos-node)."""

0 commit comments

Comments
 (0)