Skip to content

Commit 8754d6b

Browse files
committed
feat: add api keys rotating - add network.py tests
1 parent bdc7117 commit 8754d6b

File tree

2 files changed

+80
-4
lines changed

2 files changed

+80
-4
lines changed

aioetherscan/network.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ async def inner(self, *args, **kwargs):
3131
return await f(self, *args, **kwargs)
3232
except EtherscanClientApiRateLimitError as e:
3333
self._logger.warning(f'Key daily limit exceeded, {attempt=}: {e}')
34-
if attempt >= max_attempts:
34+
attempt += 1
35+
if attempt > max_attempts:
3536
raise e
3637
await asyncio.sleep(0.01)
3738
self._url_builder.rotate_api_key()

tests/test_network.py

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import asyncio
22
import json
33
import logging
4-
from unittest.mock import patch, AsyncMock, MagicMock, Mock
4+
from unittest.mock import patch, AsyncMock, MagicMock, Mock, call
55

66
import aiohttp
77
import aiohttp_retry
@@ -17,8 +17,9 @@
1717
EtherscanClientError,
1818
EtherscanClientApiError,
1919
EtherscanClientProxyError,
20+
EtherscanClientApiRateLimitError,
2021
)
21-
from aioetherscan.network import Network
22+
from aioetherscan.network import Network, retry_limit_attempt
2223
from aioetherscan.url_builder import UrlBuilder
2324

2425

@@ -45,7 +46,7 @@ def get_loop():
4546

4647
@pytest_asyncio.fixture
4748
async def ub():
48-
ub = UrlBuilder('test_api_key', 'eth', 'main')
49+
ub = UrlBuilder(['test_api_key'], 'eth', 'main')
4950
yield ub
5051

5152

@@ -238,3 +239,77 @@ def test_get_retry_client(nw):
238239
retry_options=nw._retry_options,
239240
)
240241
assert result is m.return_value
242+
243+
244+
def test_raise_if_error_daily_limit_reached(nw):
245+
data = dict(
246+
status='0',
247+
message='NOTOK',
248+
result='Max daily rate limit reached. 110000 (100%) of 100000 day/limit',
249+
)
250+
with pytest.raises(EtherscanClientApiRateLimitError) as e:
251+
nw._raise_if_error(data)
252+
253+
assert e.value.message == data['message']
254+
assert e.value.result == data['result']
255+
256+
257+
class TestRetryClass:
258+
def __init__(self, limit: int, keys_count: int):
259+
self._url_builder = Mock()
260+
self._url_builder.keys_count = keys_count
261+
self._url_builder.rotate_api_key = Mock()
262+
263+
self._logger = Mock()
264+
self._logger.warning = Mock()
265+
266+
self._count = 1
267+
self._limit = limit
268+
269+
@retry_limit_attempt
270+
async def some_method(self):
271+
self._count += 1
272+
273+
if self._count > self._limit:
274+
raise EtherscanClientApiRateLimitError(
275+
'NOTOK',
276+
'Max daily rate limit reached. 110000 (100%) of 100000 day/limit',
277+
)
278+
279+
280+
@pytest.mark.asyncio
281+
async def test_retry_limit_attempt_error_limit_exceeded(nw):
282+
c = TestRetryClass(1, 1)
283+
284+
with pytest.raises(EtherscanClientApiRateLimitError):
285+
await c.some_method()
286+
c._url_builder.rotate_api_key.assert_not_called()
287+
c._logger.warning.assert_called_once_with(
288+
'Key daily limit exceeded, attempt=1: [NOTOK] Max daily rate limit reached. 110000 (100%) of 100000 day/limit'
289+
)
290+
291+
292+
@pytest.mark.asyncio
293+
async def test_retry_limit_attempt_error_limit_rotate(nw):
294+
c = TestRetryClass(1, 2)
295+
296+
with pytest.raises(EtherscanClientApiRateLimitError):
297+
await c.some_method()
298+
c._url_builder.rotate_api_key.assert_called_once()
299+
c._logger.warning.assert_has_calls(
300+
[
301+
call(
302+
'Key daily limit exceeded, attempt=1: [NOTOK] Max daily rate limit reached. 110000 (100%) of 100000 day/limit'
303+
),
304+
call(
305+
'Key daily limit exceeded, attempt=2: [NOTOK] Max daily rate limit reached. 110000 (100%) of 100000 day/limit'
306+
),
307+
]
308+
)
309+
310+
311+
@pytest.mark.asyncio
312+
async def test_retry_limit_attempt_ok(nw):
313+
c = TestRetryClass(2, 1)
314+
315+
await c.some_method()

0 commit comments

Comments
 (0)