use core::cmp::Ordering;
use aok::{OK, Void};
use log::info;
use whasher::HashMap;
use wrecord::{
BfTag, Error, KeyTag, MEMBER_KEY_HEADER_SIZE, RecordRef, SCORE_KEY_HEADER_SIZE, ZMemberKeyRef,
ZSET_SUBKEY_STACK_CAP, ZScoreKeyRef, ZSetSubKeyBuf, ZSetSubKeyCodec, decode_order_preserving_f64,
encode_order_preserving_f64, try_encode_to_vec,
};
#[ctor::ctor(unsafe)]
fn _log_init() {
log_init::init();
}
#[test]
fn test_key_tag_zset_and_subkeys() -> Void {
info!("测试 KeyTag 针对 HASH_FIELD、SET_MEMBER、Z_MEMBER、Z_SCORE 的单字节映射与匹配");
assert_eq!(KeyTag::HASH_FIELD.as_u8(), 0x02);
assert_eq!(u8::from(KeyTag::HASH_FIELD), 0x02);
assert_eq!(KeyTag::HASH_FIELD as u8, 0x02);
assert_eq!(KeyTag::from_u8(0x02), Some(KeyTag::HASH_FIELD));
assert_eq!(KeyTag::try_from(0x02)?, KeyTag::HASH_FIELD);
assert_eq!(KeyTag::SET_MEMBER.as_u8(), 0x03);
assert_eq!(u8::from(KeyTag::SET_MEMBER), 0x03);
assert_eq!(KeyTag::SET_MEMBER as u8, 0x03);
assert_eq!(KeyTag::from_u8(0x03), Some(KeyTag::SET_MEMBER));
assert_eq!(KeyTag::try_from(0x03)?, KeyTag::SET_MEMBER);
assert_eq!(KeyTag::Z_MEMBER.as_u8(), 0x04);
assert_eq!(u8::from(KeyTag::Z_MEMBER), 0x04);
assert_eq!(KeyTag::Z_MEMBER as u8, 0x04);
assert_eq!(KeyTag::from_u8(0x04), Some(KeyTag::Z_MEMBER));
assert_eq!(KeyTag::try_from(0x04)?, KeyTag::Z_MEMBER);
assert_eq!(KeyTag::Z_SCORE.as_u8(), 0x05);
assert_eq!(u8::from(KeyTag::Z_SCORE), 0x05);
assert_eq!(KeyTag::Z_SCORE as u8, 0x05);
assert_eq!(KeyTag::from_u8(0x05), Some(KeyTag::Z_SCORE));
assert_eq!(KeyTag::try_from(0x05)?, KeyTag::Z_SCORE);
assert_eq!(KeyTag::HASH_FIELD, KeyTag::Hash);
assert_eq!(KeyTag::SET_MEMBER, KeyTag::Set);
assert_eq!(KeyTag::Z_MEMBER, KeyTag::ZSetChunk);
assert_eq!(KeyTag::Z_SCORE, KeyTag::ZSetM2s);
OK
}
#[test]
fn test_order_preserving_f64_codec() -> Void {
info!("测试浮点数保序编码与无损往返还原");
let test_floats = [
f64::NEG_INFINITY,
-1e300,
-1e100,
-100.5,
-42.0,
-1.0,
-0.1,
-1e-300,
-0.0,
0.0,
1e-300,
0.1,
1.0,
42.0,
100.5,
1e100,
1e300,
f64::INFINITY,
];
for i in 0..test_floats.len() - 1 {
let a = test_floats[i];
let b = test_floats[i + 1];
let enc_a = encode_order_preserving_f64(a);
let enc_b = encode_order_preserving_f64(b);
assert!(
enc_a < enc_b,
"保序失败: a={a} enc={enc_a:?} 应当小于 b={b} enc={enc_b:?}"
);
}
for &v in &test_floats {
let enc = encode_order_preserving_f64(v);
let dec = decode_order_preserving_f64(enc);
assert_eq!(
dec.to_bits(),
v.to_bits(),
"解码还原位不匹配: 原始 {v} (bits: {:#x}) -> 解码 {dec} (bits: {:#x})",
v.to_bits(),
dec.to_bits()
);
}
let mut current = -10.0;
let mut prev_enc = encode_order_preserving_f64(current);
for _ in 0..1000 {
current += 0.02;
let enc = encode_order_preserving_f64(current);
assert!(prev_enc < enc);
prev_enc = enc;
}
OK
}
#[test]
fn test_zset_member_key_codec() -> Void {
info!("测试 ZSetSubKeyCodec 成员子键编解码 (0)");
assert_eq!(MEMBER_KEY_HEADER_SIZE, 17);
let key_id = 0x0123_4567_89ab_cdef_u64;
let version = 0x0011_2233_4455_6677_u64;
let member = b"user:profile:10086";
let encoded = ZSetSubKeyCodec::encode_member_key(key_id, version, member)?;
assert_eq!(encoded.len(), MEMBER_KEY_HEADER_SIZE + member.len());
assert_eq!(encoded[0], BfTag::ZMember.as_u8());
assert_eq!(encoded[0], 0);
assert_eq!(&encoded[1..9], &key_id.to_be_bytes());
assert_eq!(&encoded[9..17], &version.to_be_bytes());
assert_eq!(&encoded[17..], member);
let key_ref = ZMemberKeyRef::from_slice(&encoded)?;
assert_eq!(key_ref.key_id, key_id);
let key_ref2 = ZSetSubKeyCodec::decode_member_key(&encoded)?;
assert_eq!(key_ref2.key_id, key_id);
assert_eq!(key_ref.version, version);
assert_eq!(key_ref.member, member);
assert_eq!(key_ref.encoded_len(), encoded.len());
assert_eq!(
key_ref.header(),
ZSetSubKeyCodec::encode_member_header(key_id, version)
);
let mut dst = vec![0u8; encoded.len()];
let written = ZSetSubKeyCodec::encode_member_key_to_slice(key_id, version, member, &mut dst)?;
assert_eq!(written, encoded.len());
assert_eq!(dst, encoded);
OK
}
#[test]
fn test_zset_score_key_codec() -> Void {
info!("测试 ZSetSubKeyCodec 分值子键编解码 (1)");
assert_eq!(SCORE_KEY_HEADER_SIZE, 25);
let key_id = 0x0123_4567_89ab_cdef_u64;
let version = 0x0011_2233_4455_6677_u64;
let score = -123.456_f64;
let member = b"leaderboard:top_1";
let encoded = ZSetSubKeyCodec::encode_score_key(key_id, version, score, member)?;
assert_eq!(encoded.len(), SCORE_KEY_HEADER_SIZE + member.len());
assert_eq!(encoded[0], BfTag::ZScore.as_u8());
assert_eq!(encoded[0], 1);
assert_eq!(&encoded[1..9], &key_id.to_be_bytes());
assert_eq!(&encoded[9..17], &version.to_be_bytes());
assert_eq!(&encoded[17..25], &encode_order_preserving_f64(score));
assert_eq!(&encoded[25..], member);
let score_ref = ZScoreKeyRef::from_slice(&encoded)?;
let (h_id, h_ver, h_score) = ZSetSubKeyCodec::decode_score_header(&encoded)?;
assert_eq!((h_id, h_ver), (key_id, version));
assert_eq!(h_score.to_bits(), score.to_bits());
assert_eq!(score_ref.key_id, key_id);
assert_eq!(score_ref.version, version);
assert_eq!(score_ref.score.to_bits(), score.to_bits());
assert_eq!(score_ref.raw_score, encode_order_preserving_f64(score));
assert_eq!(score_ref.member, member);
assert_eq!(score_ref.encoded_len(), encoded.len());
assert_eq!(
score_ref.header(),
ZSetSubKeyCodec::encode_score_header(key_id, version, score)
);
let mut dst = vec![0u8; encoded.len()];
let written =
ZSetSubKeyCodec::encode_score_key_to_slice(key_id, version, score, member, &mut dst)?;
assert_eq!(written, encoded.len());
assert_eq!(dst, encoded);
let score1 = -500.0;
let score2 = 0.0;
let score3 = 100.5;
let k1 = ZSetSubKeyCodec::encode_score_key(key_id, version, score1, member)?;
let k2 = ZSetSubKeyCodec::encode_score_key(key_id, version, score2, member)?;
let k3 = ZSetSubKeyCodec::encode_score_key(key_id, version, score3, member)?;
assert!(k1 < k2);
assert!(k2 < k3);
let k_a = ZSetSubKeyCodec::encode_score_key(key_id, version, 100.0, b"member_a")?;
let k_b = ZSetSubKeyCodec::encode_score_key(key_id, version, 100.0, b"member_b")?;
assert!(k_a < k_b);
OK
}
#[test]
fn test_zset_codec_boundary_and_defense() -> Void {
info!("测试 ZSetSubKeyCodec 严格边界安全防御(空、短、巨型载荷不越界 panic)");
let key_id = 42_u64;
let version = 1_u64;
let member_key = ZSetSubKeyCodec::encode_member_key(key_id, version, b"")?;
let score_key = ZSetSubKeyCodec::encode_score_key(key_id, version, 1.0, b"")?;
assert_eq!(member_key.len(), MEMBER_KEY_HEADER_SIZE);
assert_eq!(score_key.len(), SCORE_KEY_HEADER_SIZE);
assert_eq!(ZSetSubKeyCodec::decode_member_key(&member_key)?.member, b"");
assert_eq!(ZSetSubKeyCodec::decode_score_key(&score_key)?.member, b"");
let mut buf = [0u8; 32];
assert_eq!(
ZSetSubKeyCodec::encode_member_key_to_slice(key_id, version, b"", &mut buf)?,
MEMBER_KEY_HEADER_SIZE
);
assert_eq!(
ZSetSubKeyCodec::encode_score_key_to_slice(key_id, version, 1.0, b"", &mut buf)?,
SCORE_KEY_HEADER_SIZE
);
for len in 0..MEMBER_KEY_HEADER_SIZE {
let short_slice = vec![0u8; len];
assert!(matches!(
ZSetSubKeyCodec::decode_member_key(&short_slice),
Err(Error::BufferTooShort {
expected: MEMBER_KEY_HEADER_SIZE,
actual
}) if actual == len
));
}
for len in 0..SCORE_KEY_HEADER_SIZE {
let short_slice = vec![0u8; len];
assert!(matches!(
ZSetSubKeyCodec::decode_score_key(&short_slice),
Err(Error::BufferTooShort {
expected: SCORE_KEY_HEADER_SIZE,
actual
}) if actual == len
));
}
let mut tiny_buf = [0u8; 10];
assert!(matches!(
ZSetSubKeyCodec::encode_member_key_to_slice(key_id, version, b"alice", &mut tiny_buf),
Err(Error::BufferTooShort { .. })
));
assert!(matches!(
ZSetSubKeyCodec::encode_score_key_to_slice(key_id, version, 1.0, b"alice", &mut tiny_buf),
Err(Error::BufferTooShort { .. })
));
let mut corrupt_member_key = ZSetSubKeyCodec::encode_member_key(key_id, version, b"hello")?;
corrupt_member_key[0] = 0x99;
assert!(matches!(
ZSetSubKeyCodec::decode_member_key(&corrupt_member_key),
Err(Error::InvalidKeyTag(0x99))
));
let mut corrupt_score_key = ZSetSubKeyCodec::encode_score_key(key_id, version, 1.0, b"hello")?;
corrupt_score_key[0] = 0x88;
assert!(matches!(
ZSetSubKeyCodec::decode_score_key(&corrupt_score_key),
Err(Error::InvalidKeyTag(0x88))
));
let huge_member = vec![b'z'; 1024 * 1024];
let huge_encoded = ZSetSubKeyCodec::encode_member_key(key_id, version, &huge_member)?;
assert_eq!(
huge_encoded.len(),
MEMBER_KEY_HEADER_SIZE + huge_member.len()
);
let huge_ref = ZSetSubKeyCodec::decode_member_key(&huge_encoded)?;
assert_eq!(huge_ref.member.len(), huge_member.len());
assert_eq!(huge_ref.member, huge_member.as_slice());
let huge_score_encoded =
ZSetSubKeyCodec::encode_score_key(key_id, version, 999.99, &huge_member)?;
let huge_score_ref = ZSetSubKeyCodec::decode_score_key(&huge_score_encoded)?;
assert_eq!(huge_score_ref.member.len(), huge_member.len());
assert_eq!(huge_score_ref.member, huge_member.as_slice());
OK
}
#[test]
fn test_zset_sub_key_buf() -> Void {
info!("测试 ZSetSubKeyBuf 栈分配与堆自动回退");
assert_eq!(ZSET_SUBKEY_STACK_CAP, 128);
let key_id = 8888_u64;
let version = 2_u64;
let short_member = b"user_1001";
let member_buf = ZSetSubKeyCodec::encode_member_key_buf(key_id, version, short_member)?;
assert!(member_buf.is_stack());
assert!(!member_buf.is_heap());
assert_eq!(
member_buf.as_slice(),
ZSetSubKeyCodec::encode_member_key(key_id, version, short_member)?.as_slice()
);
let score_buf = ZSetSubKeyCodec::encode_score_key_buf(key_id, version, 88.8, short_member)?;
assert!(score_buf.is_stack());
assert!(!score_buf.is_heap());
assert_eq!(
score_buf.as_slice(),
ZSetSubKeyCodec::encode_score_key(key_id, version, 88.8, short_member)?.as_slice()
);
let exact_stack_member = vec![b'k'; 128 - MEMBER_KEY_HEADER_SIZE];
let exact_buf = ZSetSubKeyCodec::encode_member_key_buf(key_id, version, &exact_stack_member)?;
assert!(exact_buf.is_stack());
assert_eq!(exact_buf.len(), 128);
let overflow_member = vec![b'k'; 128 - MEMBER_KEY_HEADER_SIZE + 1];
let heap_buf = ZSetSubKeyCodec::encode_member_key_buf(key_id, version, &overflow_member)?;
assert!(heap_buf.is_heap());
assert!(!heap_buf.is_stack());
assert_eq!(heap_buf.len(), 129);
assert_eq!(
heap_buf.as_slice(),
ZSetSubKeyCodec::encode_member_key(key_id, version, &overflow_member)?.as_slice()
);
let buf1 = ZSetSubKeyBuf::from_member(key_id, version, short_member)?;
assert!(buf1.is_stack());
assert_eq!(buf1.as_slice(), member_buf.as_slice());
let buf2 = ZSetSubKeyBuf::from_score(key_id, version, 88.8, short_member)?;
assert!(buf2.is_stack());
assert_eq!(buf2.as_slice(), score_buf.as_slice());
assert!(ZSetSubKeyBuf::from_member(key_id, version, b"")?.is_stack());
assert!(ZSetSubKeyBuf::from_score(key_id, version, 1.0, b"")?.is_stack());
OK
}
#[test]
fn test_zset_sub_key_buf_borrow_and_ord_contract() -> Void {
info!("测试 ZSetSubKeyBuf 严格遵循标准库 Borrow、Eq、Ord、Hash 契约与跨存储形式等价性");
let key_id = 999_u64;
let version = 1_u64;
let member = b"user:session:token";
let stack_buf = ZSetSubKeyBuf::from_member(key_id, version, member)?;
assert!(stack_buf.is_stack());
let heap_buf = ZSetSubKeyBuf::Heap(stack_buf.as_slice().to_vec());
assert!(heap_buf.is_heap());
assert_eq!(stack_buf, heap_buf);
assert_eq!(heap_buf, stack_buf);
assert_eq!(stack_buf.cmp(&heap_buf), Ordering::Equal);
assert_eq!(stack_buf, stack_buf.as_slice());
assert_eq!(stack_buf.as_slice(), stack_buf.as_slice());
let mut map: HashMap<ZSetSubKeyBuf, u32> = HashMap::default();
map.insert(stack_buf.clone(), 12345);
let found = map.get(stack_buf.as_slice());
assert_eq!(found, Some(&12345));
let found_heap = map.get(&heap_buf);
assert_eq!(found_heap, Some(&12345));
let from_slice = ZSetSubKeyBuf::from(stack_buf.as_slice());
assert!(from_slice.is_stack());
assert_eq!(from_slice, stack_buf);
let huge = vec![b'a'; 200];
let from_huge = ZSetSubKeyBuf::from(huge.as_slice());
assert!(from_huge.is_heap());
assert_eq!(from_huge.len(), 200);
let moved_vec = heap_buf.into_vec();
assert_eq!(moved_vec, stack_buf.as_slice());
OK
}
#[test]
fn test_zscore_key_ref_lexicographical_ordering() -> Void {
info!("测试 ZScoreKeyRef 的 Ord 全序与底层存储物理字节字典序 100% 同构");
let key_id = 77_u64;
let version = 3_u64;
let test_cases = [
(f64::NEG_INFINITY, b"a".as_slice()),
(-1000.0, b"alpha".as_slice()),
(-1000.0, b"beta".as_slice()),
(-0.5, b"x".as_slice()),
(-0.0, b"zero".as_slice()),
(0.0, b"zero".as_slice()),
(0.0, b"zero_2".as_slice()),
(1e-100, b"tiny".as_slice()),
(42.0, b"ans".as_slice()),
(100.0, b"a".as_slice()),
(100.0, b"b".as_slice()),
(f64::INFINITY, b"inf".as_slice()),
];
let mut key_refs: Vec<ZScoreKeyRef> = test_cases
.iter()
.map(|&(score, member)| ZScoreKeyRef::new(key_id, version, score, member))
.collect();
key_refs.sort();
let encoded_bytes: Vec<Vec<u8>> = key_refs.iter().map(|r| r.to_vec()).collect();
for i in 0..encoded_bytes.len() - 1 {
assert!(
encoded_bytes[i] < encoded_bytes[i + 1],
"排序同构性校验失败: [{i}] 应小于 [{}]",
i + 1
);
}
for r in &key_refs {
let recreated = ZScoreKeyRef::from_raw(r.key_id, r.version, r.raw_score, r.member);
assert_eq!(*r, recreated);
}
OK
}
#[test]
fn test_record_ref_zset_helpers() -> Void {
info!("测试 RecordRef 对 ZMember 和 ZScore 子键的直接解析能力");
let key_id = 1234_u64;
let version = 5678_u64;
let member = b"item_99";
let score = 98.765_f64;
let member_key = ZSetSubKeyCodec::encode_member_key(key_id, version, member)?;
let member_record = try_encode_to_vec(0, &member_key, b"value_payload", false)?;
let rec_ref = RecordRef::from_slice(&member_record)?;
assert_eq!(rec_ref.bftag(), Some(BfTag::ZMember));
let parsed_member: ZMemberKeyRef = rec_ref.zmember_key_ref()?;
assert_eq!(parsed_member.key_id, key_id);
assert_eq!(parsed_member.version, version);
assert_eq!(parsed_member.member, member);
let score_key = ZSetSubKeyCodec::encode_score_key(key_id, version, score, member)?;
let score_record = try_encode_to_vec(0, &score_key, b"", false)?;
let score_rec_ref = RecordRef::from_slice(&score_record)?;
assert_eq!(score_rec_ref.bftag(), Some(BfTag::ZScore));
let parsed_score: ZScoreKeyRef = score_rec_ref.zscore_key_ref()?;
assert_eq!(parsed_score.key_id, key_id);
assert_eq!(parsed_score.version, version);
assert_eq!(parsed_score.score.to_bits(), score.to_bits());
assert_eq!(parsed_score.member, member);
let mut invalid_key = member_key.clone();
invalid_key[0] = 0x99;
let invalid_record = try_encode_to_vec(0, &invalid_key, b"", false)?;
let invalid_rec_ref = RecordRef::from_slice(&invalid_record)?;
assert!(matches!(
invalid_rec_ref.zmember_key_ref(),
Err(Error::InvalidKeyTag(0x99))
));
OK
}
#[test]
fn test_extreme_floats_and_denormals() -> Void {
info!("测试极值浮点数、极小非正规数 (Subnormal/Denormal) 与 NaN 的保序与位还原");
let extremes = [
f64::NAN,
f64::NEG_INFINITY,
f64::MIN,
-1.0e-323,
-5.0e-324, -0.0,
0.0,
5.0e-324, 1.0e-323,
f64::MIN_POSITIVE, f64::MAX,
f64::INFINITY,
];
for &val in &extremes {
let enc = encode_order_preserving_f64(val);
let dec = decode_order_preserving_f64(enc);
assert_eq!(
dec.to_bits(),
val.to_bits(),
"极值还原位不匹配: {val:?} bits={:#x} -> {dec:?} bits={:#x}",
val.to_bits(),
dec.to_bits()
);
}
let non_nan_extremes = [
f64::NEG_INFINITY,
f64::MIN,
-1.0e-323,
-5.0e-324,
-0.0,
0.0,
5.0e-324,
1.0e-323,
f64::MIN_POSITIVE,
f64::MAX,
f64::INFINITY,
];
for i in 0..non_nan_extremes.len() - 1 {
let a = non_nan_extremes[i];
let b = non_nan_extremes[i + 1];
let enc_a = encode_order_preserving_f64(a);
let enc_b = encode_order_preserving_f64(b);
assert!(enc_a < enc_b, "极端数值序校验失败: {a:?} 应小于 {b:?}");
}
OK
}