use spiresql::sql::cache::{LruCache, SharedLruCache, new_shared_cache};
use std::sync::Arc;
use std::thread;
#[test]
fn test_cache_basic_operations() {
let cache: LruCache<String> = LruCache::new(10);
let key1 = LruCache::<String>::hash_key(&"key1");
cache.insert(key1, "value1".to_string());
assert_eq!(cache.get(key1), Some("value1".to_string()));
assert_eq!(cache.len(), 1);
cache.insert(key1, "value1_updated".to_string());
assert_eq!(cache.get(key1), Some("value1_updated".to_string()));
assert_eq!(cache.len(), 1);
let removed = cache.remove(key1);
assert_eq!(removed, Some("value1_updated".to_string()));
assert!(cache.get(key1).is_none());
assert_eq!(cache.len(), 0);
}
#[test]
fn test_cache_lru_eviction() {
let cache: LruCache<i32> = LruCache::new(3);
cache.insert(1, 100);
thread::sleep(std::time::Duration::from_millis(5));
cache.insert(2, 200);
thread::sleep(std::time::Duration::from_millis(5));
cache.insert(3, 300);
assert_eq!(cache.len(), 3);
cache.get_and_touch(1);
thread::sleep(std::time::Duration::from_millis(5));
cache.insert(4, 400);
assert_eq!(cache.len(), 3);
assert!(
cache.get(1).is_some(),
"Key 1 should still exist (recently touched)"
);
assert!(cache.get(2).is_none(), "Key 2 should be evicted (oldest)");
assert!(cache.get(3).is_some(), "Key 3 should still exist");
assert!(cache.get(4).is_some(), "Key 4 should exist (just inserted)");
}
#[test]
fn test_cache_capacity_one() {
let cache: LruCache<String> = LruCache::new(1);
cache.insert(1, "first".to_string());
assert_eq!(cache.len(), 1);
assert_eq!(cache.get(1), Some("first".to_string()));
cache.insert(2, "second".to_string());
assert_eq!(cache.len(), 1);
assert!(cache.get(1).is_none());
assert_eq!(cache.get(2), Some("second".to_string()));
}
#[test]
fn test_cache_empty_operations() {
let cache: LruCache<String> = LruCache::new(10);
assert!(cache.is_empty());
assert_eq!(cache.len(), 0);
assert!(cache.get(12345).is_none());
assert!(cache.remove(12345).is_none());
cache.clear();
assert!(cache.is_empty());
}
#[test]
fn test_cache_concurrent_access() {
let cache: SharedLruCache<i32> = new_shared_cache(100);
let mut handles = vec![];
for i in 0..10 {
let cache_clone: SharedLruCache<i32> = Arc::clone(&cache);
handles.push(thread::spawn(move || {
for j in 0..100 {
let key = (i * 100 + j) as u64;
cache_clone.insert(key, i * 1000 + j);
}
}));
}
for _ in 0..10 {
let cache_clone: SharedLruCache<i32> = Arc::clone(&cache);
handles.push(thread::spawn(move || {
for key in 0..1000u64 {
let _ = cache_clone.get(key);
}
}));
}
for handle in handles {
handle.join().expect("Thread panicked");
}
assert!(cache.len() <= 100);
}
#[test]
fn test_cache_clear() {
let cache: LruCache<i32> = LruCache::new(10);
for i in 0..5u64 {
cache.insert(i, i as i32 * 10);
}
assert_eq!(cache.len(), 5);
cache.clear();
assert!(cache.is_empty());
assert_eq!(cache.len(), 0);
}
#[test]
fn test_cache_capacity() {
let cache: LruCache<i32> = LruCache::new(42);
assert_eq!(cache.capacity(), 42);
}
#[test]
fn test_hash_key_consistency() {
let hash1 = LruCache::<i32>::hash_key(&"test_key");
let hash2 = LruCache::<i32>::hash_key(&"test_key");
let hash3 = LruCache::<i32>::hash_key(&"different_key");
assert_eq!(hash1, hash2, "Same keys should produce same hash");
assert_ne!(
hash1, hash3,
"Different keys should produce different hashes"
);
}