1818 cached_return_view ,
1919 services ,
2020)
21+ from warehouse .legacy .api .xmlrpc .cache .fncache import StubMetricReporter
2122from warehouse .legacy .api .xmlrpc .cache .interfaces import CacheError , IXMLRPCCache
2223
2324
@@ -160,12 +161,29 @@ def test_create_redis_service(self, pyramid_request, mocker):
160161
161162 assert isinstance (service , RedisXMLRPCCache )
162163 assert service ._purger is delay
163- assert delay .call_args_list == [
164- mocker .call ("wu" ),
165- mocker .call ("tang" ),
166- mocker .call ("4" ),
167- mocker .call ("evah" ),
168- ]
164+ # Without this the cache falls back to StubMetricReporter and reports nothing.
165+ assert service .redis_lru .metric_reporter is pyramid_request .metrics
166+
167+ def test_create_redis_service_without_a_request (self , mocker ):
168+ """The factory tolerates being handed something that is not a request.
169+
170+ `execute_purge` calls it with the Configurator, which has no `metrics`. That
171+ path only enqueues purges, so a stubbed reporter there costs nothing, but an
172+ AttributeError would break every `after_commit`.
173+ """
174+ delay = mocker .stub (name = "delay" )
175+ config = types .SimpleNamespace (
176+ registry = types .SimpleNamespace (
177+ settings = {"warehouse.xmlrpc.cache.url" : "redis://" }
178+ ),
179+ task = mocker .Mock (return_value = mocker .Mock (delay = delay )),
180+ )
181+
182+ service = RedisXMLRPCCache .create_service (None , config )
183+ service .purge_tags (["wu" , "tang" ])
184+
185+ assert isinstance (service .redis_lru .metric_reporter , StubMetricReporter )
186+ assert delay .call_args_list == [mocker .call ("wu" ), mocker .call ("tang" )]
169187
170188
171189class TestRedisLru :
@@ -193,8 +211,28 @@ def test_redis_custom_metrics(self, metrics, mockredis, mocker):
193211 func_test , [0 , 1 ], {"kwarg0" : 2 , "kwarg1" : 3 }, None , None , None
194212 )
195213 assert metrics .increment .call_args_list == [
196- mocker .call ("lru.cache.miss" ),
197- mocker .call ("lru.cache.hit" ),
214+ mocker .call ("warehouse.lru.cache.miss" ),
215+ mocker .call ("warehouse.lru.cache.hit" ),
216+ ]
217+
218+ def test_falsy_cached_value_is_a_single_hit (self , metrics , mockredis , mocker ):
219+ """An empty result is a real hit: counted once, and the view is not re-run.
220+
221+ `fetch` compares the cached value against None instead of testing its
222+ truthiness. Testing truthiness reports the same request as both a hit and a
223+ miss, which would make the hit rate unreadable, and re-runs the query every
224+ time. `package_roles` returns `[]` for any unknown name.
225+ """
226+ empty_func = mocker .Mock (return_value = [], __name__ = "empty_func" )
227+ redis_lru = RedisLru (mockredis , metric_reporter = metrics )
228+
229+ assert redis_lru .fetch (empty_func , [], {}, "[]" , None , None ) == []
230+ assert redis_lru .fetch (empty_func , [], {}, "[]" , None , None ) == []
231+
232+ empty_func .assert_called_once_with ()
233+ assert metrics .increment .call_args_list == [
234+ mocker .call ("warehouse.lru.cache.miss" ),
235+ mocker .call ("warehouse.lru.cache.hit" ),
198236 ]
199237
200238 def test_redis_purge (self , metrics , mockredis , mocker ):
@@ -216,11 +254,11 @@ def test_redis_purge(self, metrics, mockredis, mocker):
216254 func_test , [0 , 1 ], {"kwarg0" : 2 , "kwarg1" : 3 }, None , "test" , None
217255 )
218256 assert metrics .increment .call_args_list == [
219- mocker .call ("lru.cache.miss" ),
220- mocker .call ("lru.cache.hit" ),
221- mocker .call ("lru.cache.purge" ),
222- mocker .call ("lru.cache.miss" ),
223- mocker .call ("lru.cache.hit" ),
257+ mocker .call ("warehouse. lru.cache.miss" ),
258+ mocker .call ("warehouse. lru.cache.hit" ),
259+ mocker .call ("warehouse. lru.cache.purge" ),
260+ mocker .call ("warehouse. lru.cache.miss" ),
261+ mocker .call ("warehouse. lru.cache.hit" ),
224262 ]
225263
226264 def test_redis_down (self , metrics , mocker ):
@@ -242,13 +280,13 @@ def test_redis_down(self, metrics, mocker):
242280 redis_lru .purge ("test" )
243281
244282 assert metrics .increment .call_args_list == [
245- mocker .call ("lru.cache.error" ), # Failed get
246- mocker .call ("lru.cache.miss" ),
247- mocker .call ("lru.cache.error" ), # Failed add
248- mocker .call ("lru.cache.error" ), # Failed get
249- mocker .call ("lru.cache.miss" ),
250- mocker .call ("lru.cache.error" ), # Failed add
251- mocker .call ("lru.cache.error" ), # Failed purge
283+ mocker .call ("warehouse. lru.cache.error" ), # Failed get
284+ mocker .call ("warehouse. lru.cache.miss" ),
285+ mocker .call ("warehouse. lru.cache.error" ), # Failed add
286+ mocker .call ("warehouse. lru.cache.error" ), # Failed get
287+ mocker .call ("warehouse. lru.cache.miss" ),
288+ mocker .call ("warehouse. lru.cache.error" ), # Failed add
289+ mocker .call ("warehouse. lru.cache.error" ), # Failed purge
252290 ]
253291
254292
0 commit comments