Skip to content

Commit 91c36f1

Browse files
Copilotcgillum
andcommitted
Fix linter issues and finalize implementation
Co-authored-by: cgillum <2704139+cgillum@users.noreply.github.com>
1 parent b2c933d commit 91c36f1

2 files changed

Lines changed: 71 additions & 62 deletions

File tree

azure/durable_functions/models/utils/http_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ async def post_async_request(url: str,
9797
# More here: https://docs.aiohttp.org/en/stable/client_advanced.html
9898
data = await response.json(content_type=None)
9999
return [response.status, data]
100-
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
100+
except (aiohttp.ClientError, asyncio.TimeoutError):
101101
# On connection errors, close and recreate session for next request
102102
# This handles cases where the remote host process recycles
103103
global _client_session
@@ -128,7 +128,7 @@ async def get_async_request(url: str) -> List[Any]:
128128
if data is None:
129129
data = ""
130130
return [response.status, data]
131-
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
131+
except (aiohttp.ClientError, asyncio.TimeoutError):
132132
# On connection errors, close and recreate session for next request
133133
# This handles cases where the remote host process recycles
134134
global _client_session
@@ -157,7 +157,7 @@ async def delete_async_request(url: str) -> List[Union[int, Any]]:
157157
async with session.delete(url) as response:
158158
data = await response.json(content_type=None)
159159
return [response.status, data]
160-
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
160+
except (aiohttp.ClientError, asyncio.TimeoutError):
161161
# On connection errors, close and recreate session for next request
162162
# This handles cases where the remote host process recycles
163163
global _client_session

tests/utils/test_http_utils.py

Lines changed: 68 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Tests for http_utils module to verify ClientSession reuse."""
22
import pytest
3-
from unittest.mock import AsyncMock, patch, MagicMock, Mock
3+
from unittest.mock import AsyncMock, patch, Mock
44
from azure.durable_functions.models.utils import http_utils
55

66

@@ -9,32 +9,34 @@ async def test_session_is_reused_across_requests():
99
"""Test that the same session is reused for multiple requests."""
1010
# Reset the session to start fresh
1111
http_utils._client_session = None
12-
12+
1313
# Make first request to create session
1414
with patch('aiohttp.ClientSession') as mock_session_class:
1515
mock_session = Mock()
1616
mock_response = AsyncMock()
1717
mock_response.status = 200
1818
mock_response.json = AsyncMock(return_value={"result": "success"})
19-
19+
2020
# Create a proper async context manager
2121
mock_post_context = AsyncMock()
2222
mock_post_context.__aenter__.return_value = mock_response
2323
mock_post_context.__aexit__.return_value = None
2424
mock_session.post.return_value = mock_post_context
2525
mock_session.closed = False
2626
mock_session_class.return_value = mock_session
27-
27+
2828
# First request
29-
await http_utils.post_async_request("http://test.com", {"data": "test1"})
30-
29+
await http_utils.post_async_request("http://test.com",
30+
{"data": "test1"})
31+
3132
# Verify session was created once
3233
assert mock_session_class.call_count == 1
3334
first_session = http_utils._client_session
34-
35+
3536
# Second request - should reuse same session
36-
await http_utils.post_async_request("http://test.com", {"data": "test2"})
37-
37+
await http_utils.post_async_request("http://test.com",
38+
{"data": "test2"})
39+
3840
# Verify session was NOT created again
3941
assert mock_session_class.call_count == 1
4042
assert http_utils._client_session is first_session
@@ -45,34 +47,36 @@ async def test_session_recreated_after_close():
4547
"""Test that a new session is created if the previous one was closed."""
4648
# Reset the session
4749
http_utils._client_session = None
48-
50+
4951
with patch('aiohttp.ClientSession') as mock_session_class:
5052
mock_session1 = Mock()
5153
mock_session1.closed = False
5254
mock_response = AsyncMock()
5355
mock_response.status = 200
5456
mock_response.json = AsyncMock(return_value={"result": "success"})
55-
57+
5658
mock_post_context = AsyncMock()
5759
mock_post_context.__aenter__.return_value = mock_response
5860
mock_post_context.__aexit__.return_value = None
5961
mock_session1.post.return_value = mock_post_context
60-
62+
6163
mock_session2 = Mock()
6264
mock_session2.closed = False
6365
mock_session2.post.return_value = mock_post_context
64-
66+
6567
mock_session_class.side_effect = [mock_session1, mock_session2]
66-
68+
6769
# First request creates session
68-
await http_utils.post_async_request("http://test.com", {"data": "test1"})
70+
await http_utils.post_async_request("http://test.com",
71+
{"data": "test1"})
6972
assert mock_session_class.call_count == 1
70-
73+
7174
# Simulate session being closed
7275
mock_session1.closed = True
73-
76+
7477
# Second request should create new session
75-
await http_utils.post_async_request("http://test.com", {"data": "test2"})
78+
await http_utils.post_async_request("http://test.com",
79+
{"data": "test2"})
7680
assert mock_session_class.call_count == 2
7781

7882

@@ -81,36 +85,39 @@ async def test_session_closed_on_connection_error():
8185
"""Test that session is closed and reset on connection errors."""
8286
# Reset the session
8387
http_utils._client_session = None
84-
88+
8589
with patch('aiohttp.ClientSession') as mock_session_class:
8690
mock_session = Mock()
8791
mock_session.closed = False
8892
mock_session.close = AsyncMock()
89-
93+
9094
# First request succeeds
9195
mock_response = AsyncMock()
9296
mock_response.status = 200
9397
mock_response.json = AsyncMock(return_value={"result": "success"})
94-
98+
9599
mock_post_context_success = AsyncMock()
96100
mock_post_context_success.__aenter__.return_value = mock_response
97101
mock_post_context_success.__aexit__.return_value = None
98-
102+
99103
mock_session.post.return_value = mock_post_context_success
100104
mock_session_class.return_value = mock_session
101-
102-
await http_utils.post_async_request("http://test.com", {"data": "test1"})
105+
106+
await http_utils.post_async_request("http://test.com",
107+
{"data": "test1"})
103108
assert http_utils._client_session is not None
104-
109+
105110
# Second request raises connection error
106111
from aiohttp import ClientError
107112
mock_post_context_error = AsyncMock()
108-
mock_post_context_error.__aenter__.side_effect = ClientError("Connection failed")
113+
mock_post_context_error.__aenter__.side_effect = \
114+
ClientError("Connection failed")
109115
mock_session.post.return_value = mock_post_context_error
110-
116+
111117
with pytest.raises(ClientError):
112-
await http_utils.post_async_request("http://test.com", {"data": "test2"})
113-
118+
await http_utils.post_async_request("http://test.com",
119+
{"data": "test2"})
120+
114121
# Verify close was called
115122
mock_session.close.assert_called_once()
116123

@@ -120,26 +127,26 @@ async def test_get_request_uses_shared_session():
120127
"""Test that GET requests use the shared session."""
121128
# Reset the session
122129
http_utils._client_session = None
123-
130+
124131
with patch('aiohttp.ClientSession') as mock_session_class:
125132
mock_session = Mock()
126133
mock_session.closed = False
127134
mock_response = AsyncMock()
128135
mock_response.status = 200
129136
mock_response.json = AsyncMock(return_value={"result": "data"})
130-
137+
131138
mock_get_context = AsyncMock()
132139
mock_get_context.__aenter__.return_value = mock_response
133140
mock_get_context.__aexit__.return_value = None
134141
mock_session.get.return_value = mock_get_context
135142
mock_session_class.return_value = mock_session
136-
143+
137144
# Make GET request
138145
await http_utils.get_async_request("http://test.com")
139-
146+
140147
# Make another GET request
141148
await http_utils.get_async_request("http://test.com")
142-
149+
143150
# Verify session was created only once
144151
assert mock_session_class.call_count == 1
145152

@@ -149,26 +156,26 @@ async def test_delete_request_uses_shared_session():
149156
"""Test that DELETE requests use the shared session."""
150157
# Reset the session
151158
http_utils._client_session = None
152-
159+
153160
with patch('aiohttp.ClientSession') as mock_session_class:
154161
mock_session = Mock()
155162
mock_session.closed = False
156163
mock_response = AsyncMock()
157164
mock_response.status = 200
158165
mock_response.json = AsyncMock(return_value={"result": "deleted"})
159-
166+
160167
mock_delete_context = AsyncMock()
161168
mock_delete_context.__aenter__.return_value = mock_response
162169
mock_delete_context.__aexit__.return_value = None
163170
mock_session.delete.return_value = mock_delete_context
164171
mock_session_class.return_value = mock_session
165-
172+
166173
# Make DELETE request
167174
await http_utils.delete_async_request("http://test.com")
168-
175+
169176
# Make another DELETE request
170177
await http_utils.delete_async_request("http://test.com")
171-
178+
172179
# Verify session was created only once
173180
assert mock_session_class.call_count == 1
174181

@@ -178,33 +185,34 @@ async def test_session_configured_with_timeouts():
178185
"""Test that session is configured with appropriate timeouts."""
179186
# Reset the session
180187
http_utils._client_session = None
181-
188+
182189
with patch('aiohttp.ClientSession') as mock_session_class, \
183-
patch('aiohttp.ClientTimeout') as mock_timeout_class, \
184-
patch('aiohttp.TCPConnector') as mock_connector_class:
185-
190+
patch('aiohttp.ClientTimeout') as mock_timeout_class, \
191+
patch('aiohttp.TCPConnector') as mock_connector_class:
192+
186193
mock_session = Mock()
187194
mock_session.closed = False
188195
mock_response = AsyncMock()
189196
mock_response.status = 200
190197
mock_response.json = AsyncMock(return_value={"result": "success"})
191-
198+
192199
mock_post_context = AsyncMock()
193200
mock_post_context.__aenter__.return_value = mock_response
194201
mock_post_context.__aexit__.return_value = None
195202
mock_session.post.return_value = mock_post_context
196203
mock_session_class.return_value = mock_session
197-
198-
await http_utils.post_async_request("http://test.com", {"data": "test"})
199-
204+
205+
await http_utils.post_async_request("http://test.com",
206+
{"data": "test"})
207+
200208
# Verify timeout was configured
201209
mock_timeout_class.assert_called_once()
202210
timeout_call = mock_timeout_class.call_args
203211
assert timeout_call.kwargs['total'] == 60
204212
assert timeout_call.kwargs['connect'] == 30
205213
assert timeout_call.kwargs['sock_connect'] == 30
206214
assert timeout_call.kwargs['sock_read'] == 30
207-
215+
208216
# Verify connector was configured
209217
mock_connector_class.assert_called_once()
210218
connector_call = mock_connector_class.call_args
@@ -217,28 +225,29 @@ async def test_close_session():
217225
"""Test the _close_session function."""
218226
# Reset and create a session
219227
http_utils._client_session = None
220-
228+
221229
with patch('aiohttp.ClientSession') as mock_session_class:
222230
mock_session = Mock()
223231
mock_session.closed = False
224232
mock_session.close = AsyncMock()
225233
mock_response = AsyncMock()
226234
mock_response.status = 200
227235
mock_response.json = AsyncMock(return_value={"result": "success"})
228-
236+
229237
mock_post_context = AsyncMock()
230238
mock_post_context.__aenter__.return_value = mock_response
231239
mock_post_context.__aexit__.return_value = None
232240
mock_session.post.return_value = mock_post_context
233241
mock_session_class.return_value = mock_session
234-
242+
235243
# Create session
236-
await http_utils.post_async_request("http://test.com", {"data": "test"})
244+
await http_utils.post_async_request("http://test.com",
245+
{"data": "test"})
237246
assert http_utils._client_session is not None
238-
247+
239248
# Close session
240249
await http_utils._close_session()
241-
250+
242251
# Verify close was called and session is None
243252
mock_session.close.assert_called_once()
244253
assert http_utils._client_session is None
@@ -249,30 +258,30 @@ async def test_trace_headers_are_passed():
249258
"""Test that trace headers are properly passed in requests."""
250259
# Reset the session
251260
http_utils._client_session = None
252-
261+
253262
with patch('aiohttp.ClientSession') as mock_session_class:
254263
mock_session = Mock()
255264
mock_session.closed = False
256265
mock_response = AsyncMock()
257266
mock_response.status = 200
258267
mock_response.json = AsyncMock(return_value={"result": "success"})
259-
268+
260269
mock_post_context = AsyncMock()
261270
mock_post_context.__aenter__.return_value = mock_response
262271
mock_post_context.__aexit__.return_value = None
263272
mock_session.post.return_value = mock_post_context
264273
mock_session_class.return_value = mock_session
265-
274+
266275
trace_parent = "00-trace-id-parent"
267276
trace_state = "state=value"
268-
277+
269278
await http_utils.post_async_request(
270279
"http://test.com",
271280
{"data": "test"},
272281
trace_parent=trace_parent,
273282
trace_state=trace_state
274283
)
275-
284+
276285
# Verify headers were passed
277286
call_args = mock_session.post.call_args
278287
assert call_args.kwargs['headers']['traceparent'] == trace_parent

0 commit comments

Comments
 (0)