use dig_nat::mux::{ChunkLensAssembler, ChunkLensError, RangeFrame};
use dig_nat::{MAX_CHUNK_LENS_PER_FRAME, MAX_RESOURCE_CHUNK_COUNT};
fn layout(chunk_count: usize) -> Vec<u64> {
(0..chunk_count)
.map(|i| 16_384 + (i as u64 % 977) * 241)
.collect()
}
async fn accept_over_the_wire(
assembler: &mut ChunkLensAssembler,
offset: u64,
page: &[u64],
chunk_count: u64,
total_length: u64,
) -> Result<(), ChunkLensError> {
let frame = RangeFrame::data(offset, vec![0xA5; dig_nat::MAX_RANGE_FRAME_PAYLOAD])
.with_identity("aa".repeat(32), total_length, chunk_count)
.with_chunk_lens_page(offset, page.to_vec());
let encoded = frame
.encode()
.expect("fixture frame must be encodable — an over-budget fixture is a broken fixture");
if page.len() == MAX_CHUNK_LENS_PER_FRAME {
assert!(
encoded.len() > 55_000 && encoded.len() <= dig_nat::MAX_FRAMED_BODY + 4,
"a full-page fixture frame must sit just inside MAX_FRAMED_BODY, not far below it; got {} B",
encoded.len()
);
}
let decoded = RangeFrame::decode(&mut encoded.as_slice())
.await
.expect("decode must not fail on a frame we just encoded")
.expect("a whole frame was written, so decode must yield one");
let page = decoded
.chunk_lens
.expect("the page survives the round trip");
let offset = decoded.chunk_lens_offset.expect("the offset survives too");
assembler.accept_page(offset, &page)
}
#[tokio::test]
async fn pages_of_a_multi_page_layout_reassemble_byte_identically() {
let chunk_lens = layout(5_000);
let total_length: u64 = chunk_lens.iter().sum();
let pages = RangeFrame::split_chunk_lens_pages(&chunk_lens);
assert_eq!(
pages.len(),
3,
"5,000 entries at {MAX_CHUNK_LENS_PER_FRAME} per page is three pages — the fixture must be \
genuinely above the paging threshold, not below it"
);
assert_eq!(
pages.iter().map(|(_, p)| p.len()).collect::<Vec<_>>(),
vec![2_048, 2_048, 904]
);
let mut assembler = ChunkLensAssembler::new(5_000).expect("5,000 is well inside the cap");
for (offset, page) in &pages {
assert!(
!assembler.is_complete(),
"not complete until every page lands"
);
accept_over_the_wire(&mut assembler, *offset, page, 5_000, total_length)
.await
.expect("an honest page is accepted");
}
assert!(assembler.is_complete());
assert_eq!(
assembler.into_chunk_lens().expect("complete, so it yields"),
chunk_lens,
"the reassembled array must be byte-identical to the one that was split"
);
}
#[tokio::test]
async fn a_single_unpaged_page_is_still_a_complete_prologue() {
let chunk_lens = layout(1_000);
let total_length: u64 = chunk_lens.iter().sum();
let pages = RangeFrame::split_chunk_lens_pages(&chunk_lens);
assert_eq!(pages.len(), 1, "1,000 entries fit one page");
let mut assembler = ChunkLensAssembler::new(1_000).unwrap();
accept_over_the_wire(&mut assembler, 0, &pages[0].1, 1_000, total_length)
.await
.unwrap();
assert_eq!(assembler.into_chunk_lens().unwrap(), chunk_lens);
}
#[tokio::test]
async fn a_duplicate_page_is_rejected_and_never_overwrites() {
let chunk_lens = layout(5_000);
let total_length: u64 = chunk_lens.iter().sum();
let pages = RangeFrame::split_chunk_lens_pages(&chunk_lens);
let mut assembler = ChunkLensAssembler::new(5_000).unwrap();
accept_over_the_wire(&mut assembler, 0, &pages[0].1, 5_000, total_length)
.await
.unwrap();
let lie: Vec<u64> = pages[0].1.iter().map(|_| 99_999).collect();
let err = accept_over_the_wire(&mut assembler, 0, &lie, 5_000, total_length)
.await
.expect_err("the slot is filled, so the second page is refused");
assert_eq!(err, ChunkLensError::DuplicatePage { offset: 0 });
for (offset, page) in &pages[1..] {
accept_over_the_wire(&mut assembler, *offset, page, 5_000, total_length)
.await
.unwrap();
}
assert_eq!(assembler.into_chunk_lens().unwrap(), chunk_lens);
}
#[tokio::test]
async fn a_page_extending_past_the_end_is_rejected() {
let chunk_lens = layout(5_000);
let total_length: u64 = chunk_lens.iter().sum();
let mut assembler = ChunkLensAssembler::new(5_000).unwrap();
let oversized = vec![16_384_u64; MAX_CHUNK_LENS_PER_FRAME];
let err = accept_over_the_wire(&mut assembler, 4_096, &oversized, 5_000, total_length)
.await
.expect_err("4,096 + 2,048 = 6,144 entries in a 5,000-entry array");
assert_eq!(
err,
ChunkLensError::PageExtendsPastEnd {
offset: 4_096,
entries: 2_048,
chunk_count: 5_000,
}
);
assert!(!assembler.is_complete());
}
#[tokio::test]
async fn a_short_middle_page_is_rejected_on_arrival() {
let chunk_lens = layout(5_000);
let total_length: u64 = chunk_lens.iter().sum();
let mut assembler = ChunkLensAssembler::new(5_000).unwrap();
let short = &chunk_lens[0..2_047];
let err = accept_over_the_wire(&mut assembler, 0, short, 5_000, total_length)
.await
.expect_err("a non-final page must be exactly full");
assert_eq!(
err,
ChunkLensError::UnexpectedPageLength {
offset: 0,
entries: 2_047,
expected: 2_048,
}
);
}
#[test]
fn a_misaligned_offset_is_rejected() {
let mut assembler = ChunkLensAssembler::new(5_000).unwrap();
let page = vec![16_384_u64; MAX_CHUNK_LENS_PER_FRAME];
let err = assembler
.accept_page(2_047, &page)
.expect_err("2,047 is not a multiple of the page size");
assert_eq!(err, ChunkLensError::MisalignedOffset { offset: 2_047 });
assert_eq!(
assembler.accept_page(2_049, &page).unwrap_err(),
ChunkLensError::MisalignedOffset { offset: 2_049 }
);
}
#[test]
fn a_page_over_the_per_frame_cap_is_rejected_and_the_cap_itself_is_accepted() {
let chunk_count = 3 * MAX_CHUNK_LENS_PER_FRAME;
let mut assembler = ChunkLensAssembler::new(chunk_count).unwrap();
let over = vec![16_384_u64; MAX_CHUNK_LENS_PER_FRAME + 1];
assert_eq!(
assembler.accept_page(0, &over).unwrap_err(),
ChunkLensError::PageTooLarge {
entries: MAX_CHUNK_LENS_PER_FRAME + 1,
}
);
let at_cap = vec![16_384_u64; MAX_CHUNK_LENS_PER_FRAME];
assembler
.accept_page(0, &at_cap)
.expect("exactly at the cap is legal — the bound is inclusive");
}
#[test]
fn an_empty_page_is_rejected() {
let mut assembler = ChunkLensAssembler::new(5_000).unwrap();
assert_eq!(
assembler.accept_page(0, &[]).unwrap_err(),
ChunkLensError::EmptyPage { offset: 0 }
);
}
#[test]
fn an_offset_past_the_declared_count_is_rejected() {
let mut assembler = ChunkLensAssembler::new(5_000).unwrap();
let page = vec![16_384_u64; 16];
assert_eq!(
assembler.accept_page(6_144, &page).unwrap_err(),
ChunkLensError::OffsetOutOfRange {
offset: 6_144,
chunk_count: 5_000,
}
);
assert_eq!(
assembler.accept_page(u64::MAX - 2_047, &page).unwrap_err(),
ChunkLensError::OffsetOutOfRange {
offset: u64::MAX - 2_047,
chunk_count: 5_000,
},
"a huge aligned offset must not wrap into an accepted placement"
);
}
#[test]
fn a_chunk_count_over_the_resource_cap_is_refused_before_allocating() {
assert_eq!(
ChunkLensAssembler::new(MAX_RESOURCE_CHUNK_COUNT + 1).unwrap_err(),
ChunkLensError::ChunkCountTooLarge {
chunk_count: MAX_RESOURCE_CHUNK_COUNT + 1,
}
);
ChunkLensAssembler::new(MAX_RESOURCE_CHUNK_COUNT)
.expect("exactly at the cap is legal — the bound is inclusive");
}
#[test]
fn a_zero_chunk_layout_is_complete_immediately_and_yields_an_empty_array() {
let assembler = ChunkLensAssembler::new(0).unwrap();
assert!(assembler.is_complete());
assert!(assembler.into_chunk_lens().unwrap().is_empty());
}
#[tokio::test]
async fn an_incomplete_prologue_yields_no_array_at_all() {
let chunk_lens = layout(5_000);
let total_length: u64 = chunk_lens.iter().sum();
let pages = RangeFrame::split_chunk_lens_pages(&chunk_lens);
let mut assembler = ChunkLensAssembler::new(5_000).unwrap();
for (offset, page) in [&pages[0], &pages[2]] {
accept_over_the_wire(&mut assembler, *offset, page, 5_000, total_length)
.await
.unwrap();
}
assert!(!assembler.is_complete(), "the middle page never arrived");
assert_eq!(
assembler.into_chunk_lens().unwrap_err(),
ChunkLensError::Incomplete {
have: 2_952,
want: 5_000,
}
);
}
#[test]
fn splitting_produces_aligned_full_pages_that_tile_the_array() {
for chunk_count in [0, 1, 2_047, 2_048, 2_049, 5_000, 4_096] {
let chunk_lens = layout(chunk_count);
let pages = RangeFrame::split_chunk_lens_pages(&chunk_lens);
assert_eq!(
pages.len(),
chunk_count.div_ceil(MAX_CHUNK_LENS_PER_FRAME),
"page count for {chunk_count} entries"
);
let mut next = 0_usize;
for (offset, page) in &pages {
assert_eq!(*offset as usize, next, "pages tile without gap or overlap");
assert_eq!(offset % MAX_CHUNK_LENS_PER_FRAME as u64, 0, "aligned");
assert!(!page.is_empty() && page.len() <= MAX_CHUNK_LENS_PER_FRAME);
next += page.len();
}
assert_eq!(
next, chunk_count,
"the pages cover every entry, exactly once"
);
assert_eq!(
pages
.iter()
.flat_map(|(_, p)| p)
.copied()
.collect::<Vec<_>>(),
chunk_lens
);
}
}