11import itertools
22import pytest
3+ import threading
34import uuid
45
56from web3 import Web3
1112 construct_result_generator_middleware ,
1213 construct_simple_cache_middleware ,
1314)
15+ from web3 .middleware .async_cache import (
16+ async_construct_simple_cache_middleware ,
17+ )
18+ from web3 .middleware .fixture import (
19+ async_construct_error_generator_middleware ,
20+ async_construct_result_generator_middleware ,
21+ )
1422from web3 .providers .base import (
1523 BaseProvider ,
1624)
25+ from web3 .providers .eth_tester import (
26+ AsyncEthereumTesterProvider ,
27+ )
28+ from web3 .types import (
29+ RPCEndpoint ,
30+ )
1731
1832
1933@pytest .fixture
@@ -25,8 +39,8 @@ def w3_base():
2539def result_generator_middleware ():
2640 return construct_result_generator_middleware (
2741 {
28- "fake_endpoint" : lambda * _ : str (uuid .uuid4 ()),
29- "not_whitelisted" : lambda * _ : str (uuid .uuid4 ()),
42+ RPCEndpoint ( "fake_endpoint" ) : lambda * _ : str (uuid .uuid4 ()),
43+ RPCEndpoint ( "not_whitelisted" ) : lambda * _ : str (uuid .uuid4 ()),
3044 }
3145 )
3246
@@ -40,13 +54,15 @@ def w3(w3_base, result_generator_middleware):
4054def test_simple_cache_middleware_pulls_from_cache (w3 ):
4155 def cache_class ():
4256 return {
43- generate_cache_key (("fake_endpoint" , [1 ])): {"result" : "value-a" },
57+ generate_cache_key (f"{ threading .get_ident ()} :{ ('fake_endpoint' , [1 ])} " ): {
58+ "result" : "value-a"
59+ },
4460 }
4561
4662 w3 .middleware_onion .add (
4763 construct_simple_cache_middleware (
4864 cache_class = cache_class ,
49- rpc_whitelist = {"fake_endpoint" },
65+ rpc_whitelist = {RPCEndpoint ( "fake_endpoint" ) },
5066 )
5167 )
5268
@@ -57,7 +73,7 @@ def test_simple_cache_middleware_populates_cache(w3):
5773 w3 .middleware_onion .add (
5874 construct_simple_cache_middleware (
5975 cache_class = dict ,
60- rpc_whitelist = {"fake_endpoint" },
76+ rpc_whitelist = {RPCEndpoint ( "fake_endpoint" ) },
6177 )
6278 )
6379
@@ -71,22 +87,22 @@ def test_simple_cache_middleware_does_not_cache_none_responses(w3_base):
7187 counter = itertools .count ()
7288 w3 = w3_base
7389
74- def result_cb (method , params ):
90+ def result_cb (_method , _params ):
7591 next (counter )
7692 return None
7793
7894 w3 .middleware_onion .add (
7995 construct_result_generator_middleware (
8096 {
81- "fake_endpoint" : result_cb ,
97+ RPCEndpoint ( "fake_endpoint" ) : result_cb ,
8298 }
8399 )
84100 )
85101
86102 w3 .middleware_onion .add (
87103 construct_simple_cache_middleware (
88104 cache_class = dict ,
89- rpc_whitelist = {"fake_endpoint" },
105+ rpc_whitelist = {RPCEndpoint ( "fake_endpoint" ) },
90106 )
91107 )
92108
@@ -101,15 +117,15 @@ def test_simple_cache_middleware_does_not_cache_error_responses(w3_base):
101117 w3 .middleware_onion .add (
102118 construct_error_generator_middleware (
103119 {
104- "fake_endpoint" : lambda * _ : f"msg-{ uuid .uuid4 ()} " ,
120+ RPCEndpoint ( "fake_endpoint" ) : lambda * _ : f"msg-{ uuid .uuid4 ()} " ,
105121 }
106122 )
107123 )
108124
109125 w3 .middleware_onion .add (
110126 construct_simple_cache_middleware (
111127 cache_class = dict ,
112- rpc_whitelist = {"fake_endpoint" },
128+ rpc_whitelist = {RPCEndpoint ( "fake_endpoint" ) },
113129 )
114130 )
115131
@@ -125,11 +141,165 @@ def test_simple_cache_middleware_does_not_cache_endpoints_not_in_whitelist(w3):
125141 w3 .middleware_onion .add (
126142 construct_simple_cache_middleware (
127143 cache_class = dict ,
128- rpc_whitelist = {"fake_endpoint" },
144+ rpc_whitelist = {RPCEndpoint ( "fake_endpoint" ) },
129145 )
130146 )
131147
132148 result_a = w3 .manager .request_blocking ("not_whitelisted" , [])
133149 result_b = w3 .manager .request_blocking ("not_whitelisted" , [])
134150
135151 assert result_a != result_b
152+
153+
154+ # -- async -- #
155+
156+
157+ async def _async_simple_cache_middleware_for_testing (make_request , async_w3 ):
158+ middleware = await async_construct_simple_cache_middleware (
159+ cache_class = dict ,
160+ rpc_whitelist = {RPCEndpoint ("fake_endpoint" )},
161+ )
162+ return await middleware (make_request , async_w3 )
163+
164+
165+ @pytest .fixture
166+ def async_w3 ():
167+ return Web3 (
168+ provider = AsyncEthereumTesterProvider (),
169+ middlewares = [
170+ (_async_simple_cache_middleware_for_testing , "simple_cache" ),
171+ ],
172+ )
173+
174+
175+ @pytest .mark .asyncio
176+ async def test_async_simple_cache_middleware_pulls_from_cache (async_w3 ):
177+ # remove the pre-loaded simple cache middleware to replace with test-specific:
178+ async_w3 .middleware_onion .remove ("simple_cache" )
179+
180+ def cache_class ():
181+ return {
182+ generate_cache_key (f"{ threading .get_ident ()} :{ ('fake_endpoint' , [1 ])} " ): {
183+ "result" : "value-a"
184+ },
185+ }
186+
187+ async def _properly_awaited_middleware (make_request , _async_w3 ):
188+ middleware = await async_construct_simple_cache_middleware (
189+ cache_class = cache_class ,
190+ rpc_whitelist = {RPCEndpoint ("fake_endpoint" )},
191+ )
192+ return await middleware (make_request , _async_w3 )
193+
194+ async_w3 .middleware_onion .inject (
195+ _properly_awaited_middleware ,
196+ "for_this_test_only" ,
197+ layer = 0 ,
198+ )
199+
200+ _result = await async_w3 .manager .coro_request ("fake_endpoint" , [1 ])
201+ assert _result == "value-a"
202+
203+ # -- teardown -- #
204+ async_w3 .middleware_onion .remove ("for_this_test_only" )
205+ # add back the pre-loaded simple cache middleware:
206+ async_w3 .middleware_onion .add (
207+ _async_simple_cache_middleware_for_testing , "simple_cache"
208+ )
209+
210+
211+ @pytest .mark .asyncio
212+ async def test_async_simple_cache_middleware_populates_cache (async_w3 ):
213+ async_w3 .middleware_onion .inject (
214+ await async_construct_result_generator_middleware (
215+ {
216+ RPCEndpoint ("fake_endpoint" ): lambda * _ : str (uuid .uuid4 ()),
217+ }
218+ ),
219+ "result_generator" ,
220+ layer = 0 ,
221+ )
222+
223+ result = await async_w3 .manager .coro_request ("fake_endpoint" , [])
224+
225+ _empty_params = await async_w3 .manager .coro_request ("fake_endpoint" , [])
226+ _non_empty_params = await async_w3 .manager .coro_request ("fake_endpoint" , [1 ])
227+
228+ assert _empty_params == result
229+ assert _non_empty_params != result
230+
231+ # -- teardown -- #
232+ async_w3 .middleware_onion .remove ("result_generator" )
233+
234+
235+ @pytest .mark .asyncio
236+ async def test_async_simple_cache_middleware_does_not_cache_none_responses (async_w3 ):
237+ counter = itertools .count ()
238+
239+ def result_cb (_method , _params ):
240+ next (counter )
241+ return None
242+
243+ async_w3 .middleware_onion .inject (
244+ await async_construct_result_generator_middleware (
245+ {
246+ RPCEndpoint ("fake_endpoint" ): result_cb ,
247+ },
248+ ),
249+ "result_generator" ,
250+ layer = 0 ,
251+ )
252+
253+ await async_w3 .manager .coro_request ("fake_endpoint" , [])
254+ await async_w3 .manager .coro_request ("fake_endpoint" , [])
255+
256+ assert next (counter ) == 2
257+
258+ # -- teardown -- #
259+ async_w3 .middleware_onion .remove ("result_generator" )
260+
261+
262+ @pytest .mark .asyncio
263+ async def test_async_simple_cache_middleware_does_not_cache_error_responses (async_w3 ):
264+ async_w3 .middleware_onion .inject (
265+ await async_construct_error_generator_middleware (
266+ {
267+ RPCEndpoint ("fake_endpoint" ): lambda * _ : f"msg-{ uuid .uuid4 ()} " ,
268+ }
269+ ),
270+ "error_generator" ,
271+ layer = 0 ,
272+ )
273+
274+ with pytest .raises (ValueError ) as err_a :
275+ await async_w3 .manager .coro_request ("fake_endpoint" , [])
276+ with pytest .raises (ValueError ) as err_b :
277+ await async_w3 .manager .coro_request ("fake_endpoint" , [])
278+
279+ assert str (err_a ) != str (err_b )
280+
281+ # -- teardown -- #
282+ async_w3 .middleware_onion .remove ("error_generator" )
283+
284+
285+ @pytest .mark .asyncio
286+ async def test_async_simple_cache_middleware_does_not_cache_non_whitelist_endpoints (
287+ async_w3 ,
288+ ):
289+ async_w3 .middleware_onion .inject (
290+ await async_construct_result_generator_middleware (
291+ {
292+ RPCEndpoint ("not_whitelisted" ): lambda * _ : str (uuid .uuid4 ()),
293+ }
294+ ),
295+ "result_generator" ,
296+ layer = 0 ,
297+ )
298+
299+ result_a = await async_w3 .manager .coro_request ("not_whitelisted" , [])
300+ result_b = await async_w3 .manager .coro_request ("not_whitelisted" , [])
301+
302+ assert result_a != result_b
303+
304+ # -- teardown -- #
305+ async_w3 .middleware_onion .remove ("result_generator" )
0 commit comments