use super::span_extent;
use crate::vlog::ValueHandle;
use test_log::test;
fn items(offsets: &[u64], len: usize) -> Vec<(&'static [u8], ValueHandle, usize)> {
offsets
.iter()
.map(|&offset| {
(
b"k".as_ref(),
ValueHandle {
blob_file_id: 1,
offset,
#[expect(clippy::cast_possible_truncation, reason = "test lengths are small")]
on_disk_size: len as u32,
},
len,
)
})
.collect()
}
#[test]
fn adjacent_records_merge_into_one_span() {
let items = items(&[0, 100, 200], 100);
let (end, span_end) = span_extent(&items, 0, 4096, 1_000_000, u64::MAX).expect("extent");
assert_eq!(end, 3, "all three records belong to the span");
assert_eq!(span_end, 300);
}
#[test]
fn a_gap_wider_than_the_limit_ends_the_span() {
let items = items(&[0, 100, 5_200], 100);
let (end, span_end) = span_extent(&items, 0, 4096, 1_000_000, u64::MAX).expect("extent");
assert_eq!(end, 2, "the far record starts a new span");
assert_eq!(span_end, 200);
}
#[test]
fn the_extent_bound_counts_gap_bytes_not_only_records() {
let offsets: Vec<u64> = (0..10).map(|i| i * 4_096).collect();
let items = items(&offsets, 100);
let (end, span_end) = span_extent(&items, 0, 4096, 10_240, u64::MAX).expect("extent");
assert!(
span_end <= 10_240,
"extent {span_end} must stay inside the 10 KiB bound",
);
assert!(
end < items.len(),
"the span must stop short of the full run ({end} of {})",
items.len(),
);
}
#[test]
fn a_span_never_exceeds_the_remaining_read_budget() {
let offsets: Vec<u64> = (0..10).map(|i| i * 4_096).collect();
let items = items(&offsets, 100);
let budget = 8_192;
let (end, span_end) = span_extent(&items, 0, 4096, u64::MAX, budget).expect("extent");
assert!(
span_end <= budget,
"extent {span_end} must stay inside the {budget}-byte budget left",
);
assert!(
end < items.len(),
"the span must stop short of the full run ({end} of {})",
items.len(),
);
}
#[test]
fn an_anchor_record_wider_than_the_bound_does_not_carry_a_span() {
let mut items = items(&[0], 1_048_576);
items.push((
b"k".as_ref(),
ValueHandle {
blob_file_id: 1,
offset: 100,
on_disk_size: 100,
},
100,
));
let (end, _) = span_extent(&items, 0, 4096, 4_096, u64::MAX).expect("extent");
assert_eq!(
end, 1,
"an anchor wider than the read cap must not gather a span",
);
}
#[test]
fn a_zero_extent_bound_yields_a_single_record_span() {
let items = items(&[0, 100, 200], 100);
let (end, span_end) = span_extent(&items, 0, 4096, 0, u64::MAX).expect("extent");
assert_eq!(end, 1, "nothing can be merged into a zero-byte budget");
assert_eq!(span_end, 100, "the anchor record's own end");
}
#[test]
fn a_record_end_past_u64_is_rejected() {
let items = items(&[u64::MAX - 10], 100);
assert!(
span_extent(&items, 0, 4096, 1_000_000, u64::MAX).is_none(),
"an unrepresentable record end must be refused",
);
}
#[test]
fn an_overflowing_later_record_ends_the_span() {
let mut items = items(&[0, 100], 100);
items.push((
b"k".as_ref(),
ValueHandle {
blob_file_id: 1,
offset: u64::MAX - 10,
on_disk_size: 100,
},
100,
));
let (end, span_end) = span_extent(&items, 0, 4096, u64::MAX, u64::MAX).expect("extent");
assert_eq!(end, 2);
assert_eq!(span_end, 200);
}
#[test]
fn a_span_stops_at_the_blob_file_boundary() {
let mut items = items(&[0, 100], 100);
items.push((
b"k".as_ref(),
ValueHandle {
blob_file_id: 2,
offset: 200,
on_disk_size: 100,
},
100,
));
let (end, span_end) = span_extent(&items, 0, 4096, 1_000_000, u64::MAX).expect("extent");
assert_eq!(end, 2, "the other file's record is not part of this span");
assert_eq!(span_end, 200);
}