@@ -85,6 +85,10 @@ def test_no_etag(self) -> None:
8585 entry = CacheEntry (data = [], etag = None , max_age = 60 , stored_at = 0.0 )
8686 assert entry .etag is None
8787
88+ def test_expires_at (self ) -> None :
89+ entry = CacheEntry (data = [], etag = None , max_age = 300 , stored_at = 100.0 )
90+ assert entry .expires_at == 400.0
91+
8892
8993class TestHttpCache :
9094 """Integration tests for caching in _make_request."""
@@ -351,3 +355,53 @@ def test_different_urls_cached_separately(
351355 client .get_metric_data (names = ["m1" ])
352356
353357 assert len (client ._cache ) == 2
358+
359+ @patch .object (CVec , "_login_with_supabase" , return_value = None )
360+ @patch .object (
361+ CVec ,
362+ "_fetch_config" ,
363+ autospec = True ,
364+ side_effect = mock_fetch_config_side_effect ,
365+ )
366+ @patch ("cvec.cvec.urlopen" )
367+ def test_cache_evicts_when_full (
368+ self ,
369+ mock_urlopen : Any ,
370+ mock_fetch_key : Any ,
371+ mock_login : Any ,
372+ ) -> None :
373+ """When cache is full, the entry with earliest expiration is evicted."""
374+ client = _create_client ()
375+
376+ now = time .monotonic ()
377+
378+ # Pre-fill cache to max size with entries that expire at different times
379+ for i in range (100 ):
380+ url = f"https://test.example.com/api/item/{ i } "
381+ client ._cache [url ] = CacheEntry (
382+ data = {"id" : i },
383+ etag = f'"etag{ i } "' ,
384+ max_age = 300 ,
385+ # Entry 50 expires first (stored earliest)
386+ stored_at = now - 200 if i == 50 else now ,
387+ )
388+
389+ assert len (client ._cache ) == 100
390+
391+ # Add one more entry via a real request
392+ data = [{"id" : 999 , "name" : "new_metric" }]
393+ mock_response = _make_mock_response (
394+ json .dumps (data ).encode ("utf-8" ),
395+ etag = '"etag_new"' ,
396+ cache_control = "max-age=300" ,
397+ )
398+ mock_urlopen .return_value = mock_response
399+
400+ client .get_metrics ()
401+
402+ # Cache should still be at 100 (evicted one to make room)
403+ assert len (client ._cache ) == 100
404+ # Entry 50 (earliest expiration) should have been evicted
405+ assert "https://test.example.com/api/item/50" not in client ._cache
406+ # New entry should be present
407+ assert any ("metrics" in url for url in client ._cache )
0 commit comments