use processor::state::{
CacheConfig, CachedStateBackend, EvictionPolicy, MemoryStateBackend, StateBackend,
WriteStrategy,
};
use std::time::Duration;
use tokio::time::sleep;
#[tokio::test]
async fn test_cached_backend_write_through() {
let backend = MemoryStateBackend::new(None);
let config = CacheConfig {
write_strategy: WriteStrategy::WriteThrough,
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend.clone(), None, config)
.await
.unwrap();
cached.put(b"key1", b"value1").await.unwrap();
let backend_value = backend.get(b"key1").await.unwrap();
assert_eq!(backend_value, Some(b"value1".to_vec()));
let cached_value = cached.get(b"key1").await.unwrap();
assert_eq!(cached_value, Some(b"value1".to_vec()));
let stats = cached.stats().await;
assert_eq!(stats.l1_hits, 1);
}
#[tokio::test]
async fn test_cached_backend_write_back() {
let backend = MemoryStateBackend::new(None);
let config = CacheConfig {
write_strategy: WriteStrategy::WriteBack,
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend.clone(), None, config)
.await
.unwrap();
cached.put(b"key1", b"value1").await.unwrap();
let cached_value = cached.get(b"key1").await.unwrap();
assert_eq!(cached_value, Some(b"value1".to_vec()));
sleep(Duration::from_millis(100)).await;
let backend_value = backend.get(b"key1").await.unwrap();
assert_eq!(backend_value, Some(b"value1".to_vec()));
}
#[tokio::test]
async fn test_cached_backend_l1_hit() {
let backend = MemoryStateBackend::new(None);
let config = CacheConfig {
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
cached.put(b"hot_key", b"hot_value").await.unwrap();
cached.get(b"hot_key").await.unwrap();
cached.get(b"hot_key").await.unwrap();
let stats = cached.stats().await;
assert!(stats.l1_hits >= 1);
assert!(stats.l1_hit_rate > 0.0);
}
#[tokio::test]
async fn test_cached_backend_l3_fallback() {
let backend = MemoryStateBackend::new(None);
backend.put(b"existing", b"data").await.unwrap();
let config = CacheConfig {
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
let value = cached.get(b"existing").await.unwrap();
assert_eq!(value, Some(b"data".to_vec()));
let stats = cached.stats().await;
assert_eq!(stats.l3_hits, 1);
let value = cached.get(b"existing").await.unwrap();
assert_eq!(value, Some(b"data".to_vec()));
let stats = cached.stats().await;
assert!(stats.l1_hits >= 1);
}
#[tokio::test]
async fn test_cached_backend_invalidation() {
let backend = MemoryStateBackend::new(None);
let config = CacheConfig::default();
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
cached.put(b"key1", b"value1").await.unwrap();
let value = cached.get(b"key1").await.unwrap();
assert_eq!(value, Some(b"value1".to_vec()));
cached.invalidate(b"key1").await.unwrap();
let value = cached.get(b"key1").await.unwrap();
assert_eq!(value, Some(b"value1".to_vec()));
}
#[tokio::test]
async fn test_cached_backend_delete() {
let backend = MemoryStateBackend::new(None);
let config = CacheConfig::default();
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
cached.put(b"key1", b"value1").await.unwrap();
cached.delete(b"key1").await.unwrap();
let value = cached.get(b"key1").await.unwrap();
assert_eq!(value, None);
}
#[tokio::test]
async fn test_cached_backend_cache_warming() {
let backend = MemoryStateBackend::new(None);
backend.put(b"hot1", b"data1").await.unwrap();
backend.put(b"hot2", b"data2").await.unwrap();
backend.put(b"hot3", b"data3").await.unwrap();
backend.put(b"cold", b"cold_data").await.unwrap();
let config = CacheConfig {
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
let hot_keys = vec![b"hot1".to_vec(), b"hot2".to_vec(), b"hot3".to_vec()];
cached.warm_cache(&hot_keys).await.unwrap();
cached.get(b"hot1").await.unwrap();
cached.get(b"hot2").await.unwrap();
cached.get(b"hot3").await.unwrap();
let stats = cached.stats().await;
assert_eq!(stats.l1_hits, 3);
cached.get(b"cold").await.unwrap();
let stats = cached.stats().await;
assert_eq!(stats.l3_hits, 1);
}
#[tokio::test]
async fn test_cached_backend_batch_operations() {
let backend = MemoryStateBackend::new(None);
let config = CacheConfig::default();
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
let pairs = vec![
(b"k1".to_vec(), b"v1".to_vec()),
(b"k2".to_vec(), b"v2".to_vec()),
(b"k3".to_vec(), b"v3".to_vec()),
(b"k4".to_vec(), b"v4".to_vec()),
(b"k5".to_vec(), b"v5".to_vec()),
];
cached.put_batch(&pairs).await.unwrap();
let keys = vec![
b"k1".to_vec(),
b"k2".to_vec(),
b"k3".to_vec(),
b"k4".to_vec(),
b"k5".to_vec(),
];
let values = cached.get_batch(&keys).await.unwrap();
assert_eq!(values.len(), 5);
for i in 0..5 {
assert_eq!(values[i], Some(format!("v{}", i + 1).into_bytes()));
}
}
#[tokio::test]
async fn test_cached_backend_list_keys() {
let backend = MemoryStateBackend::new(None);
let config = CacheConfig::default();
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
cached.put(b"user:1", b"alice").await.unwrap();
cached.put(b"user:2", b"bob").await.unwrap();
cached.put(b"session:1", b"sess1").await.unwrap();
cached.put(b"session:2", b"sess2").await.unwrap();
let user_keys = cached.list_keys(b"user:").await.unwrap();
assert_eq!(user_keys.len(), 2);
let session_keys = cached.list_keys(b"session:").await.unwrap();
assert_eq!(session_keys.len(), 2);
let all_keys = cached.list_keys(b"").await.unwrap();
assert_eq!(all_keys.len(), 4);
}
#[tokio::test]
async fn test_cached_backend_clear_cache() {
let backend = MemoryStateBackend::new(None);
let config = CacheConfig {
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
cached.put(b"k1", b"v1").await.unwrap();
cached.put(b"k2", b"v2").await.unwrap();
cached.get(b"k1").await.unwrap();
cached.get(b"k2").await.unwrap();
let stats = cached.stats().await;
assert_eq!(stats.l1_hits, 2);
cached.clear_cache().await.unwrap();
cached.get(b"k1").await.unwrap();
cached.get(b"k2").await.unwrap();
let stats = cached.stats().await;
assert_eq!(stats.l3_hits, 2);
}
#[tokio::test]
async fn test_cached_backend_clear_all() {
let backend = MemoryStateBackend::new(None);
let config = CacheConfig::default();
let cached = CachedStateBackend::new(backend.clone(), None, config)
.await
.unwrap();
cached.put(b"k1", b"v1").await.unwrap();
cached.put(b"k2", b"v2").await.unwrap();
assert_eq!(cached.count().await.unwrap(), 2);
cached.clear().await.unwrap();
assert_eq!(cached.count().await.unwrap(), 0);
assert_eq!(backend.count().await.unwrap(), 0);
}
#[tokio::test]
async fn test_cached_backend_contains() {
let backend = MemoryStateBackend::new(None);
let config = CacheConfig::default();
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
assert!(!cached.contains(b"nonexistent").await.unwrap());
cached.put(b"exists", b"yes").await.unwrap();
assert!(cached.contains(b"exists").await.unwrap());
}
#[tokio::test]
async fn test_cache_stats_calculation() {
let backend = MemoryStateBackend::new(None);
for i in 0..100 {
let key = format!("key{}", i);
let value = format!("value{}", i);
backend.put(key.as_bytes(), value.as_bytes()).await.unwrap();
}
let config = CacheConfig {
l1_capacity: 50, enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
for i in 0..50 {
let key = format!("key{}", i);
cached.get(key.as_bytes()).await.unwrap();
cached.get(key.as_bytes()).await.unwrap();
}
for i in 50..100 {
let key = format!("key{}", i);
cached.get(key.as_bytes()).await.unwrap();
}
let stats = cached.stats().await;
assert_eq!(stats.l1_hits, 50);
assert_eq!(stats.l3_hits, 100);
assert!((stats.l1_hit_rate - 0.333).abs() < 0.01);
assert_eq!(stats.overall_hit_rate, 1.0);
}
#[tokio::test]
async fn test_eviction_policy_configuration() {
let backend = MemoryStateBackend::new(None);
let lru_config = CacheConfig {
eviction_policy: EvictionPolicy::LRU,
..Default::default()
};
let cached_lru = CachedStateBackend::new(backend.clone(), None, lru_config)
.await
.unwrap();
cached_lru.put(b"test", b"value").await.unwrap();
assert!(cached_lru.contains(b"test").await.unwrap());
let lfu_config = CacheConfig {
eviction_policy: EvictionPolicy::LFU,
..Default::default()
};
let backend2 = MemoryStateBackend::new(None);
let cached_lfu = CachedStateBackend::new(backend2, None, lfu_config)
.await
.unwrap();
cached_lfu.put(b"test", b"value").await.unwrap();
assert!(cached_lfu.contains(b"test").await.unwrap());
}
#[tokio::test]
async fn test_concurrent_access() {
use tokio::task::JoinSet;
let backend = MemoryStateBackend::new(None);
let config = CacheConfig {
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
let cached = std::sync::Arc::new(cached);
let mut set = JoinSet::new();
for i in 0..10 {
let cached_clone = cached.clone();
set.spawn(async move {
for j in 0..100 {
let key = format!("key{}_{}", i, j);
let value = format!("value{}_{}", i, j);
cached_clone
.put(key.as_bytes(), value.as_bytes())
.await
.unwrap();
let retrieved = cached_clone.get(key.as_bytes()).await.unwrap();
assert_eq!(retrieved, Some(value.as_bytes().to_vec()));
}
});
}
while let Some(result) = set.join_next().await {
result.unwrap();
}
assert_eq!(cached.count().await.unwrap(), 1000);
let stats = cached.stats().await;
println!("Concurrent access stats: {:#?}", stats);
assert!(stats.l1_hits > 0);
}
#[tokio::test]
async fn test_large_value_handling() {
let backend = MemoryStateBackend::new(None);
let config = CacheConfig::default();
let cached = CachedStateBackend::new(backend, None, config)
.await
.unwrap();
let large_value = vec![0u8; 1024 * 1024];
cached.put(b"large_key", &large_value).await.unwrap();
let retrieved = cached.get(b"large_key").await.unwrap();
assert_eq!(retrieved, Some(large_value));
}