mod common;
use common::*;
use multi_tier_cache::{CacheBackend, CacheStrategy};
use std::time::Duration;
#[tokio::test]
async fn test_basic_set_and_get() {
let cache = setup_cache_system()
.await
.unwrap_or_else(|_| panic!("Failed to setup cache"));
let key = test_key("basic");
let value = test_data::bytes_user(1);
cache
.cache_manager()
.set_with_strategy(&key, value.clone(), CacheStrategy::ShortTerm)
.await
.unwrap_or_else(|_| panic!("Failed to set value"));
let cached = cache
.cache_manager()
.get(&key)
.await
.unwrap_or_else(|_| panic!("Failed to get value"));
assert_eq!(cached, Some(value));
let _ = cache
.l2_cache
.as_ref()
.unwrap_or_else(|| panic!("L2 cache missing"))
.remove(&key)
.await;
}
#[tokio::test]
async fn test_l1_cache_hit() {
let cache = setup_cache_system()
.await
.unwrap_or_else(|_| panic!("Failed to setup cache"));
let key = test_key("l1_hit");
let value = test_data::bytes_user(2);
cache
.cache_manager()
.set_with_strategy(&key, value.clone(), CacheStrategy::MediumTerm)
.await
.unwrap_or_else(|_| panic!("Failed to set cache"));
let _ = cache
.cache_manager()
.get(&key)
.await
.unwrap_or_else(|_| panic!("Failed to get cache"));
let cached = cache
.cache_manager()
.get(&key)
.await
.unwrap_or_else(|_| panic!("Failed to get cache"));
assert_eq!(cached, Some(value));
let stats = cache.cache_manager().get_stats();
assert!(stats.l1_hits >= 1, "Expected at least 1 L1 hit");
let _ = cache
.l2_cache
.as_ref()
.unwrap_or_else(|| panic!("L2 cache missing"))
.remove(&key)
.await;
}
#[tokio::test]
async fn test_l2_to_l1_promotion() {
let cache = setup_cache_with_n(1)
.await
.unwrap_or_else(|_| panic!("Failed to setup cache"));
let key = test_key("l2_promote");
let value = test_data::bytes_user(3);
cache
.l2_cache
.as_ref()
.unwrap_or_else(|| panic!("L2 cache missing"))
.set_with_ttl(&key, value.clone(), Duration::from_secs(300))
.await
.unwrap_or_else(|_| panic!("Failed to set L2"));
let cached = cache
.cache_manager()
.get(&key)
.await
.unwrap_or_else(|_| panic!("Failed to get cache"));
assert_eq!(cached, Some(value.clone()));
let stats = cache.cache_manager().get_stats();
assert!(stats.promotions >= 1, "Expected at least 1 promotion");
let cached2 = cache
.cache_manager()
.get(&key)
.await
.unwrap_or_else(|_| panic!("Failed to get cache"));
assert_eq!(cached2, Some(value));
let _ = cache
.l2_cache
.as_ref()
.unwrap_or_else(|| panic!("L2 cache missing"))
.remove(&key)
.await;
}
#[tokio::test]
async fn test_cache_miss() {
let cache = setup_cache_system()
.await
.unwrap_or_else(|_| panic!("Failed to setup cache"));
let key = test_key("miss");
let cached = cache
.cache_manager()
.get(&key)
.await
.unwrap_or_else(|_| panic!("Failed to get cache"));
assert_eq!(cached, None);
let stats = cache.cache_manager().get_stats();
assert!(stats.misses >= 1, "Expected at least 1 miss");
}
#[tokio::test]
async fn test_compute_on_miss() {
let cache = setup_cache_system()
.await
.unwrap_or_else(|_| panic!("Failed to setup cache"));
let key = test_key("compute");
let expected_value = test_data::bytes_user(4);
let value = cache
.cache_manager()
.get_or_compute_with(&key, CacheStrategy::ShortTerm, || {
let v = expected_value.clone();
async move { Ok(v) }
})
.await
.unwrap_or_else(|_| panic!("Failed to get/compute"));
assert_eq!(value, expected_value);
let cached = cache
.cache_manager()
.get(&key)
.await
.unwrap_or_else(|_| panic!("Failed to get cache"));
assert_eq!(cached, Some(expected_value));
let _ = cache
.l2_cache
.as_ref()
.unwrap_or_else(|| panic!("L2 cache missing"))
.remove(&key)
.await;
}
#[tokio::test]
async fn test_type_safe_caching() {
let cache = setup_cache_system()
.await
.unwrap_or_else(|_| panic!("Failed to setup cache"));
let key = test_key("typed");
let expected_user = test_data::User::new(5);
let user: test_data::User = cache
.cache_manager()
.get_or_compute_typed(&key, CacheStrategy::MediumTerm, || {
let u = expected_user.clone();
async move { Ok(u) }
})
.await
.unwrap_or_else(|_| panic!("Failed to get/compute typed"));
assert_eq!(user, expected_user);
let user2: test_data::User = cache
.cache_manager()
.get_or_compute_typed(&key, CacheStrategy::MediumTerm, || async {
panic!("Should not compute again");
})
.await
.unwrap_or_else(|_| panic!("Failed to get/compute typed"));
assert_eq!(user2, expected_user);
let _ = cache
.l2_cache
.as_ref()
.unwrap_or_else(|| panic!("L2 cache missing"))
.remove(&key)
.await;
}
#[tokio::test]
async fn test_ttl_expiration() {
let cache = setup_cache_system()
.await
.unwrap_or_else(|_| panic!("Failed to setup cache"));
let key = test_key("ttl");
let value = test_data::bytes_user(6);
cache
.cache_manager()
.set_with_strategy(
&key,
value.clone(),
CacheStrategy::Custom(Duration::from_millis(100)),
)
.await
.unwrap_or_else(|_| panic!("Failed to set cache"));
let cached = cache
.cache_manager()
.get(&key)
.await
.unwrap_or_else(|_| panic!("Failed to get cache"));
assert_eq!(cached, Some(value.clone()));
tokio::time::sleep(Duration::from_millis(200)).await;
let cached2 = cache
.cache_manager()
.get(&key)
.await
.unwrap_or_else(|_| panic!("Failed to get cache"));
assert_eq!(cached2, None);
let _ = cache
.l2_cache
.as_ref()
.unwrap_or_else(|| panic!("L2 cache missing"))
.remove(&key)
.await;
}
#[tokio::test]
async fn test_statistics_tracking() {
let cache = setup_cache_system()
.await
.unwrap_or_else(|_| panic!("Failed to setup cache"));
let key = test_key("stats");
let value = test_data::bytes_user(7);
let stats_before = cache.cache_manager().get_stats();
cache
.cache_manager()
.set_with_strategy(&key, value, CacheStrategy::ShortTerm)
.await
.unwrap_or_else(|_| panic!("Failed to set cache"));
let _ = cache
.cache_manager()
.get(&key)
.await
.unwrap_or_else(|_| panic!("Failed to get cache")); let _ = cache
.cache_manager()
.get(&test_key("nonexistent"))
.await
.unwrap_or_else(|_| panic!("Failed to get cache"));
let stats_after = cache.cache_manager().get_stats();
assert!(stats_after.total_requests > stats_before.total_requests);
assert!(
stats_after.l1_hits > stats_before.l1_hits || stats_after.l2_hits > stats_before.l2_hits
);
assert!(stats_after.misses > stats_before.misses);
let _ = cache
.l2_cache
.as_ref()
.unwrap_or_else(|| panic!("L2 cache missing"))
.remove(&key)
.await;
}
#[tokio::test]
async fn test_health_check() {
let cache = setup_cache_system()
.await
.unwrap_or_else(|_| panic!("Failed to setup cache"));
let healthy = cache.health_check().await;
assert!(healthy, "Cache system should be healthy");
}
#[tokio::test]
async fn test_cache_strategies() {
let cache = setup_cache_system()
.await
.unwrap_or_else(|_| panic!("Failed to setup cache"));
let strategies = vec![
("realtime", CacheStrategy::RealTime),
("short", CacheStrategy::ShortTerm),
("medium", CacheStrategy::MediumTerm),
("long", CacheStrategy::LongTerm),
("custom", CacheStrategy::Custom(Duration::from_secs(60))),
];
for (name, strategy) in strategies {
let key = test_key(name);
let value = test_data::bytes_user(8);
cache
.cache_manager()
.set_with_strategy(&key, value.clone(), strategy)
.await
.unwrap_or_else(|_| panic!("Failed to set with {name} strategy"));
let cached = cache
.cache_manager()
.get(&key)
.await
.unwrap_or_else(|_| panic!("Failed to get with {name} strategy"));
assert_eq!(cached, Some(value));
let _ = cache
.l2_cache
.as_ref()
.unwrap_or_else(|| panic!("L2 cache missing"))
.remove(&key)
.await;
}
}