Skip to content

Commit 08c1db5

Browse files
committed
Add new rate limiting tests.
1 parent 3eebb84 commit 08c1db5

File tree

1 file changed

+171
-0
lines changed

1 file changed

+171
-0
lines changed

tests/unit/plugins/modules/test_server_info.py

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
BaseTestModule,
1212
)
1313

14+
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import call, MagicMock
15+
1416
from ansible_collections.community.hrobot.plugins.module_utils.robot import BASE_URL
1517
from ansible_collections.community.hrobot.plugins.modules import server_info
1618

@@ -295,3 +297,172 @@ def test_server_name_none_error(self, mocker):
295297
])
296298
assert result['changed'] is False
297299
assert len(result['servers']) == 0
300+
301+
def test_server_number_rate_limit_fail(self, mocker):
302+
result = self.run_module_failed(mocker, server_info, {
303+
'hetzner_user': 'test',
304+
'hetzner_password': 'hunter2',
305+
'server_number': 23,
306+
'rate_limit_retry_timeout': 0,
307+
}, [
308+
FetchUrlCall('GET', 403)
309+
.expect_basic_auth('test', 'hunter2')
310+
.expect_force_basic_auth(True)
311+
.expect_url('{0}/server/23'.format(BASE_URL))
312+
.result_json({
313+
'error': {
314+
'status': 403,
315+
'code': 'RATE_LIMIT_EXCEEDED',
316+
'message': 'Rate limit exceeded',
317+
'interval': 5,
318+
'max_request': 1,
319+
},
320+
}),
321+
])
322+
assert result['msg'] == (
323+
'Request failed: 403 RATE_LIMIT_EXCEEDED (Rate limit exceeded).'
324+
' Maximum allowed requests: 1. Time interval in seconds: 5'
325+
)
326+
327+
def test_server_number_rate_limit(self, mocker):
328+
sleep_mock = MagicMock()
329+
mocker.patch('time.sleep', sleep_mock)
330+
result = self.run_module_success(mocker, server_info, {
331+
'hetzner_user': 'test',
332+
'hetzner_password': 'hunter2',
333+
'server_number': 23,
334+
'rate_limit_retry_timeout': -1,
335+
}, [
336+
FetchUrlCall('GET', 403)
337+
.expect_basic_auth('test', 'hunter2')
338+
.expect_force_basic_auth(True)
339+
.expect_url('{0}/server/23'.format(BASE_URL))
340+
.result_json({
341+
'error': {
342+
'status': 403,
343+
'code': 'RATE_LIMIT_EXCEEDED',
344+
'message': 'Rate limit exceeded',
345+
'interval': 5,
346+
'max_request': 1,
347+
},
348+
}),
349+
FetchUrlCall('GET', 403)
350+
.expect_basic_auth('test', 'hunter2')
351+
.expect_force_basic_auth(True)
352+
.expect_url('{0}/server/23'.format(BASE_URL))
353+
.result_json({
354+
'error': {
355+
'status': 403,
356+
'code': 'RATE_LIMIT_EXCEEDED',
357+
'message': 'Rate limit exceeded',
358+
'interval': 3,
359+
'max_request': 1,
360+
},
361+
}),
362+
FetchUrlCall('GET', 403)
363+
.expect_basic_auth('test', 'hunter2')
364+
.expect_force_basic_auth(True)
365+
.expect_url('{0}/server/23'.format(BASE_URL))
366+
.result_json({
367+
'error': {
368+
'status': 403,
369+
'code': 'RATE_LIMIT_EXCEEDED',
370+
'message': 'Rate limit exceeded',
371+
'interval': 4,
372+
'max_request': 1,
373+
},
374+
}),
375+
FetchUrlCall('GET', 403)
376+
.expect_basic_auth('test', 'hunter2')
377+
.expect_force_basic_auth(True)
378+
.expect_url('{0}/server/23'.format(BASE_URL))
379+
.result_json({
380+
'error': {
381+
'status': 403,
382+
'code': 'RATE_LIMIT_EXCEEDED',
383+
'message': 'Rate limit exceeded',
384+
'interval': 5,
385+
'max_request': 1,
386+
},
387+
}),
388+
FetchUrlCall('GET', 200)
389+
.expect_basic_auth('test', 'hunter2')
390+
.expect_force_basic_auth(True)
391+
.result_json(SERVER_DETAIL_DATA[23])
392+
.expect_url('{0}/server/23'.format(BASE_URL)),
393+
])
394+
assert result['changed'] is False
395+
assert len(result['servers']) == 1
396+
assert result['servers'][0] == SERVER_DETAIL_DATA[23]['server']
397+
sleep_mock.assert_has_calls([
398+
call(5),
399+
call(3),
400+
call(3),
401+
call(3),
402+
])
403+
404+
def test_server_number_rate_limit_timeout(self, mocker):
405+
elapsed = [123.4]
406+
407+
def sleep(duration):
408+
elapsed[0] += duration
409+
print('sleep', duration, '->', elapsed[0])
410+
411+
def get_time():
412+
elapsed[0] += 0.03
413+
print('get', elapsed[0])
414+
return elapsed[0]
415+
416+
mocker.patch('time.sleep', sleep)
417+
mocker.patch('time.time', get_time)
418+
result = self.run_module_failed(mocker, server_info, {
419+
'hetzner_user': 'test',
420+
'hetzner_password': 'hunter2',
421+
'server_number': 23,
422+
'rate_limit_retry_timeout': 7,
423+
}, [
424+
FetchUrlCall('GET', 403)
425+
.expect_basic_auth('test', 'hunter2')
426+
.expect_force_basic_auth(True)
427+
.expect_url('{0}/server/23'.format(BASE_URL))
428+
.result_json({
429+
'error': {
430+
'status': 403,
431+
'code': 'RATE_LIMIT_EXCEEDED',
432+
'message': 'Rate limit exceeded',
433+
'interval': 5,
434+
'max_request': 1,
435+
},
436+
}),
437+
FetchUrlCall('GET', 403)
438+
.expect_basic_auth('test', 'hunter2')
439+
.expect_force_basic_auth(True)
440+
.expect_url('{0}/server/23'.format(BASE_URL))
441+
.result_json({
442+
'error': {
443+
'status': 403,
444+
'code': 'RATE_LIMIT_EXCEEDED',
445+
'message': 'Rate limit exceeded',
446+
'interval': 3,
447+
'max_request': 1,
448+
},
449+
}),
450+
FetchUrlCall('GET', 403)
451+
.expect_basic_auth('test', 'hunter2')
452+
.expect_force_basic_auth(True)
453+
.expect_url('{0}/server/23'.format(BASE_URL))
454+
.result_json({
455+
'error': {
456+
'status': 403,
457+
'code': 'RATE_LIMIT_EXCEEDED',
458+
'message': 'Rate limit exceeded',
459+
'interval': 4,
460+
'max_request': 1,
461+
},
462+
}),
463+
])
464+
assert result['msg'] == (
465+
'Request failed: 403 RATE_LIMIT_EXCEEDED (Rate limit exceeded).'
466+
' Maximum allowed requests: 1. Time interval in seconds: 4.'
467+
' Waited a total of 5.1 seconds for rate limit errors to go away'
468+
)

0 commit comments

Comments
 (0)