Skip to content

Commit 922ffe7

Browse files
stuff
1 parent 311d4ca commit 922ffe7

File tree

2 files changed

+104
-56
lines changed

2 files changed

+104
-56
lines changed

tests/test_audit_logs.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,10 @@ def test_auto_generates_idempotency_key(
122122
# No idempotency_key provided
123123
)
124124

125-
# Assert header exists and is a valid UUID v4
125+
# Assert header exists and has a non-empty value
126126
assert "idempotency-key" in request_kwargs["headers"]
127127
idempotency_key = request_kwargs["headers"]["idempotency-key"]
128-
assert len(idempotency_key) == 36 # UUID format: 8-4-4-4-12
129-
assert idempotency_key.count("-") == 4
130-
# Verify it's a valid UUID by checking the version field (4th section starts with '4')
131-
uuid_parts = idempotency_key.split("-")
132-
assert len(uuid_parts) == 5
133-
assert uuid_parts[2][0] == "4" # UUID v4 identifier
128+
assert idempotency_key and idempotency_key.strip()
134129
assert response is None
135130

136131
def test_throws_unauthorized_exception(

tests/test_http_client_retry.py

Lines changed: 102 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -32,46 +32,75 @@ def retry_config(self):
3232
jitter=0.0, # No jitter for predictable tests
3333
)
3434

35-
def test_retries_on_500_error(self, sync_http_client, retry_config, monkeypatch):
36-
"""Test that 500 errors trigger retry."""
37-
call_count = 0
35+
@staticmethod
36+
def create_mock_request_with_retries(
37+
failure_count: int,
38+
failure_response=None,
39+
failure_exception=None,
40+
success_response=None
41+
):
42+
"""
43+
Create a mock request function that fails N times before succeeding.
44+
45+
Args:
46+
failure_count: Number of times to fail before success
47+
failure_response: Response to return on failure (status_code, json, headers)
48+
failure_exception: Exception to raise on failure (instead of response)
49+
success_response: Response to return on success (default: 200 with {"success": True})
50+
51+
Returns:
52+
A tuple of (mock_function, call_count_tracker) where call_count_tracker
53+
is a list that tracks the number of calls
54+
"""
55+
call_count = [0] # Use list to allow modification in nested function
3856

3957
def mock_request(*args, **kwargs):
40-
nonlocal call_count
41-
call_count += 1
42-
if call_count < 3:
43-
return httpx.Response(status_code=500, json={"error": "Server error"})
58+
call_count[0] += 1
59+
if call_count[0] <= failure_count:
60+
if failure_exception:
61+
raise failure_exception
62+
if failure_response:
63+
return httpx.Response(**failure_response)
64+
65+
if success_response:
66+
return httpx.Response(**success_response)
4467
return httpx.Response(status_code=200, json={"success": True})
4568

69+
return mock_request, call_count
70+
71+
def test_retries_on_408_error(self, sync_http_client, retry_config, monkeypatch):
72+
"""Test that 408 (Request Timeout) errors trigger retry."""
73+
mock_request, call_count = self.create_mock_request_with_retries(
74+
failure_count=1,
75+
failure_response={"status_code": 408, "json": {"error": "Request timeout"}}
76+
)
77+
4678
monkeypatch.setattr(sync_http_client._client, "request", MagicMock(side_effect=mock_request))
4779

4880
with patch("time.sleep") as mock_sleep:
4981
response = sync_http_client.request("test/path", retry_config=retry_config)
5082

51-
assert call_count == 3
83+
assert call_count[0] == 2
5284
assert response == {"success": True}
53-
# Verify sleep was called with exponential backoff
54-
assert mock_sleep.call_count == 2
85+
assert mock_sleep.call_count == 1
5586

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})
87+
def test_retries_on_500_error(self, sync_http_client, retry_config, monkeypatch):
88+
"""Test that 500 errors trigger retry."""
89+
mock_request, call_count = self.create_mock_request_with_retries(
90+
failure_count=2,
91+
failure_response={"status_code": 500, "json": {"error": "Server error"}}
92+
)
6693

6794
monkeypatch.setattr(sync_http_client._client, "request", MagicMock(side_effect=mock_request))
6895

6996
with patch("time.sleep") as mock_sleep:
7097
response = sync_http_client.request("test/path", retry_config=retry_config)
7198

72-
assert call_count == 2
99+
assert call_count[0] == 3
73100
assert response == {"success": True}
74-
assert mock_sleep.call_count == 1
101+
# Verify sleep was called with exponential backoff
102+
assert mock_sleep.call_count == 2
103+
75104

