use super::*;
use std::sync::Barrier;
use tempfile::TempDir;
fn touch_palaces(cache: &SessionStoreCache, root: &Path, count: usize) {
for i in 0..count {
let id = format!("palace-{i}");
let store = cache
.get_or_open(&id, &root.join(&id))
.expect("open should succeed");
drop(store);
}
}
#[test]
fn open_handles_are_bounded_by_cap() {
let dir = TempDir::new().unwrap();
let cache = SessionStoreCache::with_max_open(8);
touch_palaces(&cache, dir.path(), 64);
assert_eq!(
cache.len(),
8,
"resident stores must be capped at 8, got {}",
cache.len()
);
}
#[test]
fn evicted_store_reopens_with_data_intact() {
let dir = TempDir::new().unwrap();
let cache = SessionStoreCache::with_max_open(4);
let sid = {
let store = cache
.get_or_open("palace-0", &dir.path().join("palace-0"))
.unwrap();
store.create_session(Some("kept".to_string())).unwrap()
};
touch_palaces(&cache, dir.path(), 40);
let reopened = cache
.get_or_open("palace-0", &dir.path().join("palace-0"))
.expect("evicted palace must reopen cleanly");
let session = reopened
.get_session(&sid)
.expect("read after reopen")
.expect("session must survive eviction");
assert_eq!(session.title.as_deref(), Some("kept"));
}
#[test]
fn in_use_store_is_never_evicted() {
let dir = TempDir::new().unwrap();
let cache = SessionStoreCache::with_max_open(4);
let held = cache
.get_or_open("palace-0", &dir.path().join("palace-0"))
.unwrap();
touch_palaces(&cache, dir.path(), 40);
let again = cache
.get_or_open("palace-0", &dir.path().join("palace-0"))
.expect("an in-use store must never be evicted and double-opened");
assert!(
Arc::ptr_eq(&held, &again),
"the held store must be returned, not a second open of the same file"
);
}
#[test]
fn concurrent_callers_share_one_store() {
let dir = TempDir::new().unwrap();
let cache = Arc::new(SessionStoreCache::with_max_open(4));
let barrier = Arc::new(Barrier::new(8));
let handles: Vec<_> = (0..8)
.map(|_| {
let cache = Arc::clone(&cache);
let barrier = Arc::clone(&barrier);
let path = dir.path().join("shared");
std::thread::spawn(move || {
barrier.wait();
cache.get_or_open("shared", &path).expect("concurrent open")
})
})
.collect();
let stores: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
for store in &stores[1..] {
assert!(
Arc::ptr_eq(&stores[0], store),
"all concurrent callers must share one store"
);
}
assert_eq!(cache.len(), 1);
}
#[test]
fn remove_drops_cached_handle() {
let dir = TempDir::new().unwrap();
let cache = SessionStoreCache::with_max_open(4);
drop(cache.get_or_open("gone", &dir.path().join("gone")).unwrap());
assert_eq!(cache.len(), 1);
cache.remove("gone");
assert!(cache.is_empty(), "remove must drop the cached handle");
cache.remove("never-opened");
assert!(cache.is_empty());
}
#[test]
fn env_override_is_honoured() {
if std::env::var(MAX_OPEN_SESSION_STORES_ENV).is_err() {
assert_eq!(
max_open_session_stores_from_env(),
DEFAULT_MAX_OPEN_SESSION_STORES
);
}
assert_eq!(SessionStoreCache::with_max_open(0).capacity(), 1);
assert_eq!(SessionStoreCache::with_max_open(17).capacity(), 17);
}