11import asyncio
22import json
33import logging
4- from unittest .mock import patch , AsyncMock , MagicMock , Mock
4+ from unittest .mock import patch , AsyncMock , MagicMock , Mock , call
55
66import aiohttp
77import aiohttp_retry
1717 EtherscanClientError ,
1818 EtherscanClientApiError ,
1919 EtherscanClientProxyError ,
20+ EtherscanClientApiRateLimitError ,
2021)
21- from aioetherscan .network import Network
22+ from aioetherscan .network import Network , retry_limit_attempt
2223from aioetherscan .url_builder import UrlBuilder
2324
2425
@@ -45,7 +46,7 @@ def get_loop():
4546
4647@pytest_asyncio .fixture
4748async 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