Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c6e272e

Browse files
committed
tests: Add tests for new RestClient rate limit handling
1 parent 7f810a7 commit c6e272e

File tree

1 file changed

+176
-3
lines changed

1 file changed

+176
-3
lines changed

auth0/v3/test/management/test_rest.py

Lines changed: 176 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ def test_get_errors(self, mock_get):
151151

152152
@mock.patch('requests.get')
153153
def test_get_rate_limit_error(self, mock_get):
154-
rc = RestClient(jwt='a-token', telemetry=False)
154+
rc = RestClient(jwt='a-token', telemetry=False, retries=0)
155+
rc._skip_sleep = True
155156

156157
mock_get.return_value.text = '{"statusCode": 429,' \
157158
' "errorCode": "code",' \
@@ -172,15 +173,18 @@ def test_get_rate_limit_error(self, mock_get):
172173
self.assertIsInstance(context.exception, RateLimitError)
173174
self.assertEqual(context.exception.reset_at, 9)
174175

176+
self.assertEqual(rc._metrics['retries'], 0)
177+
175178
@mock.patch('requests.get')
176179
def test_get_rate_limit_error_without_headers(self, mock_get):
177-
rc = RestClient(jwt='a-token', telemetry=False)
180+
rc = RestClient(jwt='a-token', telemetry=False, retries=0)
181+
rc._skip_sleep = True
178182

179183
mock_get.return_value.text = '{"statusCode": 429,' \
180184
' "errorCode": "code",' \
181185
' "message": "message"}'
182186
mock_get.return_value.status_code = 429
183-
187+
184188
mock_get.return_value.headers = {}
185189
with self.assertRaises(Auth0Error) as context:
186190
rc.get('the/url')
@@ -191,6 +195,175 @@ def test_get_rate_limit_error_without_headers(self, mock_get):
191195
self.assertIsInstance(context.exception, RateLimitError)
192196
self.assertEqual(context.exception.reset_at, -1)
193197

