#![cfg(feature = "memory")]
use std::time::Duration;
use cachet::{Cache, CacheEntry, InsertPolicy};
use cachet_tier::{CacheOp, MockCache};
use tick::Clock;
fn block_on<F: std::future::Future>(f: F) -> F::Output {
futures::executor::block_on(f)
}
#[test]
fn cache_builder_with_storage() {
let clock = Clock::new_frozen();
let storage = MockCache::<String, i32>::new();
let cache = Cache::builder::<String, i32>(clock).storage(storage).build();
block_on(async {
assert!(cache.get(&"key".to_string()).await.unwrap().is_none());
});
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn mock_cache_with_storage() {
let clock = Clock::new_frozen();
let mock = MockCache::<String, i32>::new();
let cache = Cache::builder(clock).storage(mock.clone()).build();
cache.insert("key".to_string(), CacheEntry::new(42)).await.unwrap();
let value = cache.get(&"key".to_string()).await.unwrap();
assert_eq!(*value.unwrap().value(), 42);
assert_eq!(mock.operations().len(), 2);
}
#[test]
fn mock_cache_failure_injection() {
block_on(async {
let clock = Clock::new_frozen();
let mock = MockCache::<String, i32>::new();
let cache = Cache::builder(clock).storage(mock.clone()).build();
mock.fail_when(|op| matches!(op, CacheOp::Get(_)));
let result = cache.get(&"key".to_string()).await;
result.expect_err("mock configured to fail on get");
mock.clear_failures();
let result = cache.get(&"key".to_string()).await;
result.expect("get should succeed after clearing failures");
});
}
#[test]
fn mock_cache_shares_state_with_handle() {
block_on(async {
let clock = Clock::new_frozen();
let mock = MockCache::<String, i32>::new();
let cache = Cache::builder(clock).storage(mock.clone()).build();
cache.insert("key".to_string(), CacheEntry::new(42)).await.unwrap();
assert!(mock.contains_key(&"key".to_string()));
assert_eq!(mock.entry_count(), 1);
});
}
#[test]
fn cache_builder_clock() {
let clock = Clock::new_frozen();
let expected_instant = clock.instant();
let builder = Cache::builder::<String, i32>(clock);
let builder_clock = builder.clock();
assert_eq!(builder_clock.instant(), expected_instant);
}
#[cfg_attr(miri, ignore)]
#[test]
fn cache_builder_name() {
let clock = Clock::new_frozen();
let cache = Cache::builder::<String, i32>(clock).memory().name("test_cache").build();
assert_eq!(cache.name(), "test_cache");
}
#[cfg_attr(miri, ignore)]
#[test]
fn fallback_builder_basic() {
let clock = Clock::new_frozen();
let fallback = Cache::builder::<String, i32>(clock.clone()).memory().ttl(Duration::from_hours(1));
let cache = Cache::builder::<String, i32>(clock)
.memory()
.ttl(Duration::from_mins(1))
.fallback(fallback)
.build();
block_on(async {
let key = "key".to_string();
cache.insert(key.clone(), CacheEntry::new(42)).await.unwrap();
let entry = cache.get(&key).await.unwrap();
assert_eq!(*entry.unwrap().value(), 42);
});
}
#[cfg_attr(miri, ignore)]
#[test]
fn fallback_builder_insert_policy() {
let clock = Clock::new_frozen();
let fallback = Cache::builder::<String, i32>(clock.clone()).memory();
let cache = Cache::builder::<String, i32>(clock)
.memory()
.insert_policy(InsertPolicy::never())
.fallback(fallback)
.build();
block_on(async {
cache.insert("key".to_string(), CacheEntry::new(42)).await.unwrap();
let entry = cache.get(&"key".to_string()).await.unwrap();
assert_eq!(*entry.unwrap().value(), 42);
});
}
#[cfg_attr(miri, ignore)]
#[test]
fn fallback_builder_nested_fallback() {
let clock = Clock::new_frozen();
let l3 = Cache::builder::<String, i32>(clock.clone()).memory();
let l2 = Cache::builder::<String, i32>(clock.clone())
.memory()
.insert_policy(InsertPolicy::always())
.fallback(l3);
let cache = Cache::builder::<String, i32>(clock)
.memory()
.insert_policy(InsertPolicy::never())
.fallback(l2)
.build();
block_on(async {
cache.insert("key".to_string(), CacheEntry::new(42)).await.unwrap();
let entry = cache.get(&"key".to_string()).await.unwrap();
assert_eq!(*entry.unwrap().value(), 42);
});
}