@@ -43,7 +43,9 @@ def rate_limit_detected(w):
4343 return False
4444
4545
46- def create_mock_rate_limit_response (status_code = 429 , retry_after = None , content_type = "application/json" ):
46+ def create_mock_rate_limit_response (
47+ status_code = 429 , retry_after = None , content_type = "application/json"
48+ ):
4749 """Create a mock response object for testing rate limit scenarios."""
4850 # Use Mock(spec=requests.Response) to properly simulate a requests.Response object
4951 mock_response = Mock (spec = requests .Response )
@@ -52,12 +54,12 @@ def create_mock_rate_limit_response(status_code=429, retry_after=None, content_t
5254 mock_response .headers = {}
5355
5456 if retry_after is not None :
55- mock_response .headers [' Retry-After' ] = retry_after
57+ mock_response .headers [" Retry-After" ] = retry_after
5658
57- mock_response .headers [' Content-Type' ] = content_type
59+ mock_response .headers [" Content-Type" ] = content_type
5860 mock_response .json .return_value = {
59- ' message' : ' Rate limit exceeded' ,
60- ' trackingId' : ' test-tracking-id-12345'
61+ " message" : " Rate limit exceeded" ,
62+ " trackingId" : " test-tracking-id-12345" ,
6163 }
6264
6365 # Mock the request attribute that ApiError constructor needs
@@ -91,22 +93,27 @@ def test_rate_limit_error_with_valid_retry_after():
9193 """Test RateLimitError works correctly with valid Retry-After headers."""
9294 # Test with various valid integer values
9395 test_cases = [
94- ('30' , 30 ), # Normal case
95- ('60' , 60 ), # One minute
96- ('0' , 1 ), # Zero should default to 1 (minimum)
97- ('1' , 1 ), # Minimum value
98- (' 300' , 300 ), # Five minutes
96+ ("30" , 30 ), # Normal case
97+ ("60" , 60 ), # One minute
98+ ("0" , 1 ), # Zero should default to 1 (minimum)
99+ ("1" , 1 ), # Minimum value
100+ (" 300" , 300 ), # Five minutes
99101 ]
100102
101103 for header_value , expected_value in test_cases :
102- mock_response = create_mock_rate_limit_response (retry_after = header_value )
104+ mock_response = create_mock_rate_limit_response (
105+ retry_after = header_value
106+ )
103107
104108 try :
105109 error = webexpythonsdk .RateLimitError (mock_response )
106- assert error .retry_after == expected_value , \
107- f"Expected retry_after={ expected_value } , got { error .retry_after } for header '{ header_value } '"
110+ assert (
111+ error .retry_after == expected_value
112+ ), f"Expected retry_after={ expected_value } , got { error .retry_after } for header '{ header_value } '"
108113 except Exception as e :
109- pytest .fail (f"RateLimitError creation failed for valid header '{ header_value } ': { e } " )
114+ pytest .fail (
115+ f"RateLimitError creation failed for valid header '{ header_value } ': { e } "
116+ )
110117
111118
112119def test_rate_limit_error_without_retry_after ():
@@ -115,9 +122,13 @@ def test_rate_limit_error_without_retry_after():
115122
116123 try :
117124 error = webexpythonsdk .RateLimitError (mock_response )
118- assert error .retry_after == 15 , f"Expected default retry_after=15, got { error .retry_after } "
125+ assert (
126+ error .retry_after == 15
127+ ), f"Expected default retry_after=15, got { error .retry_after } "
119128 except Exception as e :
120- pytest .fail (f"RateLimitError creation failed when Retry-After header is missing: { e } " )
129+ pytest .fail (
130+ f"RateLimitError creation failed when Retry-After header is missing: { e } "
131+ )
121132
122133
123134def test_rate_limit_error_with_malformed_retry_after ():
@@ -127,65 +138,77 @@ def test_rate_limit_error_with_malformed_retry_after():
127138 like 'rand(30),add(30)' cause ValueError exceptions.
128139 """
129140 malformed_headers = [
130- ' rand(30),add(30)' , # The exact case from the user report
131- ' invalid' , # Non-numeric string
132- ' 30.5' , # Float (should fail int conversion)
133- ' 30 seconds' , # String with numbers and text
134- ' 30,60' , # Comma-separated values
135- '' , # Empty string
136- ' None' , # String 'None'
137- ' null' , # String 'null'
141+ " rand(30),add(30)" , # The exact case from the user report
142+ " invalid" , # Non-numeric string
143+ " 30.5" , # Float (should fail int conversion)
144+ " 30 seconds" , # String with numbers and text
145+ " 30,60" , # Comma-separated values
146+ "" , # Empty string
147+ " None" , # String 'None'
148+ " null" , # String 'null'
138149 ]
139150
140151 for malformed_header in malformed_headers :
141- mock_response = create_mock_rate_limit_response (retry_after = malformed_header )
152+ mock_response = create_mock_rate_limit_response (
153+ retry_after = malformed_header
154+ )
142155
143156 try :
144157 # This should NOT raise a ValueError - it should handle gracefully
145158 error = webexpythonsdk .RateLimitError (mock_response )
146159 # If we get here, the error was handled gracefully
147160 # The retry_after should default to 15 for malformed headers
148- assert error .retry_after == 15 , \
149- f"Expected default retry_after=15 for malformed header '{ malformed_header } ', got { error .retry_after } "
161+ assert (
162+ error .retry_after == 15
163+ ), f"Expected default retry_after=15 for malformed header '{ malformed_header } ', got { error .retry_after } "
150164 except ValueError as e :
151165 # This is the bug we're testing for - it should NOT happen
152- pytest .fail (f"RateLimitError raised ValueError for malformed header '{ malformed_header } ': { e } " )
166+ pytest .fail (
167+ f"RateLimitError raised ValueError for malformed header '{ malformed_header } ': { e } "
168+ )
153169 except Exception as e :
154170 # Other exceptions are acceptable as long as they're not ValueError
155171 if isinstance (e , ValueError ):
156- pytest .fail (f"RateLimitError raised ValueError for malformed header '{ malformed_header } ': { e } " )
172+ pytest .fail (
173+ f"RateLimitError raised ValueError for malformed header '{ malformed_header } ': { e } "
174+ )
157175
158176
159177def test_rate_limit_error_with_non_string_retry_after ():
160178 """Test RateLimitError handles non-string Retry-After header values."""
161179 # Test cases with expected behavior based on how Python int() actually works
162180 test_cases = [
163- (None , 15 ), # None value -> defaults to 15
164- (30 , 30 ), # Integer -> converts to 30 (not malformed)
165- (30.5 , 30 ), # Float -> converts to 30 (truncated)
166- (True , 1 ), # Boolean True -> converts to 1
167- (False , 1 ), # Boolean False -> converts to 0, then max(1, 0) = 1
168- ([], 15 ), # List -> TypeError, defaults to 15
169- ({}, 15 ), # Dict -> TypeError, defaults to 15
170- ]
181+ (None , 15 ), # None value -> defaults to 15
182+ (30 , 30 ), # Integer -> converts to 30 (not malformed)
183+ (30.5 , 30 ), # Float -> converts to 30 (truncated)
184+ (True , 1 ), # Boolean True -> converts to 1
185+ (False , 1 ), # Boolean False -> converts to 0, then max(1, 0) = 1
186+ ([], 15 ), # List -> TypeError, defaults to 15
187+ ({}, 15 ), # Dict -> TypeError, defaults to 15
188+ ]
171189
172190 for non_string_value , expected_value in test_cases :
173- mock_response = create_mock_rate_limit_response (retry_after = non_string_value )
191+ mock_response = create_mock_rate_limit_response (
192+ retry_after = non_string_value
193+ )
174194
175195 try :
176196 error = webexpythonsdk .RateLimitError (mock_response )
177- assert error .retry_after == expected_value , \
178- f"Expected retry_after={ expected_value } , got { error .retry_after } for non-string value { non_string_value } "
197+ assert (
198+ error .retry_after == expected_value
199+ ), f"Expected retry_after={ expected_value } , got { error .retry_after } for non-string value { non_string_value } "
179200 except Exception as e :
180- pytest .fail (f"RateLimitError creation failed for non-string value { non_string_value } : { e } " )
201+ pytest .fail (
202+ f"RateLimitError creation failed for non-string value { non_string_value } : { e } "
203+ )
181204
182205
183206def test_rate_limit_error_integration_with_check_response_code ():
184207 """Test that check_response_code properly raises RateLimitError for 429 responses."""
185208 from webexpythonsdk .utils import check_response_code
186209
187210 # Test with valid Retry-After header
188- mock_response = create_mock_rate_limit_response (retry_after = '45' )
211+ mock_response = create_mock_rate_limit_response (retry_after = "45" )
189212
190213 with pytest .raises (webexpythonsdk .RateLimitError ) as exc_info :
191214 check_response_code (mock_response , 200 ) # Expect 200, get 429
@@ -200,7 +223,9 @@ def test_rate_limit_error_integration_with_malformed_header():
200223 from webexpythonsdk .utils import check_response_code
201224
202225 # Test with malformed Retry-After header
203- mock_response = create_mock_rate_limit_response (retry_after = 'rand(30),add(30)' )
226+ mock_response = create_mock_rate_limit_response (
227+ retry_after = "rand(30),add(30)"
228+ )
204229
205230 with pytest .raises (webexpythonsdk .RateLimitError ) as exc_info :
206231 check_response_code (mock_response , 200 ) # Expect 200, get 429
@@ -213,37 +238,42 @@ def test_rate_limit_error_integration_with_malformed_header():
213238
214239def test_rate_limit_error_edge_cases ():
215240 """Test RateLimitError with edge case Retry-After values."""
216- # Test cases based on how Python int() actually works with strings
241+ # Test cases based on how Python int() actually works with strings
217242 edge_cases = [
218- ( '-1' , 1 ), # Negative string -> converts to -1, then max(1, -1) = 1
219- ( ' 999999' , 999999 ), # Very large number string -> converts to 999999
220- ( ' 0.0' , 15 ), # Float string -> treated as malformed, defaults to 15
221- ( ' 0.9' , 15 ), # Float string -> treated as malformed, defaults to 15
222- ( ' 1.0' , 15 ), # Float string -> treated as malformed, defaults to 15
223- ( ' 1.9' , 15 ), # Float string -> treated as malformed, defaults to 15
224- ( ' 2.0' , 15 ), # Float string -> treated as malformed, defaults to 15
225- ]
243+ ( "-1" , 1 ), # Negative string -> converts to -1, then max(1, -1) = 1
244+ ( " 999999" , 999999 ), # Very large number string -> converts to 999999
245+ ( " 0.0" , 15 ), # Float string -> treated as malformed, defaults to 15
246+ ( " 0.9" , 15 ), # Float string -> treated as malformed, defaults to 15
247+ ( " 1.0" , 15 ), # Float string -> treated as malformed, defaults to 15
248+ ( " 1.9" , 15 ), # Float string -> treated as malformed, defaults to 15
249+ ( " 2.0" , 15 ), # Float string -> treated as malformed, defaults to 15
250+ ]
226251
227252 for header_value , expected_value in edge_cases :
228- mock_response = create_mock_rate_limit_response (retry_after = header_value )
253+ mock_response = create_mock_rate_limit_response (
254+ retry_after = header_value
255+ )
229256
230257 try :
231258 error = webexpythonsdk .RateLimitError (mock_response )
232259 # All float strings are being treated as malformed and defaulting to 15
233260 # Integer strings work normally with max(1, value)
234- if '.' in header_value : # Float strings
261+ if "." in header_value : # Float strings
235262 actual_expected = 15 # Treated as malformed
236263 else :
237264 actual_expected = max (1 , expected_value )
238- assert error .retry_after == actual_expected , \
239- f"Expected retry_after={ actual_expected } , got { error .retry_after } for header '{ header_value } '"
265+ assert (
266+ error .retry_after == actual_expected
267+ ), f"Expected retry_after={ actual_expected } , got { error .retry_after } for header '{ header_value } '"
240268 except Exception as e :
241- pytest .fail (f"RateLimitError creation failed for edge case header '{ header_value } ': { e } " )
269+ pytest .fail (
270+ f"RateLimitError creation failed for edge case header '{ header_value } ': { e } "
271+ )
242272
243273
244274def test_rate_limit_error_response_attributes ():
245275 """Test that RateLimitError properly extracts all response attributes."""
246- mock_response = create_mock_rate_limit_response (retry_after = '60' )
276+ mock_response = create_mock_rate_limit_response (retry_after = "60" )
247277
248278 error = webexpythonsdk .RateLimitError (mock_response )
249279
0 commit comments