Files
rfcp/backend/tests/test_services/test_cache.py
mytec defa3ad440 @mytec: feat: Phase 3.0 Architecture Refactor
Major refactoring of RFCP backend:
- Modular propagation models (8 models)
- SharedMemoryManager for terrain data
- ProcessPoolExecutor parallel processing
- WebSocket progress streaming
- Building filtering pipeline (351k → 15k)
- 82 unit tests

Performance: Standard preset 38s → 5s (7.6x speedup)

Known issue: Detailed preset timeout (fix in 3.1.0)
2026-02-01 23:12:26 +02:00

127 lines
4.0 KiB
Python

"""
Unit tests for the unified cache service.
"""
import sys
import os
import time
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from app.services.cache import MemoryCache, CacheManager
class TestMemoryCache:
def test_get_miss(self):
cache = MemoryCache("test", max_entries=10)
assert cache.get("nonexistent") is None
def test_put_get(self):
cache = MemoryCache("test", max_entries=10)
cache.put("key1", "value1", size_bytes=100)
assert cache.get("key1") == "value1"
def test_overwrite(self):
cache = MemoryCache("test", max_entries=10)
cache.put("key1", "v1", size_bytes=100)
cache.put("key1", "v2", size_bytes=200)
assert cache.get("key1") == "v2"
assert cache.size == 1
assert cache.size_bytes == 200
def test_eviction_by_entries(self):
cache = MemoryCache("test", max_entries=3)
cache.put("a", 1)
cache.put("b", 2)
cache.put("c", 3)
assert cache.size == 3
cache.put("d", 4) # Should evict 'a' (LRU)
assert cache.size == 3
assert cache.get("a") is None
assert cache.get("d") == 4
def test_eviction_by_size(self):
cache = MemoryCache("test", max_entries=100, max_size_bytes=300)
cache.put("a", 1, size_bytes=100)
cache.put("b", 2, size_bytes=100)
cache.put("c", 3, size_bytes=100)
assert cache.size_bytes == 300
cache.put("d", 4, size_bytes=100) # Should evict 'a'
assert cache.size_bytes == 300
assert cache.get("a") is None
def test_lru_access_order(self):
cache = MemoryCache("test", max_entries=3)
cache.put("a", 1)
cache.put("b", 2)
cache.put("c", 3)
# Access 'a' to make it recently used
cache.get("a")
# Add 'd' — should evict 'b' (now LRU)
cache.put("d", 4)
assert cache.get("a") == 1 # Still there
assert cache.get("b") is None # Evicted
def test_remove(self):
cache = MemoryCache("test", max_entries=10)
cache.put("key1", "val", size_bytes=50)
assert cache.remove("key1") is True
assert cache.get("key1") is None
assert cache.size_bytes == 0
def test_clear(self):
cache = MemoryCache("test", max_entries=10)
cache.put("a", 1, size_bytes=100)
cache.put("b", 2, size_bytes=100)
cache.clear()
assert cache.size == 0
assert cache.size_bytes == 0
def test_stats(self):
cache = MemoryCache("test", max_entries=10, max_size_bytes=1024)
cache.put("a", 1, size_bytes=100)
cache.get("a") # hit
cache.get("b") # miss
s = cache.stats()
assert s["name"] == "test"
assert s["entries"] == 1
assert s["hits"] == 1
assert s["misses"] == 1
assert s["hit_rate"] == 50.0
class TestCacheManager:
def test_singleton_structure(self):
mgr = CacheManager()
assert mgr.terrain is not None
assert mgr.buildings is not None
assert mgr.spatial is not None
assert mgr.osm_disk is not None
def test_stats(self):
mgr = CacheManager()
s = mgr.stats()
assert "terrain" in s
assert "buildings" in s
assert "total_memory_mb" in s
def test_clear_all(self):
mgr = CacheManager()
mgr.terrain.put("test", "data", 100)
mgr.buildings.put("test", "data", 100)
mgr.clear_all()
assert mgr.terrain.size == 0
assert mgr.buildings.size == 0
if __name__ == "__main__":
for cls_name, cls in [("MemoryCache", TestMemoryCache), ("CacheManager", TestCacheManager)]:
instance = cls()
for method_name in [m for m in dir(instance) if m.startswith("test_")]:
try:
getattr(instance, method_name)()
print(f" PASS {cls_name}.{method_name}")
except Exception as e:
print(f" FAIL {cls_name}.{method_name}: {e}")
print("\nAll tests completed.")