11"""Tests for http_utils module to verify ClientSession reuse."""
22import pytest
3- from unittest .mock import AsyncMock , patch , MagicMock , Mock
3+ from unittest .mock import AsyncMock , patch , Mock
44from 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