88from tfe .exception import (
99 TFEConnectionException ,
1010 TFEEndpointException ,
11- TFEForbiddenException ,
12- TFENotFoundException ,
13- TFEServerException ,
1411 TFETimeoutException ,
1512 TFEUnauthorizedException ,
16- TFEValidationException ,
1713)
1814
1915
@@ -90,223 +86,81 @@ def test_make_request_unsupported_method(self, endpoint):
9086 ):
9187 endpoint ._make_request ("INVALID" , "/test/path" )
9288
93- def test_get_method (self , endpoint , mock_response ):
94- """Test the _get convenience method."""
95- endpoint ._http_client .get .return_value = mock_response
96-
97- response = endpoint ._get ("/test/path" )
98-
99- endpoint ._http_client .get .assert_called_once_with ("/test/path" )
100- assert response == mock_response
101-
102- def test_post_method (self , endpoint , mock_response ):
103- """Test the _post convenience method."""
104- endpoint ._http_client .post .return_value = mock_response
105- test_data = {"key" : "value" }
106-
107- response = endpoint ._post ("/test/path" , test_data )
108-
109- endpoint ._http_client .post .assert_called_once_with ("/test/path" , json = test_data )
110- assert response == mock_response
111-
112- def test_put_method (self , endpoint , mock_response ):
113- """Test the _put convenience method."""
114- endpoint ._http_client .put .return_value = mock_response
115- test_data = {"key" : "value" }
116-
117- response = endpoint ._put ("/test/path" , test_data )
118-
119- endpoint ._http_client .put .assert_called_once_with ("/test/path" , json = test_data )
120- assert response == mock_response
121-
122- def test_patch_method (self , endpoint , mock_response ):
123- """Test the _patch convenience method."""
124- endpoint ._http_client .patch .return_value = mock_response
125- test_data = {"key" : "value" }
126-
127- response = endpoint ._patch ("/test/path" , test_data )
128-
129- endpoint ._http_client .patch .assert_called_once_with (
130- "/test/path" , json = test_data
131- )
132- assert response == mock_response
133-
134- def test_delete_method (self , endpoint , mock_response ):
135- """Test the _delete convenience method."""
136- endpoint ._http_client .delete .return_value = mock_response
137-
138- response = endpoint ._delete ("/test/path" )
139-
140- endpoint ._http_client .delete .assert_called_once_with ("/test/path" )
141- assert response == mock_response
142-
14389
14490class TestEndpointErrorHandling :
14591 """Test cases for endpoint error handling."""
14692
14793 @pytest .mark .parametrize (
148- "handler,error_class,error_msg,error_type " ,
94+ "method,http_method,exception_class,request_exception,expected_message,expected_cause_type " ,
14995 [
15096 (
151- "_handle_connection_error" ,
97+ "GET" ,
98+ "get" ,
15299 TFEConnectionException ,
100+ exceptions .ConnectionError ("Connection failed" ),
153101 "Failed to connect to TFE API" ,
154102 exceptions .ConnectionError ,
155103 ),
156104 (
157- "_handle_timeout_error" ,
105+ "POST" ,
106+ "post" ,
158107 TFETimeoutException ,
108+ exceptions .Timeout ("Request timed out" ),
159109 "Request timed out" ,
160110 exceptions .Timeout ,
161111 ),
162112 (
163- "_handle_request_error" ,
113+ "PUT" ,
114+ "put" ,
164115 TFEEndpointException ,
116+ exceptions .RequestException ("Request failed" ),
165117 "Request failed: Request failed" ,
166118 exceptions .RequestException ,
167119 ),
168120 (
169- "_handle_unexpected_error" ,
121+ "DELETE" ,
122+ "delete" ,
170123 TFEEndpointException ,
124+ ValueError ("Unexpected error" ),
171125 "Unexpected error occurred during DELETE request" ,
172126 ValueError ,
173127 ),
174128 ],
175129 )
176- def test_error_handlers (
177- self , endpoint , handler , error_class , error_msg , error_type
130+ def test_error_handling (
131+ self ,
132+ endpoint ,
133+ method ,
134+ http_method ,
135+ exception_class ,
136+ request_exception ,
137+ expected_message ,
138+ expected_cause_type ,
178139 ):
179- """Test all error handler methods."""
180- method , path = "DELETE" , "/test/path"
181- error = error_type ("Request failed" )
182-
183- with pytest .raises (error_class ) as exc_info :
184- getattr (endpoint , handler )(method , path , error )
185-
186- exception = exc_info .value
187- assert exception .message == error_msg
188- assert exception .method == method
189- assert exception .path == path
190- assert exception .cause == error
191-
192- @pytest .mark .parametrize (
193- "response_data,expected" ,
194- [
195- ({"error" : "test" }, {"error" : "test" }),
196- ({"text" : "Error text" }, {"text" : "Error text" }),
197- (None , None ),
198- ],
199- )
200- def test_extract_error_data (self , endpoint , mocker , response_data , expected ):
201- """Test error data extraction from various response types."""
202- if response_data is None :
203- result = endpoint ._extract_error_data (None )
204- else :
205- mock_response = mocker .Mock ()
206- if "error" in response_data :
207- mock_response .json .return_value = response_data
208- result = endpoint ._extract_error_data (mock_response )
209- else : # text case
210- mock_response .json .side_effect = ValueError ("Invalid JSON" )
211- mock_response .text = response_data ["text" ]
212- result = endpoint ._extract_error_data (mock_response )
213-
214- assert result == expected
215-
216-
217- class TestEndpointHTTPErrorHandling :
218- """Test cases for HTTP error handling."""
219-
220- @pytest .mark .parametrize (
221- "status_code,exception_class,expected_message" ,
222- [
223- (
224- 401 ,
225- TFEUnauthorizedException ,
226- "Authentication failed - invalid or missing token" ,
227- ),
228- (403 , TFEForbiddenException , "Access forbidden - insufficient permissions" ),
229- (404 , TFENotFoundException , "Resource not found" ),
230- (422 , TFEValidationException , "Name is required; Email is invalid" ),
231- (500 , TFEServerException , "TFE server error" ),
232- (418 , TFEEndpointException , "HTTP error occurred" ), # Unknown status
233- ],
234- )
235- def test_handle_http_error_status_codes (
236- self , endpoint , mocker , status_code , exception_class , expected_message
237- ):
238- """Test HTTP error handling for different status codes."""
239- method , path = "GET" , "/test/path"
240-
241- mock_response = mocker .Mock ()
242- mock_response .status_code = status_code
243- if status_code == 422 :
244- mock_response .json .return_value = {
245- "errors" : [
246- {"detail" : "Name is required" },
247- {"detail" : "Email is invalid" },
248- ]
249- }
250- else :
251- mock_response .json .return_value = {"error" : "Test error" }
252-
253- mock_http_error = exceptions .HTTPError (f"{ status_code } Error" )
254- mock_http_error .response = mock_response
140+ """Test various error handling scenarios."""
141+ path = "/test/path"
142+ getattr (endpoint ._http_client , http_method ).side_effect = request_exception
255143
256144 with pytest .raises (exception_class ) as exc_info :
257- endpoint ._handle_http_error (method , path , mock_http_error )
145+ endpoint ._make_request (method , path )
258146
259147 exception = exc_info .value
260148 assert exception .message == expected_message
261- assert exception .status_code == status_code
262149 assert exception .method == method
263150 assert exception .path == path
151+ assert isinstance (exception .cause , expected_cause_type )
264152
265- def test_handle_http_error_no_response (self , endpoint ):
266- """Test HTTP error handling with no response."""
267- mock_http_error = exceptions .HTTPError ("HTTP Error" )
268- mock_http_error .response = None
269-
270- with pytest .raises (TFEEndpointException ) as exc_info :
271- endpoint ._handle_http_error ("GET" , "/test" , mock_http_error )
272-
273- assert exc_info .value .message == "HTTP error occurred"
274- assert exc_info .value .status_code is None
275-
276-
277- class TestEndpointIntegration :
278- """Integration tests for endpoint error handling."""
153+ def test_http_error_handling (self , endpoint , mocker ):
154+ """Test HTTP error handling (delegated to error_utils)."""
155+ method , path = "GET" , "/test/path"
279156
280- @pytest .mark .parametrize (
281- "method,error_type,exception_class,expected_message" ,
282- [
283- (
284- "GET" ,
285- exceptions .ConnectionError ,
286- TFEConnectionException ,
287- "Failed to connect to TFE API" ,
288- ),
289- ("POST" , exceptions .Timeout , TFETimeoutException , "Request timed out" ),
290- ],
291- )
292- def test_make_request_errors (
293- self , endpoint , method , error_type , exception_class , expected_message
294- ):
295- """Test _make_request with various error types."""
296- path = "/test/path"
297- getattr (endpoint ._http_client , method .lower ()).side_effect = error_type (
298- "Test error"
157+ # Mock the handle_http_error function to raise an exception
158+ mock_handle_http_error = mocker .patch ("tfe.endpoint.handle_http_error" )
159+ mock_handle_http_error .side_effect = TFEUnauthorizedException (
160+ message = "Authentication failed" , status_code = 401 , method = method , path = path
299161 )
300162
301- with pytest .raises (exception_class ) as exc_info :
302- endpoint ._make_request (method , path )
303-
304- assert exc_info .value .message == expected_message
305- assert exc_info .value .method == method
306- assert exc_info .value .path == path
307-
308- def test_make_request_http_error (self , endpoint , mocker ):
309- """Test _make_request with HTTP error."""
163+ # Create mock response that raises HTTPError
310164 mock_response = mocker .Mock ()
311165 mock_response .status_code = 401
312166 mock_response .json .return_value = {"error" : "Unauthorized" }
@@ -317,75 +171,9 @@ def test_make_request_http_error(self, endpoint, mocker):
317171
318172 endpoint ._http_client .get .return_value = mock_response
319173
320- with pytest .raises (TFEUnauthorizedException ) as exc_info :
321- endpoint ._make_request ("GET" , "/test" )
322-
323- assert exc_info .value .status_code == 401
324-
325- def test_make_request_success (self , endpoint , mock_response ):
326- """Test successful _make_request."""
327- endpoint ._http_client .get .return_value = mock_response
328- response = endpoint ._make_request ("GET" , "/test" )
329- assert response == mock_response
330-
331-
332- class TestEndpointErrorParsing :
333- """Test cases for error response parsing."""
334-
335- @pytest .mark .parametrize (
336- "response_data,expected_message,expected_errors,expected_code" ,
337- [
338- # JSON:API format with multiple errors
339- (
340- {
341- "errors" : [
342- {"detail" : "Name has already been taken" },
343- {"detail" : "Email is invalid" },
344- ]
345- },
346- "Name has already been taken; Email is invalid" ,
347- ["Name has already been taken" , "Email is invalid" ],
348- None ,
349- ),
350- # JSON:API format with error code
351- (
352- {
353- "errors" : [
354- {"detail" : "Name is required" , "code" : "VALIDATION_ERROR" }
355- ]
356- },
357- "Name is required" ,
358- ["Name is required" ],
359- "VALIDATION_ERROR" ,
360- ),
361- # Simple message format
362- ({"message" : "Resource not found" }, "Resource not found" , [], None ),
363- # Error field format
364- (
365- {"error" : "Invalid request parameters" },
366- "Invalid request parameters" ,
367- [],
368- None ,
369- ),
370- # Empty errors list
371- ({"errors" : []}, "Unknown API error" , [], None ),
372- # Unknown format
373- ({"unknown_field" : "some value" }, "Unknown API error" , [], None ),
374- # Malformed input
375- ("invalid json" , "Unknown API error" , [], None ),
376- ],
377- )
378- def test_parse_tfe_error_response_formats (
379- self , endpoint , response_data , expected_message , expected_errors , expected_code
380- ):
381- """Test parsing various error response formats."""
382- result = endpoint .parse_tfe_error_response (response_data )
383-
384- assert result ["message" ] == expected_message
385- assert result ["errors" ] == expected_errors
386- assert result ["error_code" ] == expected_code
174+ # This should call handle_http_error and raise TFEUnauthorizedException
175+ with pytest .raises (TFEUnauthorizedException ):
176+ endpoint ._make_request (method , path )
387177
388- def test_parse_tfe_error_response_none (self , endpoint ):
389- """Test parsing None response."""
390- result = endpoint .parse_tfe_error_response (None )
391- assert result ["message" ] == "Failed to parse error response: None"
178+ # Verify that handle_http_error was called
179+ mock_handle_http_error .assert_called_once ()
0 commit comments