#[cfg(test)]
mod tests {
use crate::tests::test_utils::{assert_ids_monotonic, assert_unique_ids};
use crate::*;
use std::collections::HashSet;
use std::sync::{Arc, Barrier};
use std::thread;
#[test]
fn test_min_node_bits_config() {
let cfg = SnowIDConfig::builder().node_bits(6).unwrap().build();
assert_eq!(cfg.max_node_id(), 63);
assert_eq!(cfg.max_sequence_id(), 65535);
let g = SnowID::with_config(63, cfg).unwrap();
let id = g.generate();
let (ts, node, seq) = g.extract.decompose(id);
assert!(ts > 0);
assert_eq!(node, 63);
assert!(seq <= cfg.max_sequence_id());
}
#[test]
fn test_max_node_bits_config() {
let cfg = SnowIDConfig::builder().node_bits(16).unwrap().build();
assert_eq!(cfg.max_node_id(), 65535);
assert_eq!(cfg.max_sequence_id(), 63);
let g = SnowID::with_config(65535, cfg).unwrap();
let id = g.generate();
let (ts, node, seq) = g.extract.decompose(id);
assert!(ts > 0);
assert_eq!(node, 65535);
assert!(seq <= 63);
}
#[test]
fn test_id_bit_structure() {
let cfg = SnowIDConfig::builder().node_bits(10).unwrap().epoch(0).build();
let g = SnowID::with_config(0b1010101010, cfg).unwrap();
let id = g.generate();
let node = g.extract.node(id);
assert_eq!(node, 0b1010101010, "Node bits should be preserved");
let seq_mask = 0xFFF; let node_mask = 0x3FF << 12; let ts_mask = !0u64 << 22;
assert_eq!(id & seq_mask, g.extract.sequence(id) as u64);
assert_eq!((id & node_mask) >> 12, g.extract.node(id) as u64);
assert_eq!((id & ts_mask) >> 22, g.extract.timestamp(id));
}
#[test]
fn test_cross_node_uniqueness() {
let mut all_ids = HashSet::new();
for node in 0..10 {
let g = SnowID::new(node).unwrap();
for _ in 0..100 {
let id = g.generate();
assert!(all_ids.insert(id), "Collision from node {}", node);
}
}
assert_eq!(all_ids.len(), 1000);
}
#[test]
fn test_numeric_sorting() {
let g = SnowID::new(1).unwrap();
let ids: Vec<u64> = (0..100).map(|_| g.generate()).collect();
let mut sorted = ids.clone();
sorted.sort();
assert_eq!(ids, sorted, "IDs should already be numerically sorted");
}
#[test]
fn test_high_contention() {
let g = Arc::new(SnowID::new(1).unwrap());
let barrier = Arc::new(Barrier::new(4));
let mut handles = vec![];
for _ in 0..4 {
let g = Arc::clone(&g);
let b = Arc::clone(&barrier);
handles.push(thread::spawn(move || {
b.wait(); (0..250).map(|_| g.generate()).collect::<Vec<_>>()
}));
}
let mut all_ids = vec![];
for h in handles {
all_ids.extend(h.join().unwrap());
}
assert_unique_ids(&all_ids, 1000);
}
#[test]
fn test_sequence_exhaustion_advances_timestamp() {
let cfg = SnowIDConfig::builder().node_bits(16).unwrap().build();
let g = SnowID::with_config(0, cfg).unwrap();
let first_id = g.generate();
let first_ts = g.extract.timestamp(first_id);
let mut ids = vec![first_id];
for _ in 0..63 {
ids.push(g.generate());
}
let next_id = g.generate();
let next_ts = g.extract.timestamp(next_id);
assert!(next_ts >= first_ts, "Timestamp should not regress");
assert_ids_monotonic(&ids);
}
#[test]
fn test_recent_epoch() {
let recent_epoch = 1735689600000u64; let cfg = SnowIDConfig::builder().epoch(recent_epoch).build();
let g = SnowID::with_config(1, cfg).unwrap();
let id = g.generate();
let ts = g.extract.timestamp(id);
assert!(ts < 2 * 365 * 24 * 60 * 60 * 1000, "Expected ts < 2 years, got {}", ts);
}
#[test]
fn test_all_node_bits_configs() {
for bits in 6..=16 {
let cfg = SnowIDConfig::builder().node_bits(bits).unwrap().build();
let max_node = cfg.max_node_id();
let max_seq = cfg.max_sequence_id();
assert_eq!(bits + cfg.sequence_bits(), 22);
assert_eq!(max_node as u32, (1u32 << bits) - 1);
assert_eq!(max_seq as u32, (1u32 << (22 - bits)) - 1);
let g = SnowID::with_config(max_node, cfg).unwrap();
let id = g.generate();
let (_, node, seq) = g.extract.decompose(id);
assert_eq!(node, max_node);
assert!(seq <= max_seq);
}
}
#[test]
fn test_base62_sort_order() {
let g = SnowID::new(1).unwrap();
let id1 = g.generate();
thread::sleep(std::time::Duration::from_millis(5));
let id2 = g.generate();
let (s1, _) = g.generate_base62_with_raw();
let (s2, _) = g.generate_base62_with_raw();
assert!(id2 > id1);
assert!(base62_decode(&s2).unwrap() > base62_decode(&s1).unwrap());
}
#[test]
fn test_decomposition_roundtrip() {
let g = SnowID::new(42).unwrap();
for _ in 0..100 {
let id = g.generate();
let (ts, node, seq) = g.extract.decompose(id);
let reconstructed = (ts << 22) | ((node as u64) << 12) | (seq as u64);
assert_eq!(id, reconstructed, "ID should round-trip through decomposition");
}
}
}