#![cfg(feature = "serialize")]
use bytesbuf::BytesView;
use cachet::{Cache, CacheOp, CacheTier, MockCache};
use tick::Clock;
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn serialize_encode_decode_returns_correct_output() {
let mock_cache_before = MockCache::<String, String>::new();
let mock_cache_after = MockCache::<BytesView, BytesView>::new();
let cache = Cache::builder(Clock::new_frozen())
.storage(mock_cache_before.clone())
.serialize()
.fallback(Cache::builder(Clock::new_frozen()).storage(mock_cache_after.clone()))
.build();
let expected_key = "greeting".to_string();
let expected_value = "Hello, world!".to_string();
cache
.insert(expected_key.clone(), expected_value.clone())
.await
.expect("Insert failed");
mock_cache_before.invalidate(&expected_key).await.expect("Invalidate failed");
let actual_value = cache.get(&expected_key).await.expect("Should be Ok").expect("Should be Some");
let before_ops = mock_cache_before.operations();
assert_eq!(before_ops.len(), 4);
assert!(matches!(&before_ops[0], CacheOp::Insert { key, entry } if key == &expected_key && entry.value() == &expected_value));
assert!(matches!(&before_ops[1], CacheOp::Invalidate(k) if k == &expected_key));
assert!(matches!(&before_ops[2], CacheOp::Get(k) if k == &expected_key));
assert!(matches!(&before_ops[3], CacheOp::Insert { key, entry } if key == &expected_key && entry.value() == &expected_value));
let after_ops = mock_cache_after.operations();
assert_eq!(after_ops.len(), 2);
let mut serialized_key = vec![1u8];
serialized_key.extend_from_slice(&postcard::to_allocvec(&expected_key).expect("postcard serialization should not fail"));
let mut serialized_value = vec![1u8];
serialized_value.extend_from_slice(&postcard::to_allocvec(&expected_value).expect("postcard serialization should not fail"));
assert!(
matches!(&after_ops[0], CacheOp::Insert { key, entry } if *key == serialized_key.as_slice() && *entry.value() == serialized_value.as_slice())
);
assert!(matches!(&after_ops[1], CacheOp::Get(k) if *k == serialized_key.as_slice()));
assert_eq!(*actual_value.value(), expected_value);
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn serialize_on_fallback_builder() {
let l1 = MockCache::<String, String>::new();
let l2 = MockCache::<String, String>::new();
let l3 = MockCache::<BytesView, BytesView>::new();
let cache = Cache::builder(Clock::new_frozen())
.storage(l1)
.fallback(Cache::builder(Clock::new_frozen()).storage(l2))
.serialize()
.fallback(Cache::builder(Clock::new_frozen()).storage(l3.clone()))
.build();
cache.insert("key".to_string(), "value".to_string()).await.expect("Insert failed");
let l3_ops = l3.operations();
assert_eq!(l3_ops.len(), 1);
assert!(matches!(&l3_ops[0], CacheOp::Insert { .. }));
}
#[cfg_attr(miri, ignore)]
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn concurrent_serialize_from_multiple_tasks() {
use std::sync::Arc;
let mock_after = MockCache::<BytesView, BytesView>::new();
let cache = Arc::new(
Cache::builder(Clock::new_frozen())
.storage(MockCache::<String, String>::new())
.serialize()
.fallback(Cache::builder(Clock::new_frozen()).storage(mock_after.clone()))
.build(),
);
let mut handles = Vec::new();
for i in 0..20 {
let cache = Arc::clone(&cache);
handles.push(tokio::spawn(async move {
let key = format!("key-{i}");
let value = format!("value-{i}");
cache.insert(key.clone(), value.clone()).await.expect("Insert failed");
let result = cache.get(&key).await.expect("Get failed").expect("Should be Some");
assert_eq!(*result.value(), value);
}));
}
for handle in handles {
handle.await.expect("Task panicked");
}
let after_ops = mock_after.operations();
let insert_count = after_ops.iter().filter(|op| matches!(op, CacheOp::Insert { .. })).count();
assert_eq!(insert_count, 20);
for i in 0..20 {
let expected_key = format!("key-{i}");
let expected_value = format!("value-{i}");
let mut serialized_key = vec![1u8];
serialized_key.extend_from_slice(&postcard::to_allocvec(&expected_key).expect("postcard serialization should not fail"));
let mut serialized_value = vec![1u8];
serialized_value.extend_from_slice(&postcard::to_allocvec(&expected_value).expect("postcard serialization should not fail"));
assert!(
after_ops.iter().any(|op| matches!(op, CacheOp::Insert { key, entry } if *key == serialized_key.as_slice() && *entry.value() == serialized_value.as_slice())),
"missing serialized insert for {expected_key}={expected_value}"
);
}
}
#[cfg_attr(miri, ignore)]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn serialize_on_one_thread_deserialize_on_another() {
use std::sync::Arc;
let mock_before = MockCache::<String, String>::new();
let cache = Arc::new(
Cache::builder(Clock::new_frozen())
.storage(mock_before.clone())
.serialize()
.fallback(Cache::builder(Clock::new_frozen()).storage(MockCache::<BytesView, BytesView>::new()))
.build(),
);
let cache_clone = Arc::clone(&cache);
tokio::spawn(async move {
cache_clone
.insert("cross-thread".to_string(), "hello from another task".to_string())
.await
.expect("Insert failed");
})
.await
.expect("Insert task panicked");
mock_before
.invalidate(&"cross-thread".to_string())
.await
.expect("Invalidate failed");
let cache_clone = Arc::clone(&cache);
let result = tokio::spawn(async move {
cache_clone
.get(&"cross-thread".to_string())
.await
.expect("Get failed")
.expect("Should be Some")
})
.await
.expect("Get task panicked");
assert_eq!(*result.value(), "hello from another task");
}