#![expect(
clippy::expect_used,
clippy::unwrap_used,
clippy::indexing_slicing,
reason = "test assertions over known-good fixtures; failure surfaces via panic"
)]
use super::*;
fn key_hash(i: u64) -> u64 {
crate::hash::hash64(&i.to_le_bytes())
}
#[test]
fn build_returns_none_for_empty_input() {
let spec = LocatorSpec {
precision: LocatorPrecision::Restart,
block_id_bits: None,
slot_bits: None,
};
assert!(build_locator_section(&[], spec).is_none());
}
#[test]
fn auto_width_section_round_trips_block_id_and_slot() {
let spec = LocatorSpec {
precision: LocatorPrecision::Restart,
block_id_bits: None,
slot_bits: None,
};
let entries: Vec<(u64, u64, u64)> =
(0..300u64).map(|i| (key_hash(i), i % 12, i % 25)).collect();
let bytes = build_locator_section(&entries, spec).expect("section built");
assert_eq!(bytes[2], 4);
assert_eq!(bytes[3], 5);
for i in 0..300u64 {
assert_eq!(
locate(&bytes, key_hash(i)).unwrap(),
Some((i % 12, i % 25)),
"key {i} locator mismatch",
);
}
}
#[test]
fn explicit_widths_too_small_skips_gracefully() {
let spec = LocatorSpec {
precision: LocatorPrecision::Restart,
block_id_bits: Some(4),
slot_bits: Some(4),
};
let entries: Vec<(u64, u64, u64)> =
(0..200u64).map(|i| (key_hash(i), i % 101, i % 8)).collect();
assert!(build_locator_section(&entries, spec).is_none());
}
#[test]
fn explicit_widths_that_fit_round_trip() {
let spec = LocatorSpec {
precision: LocatorPrecision::Entry,
block_id_bits: Some(10),
slot_bits: Some(12),
};
let entries: Vec<(u64, u64, u64)> = (0..500u64)
.map(|i| (key_hash(i), i % 1000, i % 4000))
.collect();
let bytes = build_locator_section(&entries, spec).expect("section built");
assert_eq!(bytes[2], 10);
assert_eq!(bytes[3], 12);
for i in 0..500u64 {
assert_eq!(
locate(&bytes, key_hash(i)).unwrap(),
Some((i % 1000, i % 4000))
);
}
}
#[test]
fn block_precision_section_round_trips_block_id_only() {
let spec = LocatorSpec {
precision: LocatorPrecision::Block,
block_id_bits: None,
slot_bits: None,
};
let entries: Vec<(u64, u64, u64)> =
(0..300u64).map(|i| (key_hash(i), i % 12, i % 25)).collect();
let bytes = build_locator_section(&entries, spec).expect("section built");
assert_eq!(bytes[3], 0, "block precision must record slot_bits = 0");
for i in 0..300u64 {
assert_eq!(
locate(&bytes, key_hash(i)).unwrap(),
Some((i % 12, 0)),
"key {i} must resolve to its block with slot 0",
);
}
}
#[test]
fn locate_rejects_truncated_section() {
let err = locate(&[0u8; SECTION_HEADER_LEN - 1], 123).unwrap_err();
assert!(matches!(err, crate::Error::InvalidHeader("LocatorSection")));
}
#[test]
fn build_skips_when_ribbon_cannot_satisfy_conflicting_values() {
let spec = LocatorSpec {
precision: LocatorPrecision::Restart,
block_id_bits: None,
slot_bits: None,
};
let mut entries: Vec<(u64, u64, u64)> =
(0..200u64).map(|i| (key_hash(i), i % 8, i % 4)).collect();
entries.push((key_hash(0), 7, 3));
assert!(
build_locator_section(&entries, spec).is_none(),
"a conflicting hash collision must skip the section, not panic or abort",
);
}
#[test]
fn locate_does_not_panic_on_forged_slot_bits_64() {
let spec = LocatorSpec {
precision: LocatorPrecision::Restart,
block_id_bits: None,
slot_bits: None,
};
let entries: Vec<(u64, u64, u64)> = (0..100u64).map(|i| (key_hash(i), i % 8, i % 4)).collect();
let mut bytes = build_locator_section(&entries, spec).expect("section built");
bytes[3] = 64; for i in 0..100u64 {
let _ = locate(&bytes, key_hash(i)).expect("locate must not error");
}
}
#[test]
fn locate_rejects_unknown_version() {
let mut section = [0u8; SECTION_HEADER_LEN];
section[0] = SECTION_VERSION.wrapping_add(1);
let err = locate(§ion, 123).unwrap_err();
assert!(matches!(
err,
crate::Error::InvalidHeader("LocatorSection version")
));
}