198+
self.assertEqual(rc._metrics['retries'], 0)
199+
200+
@mock.patch('requests.get')
201+
def test_get_rate_limit_custom_retries(self, mock_get):
202+
rc = RestClient(jwt='a-token', telemetry=False, retries=5)
203+
rc._skip_sleep = True
204+
205+
mock_get.return_value.text = '{"statusCode": 429,' \
206+
' "errorCode": "code",' \
207+
' "message": "message"}'
208+
mock_get.return_value.status_code = 429
209+
mock_get.return_value.headers = {
210+
'x-ratelimit-limit': '3',
211+
'x-ratelimit-remaining': '6',
212+
'x-ratelimit-reset': '9',
213+
}
214+
215+
with self.assertRaises(Auth0Error) as context:
216+
response = rc.get('the/url')
217+
218+
self.assertEqual(context.exception.status_code, 429)
219+
self.assertEqual(context.exception.error_code, 'code')
220+
self.assertEqual(context.exception.message, 'message')
221+
self.assertIsInstance(context.exception, RateLimitError)
222+
self.assertEqual(context.exception.reset_at, 9)
223+
224+
self.assertEqual(rc._metrics['retries'], 5)
225+
self.assertEqual(rc._metrics['retries'], len(rc._metrics['backoff']))
226+
227+
@mock.patch('requests.get')
228+
def test_get_rate_limit_invalid_retries_below_min(self, mock_get):
229+
rc = RestClient(jwt='a-token', telemetry=False, retries=-1)
230+
rc._skip_sleep = True
231+
232+
mock_get.return_value.text = '{"statusCode": 429,' \
233+
' "errorCode": "code",' \
234+
' "message": "message"}'
235+
mock_get.return_value.status_code = 429
236+
mock_get.return_value.headers = {
237+
'x-ratelimit-limit': '3',
238+
'x-ratelimit-remaining': '6',
239+
'x-ratelimit-reset': '9',
240+
}
241+
242+
with self.assertRaises(Auth0Error) as context:
243+
response = rc.get('the/url')
244+
245+
self.assertEqual(context.exception.status_code, 429)
246+
self.assertEqual(context.exception.error_code, 'code')
247+
self.assertEqual(context.exception.message, 'message')
248+
self.assertIsInstance(context.exception, RateLimitError)
249+
self.assertEqual(context.exception.reset_at, 9)
250+
251+
self.assertEqual(rc._metrics['retries'], 0)
252+
253+
254+
@mock.patch('requests.get')
255+
def test_get_rate_limit_invalid_retries_above_max(self, mock_get):
256+
rc = RestClient(jwt='a-token', telemetry=False, retries=11)
257+
rc._skip_sleep = True
258+
259+
mock_get.return_value.text = '{"statusCode": 429,' \
260+
' "errorCode": "code",' \
261+
' "message": "message"}'
262+
mock_get.return_value.status_code = 429
263+
mock_get.return_value.headers = {
264+
'x-ratelimit-limit': '3',
265+
'x-ratelimit-remaining': '6',
266+
'x-ratelimit-reset': '9',
267+
}
268+
269+
with self.assertRaises(Auth0Error) as context:
270+
response = rc.get('the/url')
271+
272+
self.assertEqual(context.exception.status_code, 429)
273+
self.assertEqual(context.exception.error_code, 'code')
274+
self.assertEqual(context.exception.message, 'message')
275+
self.assertIsInstance(context.exception, RateLimitError)
276+
self.assertEqual(context.exception.reset_at, 9)
277+
278+
self.assertEqual(rc._metrics['retries'], rc.MAX_REQUEST_RETRIES())
279+
280+
@mock.patch('requests.get')
281+
def test_get_rate_limit_retries_use_exponential_backoff(self, mock_get):
282+
rc = RestClient(jwt='a-token', telemetry=False, retries=10)
283+
rc._skip_sleep = True
284+
285+
mock_get.return_value.text = '{"statusCode": 429,' \
286+
' "errorCode": "code",' \
287+
' "message": "message"}'
288+
mock_get.return_value.status_code = 429
289+
mock_get.return_value.headers = {
290+
'x-ratelimit-limit': '3',
291+
'x-ratelimit-remaining': '6',
292+
'x-ratelimit-reset': '9',
293+
}
294+
295+
with self.assertRaises(Auth0Error) as context:
296+
response = rc.get('the/url')
297+
298+
self.assertEqual(context.exception.status_code, 429)
299+
self.assertEqual(context.exception.error_code, 'code')
300+
self.assertEqual(context.exception.message, 'message')
301+
self.assertIsInstance(context.exception, RateLimitError)
302+
self.assertEqual(context.exception.reset_at, 9)
303+
304+
self.assertEqual(rc._metrics['retries'], 10)
305+
self.assertEqual(rc._metrics['retries'], len(rc._metrics['backoff']))
306+
307+
baseBackoff = [0]
308+
baseBackoffSum = 0
309+
finalBackoff = 0
310+
311+
for i in range(0, 9):
312+
backoff = 100 * 2 ** i
313+
baseBackoff.append(backoff)
314+
baseBackoffSum += backoff
315+
316+
for backoff in rc._metrics['backoff']:
317+
finalBackoff += backoff
318+
319+
# Assert that exponential backoff is happening.
320+
self.assertGreaterEqual(rc._metrics['backoff'][1], rc._metrics['backoff'][0])
321+
self.assertGreaterEqual(rc._metrics['backoff'][2], rc._metrics['backoff'][1])
322+
self.assertGreaterEqual(rc._metrics['backoff'][3], rc._metrics['backoff'][2])
323+
self.assertGreaterEqual(rc._metrics['backoff'][4], rc._metrics['backoff'][3])
324+
self.assertGreaterEqual(rc._metrics['backoff'][5], rc._metrics['backoff'][4])
325+
self.assertGreaterEqual(rc._metrics['backoff'][6], rc._metrics['backoff'][5])
326+
self.assertGreaterEqual(rc._metrics['backoff'][7], rc._metrics['backoff'][6])
327+
self.assertGreaterEqual(rc._metrics['backoff'][8], rc._metrics['backoff'][7])
328+
self.assertGreaterEqual(rc._metrics['backoff'][9], rc._metrics['backoff'][8])
329+
330+
# Ensure jitter is being applied.
331+
self.assertNotEquals(rc._metrics['backoff'][1], baseBackoff[1])
332+
self.assertNotEquals(rc._metrics['backoff'][2], baseBackoff[2])
333+
self.assertNotEquals(rc._metrics['backoff'][3], baseBackoff[3])
334+
self.assertNotEquals(rc._metrics['backoff'][4], baseBackoff[4])
335+
self.assertNotEquals(rc._metrics['backoff'][5], baseBackoff[5])
336+
self.assertNotEquals(rc._metrics['backoff'][6], baseBackoff[6])
337+
self.assertNotEquals(rc._metrics['backoff'][7], baseBackoff[7])
338+
self.assertNotEquals(rc._metrics['backoff'][8], baseBackoff[8])
339+
self.assertNotEquals(rc._metrics['backoff'][9], baseBackoff[9])
340+
341+
# Ensure subsequent delay is never less than the minimum.
342+
self.assertGreaterEqual(rc._metrics['backoff'][1], rc.MIN_REQUEST_RETRY_DELAY())
343+
self.assertGreaterEqual(rc._metrics['backoff'][2], rc.MIN_REQUEST_RETRY_DELAY())
344+
self.assertGreaterEqual(rc._metrics['backoff'][3], rc.MIN_REQUEST_RETRY_DELAY())
345+
self.assertGreaterEqual(rc._metrics['backoff'][4], rc.MIN_REQUEST_RETRY_DELAY())
346+
self.assertGreaterEqual(rc._metrics['backoff'][5], rc.MIN_REQUEST_RETRY_DELAY())
347+
self.assertGreaterEqual(rc._metrics['backoff'][6], rc.MIN_REQUEST_RETRY_DELAY())
348+
self.assertGreaterEqual(rc._metrics['backoff'][7], rc.MIN_REQUEST_RETRY_DELAY())
349+
self.assertGreaterEqual(rc._metrics['backoff'][8], rc.MIN_REQUEST_RETRY_DELAY())
350+
self.assertGreaterEqual(rc._metrics['backoff'][9], rc.MIN_REQUEST_RETRY_DELAY())
351+
352+
# Ensure delay is never more than the maximum.
353+
self.assertLessEqual(rc._metrics['backoff'][0], rc.MAX_REQUEST_RETRY_DELAY())
354+
self.assertLessEqual(rc._metrics['backoff'][1], rc.MAX_REQUEST_RETRY_DELAY())
355+
self.assertLessEqual(rc._metrics['backoff'][2], rc.MAX_REQUEST_RETRY_DELAY())
356+
self.assertLessEqual(rc._metrics['backoff'][3], rc.MAX_REQUEST_RETRY_DELAY())
357+
self.assertLessEqual(rc._metrics['backoff'][4], rc.MAX_REQUEST_RETRY_DELAY())
358+
self.assertLessEqual(rc._metrics['backoff'][5], rc.MAX_REQUEST_RETRY_DELAY())
359+
self.assertLessEqual(rc._metrics['backoff'][6], rc.MAX_REQUEST_RETRY_DELAY())
360+
self.assertLessEqual(rc._metrics['backoff'][7], rc.MAX_REQUEST_RETRY_DELAY())
361+
self.assertLessEqual(rc._metrics['backoff'][8], rc.MAX_REQUEST_RETRY_DELAY())
362+
self.assertLessEqual(rc._metrics['backoff'][9], rc.MAX_REQUEST_RETRY_DELAY())
363+
364+
# Ensure total delay sum is never more than 10s.
365+
self.assertLessEqual(finalBackoff, 10000)
366+
194367
@mock.patch('requests.post')
195368
def test_post(self, mock_post):
196369
rc = RestClient(jwt='a-token', telemetry=False)

0 commit comments

Comments
 (0)