#![allow(dead_code)]
use anyhow::Result;
use multi_tier_cache::backends::MokaCacheConfig;
use multi_tier_cache::{
CacheManager, CacheSystem, CacheSystemBuilder, InvalidationConfig, L1Cache, L2Cache,
L2CacheBackend, TierConfig,
};
use std::sync::Arc;
use std::sync::Once;
static INIT: Once = Once::new();
pub fn init_test_tracing() {
INIT.call_once(|| {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
});
}
pub fn redis_url() -> String {
std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string())
}
pub fn test_key_prefix() -> String {
use std::time::{Duration, SystemTime, UNIX_EPOCH};
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or(Duration::from_secs(0))
.as_millis();
format!("test:{timestamp}:")
}
pub fn test_key(name: &str) -> String {
format!("test_{}_{}", name, rand::random::<u32>())
}
pub async fn setup_cache_system() -> Result<CacheSystem> {
unsafe { std::env::set_var("REDIS_URL", redis_url()) };
CacheSystem::new()
.await
.map_err(|e| anyhow::anyhow!(e.to_string()))
}
pub async fn setup_cache_with_n(n: usize) -> Result<CacheSystem> {
let l1 = Arc::new(L1Cache::new(MokaCacheConfig::default())?);
let l2 = Arc::new(L2Cache::new().await?);
let cache = CacheSystemBuilder::new()
.with_tier(
Arc::clone(&l1) as Arc<dyn L2CacheBackend>,
TierConfig::as_l1(),
)
.with_tier(
Arc::clone(&l2) as Arc<dyn L2CacheBackend>,
TierConfig::as_l2().with_promotion_frequency(n),
)
.build()
.await?;
let mut cache = cache;
cache.l1_cache = Some(l1);
cache.l2_cache = Some(l2);
Ok(cache)
}
pub async fn setup_cache_with_invalidation() -> Result<Arc<CacheManager>> {
let l1 = Arc::new(L1Cache::new(MokaCacheConfig::default())?);
let l2 = Arc::new(L2Cache::new().await?);
let config = InvalidationConfig::default();
let manager = CacheManager::new_with_invalidation(l1, l2, &redis_url(), config)
.await
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
Ok(Arc::new(manager))
}
pub async fn cleanup_test_keys(prefix: &str) -> Result<()> {
let _cache = setup_cache_system().await?;
let l2 = Arc::new(L2Cache::new().await?);
let pattern = format!("{prefix}*");
let keys = l2.scan_keys(&pattern).await?;
if !keys.is_empty() {
l2.remove_bulk(&keys).await?;
}
Ok(())
}
pub mod test_data {
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct User {
pub id: u64,
pub name: String,
pub email: String,
}
impl User {
pub fn new(id: u64) -> Self {
Self {
id,
name: format!("User {id}"),
email: format!("user{id}@example.com"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Product {
pub id: u64,
pub name: String,
pub price: f64,
pub category: String,
}
impl Product {
pub fn new(id: u64) -> Self {
Self {
id,
name: format!("Product {id}"),
#[allow(clippy::cast_precision_loss)]
price: 99.99 + (id as f64),
category: format!("Category {}", id % 5),
}
}
}
pub fn json_user(id: u64) -> serde_json::Value {
serde_json::json!({
"id": id,
"name": format!("User {}", id),
"email": format!("user{}@example.com", id),
"created_at": "2025-01-01T00:00:00Z"
})
}
pub fn bytes_user(id: u64) -> bytes::Bytes {
bytes::Bytes::from(json_user(id).to_string())
}
pub fn json_data_sized(size_kb: usize) -> serde_json::Value {
let data_string = "x".repeat(size_kb * 1024);
serde_json::json!({
"data": data_string,
"size_kb": size_kb
})
}
pub fn bytes_data_sized(size_kb: usize) -> bytes::Bytes {
bytes::Bytes::from(json_data_sized(size_kb).to_string())
}
}
pub async fn wait_for<F>(mut condition: F, timeout_ms: u64) -> bool
where
F: FnMut() -> bool,
{
use tokio::time::{Duration, sleep};
let start = std::time::Instant::now();
let timeout = Duration::from_millis(timeout_ms);
while start.elapsed() < timeout {
if condition() {
return true;
}
sleep(Duration::from_millis(10)).await;
}
false
}
#[macro_export]
macro_rules! assert_cache_stats {
($cache:expr_2021, $field:ident > $value:expr_2021) => {
let stats = $cache.cache_manager().get_stats();
assert!(
stats.$field > $value,
"Expected {} > {}, got {}",
stringify!($field),
$value,
stats.$field
);
};
($cache:expr_2021, $field:ident == $value:expr_2021) => {
let stats = $cache.cache_manager().get_stats();
assert_eq!(
stats.$field,
$value,
"Expected {} == {}, got {}",
stringify!($field),
$value,
stats.$field
);
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_generation() {
let key1 = test_key("user");
let key2 = test_key("user");
assert_ne!(key1, key2, "Keys should be unique");
assert!(key1.starts_with("test_user_"));
}
#[test]
fn test_data_generation() {
let user = test_data::User::new(123);
assert_eq!(user.id, 123);
assert_eq!(user.name, "User 123");
assert_eq!(user.email, "user123@example.com");
}
}