#[cfg(test)]
mod tests {
use super::super::key_digest::KeyDigestComputer;
use crate::schema::{registry::ParsingContext, KeyColumn, TableSchema};
use crate::types::ComparatorType;
use proptest::prelude::*;
use std::collections::HashMap;
fn create_parsing_context(partition_comparators: Vec<ComparatorType>) -> ParsingContext {
let mut partition_keys = Vec::new();
for (i, comp) in partition_comparators.iter().enumerate() {
partition_keys.push(KeyColumn {
name: format!("pk{}", i),
data_type: comp.type_name().to_string(),
position: i,
});
}
let schema = TableSchema {
keyspace: "test".to_string(),
table: "table".to_string(),
partition_keys,
clustering_keys: vec![],
columns: vec![],
comments: HashMap::new(),
};
ParsingContext {
schema,
partition_comparators,
clustering_comparators: vec![],
column_comparators: HashMap::new(),
}
}
#[test]
fn test_single_component_int_key_digest() {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![ComparatorType::Int]);
let test_cases = vec![
([0x00, 0x00, 0x00, 0x00], 0i32), ([0x00, 0x00, 0x00, 0x01], 1i32), ([0x00, 0x00, 0x00, 0x2A], 42i32), ([0xFF, 0xFF, 0xFF, 0xFF], -1i32), ([0x80, 0x00, 0x00, 0x00], i32::MIN), ([0x7F, 0xFF, 0xFF, 0xFF], i32::MAX), ];
let mut digests = Vec::new();
for (bytes, value) in test_cases {
let digest = computer
.compute_partition_key_digest(&bytes, &context)
.unwrap();
assert_eq!(
digest.len(),
4,
"Digest length should be 4 bytes for value {}",
value
);
let digest2 = computer
.compute_partition_key_digest(&bytes, &context)
.unwrap();
assert_eq!(
digest, digest2,
"Digest should be deterministic for value {}",
value
);
digests.push((value, digest));
}
let unique_digests: std::collections::HashSet<_> = digests.iter().map(|(_, d)| d).collect();
assert!(unique_digests.len() >= 5, "Most digests should be unique");
}
#[test]
fn test_single_component_text_key_digest() {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![ComparatorType::Text]);
let test_cases = vec![
"hello",
"world",
"test",
"a",
"",
"unicode_测试",
"very_long_text_string_that_should_still_work_correctly",
];
let mut digests = Vec::new();
for text in test_cases {
let bytes = text.as_bytes();
let digest = computer
.compute_partition_key_digest(bytes, &context)
.unwrap();
assert_eq!(
digest.len(),
4,
"Digest length should be 4 bytes for text '{}'",
text
);
let digest2 = computer
.compute_partition_key_digest(bytes, &context)
.unwrap();
assert_eq!(
digest, digest2,
"Digest should be deterministic for text '{}'",
text
);
digests.push((text, digest));
}
let unique_digests: std::collections::HashSet<_> = digests.iter().map(|(_, d)| d).collect();
assert!(
unique_digests.len() >= 6,
"Most text digests should be unique"
);
}
#[test]
fn test_single_component_bigint_key_digest() {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![ComparatorType::BigInt]);
let test_cases = vec![
0i64,
1i64,
42i64,
-1i64,
i64::MIN,
i64::MAX,
123456789012345i64,
];
for value in test_cases {
let bytes = value.to_be_bytes();
let digest = computer
.compute_partition_key_digest(&bytes, &context)
.unwrap();
assert_eq!(
digest.len(),
4,
"Digest length should be 4 bytes for bigint {}",
value
);
let digest2 = computer
.compute_partition_key_digest(&bytes, &context)
.unwrap();
assert_eq!(
digest, digest2,
"Digest should be deterministic for bigint {}",
value
);
}
}
#[test]
fn test_multi_component_key_digest() {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![ComparatorType::Int, ComparatorType::Text]);
let mut key_bytes = Vec::new();
key_bytes.extend_from_slice(&[0x00, 0x04]); key_bytes.extend_from_slice(&[0x00, 0x00, 0x00, 0x2A]); key_bytes.push(0x00);
key_bytes.extend_from_slice(&[0x00, 0x05]); key_bytes.extend_from_slice(b"hello"); key_bytes.push(0x00);
let digest = computer
.compute_partition_key_digest(&key_bytes, &context)
.unwrap();
assert_eq!(digest.len(), 4, "Multi-component digest should be 4 bytes");
let digest2 = computer
.compute_partition_key_digest(&key_bytes, &context)
.unwrap();
assert_eq!(
digest, digest2,
"Multi-component digest should be deterministic"
);
let context2 = create_parsing_context(vec![ComparatorType::Text, ComparatorType::Int]);
let mut key_bytes2 = Vec::new();
key_bytes2.extend_from_slice(&[0x00, 0x05]); key_bytes2.extend_from_slice(b"hello"); key_bytes2.push(0x00); key_bytes2.extend_from_slice(&[0x00, 0x04]); key_bytes2.extend_from_slice(&[0x00, 0x00, 0x00, 0x2A]); key_bytes2.push(0x00);
let digest_reversed = computer
.compute_partition_key_digest(&key_bytes2, &context2)
.unwrap();
assert_ne!(
digest, digest_reversed,
"Different component order should produce different digest"
);
}
#[test]
fn test_triple_component_key_digest() {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![
ComparatorType::Int,
ComparatorType::Text,
ComparatorType::BigInt,
]);
let mut key_bytes = Vec::new();
key_bytes.extend_from_slice(&[0x00, 0x04]); key_bytes.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]); key_bytes.push(0x00);
key_bytes.extend_from_slice(&[0x00, 0x01]); key_bytes.extend_from_slice(b"a"); key_bytes.push(0x00);
key_bytes.extend_from_slice(&[0x00, 0x08]); key_bytes.extend_from_slice(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64]); key_bytes.push(0x00);
let digest = computer
.compute_partition_key_digest(&key_bytes, &context)
.unwrap();
assert_eq!(digest.len(), 4, "Triple-component digest should be 4 bytes");
let digest2 = computer
.compute_partition_key_digest(&key_bytes, &context)
.unwrap();
assert_eq!(
digest, digest2,
"Triple-component digest should be deterministic"
);
}
#[test]
fn test_byte_ordering_vs_typed_ordering_equivalence() {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![ComparatorType::Int]);
let values = vec![1i32, 2i32, 10i32, 100i32, 1000i32];
let mut digests = Vec::new();
for value in values {
let bytes = value.to_be_bytes();
let digest = computer
.compute_partition_key_digest(&bytes, &context)
.unwrap();
digests.push((value, digest));
}
for i in 0..digests.len() {
for j in i + 1..digests.len() {
assert_ne!(
digests[i].1, digests[j].1,
"Values {} and {} should produce different digests",
digests[i].0, digests[j].0
);
}
}
}
#[test]
fn test_simple_digest_fallback() {
let computer = KeyDigestComputer::new();
let test_cases: Vec<&[u8]> = vec![
b"simple_key",
b"another_key",
b"",
b"unicode_\xc3\xa9\xc3\xa8\xc3\xa7",
&[0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE],
];
for key_bytes in test_cases {
let digest = computer.compute_simple_digest(key_bytes).unwrap();
assert_eq!(digest.len(), 4, "Simple digest should be 4 bytes");
let digest2 = computer.compute_simple_digest(key_bytes).unwrap();
assert_eq!(digest, digest2, "Simple digest should be deterministic");
}
}
#[test]
fn test_uuid_key_digest() {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![ComparatorType::Uuid]);
let uuid_bytes = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54,
0x32, 0x10,
];
let digest = computer
.compute_partition_key_digest(&uuid_bytes, &context)
.unwrap();
assert_eq!(digest.len(), 4, "UUID digest should be 4 bytes");
let digest2 = computer
.compute_partition_key_digest(&uuid_bytes, &context)
.unwrap();
assert_eq!(digest, digest2, "UUID digest should be deterministic");
}
#[test]
fn test_boolean_key_digest() {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![ComparatorType::Boolean]);
let true_bytes = [0x01];
let false_bytes = [0x00];
let true_digest = computer
.compute_partition_key_digest(&true_bytes, &context)
.unwrap();
let false_digest = computer
.compute_partition_key_digest(&false_bytes, &context)
.unwrap();
assert_eq!(true_digest.len(), 4, "Boolean digest should be 4 bytes");
assert_eq!(false_digest.len(), 4, "Boolean digest should be 4 bytes");
assert_ne!(
true_digest, false_digest,
"True and false should produce different digests"
);
}
#[test]
fn test_blob_key_digest() {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![ComparatorType::Blob]);
let test_blobs = vec![
vec![],
vec![0x00],
vec![0xFF],
vec![0x01, 0x02, 0x03, 0x04, 0x05],
vec![0xFF; 100], ];
for blob in test_blobs {
let digest = computer
.compute_partition_key_digest(&blob, &context)
.unwrap();
assert_eq!(digest.len(), 4, "Blob digest should be 4 bytes");
let digest2 = computer
.compute_partition_key_digest(&blob, &context)
.unwrap();
assert_eq!(digest, digest2, "Blob digest should be deterministic");
}
}
proptest! {
#[test]
fn prop_single_int_key_deterministic(value in any::<i32>()) {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![ComparatorType::Int]);
let bytes = value.to_be_bytes();
let digest1 = computer.compute_partition_key_digest(&bytes, &context).unwrap();
let digest2 = computer.compute_partition_key_digest(&bytes, &context).unwrap();
prop_assert_eq!(digest1.len(), 4);
prop_assert_eq!(digest1, digest2);
}
#[test]
fn prop_single_bigint_key_deterministic(value in any::<i64>()) {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![ComparatorType::BigInt]);
let bytes = value.to_be_bytes();
let digest1 = computer.compute_partition_key_digest(&bytes, &context).unwrap();
let digest2 = computer.compute_partition_key_digest(&bytes, &context).unwrap();
prop_assert_eq!(digest1.len(), 4);
prop_assert_eq!(digest1, digest2);
}
#[test]
fn prop_text_key_deterministic(text in ".*") {
let mut computer = KeyDigestComputer::new();
let context = create_parsing_context(vec![ComparatorType::Text]);
let bytes = text.as_bytes();
let digest1 = computer.compute_partition_key_digest(bytes, &context).unwrap();
let digest2 = computer.compute_partition_key_digest(bytes, &context).unwrap();
prop_assert_eq!(digest1.len(), 4);
prop_assert_eq!(digest1, digest2);
}
#[test]
fn prop_simple_digest_deterministic(bytes in prop::collection::vec(any::<u8>(), 0..1000)) {
let computer = KeyDigestComputer::new();
let digest1 = computer.compute_simple_digest(&bytes).unwrap();
let digest2 = computer.compute_simple_digest(&bytes).unwrap();
prop_assert_eq!(digest1.len(), 4);
prop_assert_eq!(digest1, digest2);
}
}
#[test]
fn test_murmur3_compatibility() {
let computer = KeyDigestComputer::new();
let empty_digest = computer.compute_simple_digest(b"").unwrap();
assert_eq!(empty_digest.len(), 4);
let test_inputs = vec![
b"test".as_slice(),
b"hello world".as_slice(),
&[0x00, 0x01, 0x02, 0x03],
];
for input in test_inputs {
let digest = computer.compute_simple_digest(input).unwrap();
assert_eq!(digest.len(), 4);
let digest2 = computer.compute_simple_digest(input).unwrap();
assert_eq!(digest, digest2);
}
}
}