76105
def test_retries_on_502_error(self, sync_http_client, retry_config, monkeypatch):
77106
"""Test that 502 (Bad Gateway) errors trigger retry."""
@@ -233,21 +262,17 @@ def mock_request(*args, **kwargs):
233262

234263
def test_retries_on_network_error(self, sync_http_client, retry_config, monkeypatch):
235264
"""Test that network errors trigger retry."""
236-
call_count = 0
237-
238-
def mock_request(*args, **kwargs):
239-
nonlocal call_count
240-
call_count += 1
241-
if call_count < 3:
242-
raise httpx.ConnectError("Connection failed")
243-
return httpx.Response(status_code=200, json={"success": True})
265+
mock_request, call_count = self.create_mock_request_with_retries(
266+
failure_count=2,
267+
failure_exception=httpx.ConnectError("Connection failed")
268+
)
244269

245270
monkeypatch.setattr(sync_http_client._client, "request", MagicMock(side_effect=mock_request))
246271

247272
with patch("time.sleep"):
248273
response = sync_http_client.request("test/path", retry_config=retry_config)
249274

250-
assert call_count == 3
275+
assert call_count[0] == 3
251276
assert response == {"success": True}
252277

253278
def test_retries_on_timeout_error(self, sync_http_client, retry_config, monkeypatch):
@@ -394,46 +419,74 @@ def retry_config(self):
394419
jitter=0.0, # No jitter for predictable tests
395420
)
396421

397-
@pytest.mark.asyncio
398-
async def test_retries_on_500_error(self, async_http_client, retry_config, monkeypatch):
399-
"""Test that 500 errors trigger retry."""
400-
call_count = 0
422+
@staticmethod
423+
def create_mock_request_with_retries(
424+
failure_count: int,
425+
failure_response=None,
426+
failure_exception=None,
427+
success_response=None
428+
):
429+
"""
430+
Create an async mock request function that fails N times before succeeding.
431+
432+
Args:
433+
failure_count: Number of times to fail before success
434+
failure_response: Response to return on failure (status_code, json, headers)
435+
failure_exception: Exception to raise on failure (instead of response)
436+
success_response: Response to return on success (default: 200 with {"success": True})
437+
438+
Returns:
439+
A tuple of (mock_function, call_count_tracker) where call_count_tracker
440+
is a list that tracks the number of calls
441+
"""
442+
call_count = [0] # Use list to allow modification in nested function
401443

402444
async def mock_request(*args, **kwargs):
403-
nonlocal call_count
404-
call_count += 1
405-
if call_count < 3:
406-
return httpx.Response(status_code=500, json={"error": "Server error"})
445+
call_count[0] += 1
446+
if call_count[0] <= failure_count:
447+
if failure_exception:
448+
raise failure_exception
449+
if failure_response:
450+
return httpx.Response(**failure_response)
451+
452+
if success_response:
453+
return httpx.Response(**success_response)
407454
return httpx.Response(status_code=200, json={"success": True})
408455

456+
return mock_request, call_count
457+
458+
@pytest.mark.asyncio
459+
async def test_retries_on_500_error(self, async_http_client, retry_config, monkeypatch):
460+
"""Test that 500 errors trigger retry."""
461+
mock_request, call_count = self.create_mock_request_with_retries(
462+
failure_count=2,
463+
failure_response={"status_code": 500, "json": {"error": "Server error"}}
464+
)
465+
409466
monkeypatch.setattr(async_http_client._client, "request", AsyncMock(side_effect=mock_request))
410467

411468
with patch("asyncio.sleep") as mock_sleep:
412469
response = await async_http_client.request("test/path", retry_config=retry_config)
413470

414-
assert call_count == 3
471+
assert call_count[0] == 3
415472
assert response == {"success": True}
416473
# Verify sleep was called with exponential backoff
417474
assert mock_sleep.call_count == 2
418475

419476
@pytest.mark.asyncio
420477
async def test_retries_on_408_error(self, async_http_client, retry_config, monkeypatch):
421478
"""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})
479+
mock_request, call_count = self.create_mock_request_with_retries(
480+
failure_count=1,
481+
failure_response={"status_code": 408, "json": {"error": "Request timeout"}}
482+
)
430483

431484
monkeypatch.setattr(async_http_client._client, "request", AsyncMock(side_effect=mock_request))
432485

433486
with patch("asyncio.sleep") as mock_sleep:
434487
response = await async_http_client.request("test/path", retry_config=retry_config)
435488

436-
assert call_count == 2
489+
assert call_count[0] == 2
437490
assert response == {"success": True}
438491
assert mock_sleep.call_count == 1
439492

0 commit comments

Comments
 (0)