rez-next-cache 0.1.0

Intelligent caching system for rez-next with multi-level cache and predictive preheating
Documentation
//! Tests for the intelligent cache system

#[cfg(test)]
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]
    fn test_version_info() {
        assert!(!CACHE_VERSION.is_empty());
    }

    #[test]
    fn test_default_constants() {
        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);

        // Test put and get
        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()));

        // Test cache miss
        let result = cache.get(&"nonexistent".to_string()).await;
        assert_eq!(result, None);

        // Test contains_key
        assert!(cache.contains_key(&"key1".to_string()).await);
        assert!(!cache.contains_key(&"nonexistent".to_string()).await);

        // Test remove
        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, // Small L1 to trigger L2 usage
                promotion_threshold: 2,
                ..Default::default()
            },
            ..Default::default()
        };

        let cache = IntelligentCacheManager::<String, String>::new(config);

        // Fill L1 beyond capacity
        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);
        // L2 may or may not have entries depending on implementation
        // Just verify we can get stats without error

        // Access an item multiple times to trigger promotion
        for _ in 0..3 {
            let _ = cache.get(&"key_1".to_string()).await;
        }

        let _stats_after = cache.get_stats().await;
        // Promotions may or may not occur depending on implementation
        // Just verify we can get stats without error
    }

    #[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);

        // Create access patterns
        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);

        // Test prediction
        let _recommendations = cache.preheater().get_preheat_recommendations().await;
        // Should have some recommendations based on patterns (may be 0 if confidence is too low)
        // Just verify we can get recommendations without error
    }

    #[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);

        // Simulate workload
        for i in 0..10 {
            let key = format!("key_{}", i);
            let value = format!("value_{}", i);

            let _ = cache.get(&key).await; // Miss
            cache.put(key, value).await.unwrap(); // Put
        }

        // Record performance
        let stats = cache.get_stats().await;
        cache.tuner().record_performance(&stats).await;

        // Trigger tuning
        let _recommendations = cache.tuner().analyze_and_tune().await;

        // Should generate some recommendations (may be 0)
        // Just verify we can get recommendations without error

        let _tuning_stats = cache.tuner().get_stats();
        // Just verify we can get tuning stats without error
    }

    #[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();

        // Perform operations
        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;
        }

        // Check metrics
        let metrics = monitor.get_performance_metrics().await;
        assert!(metrics.avg_get_latency_us >= 0.0);
        assert!(metrics.avg_put_latency_us >= 0.0);

        // Check events
        let events = monitor.get_recent_events(5).await;
        assert!(events.len() > 0);
    }

    #[tokio::test]
    async fn test_cache_statistics() {
        let config = UnifiedCacheConfig::default();
        let cache = IntelligentCacheManager::<String, String>::new(config);

        // Initially empty
        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);

        // Add some data
        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);

        // Test some gets to generate hit/miss stats
        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);

        // Fill beyond L1 capacity
        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);
        // Total entries may be less than 5 due to eviction policies
        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);

        // Add data
        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);

        // Clear cache
        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();

        // Spawn multiple tasks
        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);
        }

        // Wait for all tasks
        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() {
        // Valid configuration
        let config = UnifiedCacheConfig::default();
        assert!(config.validate().is_ok());

        // Invalid L1 configuration
        let mut config = UnifiedCacheConfig::default();
        config.l1_config.max_entries = 0;
        assert!(config.validate().is_err());

        // Invalid preheating configuration
        let mut config = UnifiedCacheConfig::default();
        config.preheating_config.min_confidence_threshold = 1.5;
        assert!(config.validate().is_err());
    }
}