use indexmap::IndexMap;
use vantage_live::cache::{Cache, CachedRows, MemCache, NoCache};
use vantage_types::Record;
fn empty_rows() -> CachedRows {
CachedRows::new(IndexMap::new())
}
fn rows_with_one(name: &str) -> CachedRows {
let mut r: Record<ciborium::Value> = Record::new();
r.insert("name".into(), ciborium::Value::Text(name.into()));
let mut map: IndexMap<String, Record<ciborium::Value>> = IndexMap::new();
map.insert("id1".into(), r);
CachedRows::new(map)
}
#[tokio::test]
async fn memcache_miss_returns_none() {
let c = MemCache::new();
assert!(c.get("absent").await.unwrap().is_none());
}
#[tokio::test]
async fn memcache_put_then_get_round_trip() {
let c = MemCache::new();
c.put("k", rows_with_one("Alice")).await.unwrap();
let back = c.get("k").await.unwrap().expect("hit");
assert_eq!(back.rows.len(), 1);
assert_eq!(
back.rows["id1"]["name"],
ciborium::Value::Text("Alice".into())
);
}
#[tokio::test]
async fn memcache_put_overwrites() {
let c = MemCache::new();
c.put("k", rows_with_one("Alice")).await.unwrap();
c.put("k", rows_with_one("Bob")).await.unwrap();
let back = c.get("k").await.unwrap().expect("hit");
assert_eq!(
back.rows["id1"]["name"],
ciborium::Value::Text("Bob".into())
);
}
#[tokio::test]
async fn memcache_invalidate_prefix_drops_matching() {
let c = MemCache::new();
c.put("clients/page_0", empty_rows()).await.unwrap();
c.put("clients/page_1", empty_rows()).await.unwrap();
c.put("orders/page_0", empty_rows()).await.unwrap();
c.invalidate_prefix("clients").await.unwrap();
assert!(c.get("clients/page_0").await.unwrap().is_none());
assert!(c.get("clients/page_1").await.unwrap().is_none());
assert!(c.get("orders/page_0").await.unwrap().is_some());
}
#[tokio::test]
async fn memcache_invalidate_prefix_empty_string_clears_all() {
let c = MemCache::new();
c.put("a", empty_rows()).await.unwrap();
c.put("b", empty_rows()).await.unwrap();
c.invalidate_prefix("").await.unwrap();
assert!(c.get("a").await.unwrap().is_none());
assert!(c.get("b").await.unwrap().is_none());
}
#[tokio::test]
async fn memcache_clone_shares_state() {
let a = MemCache::new();
let b = a.clone();
a.put("k", rows_with_one("Alice")).await.unwrap();
assert!(b.get("k").await.unwrap().is_some());
}
#[tokio::test]
async fn nocache_get_always_returns_none() {
let c = NoCache;
assert!(c.get("anything").await.unwrap().is_none());
}
#[tokio::test]
async fn nocache_put_is_noop() {
let c = NoCache;
c.put("k", rows_with_one("Alice")).await.unwrap();
assert!(c.get("k").await.unwrap().is_none());
}
#[tokio::test]
async fn nocache_invalidate_prefix_succeeds() {
let c = NoCache;
c.invalidate_prefix("anything").await.unwrap();
}
#[tokio::test]
async fn cache_can_be_used_as_dyn_trait() {
use std::sync::Arc;
let cache: Arc<dyn Cache> = Arc::new(MemCache::new());
cache.put("k", rows_with_one("Alice")).await.unwrap();
let v = cache.get("k").await.unwrap();
assert!(v.is_some());
}