use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use brz_memcache::cacheservice::CacheServiceConfig;
use brz_memcache::{
CacheNamespaceConf, CacheService, CacheServiceConfigSource, CacheServiceOptions,
SubscriptionHandle,
};
#[derive(Clone)]
struct FakeSource {
pushed: Arc<Mutex<Vec<CacheNamespaceConf>>>,
}
impl FakeSource {
fn new() -> Self {
Self {
pushed: Arc::new(Mutex::new(Vec::new())),
}
}
fn push(&self, conf: CacheNamespaceConf) {
self.pushed.lock().unwrap().push(conf.clone());
if let Some(cb) = CALLBACK.with(|cell| cell.borrow().clone()) {
cb(conf);
}
}
}
type Callback = Arc<dyn Fn(CacheNamespaceConf) + Send + Sync>;
thread_local! {
static CALLBACK: std::cell::RefCell<Option<Callback>> =
const { std::cell::RefCell::new(None) };
}
#[async_trait]
impl CacheServiceConfigSource for FakeSource {
async fn load(&self) -> Result<CacheNamespaceConf, brz_memcache::Error> {
Ok(conf("127.0.0.1:11211"))
}
async fn subscribe(
&self,
on_update: Arc<dyn Fn(CacheNamespaceConf) + Send + Sync>,
) -> Result<SubscriptionHandle, brz_memcache::Error> {
CALLBACK.with(|cell| *cell.borrow_mut() = Some(on_update));
Ok(SubscriptionHandle::empty())
}
}
fn conf(master: &str) -> CacheNamespaceConf {
let yaml = format!("ns:\n hash: crc32\n distribution: modula\n master:\n - {master}\n");
CacheServiceConfig::from_yaml_str(&yaml)
.unwrap()
.namespace("ns")
.unwrap()
.clone()
}
#[tokio::test]
async fn static_new_builds_from_a_config() {
let cache = CacheService::new(conf("127.0.0.1:11211"), CacheServiceOptions::default())
.await
.unwrap();
drop(cache);
}
#[tokio::test]
async fn new_live_loads_initial_config() {
let source: Arc<dyn CacheServiceConfigSource> = Arc::new(FakeSource::new());
let cache = CacheService::new_live(source, CacheServiceOptions::default())
.await
.unwrap();
drop(cache);
}
#[tokio::test]
async fn new_live_applies_pushed_config() {
let source = Arc::new(FakeSource::new());
let pushed = Arc::clone(&source.pushed);
let driver = source.clone();
let source_dyn: Arc<dyn CacheServiceConfigSource> = source;
let cache = CacheService::new_live(source_dyn, CacheServiceOptions::default())
.await
.unwrap();
driver.push(conf("127.0.0.1:11212"));
assert_eq!(pushed.lock().unwrap().len(), 1);
driver.push(conf("127.0.0.1:11212"));
drop(cache);
}
#[tokio::test]
async fn partial_eq_skip_is_semantic() {
let yaml = "ns:\n hash: crc32\n distribution: modula\n master:\n - 127.0.0.1:9\n";
let c1 = CacheServiceConfig::from_yaml_str(yaml)
.unwrap()
.namespace("ns")
.unwrap()
.clone();
let c2 = CacheServiceConfig::from_yaml_str(yaml)
.unwrap()
.namespace("ns")
.unwrap()
.clone();
assert_eq!(c1, c2);
}
#[tokio::test]
async fn parse_namespace_yaml_errors_on_missing() {
let cfg = CacheServiceConfig::from_yaml_str("other:\n master: []\n").unwrap();
assert!(cfg.namespace("ns").is_none());
}