#[cfg(test)]
mod cache_tests {
use crate::episode::Episode;
use crate::retrieval::cache::lru::QueryCache;
use crate::retrieval::cache::types::{CacheKey, DEFAULT_CACHE_TTL};
use crate::types::{TaskContext, TaskType};
use chrono;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use uuid::Uuid;
fn create_test_episode(id: &str) -> Arc<Episode> {
Arc::new(Episode {
episode_id: Uuid::parse_str(id).unwrap_or_else(|_| Uuid::new_v4()),
task_type: TaskType::CodeGeneration,
task_description: "test task".to_string(),
context: TaskContext::default(),
start_time: chrono::Utc::now(),
end_time: None,
steps: vec![],
outcome: None,
reward: None,
reflection: None,
patterns: vec![],
heuristics: vec![],
applied_patterns: vec![],
salient_features: None,
tags: vec![],
checkpoints: vec![],
metadata: HashMap::new(),
})
}
#[test]
fn test_cache_hit() {
let cache = QueryCache::new();
let key = CacheKey::new("test query".to_string());
let episodes = vec![create_test_episode("ep1")];
assert!(cache.get(&key).is_none());
cache.put(key.clone(), episodes);
let result = cache.get(&key);
assert!(result.is_some());
assert_eq!(result.unwrap().len(), 1);
let metrics = cache.metrics();
assert_eq!(metrics.hits, 1);
assert_eq!(metrics.misses, 1);
assert_eq!(metrics.hit_rate(), 0.5);
}
#[test]
fn test_cache_expiration() {
let cache = QueryCache::with_capacity_and_ttl(100, Duration::from_millis(10));
let key = CacheKey::new("test query".to_string());
let episodes = vec![create_test_episode("ep1")];
cache.put(key.clone(), episodes);
assert!(cache.get(&key).is_some());
std::thread::sleep(Duration::from_millis(15));
assert!(cache.get(&key).is_none());
}
#[test]
fn test_cache_invalidation() {
let cache = QueryCache::new();
let key1 = CacheKey::new("query1".to_string());
let key2 = CacheKey::new("query2".to_string());
cache.put(key1.clone(), vec![create_test_episode("ep1")]);
cache.put(key2.clone(), vec![create_test_episode("ep2")]);
assert_eq!(cache.size(), 2);
cache.invalidate_all();
assert_eq!(cache.size(), 0);
assert!(cache.get(&key1).is_none());
assert!(cache.get(&key2).is_none());
let metrics = cache.metrics();
assert_eq!(metrics.invalidations, 2);
}
#[test]
fn test_lru_eviction() {
let cache = QueryCache::with_capacity_and_ttl(2, DEFAULT_CACHE_TTL);
let key1 = CacheKey::new("query1".to_string());
let key2 = CacheKey::new("query2".to_string());
let key3 = CacheKey::new("query3".to_string());
cache.put(key1.clone(), vec![create_test_episode("ep1")]);
cache.put(key2.clone(), vec![create_test_episode("ep2")]);
assert_eq!(cache.size(), 2);
cache.put(key3.clone(), vec![create_test_episode("ep3")]);
assert_eq!(cache.size(), 2);
assert!(cache.get(&key1).is_none()); assert!(cache.get(&key2).is_some()); assert!(cache.get(&key3).is_some());
let metrics = cache.metrics();
assert_eq!(metrics.evictions, 1);
}
#[test]
fn test_cache_key_with_filters() {
let key1 = CacheKey::new("test".to_string())
.with_domain(Some("web".to_string()))
.with_task_type(Some("api".to_string()))
.with_limit(5);
let key2 = CacheKey::new("test".to_string())
.with_domain(Some("web".to_string()))
.with_task_type(Some("api".to_string()))
.with_limit(5);
let key3 = CacheKey::new("test".to_string())
.with_domain(Some("data".to_string())) .with_task_type(Some("api".to_string()))
.with_limit(5);
assert_eq!(key1.compute_hash(), key2.compute_hash());
assert_ne!(key1.compute_hash(), key3.compute_hash());
}
#[test]
fn test_metrics_effectiveness() {
let cache = QueryCache::new();
let key = CacheKey::new("test".to_string());
let episodes = vec![create_test_episode("ep1")];
cache.put(key.clone(), episodes);
for _ in 0..10 {
let _ = cache.get(&key);
}
let metrics = cache.metrics();
assert!(metrics.is_effective()); assert!(metrics.hit_rate() > 0.9); }
#[test]
fn test_domain_based_invalidation() {
let cache = QueryCache::new();
let key_web = CacheKey::new("query1".to_string()).with_domain(Some("web-api".to_string()));
let key_data =
CacheKey::new("query2".to_string()).with_domain(Some("data-processing".to_string()));
let key_no_domain = CacheKey::new("query3".to_string());
cache.put(key_web.clone(), vec![create_test_episode("ep1")]);
cache.put(key_data.clone(), vec![create_test_episode("ep2")]);
cache.put(key_no_domain.clone(), vec![create_test_episode("ep3")]);
assert_eq!(cache.size(), 3);
cache.invalidate_domain("web-api");
assert!(cache.get(&key_web).is_none());
assert!(cache.get(&key_data).is_some());
assert!(cache.get(&key_no_domain).is_some());
assert_eq!(cache.size(), 3); assert_eq!(cache.effective_size(), 2);
let metrics = cache.metrics();
assert_eq!(metrics.invalidations, 1);
}
#[test]
fn test_domain_invalidation_multiple_entries() {
let cache = QueryCache::new();
let key1 = CacheKey::new("query1".to_string()).with_domain(Some("web-api".to_string()));
let key2 = CacheKey::new("query2".to_string()).with_domain(Some("web-api".to_string()));
let key3 = CacheKey::new("query3".to_string()).with_domain(Some("data".to_string()));
cache.put(key1.clone(), vec![create_test_episode("ep1")]);
cache.put(key2.clone(), vec![create_test_episode("ep2")]);
cache.put(key3.clone(), vec![create_test_episode("ep3")]);
assert_eq!(cache.size(), 3);
cache.invalidate_domain("web-api");
assert!(cache.get(&key1).is_none());
assert!(cache.get(&key2).is_none());
assert!(cache.get(&key3).is_some());
assert_eq!(cache.size(), 3);
assert_eq!(cache.effective_size(), 1);
let metrics = cache.metrics();
assert_eq!(metrics.invalidations, 2);
}
#[test]
fn test_domain_invalidation_nonexistent() {
let cache = QueryCache::new();
let key = CacheKey::new("query".to_string()).with_domain(Some("web-api".to_string()));
cache.put(key.clone(), vec![create_test_episode("ep1")]);
cache.invalidate_domain("nonexistent-domain");
assert!(cache.get(&key).is_some());
assert_eq!(cache.size(), 1);
let metrics = cache.metrics();
assert_eq!(metrics.invalidations, 0);
}
#[test]
fn test_domain_invalidation_empty_cache() {
let cache = QueryCache::new();
cache.invalidate_domain("any-domain");
assert_eq!(cache.size(), 0);
let metrics = cache.metrics();
assert_eq!(metrics.invalidations, 0);
}
#[test]
fn test_invalidate_all_clears_domain_index() {
let cache = QueryCache::new();
let key_web = CacheKey::new("query1".to_string()).with_domain(Some("web-api".to_string()));
let key_data = CacheKey::new("query2".to_string()).with_domain(Some("data".to_string()));
cache.put(key_web.clone(), vec![create_test_episode("ep1")]);
cache.put(key_data.clone(), vec![create_test_episode("ep2")]);
assert_eq!(cache.size(), 2);
cache.invalidate_all();
assert_eq!(cache.size(), 0);
cache.put(key_web.clone(), vec![create_test_episode("ep3")]);
assert_eq!(cache.size(), 1);
cache.invalidate_domain("web-api");
assert_eq!(cache.size(), 1); assert_eq!(cache.effective_size(), 0); assert!(cache.get(&key_web).is_none()); }
#[test]
fn test_cache_update() {
let cache = QueryCache::new();
let key = CacheKey::new("test".to_string());
let episodes1 = vec![create_test_episode("ep1")];
let episodes2 = vec![create_test_episode("ep2")];
cache.put(key.clone(), episodes1);
assert_eq!(cache.get(&key).unwrap().len(), 1);
cache.put(key.clone(), episodes2);
assert_eq!(cache.get(&key).unwrap().len(), 1);
let metrics = cache.metrics();
assert_eq!(metrics.evictions, 0); }
#[test]
fn test_cache_is_empty_and_clear_metrics() {
let cache = QueryCache::new();
assert!(cache.is_empty());
assert_eq!(cache.size(), 0);
let key = CacheKey::new("test".to_string());
cache.put(key.clone(), vec![create_test_episode("ep1")]);
assert!(!cache.is_empty());
cache.clear_metrics();
let metrics = cache.metrics();
assert_eq!(metrics.hits, 0);
assert_eq!(metrics.misses, 0);
}
#[test]
fn test_cache_default() {
let cache = QueryCache::default();
assert!(cache.is_empty());
assert_eq!(cache.size(), 0);
}
}