#![allow(deprecated)]
#[cfg(feature = "l2-redis")]
mod tests {
use oxcache::backend::l1::L1Backend;
use oxcache::backend::l2::L2Backend;
use oxcache::client::tiered_cache::TieredCacheControl;
use oxcache::client::two_level::TwoLevelClient;
use oxcache::config::{L2Config, RedisMode, TwoLevelConfig};
use oxcache::serialization::{JsonSerializer, SerializerEnum};
use oxcache::CacheOps;
use secrecy::SecretString;
use serial_test::serial;
use std::sync::Arc;
#[allow(dead_code)]
async fn get_test_two_level_client() -> Arc<TwoLevelClient> {
get_test_two_level_client_with_name("test_tiered_service".to_string()).await
}
async fn get_test_two_level_client_with_name(service_name: String) -> Arc<TwoLevelClient> {
let l2_config = L2Config {
connection_string: SecretString::new("redis://127.0.0.1:6379".to_string()),
mode: RedisMode::Standalone,
default_ttl: Some(300),
..Default::default()
};
let l2_backend = Arc::new(
L2Backend::new(&l2_config)
.await
.expect("Failed to create L2 backend"),
);
let l1_backend = Arc::new(L1Backend::new(1000));
let two_level_config = TwoLevelConfig {
promote_on_hit: false, enable_batch_write: false,
..Default::default()
};
Arc::new(
TwoLevelClient::new(
service_name,
two_level_config,
l1_backend,
l2_backend,
SerializerEnum::Json(JsonSerializer::new()),
)
.await
.expect("Failed to create TwoLevel client"),
)
}
async fn cleanup_test_keys(client: &TwoLevelClient, pattern: &str) {
let _ = client.del_pattern(pattern).await;
}
#[tokio::test]
#[serial]
async fn test_get_l1_direct() {
let client = get_test_two_level_client_with_name("test_tiered_l1".to_string()).await;
let test_key = "oxcache:test:tiered:l1direct";
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
client
.set_l1_direct(test_key, b"l1_value".to_vec(), Some(300))
.await
.unwrap();
let value = client.get_l1_direct(test_key).await.unwrap();
assert!(value.is_some());
assert_eq!(value.unwrap(), b"l1_value");
let value = client.get_l1_direct(test_key).await.unwrap();
assert!(value.is_some());
assert_eq!(value.unwrap(), b"l1_value");
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
}
#[tokio::test]
#[serial]
async fn test_get_l1_direct_not_exists() {
let client =
get_test_two_level_client_with_name("test_tiered_l1_not_exists".to_string()).await;
let value = client.get_l1_direct("nonexistent_l1_key").await.unwrap();
assert!(value.is_none());
}
#[tokio::test]
#[serial]
async fn test_set_l1_direct() {
let client = get_test_two_level_client_with_name("test_tiered_set_l1".to_string()).await;
let test_key = "oxcache:test:tiered:setl1";
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
client
.set_l1_direct(test_key, b"only_in_l1".to_vec(), Some(300))
.await
.unwrap();
let l1_value = client.get_l1_direct(test_key).await.unwrap();
assert!(l1_value.is_some());
let l2_value = client.get_l2_direct(test_key).await.unwrap();
assert!(l2_value.is_none());
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
}
#[tokio::test]
#[serial]
async fn test_delete_l1_direct() {
let client = get_test_two_level_client_with_name("test_tiered_delete_l1".to_string()).await;
let test_key = "oxcache:test:tiered:deletel1";
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
client
.set_l1_direct(test_key, b"to_delete".to_vec(), Some(300))
.await
.unwrap();
assert!(client.get_l1_direct(test_key).await.unwrap().is_some());
let result = client.delete_l1_direct(test_key).await.unwrap();
assert!(result);
assert!(client.get_l1_direct(test_key).await.unwrap().is_none());
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
}
#[tokio::test]
#[serial]
async fn test_get_l2_direct() {
let client = get_test_two_level_client_with_name("test_tiered_l2".to_string()).await;
let test_key = "oxcache:test:tiered:l2direct";
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
client
.set_l2_direct(test_key, b"l2_value".to_vec(), Some(300))
.await
.unwrap();
let value = client.get_l2_direct(test_key).await.unwrap();
assert!(value.is_some());
assert_eq!(value.unwrap(), b"l2_value".to_vec());
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
}
#[tokio::test]
#[serial]
async fn test_set_l2_direct() {
let client = get_test_two_level_client_with_name("test_tiered_set_l2".to_string()).await;
let test_key = format!(
"oxcache:test:tiered:setl2:{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
);
client
.set_l2_direct(&test_key, b"only_in_l2".to_vec(), Some(300))
.await
.unwrap();
let l2_value = client.get_l2_direct(&test_key).await.unwrap();
assert!(l2_value.is_some());
assert_eq!(l2_value.unwrap(), b"only_in_l2");
let l1_value = client.get_l1_direct(&test_key).await.unwrap();
assert!(l1_value.is_none());
let value = client.get_l2_direct(&test_key).await.unwrap();
assert!(value.is_some());
assert_eq!(value.unwrap(), b"only_in_l2");
let _ = client.delete(&test_key).await;
}
#[tokio::test]
#[serial]
async fn test_delete_l2_direct() {
let client = get_test_two_level_client_with_name("test_tiered_delete_l2".to_string()).await;
let test_key = "oxcache:test:tiered:deletel2";
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
client
.set_l2_direct(test_key, b"to_delete_from_l2".to_vec(), Some(300))
.await
.unwrap();
let result = client.delete_l2_direct(test_key).await.unwrap();
assert!(result);
assert!(client.get_l2_direct(test_key).await.unwrap().is_none());
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
}
#[tokio::test]
#[serial]
async fn test_promote_to_l1() {
let client = get_test_two_level_client_with_name("test_tiered_promote".to_string()).await;
let test_key = format!(
"oxcache:test:tiered:promote:{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
);
client
.set_l2_direct(&test_key, b"promote_this".to_vec(), Some(300))
.await
.unwrap();
assert!(client.get_l1_direct(&test_key).await.unwrap().is_none());
let l2_before = client.get_l2_direct(&test_key).await.unwrap();
assert!(l2_before.is_some(), "L2 should have data before promote");
let result = client.promote_to_l1(&test_key).await.unwrap();
assert!(result);
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
let l1_value = client.get_l1_direct(&test_key).await.unwrap();
assert!(l1_value.is_some());
assert_eq!(l1_value.unwrap(), b"promote_this".to_vec());
let l2_value = client.get_l2_direct(&test_key).await.unwrap();
assert!(
l2_value.is_some(),
"L2 should still have data after promote"
);
let _ = client.delete(&test_key).await;
}
#[tokio::test]
#[serial]
async fn test_promote_to_l1_not_exists() {
let client =
get_test_two_level_client_with_name("test_tiered_promote_not_exists".to_string()).await;
let result = client
.promote_to_l1("nonexistent_promote_key")
.await
.unwrap();
assert!(!result);
}
#[tokio::test]
#[serial]
async fn test_demote_to_l2() {
let client = get_test_two_level_client_with_name("test_tiered_demote".to_string()).await;
let test_key = "oxcache:test:tiered:demote";
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
client
.set_l1_direct(test_key, b"demote_this".to_vec(), Some(300))
.await
.unwrap();
let result = client.demote_to_l2(test_key, Some(600)).await.unwrap();
assert!(result);
let l1_value = client.get_l1_direct(test_key).await.unwrap();
assert!(l1_value.is_some());
let l2_value = client.get_l2_direct(test_key).await.unwrap();
assert!(l2_value.is_some());
assert_eq!(l2_value.unwrap(), b"demote_this".to_vec());
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
}
#[tokio::test]
#[serial]
async fn test_evict_all() {
let client = get_test_two_level_client_with_name("test_tiered_evict".to_string()).await;
let test_key = "oxcache:test:tiered:evict";
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
client
.set_l1_direct(test_key, b"l1_data".to_vec(), Some(300))
.await
.unwrap();
client
.set_l2_direct(test_key, b"l2_data".to_vec(), Some(300))
.await
.unwrap();
assert!(client.get_l1_direct(test_key).await.unwrap().is_some());
assert!(client.get_l2_direct(test_key).await.unwrap().is_some());
let result = client.evict_all(test_key).await.unwrap();
assert!(result);
assert!(client.get_l1_direct(test_key).await.unwrap().is_none());
assert!(client.get_l2_direct(test_key).await.unwrap().is_none());
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
}
#[tokio::test]
#[serial]
async fn test_layer_isolation() {
let client = get_test_two_level_client_with_name("test_tiered_isolation".to_string()).await;
let l1_key = "oxcache:test:tiered:isolation:l1";
let l2_key = "oxcache:test:tiered:isolation:l2";
let shared_key = "oxcache:test:tiered:isolation:shared";
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
client
.set_l1_direct(l1_key, b"l1_only".to_vec(), Some(300))
.await
.unwrap();
client
.set_l2_direct(l2_key, b"l2_only".to_vec(), Some(300))
.await
.unwrap();
client
.set_l1_direct(shared_key, b"shared_in_l1".to_vec(), Some(300))
.await
.unwrap();
client
.set_l2_direct(shared_key, b"shared_in_l2".to_vec(), Some(300))
.await
.unwrap();
assert!(client.get_l1_direct(l1_key).await.unwrap().is_some());
assert!(client.get_l2_direct(l1_key).await.unwrap().is_none());
assert!(client.get_l1_direct(l2_key).await.unwrap().is_none());
let l2_result = client.get_l2_direct(l2_key).await.unwrap();
assert!(l2_result.is_some(), "L2 key should exist in L2");
assert_eq!(
client.get_l1_direct(shared_key).await.unwrap().unwrap(),
b"shared_in_l1".to_vec()
);
assert_eq!(
client.get_l2_direct(shared_key).await.unwrap().unwrap(),
b"shared_in_l2".to_vec()
);
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
}
#[tokio::test]
#[serial]
async fn test_multiple_direct_operations() {
let client = get_test_two_level_client_with_name("test_tiered_multi".to_string()).await;
let test_prefix = "oxcache:test:tiered:multi";
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
let mut handles = Vec::new();
for i in 1..=10 {
let client = client.clone();
let key = format!("{}:{}", test_prefix, i);
handles.push(tokio::spawn(async move {
client
.set_l1_direct(&key, format!("value{}", i).into_bytes(), Some(300))
.await
}));
}
for handle in handles {
let _ = handle.await.unwrap();
}
for i in 1..=10 {
let key = format!("{}:{}", test_prefix, i);
let value = client.get_l1_direct(&key).await.unwrap();
assert!(value.is_some());
}
cleanup_test_keys(&client, "oxcache:test:tiered:*").await;
}
}