#[cfg(test)]
mod memory_profiling {
use crate::cache::{CacheKey, DEFAULT_CACHE_TTL, EvictionPolicy, UnifiedCache};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct MemTestKey(String);
impl CacheKey for MemTestKey {
fn to_cache_key(&self) -> String {
self.0.clone()
}
}
#[test]
fn test_cache_capacity_enforcement() {
let max_entries = 100;
let cache: UnifiedCache<MemTestKey, String> =
UnifiedCache::new(max_entries, DEFAULT_CACHE_TTL, EvictionPolicy::Lru);
for i in 0..150 {
let key = MemTestKey(format!("key_{}", i));
let value = format!("value_{}", i);
let size_bytes = value.len() as u64;
cache.insert(key, value, size_bytes);
}
assert!(
cache.len() <= max_entries,
"Cache size {} exceeds max capacity {}",
cache.len(),
max_entries
);
let stats = cache.stats();
assert!(stats.evictions > 0, "Expected evictions but got none");
}
#[test]
fn test_cache_expiration_cleanup() {
use std::time::Duration;
let cache: UnifiedCache<MemTestKey, String> =
UnifiedCache::new(100, Duration::from_millis(50), EvictionPolicy::Lru);
let key = MemTestKey("test".into());
cache.insert(key.clone(), "value".into(), 100);
assert!(cache.get(&key).is_some());
std::thread::sleep(Duration::from_millis(100));
assert!(cache.get(&key).is_none());
}
#[test]
fn test_cache_hit_rate_metrics() {
let cache: UnifiedCache<MemTestKey, String> =
UnifiedCache::new(10, DEFAULT_CACHE_TTL, EvictionPolicy::Lru);
let key1 = MemTestKey("key1".into());
let key2 = MemTestKey("key2".into());
cache.insert(key1.clone(), "value1".into(), 100);
cache.insert(key2.clone(), "value2".into(), 100);
let _ = cache.get(&MemTestKey("nonexistent".into()));
let _ = cache.get(&key1);
let _ = cache.get(&key2);
let stats = cache.stats();
assert_eq!(stats.hits, 2, "Expected 2 cache hits");
assert_eq!(stats.misses, 1, "Expected 1 cache miss");
assert!(stats.total_memory_bytes > 0, "Expected memory tracking");
}
#[test]
fn test_cache_memory_tracking() {
let cache: UnifiedCache<MemTestKey, String> =
UnifiedCache::new(100, DEFAULT_CACHE_TTL, EvictionPolicy::Lru);
let test_size_bytes = 1024;
let key1 = MemTestKey("key1".into());
let value1 = "x".repeat(1000);
cache.insert(key1, value1, test_size_bytes as u64);
let stats = cache.stats();
assert_eq!(
stats.total_memory_bytes, test_size_bytes as u64,
"Memory tracking mismatch"
);
}
#[test]
fn test_lru_eviction_policy() {
let cache: UnifiedCache<MemTestKey, String> =
UnifiedCache::new(2, DEFAULT_CACHE_TTL, EvictionPolicy::Lru);
let key1 = MemTestKey("key1".into());
let key2 = MemTestKey("key2".into());
let key3 = MemTestKey("key3".into());
cache.insert(key1.clone(), "value1".into(), 100);
std::thread::sleep(std::time::Duration::from_millis(10));
cache.insert(key2.clone(), "value2".into(), 100);
std::thread::sleep(std::time::Duration::from_millis(10));
let _ = cache.get(&key1);
std::thread::sleep(std::time::Duration::from_millis(10));
cache.insert(key3.clone(), "value3".into(), 100);
assert!(cache.get(&key1).is_some(), "key1 should still be in cache");
assert!(cache.get(&key2).is_none(), "key2 should have been evicted");
assert!(cache.get(&key3).is_some(), "key3 should be in cache");
}
#[test]
#[ignore]
fn bench_cache_operations() {
let cache: UnifiedCache<MemTestKey, String> =
UnifiedCache::new(1_000, DEFAULT_CACHE_TTL, EvictionPolicy::Lru);
let start = std::time::Instant::now();
for i in 0..10_000 {
let key = MemTestKey(format!("key_{}", i));
let value = format!("value_{}", i);
cache.insert(key, value.clone(), value.len() as u64);
}
let insert_time = start.elapsed();
let start = std::time::Instant::now();
for i in 0..1_000 {
let key = MemTestKey(format!("key_{}", i));
let _ = cache.get(&key);
}
let read_time = start.elapsed();
println!("Insert 10k entries: {:?}", insert_time);
println!("Read 1k entries: {:?}", read_time);
println!("Cache size: {}", cache.len());
println!("Cache stats: {:?}", cache.stats());
assert!(insert_time.as_millis() < 5000, "Inserts taking too long");
assert!(read_time.as_millis() < 1000, "Reads taking too long");
}
}