55SPDX-License-Identifier: Apache-2.0
66Authors: Mihai Criveti
77
8+ Unit tests for ResourceCache.
89"""
10+ import asyncio
11+ import time
12+ import pytest
13+
14+ from mcpgateway .cache .resource_cache import ResourceCache
15+
16+ @pytest .fixture
17+ def cache ():
18+ """Fixture for a ResourceCache with small TTL and size for testing."""
19+ return ResourceCache (max_size = 3 , ttl = 1 )
20+
21+ def test_set_and_get (cache ):
22+ """Test setting and getting a cache value."""
23+ cache .set ("foo" , "bar" )
24+ assert cache .get ("foo" ) == "bar"
25+
26+ def test_get_missing (cache ):
27+ """Test getting a missing key returns None."""
28+ assert cache .get ("missing" ) is None
29+
30+ def test_expiration (cache ):
31+ """Test that cache entry expires after TTL."""
32+ cache .set ("foo" , "bar" )
33+ time .sleep (1.1 )
34+ assert cache .get ("foo" ) is None
35+
36+ def test_lru_eviction (cache ):
37+ """Test LRU eviction when max_size is reached."""
38+ cache .set ("a" , 1 )
39+ cache .set ("b" , 2 )
40+ cache .set ("c" , 3 )
41+ # Access 'a' to update its last_access
42+ assert cache .get ("a" ) == 1
43+ # Add another entry, should evict 'b' (least recently used)
44+ cache .set ("d" , 4 )
45+ assert cache .get ("b" ) is None
46+ assert cache .get ("a" ) == 1
47+ assert cache .get ("c" ) == 3
48+ assert cache .get ("d" ) == 4
49+
50+ def test_delete (cache ):
51+ """Test deleting a cache entry."""
52+ cache .set ("foo" , "bar" )
53+ cache .delete ("foo" )
54+ assert cache .get ("foo" ) is None
55+
56+ def test_clear (cache ):
57+ """Test clearing the cache."""
58+ cache .set ("foo" , "bar" )
59+ cache .set ("baz" , "qux" )
60+ cache .clear ()
61+ assert cache .get ("foo" ) is None
62+ assert cache .get ("baz" ) is None
63+
64+ @pytest .mark .asyncio
65+ async def test_initialize_and_shutdown_logs (monkeypatch ):
66+ """Test initialize and shutdown log and cleanup."""
67+ cache = ResourceCache (max_size = 2 , ttl = 1 )
68+ monkeypatch .setattr ("mcpgateway.cache.resource_cache.logger" , DummyLogger ())
69+ await cache .initialize ()
70+ cache .set ("foo" , "bar" )
71+ await cache .shutdown ()
72+ assert cache .get ("foo" ) is None
73+
74+ @pytest .mark .asyncio
75+ async def test_cleanup_loop_removes_expired (monkeypatch ):
76+ """Test that the cleanup loop removes expired entries."""
77+ cache = ResourceCache (max_size = 2 , ttl = 0.1 )
78+ cache .set ("foo" , "bar" )
79+ await asyncio .sleep (0.15 )
80+ # Manually trigger cleanup for test speed
81+ async with cache ._lock :
82+ now = time .time ()
83+ expired = [key for key , entry in cache ._cache .items () if now > entry .expires_at ]
84+ for key in expired :
85+ del cache ._cache [key ]
86+ assert cache .get ("foo" ) is None
87+
88+ class DummyLogger :
89+ def info (self , msg ): pass
90+ def debug (self , msg ): pass
91+ def error (self , msg ): pass
0 commit comments