use super::{KvStore, KvEntry, KvMetadata, KvStoreError, KvValue};
use crate::snapshot::SnapshotId;
use std::time::{Duration, SystemTime};
#[test]
fn test_set_and_get() {
let mut store = KvStore::new();
store
.set(
b"my_key".to_vec(),
KvValue::String("my_value".to_string()),
None,
)
.unwrap();
let result = store.get(b"my_key").unwrap();
assert_eq!(result, Some(KvValue::String("my_value".to_string())));
}
#[test]
fn test_set_overwrite() {
let mut store = KvStore::new();
store
.set(b"key".to_vec(), KvValue::Integer(100), None)
.unwrap();
assert_eq!(store.get(b"key").unwrap(), Some(KvValue::Integer(100)));
store
.set(b"key".to_vec(), KvValue::Integer(200), None)
.unwrap();
assert_eq!(store.get(b"key").unwrap(), Some(KvValue::Integer(200)));
assert_eq!(store.len(), 1);
}
#[test]
fn test_delete() {
let mut store = KvStore::new();
store
.set(b"key".to_vec(), KvValue::Integer(42), None)
.unwrap();
assert!(store.exists(b"key"));
store.delete(b"key").unwrap();
assert!(!store.exists(b"key"));
assert_eq!(store.get(b"key").unwrap(), None);
}
#[test]
fn test_exists() {
let mut store = KvStore::new();
assert!(!store.exists(b"key"));
store
.set(b"key".to_vec(), KvValue::Integer(42), None)
.unwrap();
assert!(store.exists(b"key"));
store.delete(b"key").unwrap();
assert!(!store.exists(b"key"));
}
#[test]
fn test_all_value_types() {
let mut store = KvStore::new();
store
.set(b"bytes_key".to_vec(), KvValue::Bytes(vec![1, 2, 3]), None)
.unwrap();
assert_eq!(
store.get(b"bytes_key").unwrap(),
Some(KvValue::Bytes(vec![1, 2, 3]))
);
store
.set(
b"string_key".to_vec(),
KvValue::String("hello".to_string()),
None,
)
.unwrap();
assert_eq!(
store.get(b"string_key").unwrap(),
Some(KvValue::String("hello".to_string()))
);
store
.set(b"int_key".to_vec(), KvValue::Integer(-42), None)
.unwrap();
assert_eq!(store.get(b"int_key").unwrap(), Some(KvValue::Integer(-42)));
store
.set(b"float_key".to_vec(), KvValue::Float(3.14), None)
.unwrap();
assert_eq!(store.get(b"float_key").unwrap(), Some(KvValue::Float(3.14)));
store
.set(b"bool_key".to_vec(), KvValue::Boolean(true), None)
.unwrap();
assert_eq!(
store.get(b"bool_key").unwrap(),
Some(KvValue::Boolean(true))
);
let json = serde_json::json!({"foo": "bar", "num": 123});
store
.set(b"json_key".to_vec(), KvValue::Json(json.clone()), None)
.unwrap();
assert_eq!(store.get(b"json_key").unwrap(), Some(KvValue::Json(json)));
}
#[test]
fn test_ttl_set_and_expire() {
let mut store = KvStore::new();
store
.set(b"temp_key".to_vec(), KvValue::Integer(42), Some(1))
.unwrap();
assert!(store.exists(b"temp_key"));
assert_eq!(store.get(b"temp_key").unwrap(), Some(KvValue::Integer(42)));
std::thread::sleep(Duration::from_secs(2));
assert!(!store.exists(b"temp_key"));
assert_eq!(store.get(b"temp_key").unwrap(), None);
}
#[test]
fn test_ttl_none_persists() {
let mut store = KvStore::new();
store
.set(b"permanent_key".to_vec(), KvValue::Integer(42), None)
.unwrap();
std::thread::sleep(Duration::from_secs(1));
assert!(store.exists(b"permanent_key"));
assert_eq!(
store.get(b"permanent_key").unwrap(),
Some(KvValue::Integer(42))
);
}
#[test]
fn test_ttl_checked_on_get() {
let mut store = KvStore::new();
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let old_created_at = now.saturating_sub(10);
let expired_entry = KvEntry {
key: b"expired_key".to_vec(),
value: KvValue::Integer(42),
metadata: KvMetadata {
created_at: old_created_at,
updated_at: old_created_at,
ttl_seconds: Some(1), version: 100,
},
};
{
let mut entries = store.entries.write();
entries.insert(b"expired_key".to_vec(), vec![expired_entry]);
}
assert_eq!(store.len(), 1);
assert_eq!(store.get(b"expired_key").unwrap(), None);
assert!(!store.exists(b"expired_key"));
}
#[test]
fn test_manual_cleanup() {
let mut store = KvStore::new();
store
.set(b"permanent".to_vec(), KvValue::Integer(1), None)
.unwrap();
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let old_created_at = now.saturating_sub(10);
let expired_entry = KvEntry {
key: b"expired".to_vec(),
value: KvValue::Integer(2),
metadata: KvMetadata {
created_at: old_created_at,
updated_at: old_created_at,
ttl_seconds: Some(1),
version: 100,
},
};
{
let mut entries = store.entries.write();
entries.insert(b"expired".to_vec(), vec![expired_entry]);
}
assert_eq!(store.len(), 2);
let removed = store.cleanup_expired();
assert_eq!(removed, 1);
assert_eq!(store.len(), 1);
assert!(store.exists(b"permanent"));
assert!(!store.exists(b"expired"));
}
#[test]
fn test_ttl_with_snapshot_isolation() {
let mut store = KvStore::new();
store
.set_with_version(b"key".to_vec(), KvValue::Integer(42), Some(1), 100)
.unwrap();
let snapshot = SnapshotId::from_lsn(150);
assert_eq!(
store.get_at_snapshot(b"key", snapshot).unwrap(),
Some(KvValue::Integer(42))
);
std::thread::sleep(Duration::from_secs(2));
assert_eq!(store.get_at_snapshot(b"key", snapshot).unwrap(), None);
}
#[test]
fn test_snapshot_isolation() {
let mut store = KvStore::new();
store
.set_with_version(b"key".to_vec(), KvValue::Integer(100), None, 100)
.unwrap();
let snapshot_old = SnapshotId::from_lsn(50);
assert_eq!(store.get_at_snapshot(b"key", snapshot_old).unwrap(), None);
let snapshot_new = SnapshotId::from_lsn(150);
assert_eq!(
store.get_at_snapshot(b"key", snapshot_new).unwrap(),
Some(KvValue::Integer(100))
);
}
#[test]
fn test_committed_visible() {
let mut store = KvStore::new();
store
.set_with_version(b"key".to_vec(), KvValue::Integer(42), None, 100)
.unwrap();
let snapshot = SnapshotId::from_lsn(150);
let result = store.get_at_snapshot(b"key", snapshot).unwrap();
assert_eq!(result, Some(KvValue::Integer(42)));
}
#[test]
fn test_version_filtering() {
let mut store = KvStore::new();
store
.set_with_version(b"key".to_vec(), KvValue::Integer(100), None, 100)
.unwrap();
store
.set_with_version(b"key".to_vec(), KvValue::Integer(200), None, 200)
.unwrap();
store
.set_with_version(b"key".to_vec(), KvValue::Integer(300), None, 300)
.unwrap();
let snapshot_zero = SnapshotId::from_lsn(0);
assert_eq!(
store.get_at_snapshot(b"key", snapshot_zero).unwrap(),
Some(KvValue::Integer(300))
);
let snapshot_350 = SnapshotId::from_lsn(350);
assert_eq!(
store.get_at_snapshot(b"key", snapshot_350).unwrap(),
Some(KvValue::Integer(300))
);
let snapshot_250 = SnapshotId::from_lsn(250);
assert_eq!(
store.get_at_snapshot(b"key", snapshot_250).unwrap(),
Some(KvValue::Integer(200))
);
let snapshot_150 = SnapshotId::from_lsn(150);
assert_eq!(
store.get_at_snapshot(b"key", snapshot_150).unwrap(),
Some(KvValue::Integer(100))
);
let snapshot_50 = SnapshotId::from_lsn(50);
assert_eq!(store.get_at_snapshot(b"key", snapshot_50).unwrap(), None);
}
#[test]
fn test_snapshot_edge_cases() {
let store = KvStore::new();
let snapshot = SnapshotId::from_lsn(100);
assert_eq!(store.get_at_snapshot(b"missing", snapshot).unwrap(), None);
let snapshot_zero = SnapshotId::from_lsn(0);
assert_eq!(
store.get_at_snapshot(b"missing", snapshot_zero).unwrap(),
None
);
}
#[test]
fn test_kv_participates_in_transaction() {
let mut store = KvStore::new();
store
.set(b"key1".to_vec(), KvValue::Integer(1), None)
.unwrap();
store
.set(b"key2".to_vec(), KvValue::Integer(2), None)
.unwrap();
assert!(store.exists(b"key1"));
assert!(store.exists(b"key2"));
store.delete(b"key1").unwrap();
store.delete(b"key2").unwrap();
assert!(!store.exists(b"key1"));
assert!(!store.exists(b"key2"));
}
#[test]
fn test_version_tracking_for_transactions() {
let mut store = KvStore::new();
store
.set_with_version(b"tx1_key".to_vec(), KvValue::Integer(100), None, 100)
.unwrap();
store
.set_with_version(b"tx2_key".to_vec(), KvValue::Integer(200), None, 200)
.unwrap();
let snapshot_150 = SnapshotId::from_lsn(150);
assert_eq!(
store.get_at_snapshot(b"tx1_key", snapshot_150).unwrap(),
Some(KvValue::Integer(100))
);
assert_eq!(
store.get_at_snapshot(b"tx2_key", snapshot_150).unwrap(),
None
);
let snapshot_250 = SnapshotId::from_lsn(250);
assert_eq!(
store.get_at_snapshot(b"tx1_key", snapshot_250).unwrap(),
Some(KvValue::Integer(100))
);
assert_eq!(
store.get_at_snapshot(b"tx2_key", snapshot_250).unwrap(),
Some(KvValue::Integer(200))
);
}
#[test]
fn test_wal_persistence() {
let mut store = KvStore::new();
store
.set(b"key1".to_vec(), KvValue::Integer(1), None)
.unwrap();
store
.set(
b"key2".to_vec(),
KvValue::String("persist".to_string()),
None,
)
.unwrap();
assert_eq!(store.len(), 2);
assert!(store.exists(b"key1"));
assert!(store.exists(b"key2"));
store
.set(b"key3".to_vec(), KvValue::Integer(3), None)
.unwrap();
assert_eq!(store.len(), 3);
}
#[test]
fn test_wal_recovery_with_versions() {
let mut store = KvStore::new();
store
.set_with_version(b"key1".to_vec(), KvValue::Integer(100), None, 100)
.unwrap();
store
.set_with_version(b"key2".to_vec(), KvValue::Integer(200), None, 200)
.unwrap();
assert_eq!(store.len(), 2);
let snapshot = SnapshotId::from_lsn(150);
assert_eq!(
store.get_at_snapshot(b"key1", snapshot).unwrap(),
Some(KvValue::Integer(100))
);
assert_eq!(store.get_at_snapshot(b"key2", snapshot).unwrap(), None);
}
#[test]
fn test_wal_recovery_with_ttl() {
let mut store = KvStore::new();
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
store
.set_with_version(
b"temp_key".to_vec(),
KvValue::Integer(42),
Some(3600), 100,
)
.unwrap();
assert!(store.exists(b"temp_key"));
{
let entries = store.entries.read();
if let Some(versions) = entries.get(&b"temp_key".to_vec()) {
if let Some(entry) = versions.last() {
assert_eq!(entry.metadata.ttl_seconds, Some(3600));
assert_eq!(entry.metadata.version, 100);
}
}
}
}
#[test]
fn test_wal_delete_recovery() {
let mut store = KvStore::new();
store
.set_with_version(b"key".to_vec(), KvValue::Integer(42), None, 100)
.unwrap();
store.delete(b"key").unwrap();
assert!(!store.exists(b"key"));
assert_eq!(store.get(b"key").unwrap(), None);
}
#[test]
fn test_get_missing_key_returns_none() {
let store = KvStore::new();
let result = store.get(b"missing");
assert!(result.is_ok());
assert_eq!(result.unwrap(), None);
}
#[test]
fn test_get_at_snapshot_missing_key() {
let store = KvStore::new();
let snapshot = SnapshotId::from_lsn(100);
let result = store.get_at_snapshot(b"missing", snapshot);
assert!(result.is_ok());
assert_eq!(result.unwrap(), None);
}
#[test]
fn test_delete_missing_key_error() {
let mut store = KvStore::new();
let result = store.delete(b"missing");
assert!(matches!(result, Err(KvStoreError::KeyNotFound(_))));
}
#[test]
fn test_large_key_value() {
let mut store = KvStore::new();
let large_key = vec![b'X'; 1024];
store
.set(large_key.clone(), KvValue::Integer(42), None)
.unwrap();
assert_eq!(store.get(&large_key).unwrap(), Some(KvValue::Integer(42)));
let large_value = KvValue::Bytes(vec![0xAB; 1_048_576]);
store
.set(b"large_value_key".to_vec(), large_value.clone(), None)
.unwrap();
assert_eq!(store.get(b"large_value_key").unwrap(), Some(large_value));
}
#[test]
fn test_empty_key() {
let mut store = KvStore::new();
store
.set(vec![].to_vec(), KvValue::Integer(42), None)
.unwrap();
assert_eq!(store.get(b"").unwrap(), Some(KvValue::Integer(42)));
assert!(store.exists(b""));
store.delete(b"").unwrap();
assert!(!store.exists(b""));
}
#[test]
fn test_concurrent_kv_ops() {
use parking_lot::RwLock;
use std::sync::{Arc, Barrier};
use std::thread;
let store = Arc::new(RwLock::new(KvStore::new()));
let barrier = Arc::new(Barrier::new(4));
let mut handles = vec![];
for i in 0..4 {
let store_clone = Arc::clone(&store);
let barrier_clone = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier_clone.wait();
for j in 0..10 {
let key = format!("thread_{}_key_{}", i, j);
store_clone
.write()
.set(key.into_bytes(), KvValue::Integer(i * 10 + j), None)
.unwrap();
}
for j in 0..10 {
let key = format!("thread_{}_key_{}", i, j);
let result = store_clone.read().get(key.as_bytes()).unwrap();
assert_eq!(result, Some(KvValue::Integer(i * 10 + j)));
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(store.read().len(), 40);
}
#[test]
fn test_unicode_key() {
let mut store = KvStore::new();
let unicode_key = "🔑_test_key";
let key_bytes = unicode_key.as_bytes().to_vec();
store
.set(
key_bytes.clone(),
KvValue::String("value".to_string()),
None,
)
.unwrap();
assert_eq!(
store.get(&key_bytes).unwrap(),
Some(KvValue::String("value".to_string()))
);
}
#[test]
fn test_zero_ttl() {
let mut store = KvStore::new();
store
.set(b"ephemeral".to_vec(), KvValue::Integer(42), Some(0))
.unwrap();
std::thread::sleep(Duration::from_millis(10));
assert!(!store.exists(b"ephemeral"));
}
#[test]
fn test_very_large_ttl() {
let mut store = KvStore::new();
let hundred_years_seconds = 100_u64 * 365 * 24 * 60 * 60;
store
.set(
b"centennial".to_vec(),
KvValue::Integer(42),
Some(hundred_years_seconds),
)
.unwrap();
assert!(store.exists(b"centennial"));
assert_eq!(
store.get(b"centennial").unwrap(),
Some(KvValue::Integer(42))
);
}
#[test]
fn test_multiple_updates_same_key() {
let mut store = KvStore::new();
for i in 0..10 {
store
.set(b"counter".to_vec(), KvValue::Integer(i), None)
.unwrap();
}
assert_eq!(store.get(b"counter").unwrap(), Some(KvValue::Integer(9)));
assert_eq!(store.len(), 1);
}
#[test]
fn test_exists_with_expired_entry() {
let mut store = KvStore::new();
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let old_created_at = now.saturating_sub(5);
let expired_entry = KvEntry {
key: b"expired".to_vec(),
value: KvValue::Integer(42),
metadata: KvMetadata {
created_at: old_created_at,
updated_at: old_created_at,
ttl_seconds: Some(1),
version: 100,
},
};
{
let mut entries = store.entries.write();
entries.insert(b"expired".to_vec(), vec![expired_entry]);
}
assert!(!store.exists(b"expired"));
}
#[test]
fn test_cleanup_does_not_affect_valid_entries() {
let mut store = KvStore::new();
store
.set(b"no_ttl".to_vec(), KvValue::Integer(1), None)
.unwrap();
store
.set(b"long_ttl".to_vec(), KvValue::Integer(2), Some(3600))
.unwrap();
let removed = store.cleanup_expired();
assert_eq!(removed, 0);
assert_eq!(store.len(), 2);
assert!(store.exists(b"no_ttl"));
assert!(store.exists(b"long_ttl"));
}