#[cfg(test)]
#[allow(clippy::module_inception)]
mod tests {
use crate::{
IntelligentCacheManager, L1CacheConfig, MonitoringConfig, PreheatingConfig, TuningConfig,
UnifiedCache, UnifiedCacheConfig, CACHE_VERSION, DEFAULT_L1_CAPACITY, DEFAULT_L2_CAPACITY,
DEFAULT_MEMORY_LIMIT_MB, DEFAULT_TTL_SECONDS,
};
use std::time::Duration;
use tokio::time::sleep;
#[test]
#[allow(clippy::const_is_empty)]
fn test_version_info() {
assert!(
CACHE_VERSION.contains('.'),
"CACHE_VERSION should be a semver string, got: {}",
CACHE_VERSION,
);
}
#[test]
fn test_default_constants() {
const _: () = {
assert!(DEFAULT_L1_CAPACITY > 0);
assert!(DEFAULT_L2_CAPACITY > DEFAULT_L1_CAPACITY);
assert!(DEFAULT_TTL_SECONDS > 0);
assert!(DEFAULT_MEMORY_LIMIT_MB > 0);
};
}
#[tokio::test]
async fn test_basic_cache_operations() {
let config = UnifiedCacheConfig::default();
let cache = IntelligentCacheManager::<String, String>::new(config);
cache
.put("key1".to_string(), "value1".to_string())
.await
.unwrap();
let result = cache.get(&"key1".to_string()).await;
assert_eq!(result, Some("value1".to_string()));
let result = cache.get(&"nonexistent".to_string()).await;
assert_eq!(result, None);
assert!(cache.contains_key(&"key1".to_string()).await);
assert!(!cache.contains_key(&"nonexistent".to_string()).await);
assert!(cache.remove(&"key1".to_string()).await);
assert!(!cache.contains_key(&"key1".to_string()).await);
}
#[tokio::test]
async fn test_multilevel_caching() {
let config = UnifiedCacheConfig {
l1_config: L1CacheConfig {
max_entries: 3, promotion_threshold: 2,
..Default::default()
},
..Default::default()
};
let cache = IntelligentCacheManager::<String, String>::new(config);
for i in 0..5 {
let key = format!("key_{}", i);
let value = format!("value_{}", i);
cache.put(key, value).await.unwrap();
}
let stats = cache.get_stats().await;
assert!(stats.l1_stats.entries <= 3);
for _ in 0..3 {
let _ = cache.get(&"key_1".to_string()).await;
}
let _stats_after = cache.get_stats().await;
}
#[tokio::test]
async fn test_predictive_preheating() {
let config = UnifiedCacheConfig {
preheating_config: PreheatingConfig {
enable_predictive_preheating: true,
enable_pattern_learning: true,
min_confidence_threshold: 0.3,
..Default::default()
},
..Default::default()
};
let cache = IntelligentCacheManager::<String, String>::new(config);
for cycle in 0..3 {
for i in 0..5 {
if i % 2 == cycle % 2 {
let key = format!("pattern_key_{}", i);
cache.preheater().record_access(&key).await;
}
}
sleep(Duration::from_millis(10)).await;
}
let stats = cache.preheater().get_stats();
assert!(stats.patterns_learned > 0);
let _recommendations = cache.preheater().get_preheat_recommendations().await;
}
#[tokio::test]
async fn test_adaptive_tuning() {
let config = UnifiedCacheConfig {
tuning_config: TuningConfig {
enable_adaptive_tuning: true,
min_samples_for_tuning: 3,
target_hit_rate: 0.9,
..Default::default()
},
..Default::default()
};
let cache = IntelligentCacheManager::<String, String>::new(config);
for i in 0..10 {
let key = format!("key_{}", i);
let value = format!("value_{}", i);
let _ = cache.get(&key).await; cache.put(key, value).await.unwrap(); }
let stats = cache.get_stats().await;
cache.tuner().record_performance(&stats).await;
let _recommendations = cache.tuner().analyze_and_tune().await;
let _tuning_stats = cache.tuner().get_stats();
}
#[tokio::test]
async fn test_performance_monitoring() {
let config = UnifiedCacheConfig {
monitoring_config: MonitoringConfig {
enable_detailed_stats: true,
enable_performance_metrics: true,
enable_event_logging: true,
..Default::default()
},
..Default::default()
};
let cache = IntelligentCacheManager::<String, Vec<u8>>::new(config);
let monitor = cache.monitor();
for i in 0..10 {
let key = format!("key_{}", i);
let value = vec![i as u8; 100];
cache.put(key.clone(), value).await.unwrap();
let _ = cache.get(&key).await;
}
let metrics = monitor.get_performance_metrics().await;
assert!(metrics.avg_get_latency_us >= 0.0);
assert!(metrics.avg_put_latency_us >= 0.0);
let events = monitor.get_recent_events(5).await;
assert!(!events.is_empty());
}
#[tokio::test]
async fn test_cache_statistics() {
let config = UnifiedCacheConfig::default();
let cache = IntelligentCacheManager::<String, String>::new(config);
let stats = cache.get_stats().await;
assert_eq!(stats.l1_stats.entries, 0);
assert_eq!(stats.l2_stats.entries, 0);
assert_eq!(stats.overall_stats.total_entries, 0);
for i in 0..5 {
let key = format!("key_{}", i);
let value = format!("value_{}", i);
cache.put(key, value).await.unwrap();
}
let stats = cache.get_stats().await;
assert_eq!(stats.overall_stats.total_entries, 5);
for i in 0..5 {
let key = format!("key_{}", i);
let _ = cache.get(&key).await;
}
let stats = cache.get_stats().await;
assert!(stats.l1_stats.hits > 0);
}
#[tokio::test]
async fn test_cache_capacity_limits() {
let config = UnifiedCacheConfig {
l1_config: L1CacheConfig {
max_entries: 2,
..Default::default()
},
..Default::default()
};
let cache = IntelligentCacheManager::<String, String>::new(config);
for i in 0..5 {
let key = format!("key_{}", i);
let value = format!("value_{}", i);
cache.put(key, value).await.unwrap();
}
let stats = cache.get_stats().await;
assert!(stats.l1_stats.entries <= 2);
assert!(stats.overall_stats.total_entries <= 5);
}
#[tokio::test]
async fn test_cache_clear() {
let config = UnifiedCacheConfig::default();
let cache = IntelligentCacheManager::<String, String>::new(config);
for i in 0..3 {
let key = format!("key_{}", i);
let value = format!("value_{}", i);
cache.put(key, value).await.unwrap();
}
assert!(!cache.is_empty().await);
cache.clear().await.unwrap();
assert!(cache.is_empty().await);
let stats = cache.get_stats().await;
assert_eq!(stats.overall_stats.total_entries, 0);
}
#[tokio::test]
async fn test_concurrent_access() {
let config = UnifiedCacheConfig::default();
let cache = std::sync::Arc::new(IntelligentCacheManager::<String, String>::new(config));
let mut handles = Vec::new();
for worker_id in 0..4 {
let cache = std::sync::Arc::clone(&cache);
let handle = tokio::spawn(async move {
for i in 0..10 {
let key = format!("worker_{}_key_{}", worker_id, i);
let value = format!("worker_{}_value_{}", worker_id, i);
cache.put(key.clone(), value.clone()).await.unwrap();
let result = cache.get(&key).await;
assert_eq!(result, Some(value));
}
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
let stats = cache.get_stats().await;
assert_eq!(stats.overall_stats.total_entries, 40);
}
#[tokio::test]
async fn test_configuration_validation() {
let config = UnifiedCacheConfig::default();
assert!(config.validate().is_ok());
let mut config = UnifiedCacheConfig::default();
config.l1_config.max_entries = 0;
assert!(config.validate().is_err());
let mut config = UnifiedCacheConfig::default();
config.preheating_config.min_confidence_threshold = 1.5;
assert!(config.validate().is_err());
}
#[tokio::test]
async fn test_concurrent_package_version_queries() {
use std::sync::Arc;
let config = UnifiedCacheConfig::default();
let cache = Arc::new(IntelligentCacheManager::<String, Vec<String>>::new(config));
let packages = ["python", "maya", "houdini", "nuke", "hiero"];
for pkg in &packages {
let versions = (0..10)
.map(|i| format!("{}.{}.0", i / 5 + 1, i % 5))
.collect();
cache.put(pkg.to_string(), versions).await.unwrap();
}
let mut handles = Vec::new();
for worker in 0..8 {
let cache = Arc::clone(&cache);
let pkgs: Vec<String> = packages.iter().map(|s| s.to_string()).collect();
let handle = tokio::spawn(async move {
let mut hits = 0usize;
for pkg in &pkgs {
if cache.get(pkg).await.is_some() {
hits += 1;
}
}
assert_eq!(hits, 5, "worker {} should find all 5 packages", worker);
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
let stats = cache.get_stats().await;
assert!(stats.overall_stats.total_entries >= 1);
}
#[tokio::test]
async fn test_concurrent_writes_no_corruption() {
use std::sync::Arc;
let config = UnifiedCacheConfig::default();
let cache = Arc::new(IntelligentCacheManager::<String, String>::new(config));
let mut handles = Vec::new();
for writer in 0..5 {
let cache = Arc::clone(&cache);
let handle = tokio::spawn(async move {
for i in 0..20 {
let key = format!("shared_key_{}", i % 5); let value = format!("writer_{}_value_{}", writer, i);
cache.put(key.clone(), value).await.unwrap();
let got = cache.get(&key).await;
assert!(got.is_some(), "key {} should exist after write", key);
}
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
for i in 0..5 {
let key = format!("shared_key_{}", i);
assert!(
cache.contains_key(&key).await,
"shared_key_{} should exist",
i
);
}
}
#[tokio::test]
async fn test_cache_eviction_under_load() {
use std::sync::Arc;
let config = UnifiedCacheConfig {
l1_config: L1CacheConfig {
max_entries: 5, ..Default::default()
},
..Default::default()
};
let cache = Arc::new(IntelligentCacheManager::<String, String>::new(config));
for i in 0..20 {
let key = format!("evict_key_{}", i);
let value = format!("value_{}", i);
cache.put(key, value).await.unwrap();
}
let stats = cache.get_stats().await;
assert!(
stats.l1_stats.entries <= 5,
"L1 should not exceed capacity 5, got {}",
stats.l1_stats.entries
);
}
#[tokio::test]
async fn test_batch_operations() {
let config = UnifiedCacheConfig::default();
let cache = IntelligentCacheManager::<u64, String>::new(config);
let batch: Vec<(u64, String)> = (0u64..50).map(|i| (i, format!("pkg_v{}.0", i))).collect();
for (k, v) in batch {
cache.put(k, v).await.unwrap();
}
let stats = cache.get_stats().await;
assert_eq!(stats.overall_stats.total_entries, 50);
let result = cache.get(&25u64).await;
assert_eq!(result, Some("pkg_v25.0".to_string()));
let result = cache.get(&0u64).await;
assert_eq!(result, Some("pkg_v0.0".to_string()));
}
#[tokio::test]
async fn test_clear_is_safe_under_concurrent_access() {
use std::sync::Arc;
let config = UnifiedCacheConfig::default();
let cache = Arc::new(IntelligentCacheManager::<String, String>::new(config));
for i in 0..20 {
cache
.put(format!("key_{}", i), format!("val_{}", i))
.await
.unwrap();
}
let cache2 = Arc::clone(&cache);
let reader = tokio::spawn(async move {
for i in 0..20 {
let _ = cache2.get(&format!("key_{}", i)).await;
}
});
cache.clear().await.unwrap();
reader.await.unwrap();
assert!(cache.is_empty().await);
}
#[test]
fn test_default_ttl_config() {
let config = L1CacheConfig::default();
assert_eq!(config.default_ttl, DEFAULT_TTL_SECONDS);
assert!(config.default_ttl > 0);
}
#[test]
fn test_cache_config_serialization() {
let config = UnifiedCacheConfig::default();
let json = serde_json::to_string(&config).expect("serialize config");
let restored: UnifiedCacheConfig = serde_json::from_str(&json).expect("deserialize config");
assert_eq!(restored.l1_config.max_entries, config.l1_config.max_entries);
assert_eq!(restored.l1_config.default_ttl, config.l1_config.default_ttl);
}
#[tokio::test]
async fn test_lru_eviction_strategy() {
let config = UnifiedCacheConfig {
l1_config: L1CacheConfig {
max_entries: 3,
eviction_strategy: crate::EvictionStrategy::LRU,
..Default::default()
},
..Default::default()
};
let cache = IntelligentCacheManager::<String, String>::new(config);
cache.put("a".to_string(), "1".to_string()).await.unwrap();
cache.put("b".to_string(), "2".to_string()).await.unwrap();
cache.put("c".to_string(), "3".to_string()).await.unwrap();
cache.put("d".to_string(), "4".to_string()).await.unwrap();
let stats = cache.get_stats().await;
assert!(stats.l1_stats.entries <= 3, "L1 should not exceed capacity");
}
#[test]
fn test_eviction_strategy_serialization() {
let strategies = [
crate::EvictionStrategy::LRU,
crate::EvictionStrategy::LFU,
crate::EvictionStrategy::FIFO,
crate::EvictionStrategy::TTL,
];
for strategy in &strategies {
let json = serde_json::to_string(strategy).unwrap();
let restored: crate::EvictionStrategy = serde_json::from_str(&json).unwrap();
assert_eq!(restored, *strategy);
}
}
#[tokio::test]
async fn test_ttl_strategy_config() {
let config = UnifiedCacheConfig {
l1_config: L1CacheConfig {
eviction_strategy: crate::EvictionStrategy::TTL,
default_ttl: 5, max_entries: 100,
..Default::default()
},
..Default::default()
};
let cache = IntelligentCacheManager::<String, String>::new(config);
cache
.put("ttl_key".to_string(), "ttl_val".to_string())
.await
.unwrap();
assert!(cache.contains_key(&"ttl_key".to_string()).await);
}
#[tokio::test]
async fn test_cache_size_consistency() {
let config = UnifiedCacheConfig::default();
let cache = IntelligentCacheManager::<u32, String>::new(config);
for i in 0..10u32 {
cache.put(i, format!("v{}", i)).await.unwrap();
}
assert_eq!(cache.get_stats().await.overall_stats.total_entries, 10);
for i in 0..5u32 {
cache.remove(&i).await;
}
let stats = cache.get_stats().await;
assert!(
stats.overall_stats.total_entries <= 10,
"Entries should decrease after remove"
);
}
#[tokio::test]
async fn test_cache_clear_removes_all_entries() {
let config = UnifiedCacheConfig::default();
let cache = IntelligentCacheManager::<String, String>::new(config);
cache.put("a".to_string(), "1".to_string()).await.unwrap();
cache.put("b".to_string(), "2".to_string()).await.unwrap();
assert!(!cache.is_empty().await);
cache.clear().await.unwrap();
assert!(cache.is_empty().await, "Cache should be empty after clear");
assert_eq!(cache.size().await, 0, "size() should be 0 after clear");
}
#[tokio::test]
async fn test_cache_type_identifier() {
let config = UnifiedCacheConfig::default();
let cache = IntelligentCacheManager::<String, String>::new(config);
let type_id = cache.cache_type();
assert!(
!type_id.is_empty(),
"cache_type() should return a non-empty string"
);
}
#[test]
fn test_unified_cache_stats_summary_report() {
use crate::UnifiedCacheStats;
let mut stats = UnifiedCacheStats::new();
stats.l1_stats.hits = 70;
stats.l1_stats.misses = 30;
stats.update_overall_stats();
let report = stats.summary_report();
assert!(
report.contains("Hit Rate"),
"Report should contain 'Hit Rate'"
);
assert!(
report.contains("Entries"),
"Report should contain 'Entries'"
);
}
#[test]
fn test_cache_level_stats_hit_rate_precision() {
use crate::CacheLevelStats;
let mut stats = CacheLevelStats {
hits: 3,
misses: 1,
capacity: 10,
entries: 5,
..Default::default()
};
stats.update_calculated_fields();
assert!(
(stats.hit_rate - 0.75).abs() < 1e-10,
"Hit rate should be exactly 0.75 for 3 hits / 4 total"
);
assert!(
(stats.load_factor - 0.5).abs() < 1e-10,
"Load factor should be 0.5 for 5 entries / 10 capacity"
);
}
}