"""Tests for metrics pricing_cache module.""" import time from local_deep_research.metrics.pricing.pricing_cache import PricingCache class TestPricingCacheInit: """Tests for PricingCache initialization.""" def test_initializes_with_default_ttl(self): """Should initialize with default TTL of 3600 seconds.""" cache = PricingCache() assert cache.cache_ttl == 3600 def test_initializes_with_custom_ttl(self): """Should initialize with custom TTL.""" cache = PricingCache(cache_ttl=7200) assert cache.cache_ttl == 7200 def test_initializes_empty_cache(self): """Should initialize with empty in-memory cache.""" cache = PricingCache() assert len(cache._cache) == 0 def test_cache_dir_is_deprecated(self): """Cache dir parameter should be ignored (deprecated).""" cache = PricingCache(cache_dir="/some/path") # Should not raise and should work normally assert len(cache._cache) == 0 class TestPricingCacheGetSet: """Tests for PricingCache get/set methods.""" def test_get_returns_none_for_missing_key(self): """Should return None for non-existent key.""" cache = PricingCache() result = cache.get("nonexistent") assert result is None def test_set_stores_data(self): """Should store data in cache.""" cache = PricingCache() cache.set("test_key", {"value": 123}) assert "test_key" in cache._cache assert cache._cache["test_key"] == {"value": 123} def test_get_returns_valid_data(self): """Should return data for valid key.""" cache = PricingCache() cache.set("test_key", {"value": 456}) result = cache.get("test_key") assert result == {"value": 456} def test_get_returns_none_for_expired_key(self): """Should return None and remove expired entries.""" # audit: PUNCHLIST reviewed 2026-05 — KEEP (TIMING_SLEEP). cache = PricingCache(cache_ttl=1) # 1 second TTL cache.set("test_key", {"value": 789}) # Wait for expiration time.sleep(1.1) # allow: unmarked-sleep result = cache.get("test_key") assert result is None assert "test_key" not in cache._cache class TestPricingCacheModelPricing: """Tests for model pricing methods.""" def test_get_model_pricing_returns_none_for_missing(self): """Should return None for missing model pricing.""" cache = PricingCache() result = cache.get_model_pricing("gpt-4") assert result is None def test_set_model_pricing_stores_with_prefix(self): """Should store pricing with 'model:' prefix.""" cache = PricingCache() pricing = {"prompt": 0.03, "completion": 0.06} cache.set_model_pricing("gpt-4", pricing) assert "model:gpt-4" in cache._cache assert cache._cache["model:gpt-4"] == pricing def test_get_model_pricing_retrieves_stored(self): """Should retrieve stored model pricing.""" cache = PricingCache() pricing = {"prompt": 0.03, "completion": 0.06} cache.set_model_pricing("gpt-4", pricing) result = cache.get_model_pricing("gpt-4") assert result == pricing class TestPricingCacheAllPricing: """Tests for all pricing methods.""" def test_get_all_pricing_returns_none_when_not_set(self): """Should return None when all pricing not cached.""" cache = PricingCache() result = cache.get_all_pricing() assert result is None def test_set_all_pricing_stores_data(self): """Should store all pricing data.""" cache = PricingCache() all_pricing = { "gpt-4": {"prompt": 0.03, "completion": 0.06}, "gpt-3.5-turbo": {"prompt": 0.001, "completion": 0.002}, } cache.set_all_pricing(all_pricing) assert "all_models" in cache._cache assert cache._cache["all_models"] == all_pricing def test_get_all_pricing_retrieves_stored(self): """Should retrieve stored all pricing.""" cache = PricingCache() all_pricing = { "gpt-4": {"prompt": 0.03, "completion": 0.06}, } cache.set_all_pricing(all_pricing) result = cache.get_all_pricing() assert result == all_pricing class TestPricingCacheClear: """Tests for cache clearing methods.""" def test_clear_removes_all_entries(self): """Should remove all cache entries.""" cache = PricingCache() cache.set("key1", "value1") cache.set("key2", "value2") cache.clear() assert len(cache._cache) == 0 def test_clear_expired_removes_only_expired(self): """Should remove only expired entries.""" # audit: PUNCHLIST reviewed 2026-05 — KEEP (TIMING_SLEEP). cache = PricingCache(cache_ttl=2) # 2 second TTL cache.set("old_key", "old_value") # Wait for old entry to expire time.sleep(2.1) # allow: unmarked-sleep # Add new entry cache.set("new_key", "new_value") cache.clear_expired() assert "old_key" not in cache._cache assert "new_key" in cache._cache def test_clear_expired_handles_empty_cache(self): """Should handle empty cache without error.""" cache = PricingCache() cache.clear_expired() # Should not raise assert len(cache._cache) == 0 class TestPricingCacheStats: """Tests for cache statistics.""" def test_get_cache_stats_returns_correct_structure(self): """Should return stats with correct keys.""" cache = PricingCache(cache_ttl=3600) stats = cache.get_cache_stats() assert "total_entries" in stats assert "max_entries" in stats assert "cache_type" in stats assert "cache_ttl" in stats def test_get_cache_stats_counts_entries(self): """Should count total entries correctly.""" cache = PricingCache() cache.set("key1", "value1") cache.set("key2", "value2") cache.set("key3", "value3") stats = cache.get_cache_stats() assert stats["total_entries"] == 3 def test_get_cache_stats_expired_entries_evicted(self): """TTLCache automatically evicts expired entries on get_cache_stats.""" # audit: PUNCHLIST reviewed 2026-05 — KEEP (TIMING_SLEEP). cache = PricingCache(cache_ttl=1) cache.set("key1", "value1") time.sleep(1.1) # allow: unmarked-sleep stats = cache.get_cache_stats() # TTLCache evicts expired entries automatically when expire() is called assert stats["total_entries"] == 0 def test_get_cache_stats_shows_cache_type(self): """Should show TTLCache cache type.""" cache = PricingCache() stats = cache.get_cache_stats() assert stats["cache_type"] == "TTLCache" def test_get_cache_stats_shows_ttl(self): """Should show configured TTL.""" cache = PricingCache(cache_ttl=7200) stats = cache.get_cache_stats() assert stats["cache_ttl"] == 7200 class TestPricingCacheTTL: """Tests for TTLCache behavior.""" def test_cache_respects_ttl(self): """Should respect TTL for entries.""" # audit: PUNCHLIST reviewed 2026-05 — KEEP (TIMING_SLEEP). cache = PricingCache(cache_ttl=1) cache.set("key1", "value1") # Entry should be available immediately assert cache.get("key1") == "value1" # Wait for expiration time.sleep(1.1) # allow: unmarked-sleep # Entry should be expired assert cache.get("key1") is None def test_cache_respects_maxsize(self): """Should respect maxsize by evicting old entries.""" # Create cache with small maxsize cache = PricingCache() # TTLCache with maxsize=500 is default assert cache._cache.maxsize == 500