use sxurl::*;
use sha2::{Sha256, Digest};
#[test]
fn test_hash_extraction_methods() {
let test_cases = vec![
("tld\x00rs", 28, 0x4817c7e), ("domain\x00docs", 60, 0x16ca8efb818406f),
("sub\x00", 32, 0xa2921b44),
("path\x00/", 52, 0x35884d71189f9), ("params\x00", 32, 0x1c6130c3), ("frag\x00", 20, 0x676a0), ];
for (input, bits, expected) in test_cases {
let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
let hash = hasher.finalize();
let result = extract_lower_bits(&hash, bits).unwrap();
assert_eq!(result, expected, "Hash extraction failed for input: {:?}", input);
}
}
#[test]
fn test_bit_width_edge_cases() {
let test_hash = [0xFF; 32];
let test_cases = vec![
(1, 1),
(8, 255),
(16, 65535),
(24, 16777215),
(32, 4294967295),
(36, 68719476735),
(60, 1152921504606846975),
];
for (bits, expected_max) in test_cases {
let result = extract_lower_bits(&test_hash, bits).unwrap();
assert_eq!(result, expected_max, "Bit masking failed for {} bits", bits);
}
}
#[test]
fn test_hash_endianness() {
let test_hash = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
];
let result_16 = extract_lower_bits(&test_hash, 16).unwrap();
assert_eq!(result_16, 0x9988);
let result_32 = extract_lower_bits(&test_hash, 32).unwrap();
assert_eq!(result_32, 0xBBAA9988); }
#[test]
fn test_component_hasher_determinism() {
for _ in 0..10 {
assert_eq!(ComponentHasher::hash_tld("rs").unwrap(), 0x4817c7e);
assert_eq!(ComponentHasher::hash_domain("docs").unwrap(), 0x16ca8efb818406f);
assert_eq!(ComponentHasher::hash_subdomain("").unwrap(), 0x989781e3);
}
}
#[test]
fn test_manual_hash_calculation() {
let mut hasher = Sha256::new();
hasher.update(b"tld");
hasher.update(&[0x00]);
hasher.update(b"rs");
let hash = hasher.finalize();
let expected_hash = "a015df3b1d0e2e1091a8f9a4d87025c8e1874e0660b2030d7e7c81c4bd512397";
assert_eq!(hex::encode(&hash), expected_hash);
let lower_28 = extract_lower_bits(&hash, 28).unwrap();
assert_eq!(lower_28, 0x4817c7e);
}
#[test]
fn test_all_spec_hash_values() {
struct TestCase {
label: &'static str,
data: &'static str,
bits: usize,
expected: u64,
}
let test_cases = [
TestCase { label: "tld", data: "rs", bits: 28, expected: 0x4817c7e },
TestCase { label: "domain", data: "docs", bits: 60, expected: 0x16ca8efb818406f },
TestCase { label: "subdomain", data: "", bits: 32, expected: 0x989781e3 },
TestCase { label: "path", data: "/", bits: 52, expected: 0x35884d71189f9 },
TestCase { label: "query", data: "", bits: 32, expected: 0x6b4de8c2 },
TestCase { label: "fragment", data: "", bits: 20, expected: 0x22060 },
];
for test_case in &test_cases {
let result = hash_component(test_case.label, test_case.data.as_bytes(), test_case.bits).unwrap();
assert_eq!(
result,
test_case.expected,
"H{}('{}', '{}') should be 0x{:x}, got 0x{:x}",
test_case.bits,
test_case.label,
test_case.data,
test_case.expected,
result
);
}
}
#[test]
fn test_empty_string_hashes() {
let empty_sub = ComponentHasher::hash_subdomain("").unwrap();
let empty_params = ComponentHasher::hash_params("").unwrap();
let empty_frag = ComponentHasher::hash_fragment("").unwrap();
assert_eq!(empty_sub, 0x989781e3);
assert_eq!(empty_params, 0x6b4de8c2); assert_eq!(empty_frag, 0x22060);
assert_ne!(empty_sub, empty_params);
assert_ne!(empty_params, empty_frag);
assert_ne!(empty_sub, empty_frag);
}
#[test]
fn test_hash_collision_resistance() {
let test_cases = vec![
("com", "co"),
("example", "examples"),
("api", "api2"),
("/", "/index"),
("q=test", "q=tests"),
];
for (input1, input2) in test_cases {
let hash1 = hash_component("test", input1.as_bytes(), 32).unwrap();
let hash2 = hash_component("test", input2.as_bytes(), 32).unwrap();
assert_ne!(hash1, hash2, "Similar inputs '{}' and '{}' should produce different hashes", input1, input2);
}
}
#[test]
fn test_bit_width_error_handling() {
let test_hash = [0xFF; 32];
let result = extract_lower_bits(&test_hash, 65); assert!(result.is_err());
let result = extract_lower_bits(&test_hash, 0); assert!(result.is_ok());
assert_eq!(result.unwrap(), 0);
}