#[cfg(test)]
mod tests {
use super::super::temporal_index::*;
#[test]
fn test_temporal_index_insert_and_get() {
let index = TemporalIndex::new();
index.insert(1, 1000);
index.insert(2, 2000);
index.insert(3, 3000);
assert_eq!(index.get_timestamp(1), Some(1000));
assert_eq!(index.get_timestamp(2), Some(2000));
assert_eq!(index.get_timestamp(3), Some(3000));
assert_eq!(index.get_timestamp(4), None);
}
#[test]
fn test_temporal_index_remove() {
let index = TemporalIndex::new();
index.insert(1, 1000);
index.insert(2, 2000);
index.remove(1);
assert_eq!(index.get_timestamp(1), None);
assert_eq!(index.get_timestamp(2), Some(2000));
}
#[test]
fn test_temporal_index_recent() {
let index = TemporalIndex::new();
index.insert(1, 1000);
index.insert(2, 2000);
index.insert(3, 3000);
index.insert(4, 4000);
let recent = index.recent(2, None);
assert_eq!(recent.len(), 2);
assert_eq!(recent[0].id, 4);
assert_eq!(recent[1].id, 3);
}
#[test]
fn test_temporal_index_recent_with_since() {
let index = TemporalIndex::new();
index.insert(1, 1000);
index.insert(2, 2000);
index.insert(3, 3000);
index.insert(4, 4000);
let recent = index.recent(10, Some(2000));
assert_eq!(recent.len(), 2);
assert_eq!(recent[0].id, 4);
assert_eq!(recent[1].id, 3);
}
#[test]
fn test_temporal_index_older_than() {
let index = TemporalIndex::new();
index.insert(1, 1000);
index.insert(2, 2000);
index.insert(3, 3000);
index.insert(4, 4000);
let old = index.older_than(3000, 10);
assert_eq!(old.len(), 2);
assert_eq!(old[0].id, 1);
assert_eq!(old[1].id, 2);
}
#[test]
fn test_temporal_index_range() {
let index = TemporalIndex::new();
index.insert(1, 1000);
index.insert(2, 2000);
index.insert(3, 3000);
index.insert(4, 4000);
let range = index.range(2000, 3000);
assert_eq!(range.len(), 2);
}
#[test]
fn test_temporal_index_serialize_deserialize() {
let index = TemporalIndex::new();
index.insert(1, 1000);
index.insert(2, 2000);
index.insert(3, 3000);
let data = index.serialize();
let restored = TemporalIndex::deserialize(&data).expect("Failed to deserialize");
assert_eq!(restored.get_timestamp(1), Some(1000));
assert_eq!(restored.get_timestamp(2), Some(2000));
assert_eq!(restored.get_timestamp(3), Some(3000));
}
#[test]
fn test_deserialize_rejects_overflowing_count() {
let count: u64 = 1u64 << 60;
let mut data = Vec::with_capacity(8);
data.extend_from_slice(&count.to_le_bytes());
let restored = TemporalIndex::deserialize(&data);
assert!(
restored.is_none(),
"overflowing count header must be rejected"
);
}
#[test]
fn test_recent_huge_limit_does_not_overallocate() {
let index = TemporalIndex::new();
index.insert(1, 1000);
index.insert(2, 2000);
let recent = index.recent(usize::MAX, None);
assert_eq!(recent.len(), 2);
let older = index.older_than(i64::MAX, usize::MAX);
assert_eq!(older.len(), 2);
}
#[test]
fn test_temporal_index_stats() {
let index = TemporalIndex::new();
index.insert(1, 1000);
index.insert(2, 2000);
index.insert(3, 3000);
let stats = index.stats();
assert_eq!(stats.entry_count, 3);
assert_eq!(stats.unique_timestamps, 3);
assert_eq!(stats.min_timestamp, Some(1000));
assert_eq!(stats.max_timestamp, Some(3000));
}
#[test]
fn test_temporal_index_shared_timestamp_bucket() {
let index = TemporalIndex::new();
index.insert(1, 5000);
index.insert(2, 5000);
index.insert(3, 5000);
let stats = index.stats();
assert_eq!(stats.entry_count, 3, "three ids");
assert_eq!(stats.unique_timestamps, 1, "one shared bucket");
index.remove(2);
let after = index.stats();
assert_eq!(after.entry_count, 2);
assert_eq!(after.unique_timestamps, 1);
assert_eq!(index.get_timestamp(2), None);
index.remove(1);
index.remove(3);
let empty = index.stats();
assert_eq!(empty.entry_count, 0);
assert_eq!(empty.unique_timestamps, 0);
assert_eq!(empty.min_timestamp, None);
}
#[test]
fn test_temporal_index_reinsert_moves_bucket() {
let index = TemporalIndex::new();
index.insert(1, 1000);
index.insert(2, 1000);
index.insert(1, 9000);
assert_eq!(index.get_timestamp(1), Some(9000));
let at_1000 = index.range(1000, 1000);
assert_eq!(at_1000.len(), 1);
assert_eq!(at_1000[0].id, 2);
let at_9000 = index.range(9000, 9000);
assert_eq!(at_9000.len(), 1);
assert_eq!(at_9000[0].id, 1);
let stats = index.stats();
assert_eq!(stats.entry_count, 2, "still two distinct ids, no ghost");
assert_eq!(stats.unique_timestamps, 2);
}
#[test]
fn test_stats_lock_order_no_deadlock_under_contention() {
use std::sync::Arc;
use std::thread;
let index = Arc::new(TemporalIndex::new());
for id in 0i64..50 {
index.insert(u64::try_from(id).unwrap(), id);
}
let writer = {
let index = Arc::clone(&index);
thread::spawn(move || {
for round in 0i64..1000 {
let id = u64::try_from(round % 50).unwrap();
index.insert(id, round);
index.remove(id);
}
})
};
let reader = {
let index = Arc::clone(&index);
thread::spawn(move || {
let mut last = 0;
for _ in 0..1000 {
last = index.stats().entry_count;
}
last
})
};
writer.join().expect("writer thread panicked");
let _ = reader.join().expect("reader thread panicked");
}
}