use super::*;
use test_log::test;
#[test]
fn block_header_serde_roundtrip() -> crate::Result<()> {
let header = Header {
block_type: BlockType::Manifest,
block_flags: block_flags::KV_CHECKSUM_FOOTER | block_flags::COMPRESSED,
stored_checksum: Checksum::from_raw(5),
data_length: 252_356,
uncompressed_length: 124_124_124,
};
let bytes = header.encode_into_vec();
assert_eq!(bytes.len(), Header::header_len(BlockType::Manifest));
assert_eq!(header, Header::decode_from(&mut &bytes[..])?);
Ok(())
}
#[test]
fn block_header_serde_roundtrip_sst_omits_flags_byte() -> crate::Result<()> {
let header = Header {
block_type: BlockType::Data,
block_flags: 0,
stored_checksum: Checksum::from_raw(7),
data_length: 42,
uncompressed_length: 42,
};
let bytes = header.encode_into_vec();
assert_eq!(bytes.len(), Header::MIN_LEN);
assert_eq!(header, Header::decode_from(&mut &bytes[..])?);
Ok(())
}
#[test]
fn block_header_rejects_unknown_block_flags_bit() {
let header = Header {
block_type: BlockType::Manifest,
block_flags: 1 << 4,
stored_checksum: Checksum::from_raw(5),
data_length: 10,
uncompressed_length: 10,
};
let bytes = header.encode_into_vec();
assert!(
matches!(
Header::decode_from(&mut &bytes[..]),
Err(crate::Error::InvalidTag(("block_flags", _))),
),
"decode must reject an unknown block_flags bit",
);
}
#[test]
#[expect(clippy::indexing_slicing)]
fn block_header_detect_corruption() {
let header = Header {
block_type: BlockType::Data,
block_flags: 0,
stored_checksum: Checksum::from_raw(5),
data_length: 252_356,
uncompressed_length: 124_124_124,
};
let mut bytes = header.encode_into_vec();
bytes[5] += 1;
assert!(
matches!(
Header::decode_from(&mut &bytes[..]),
Err(crate::Error::ChecksumMismatch { .. }),
),
"did not detect header corruption",
);
}
#[test]
fn a_bound_checksum_reads_back_only_where_it_was_bound() {
let payload = Checksum::from_raw(0x0123_4567_89ab_cdef);
let mut header = Header::test_dummy(BlockType::Data);
header.bind_checksum(payload, ChecksumAt::table(7, 4096));
assert_eq!(header.payload_checksum(ChecksumAt::table(7, 4096)), payload);
assert_ne!(header.payload_checksum(ChecksumAt::table(7, 8192)), payload);
assert_ne!(header.payload_checksum(ChecksumAt::table(8, 4096)), payload);
assert_ne!(header.payload_checksum(ChecksumAt::Unbound), payload);
header.bind_checksum(payload, ChecksumAt::Unbound);
assert_eq!(header.stored_checksum, payload);
}
#[test]
fn distinct_places_never_share_a_modifier() {
let places = [
ChecksumAt::Unbound,
ChecksumAt::table(0, 0),
ChecksumAt::table(0, 1),
ChecksumAt::table(1, 0),
ChecksumAt::table(1, 1),
ChecksumAt::table(0, u64::MAX - 1),
ChecksumAt::table(u64::MAX, 0),
ChecksumAt::table(u64::MAX, u64::MAX - 1),
];
for (i, a) in places.iter().enumerate() {
for b in places.iter().skip(i + 1) {
assert_ne!(
a.modifier(),
b.modifier(),
"{a:?} and {b:?} share a modifier"
);
}
}
}
#[test]
fn a_rebound_frame_verifies_at_its_new_place() -> crate::Result<()> {
let payload = Checksum::from_raw(42);
let mut header = Header {
data_length: 3,
uncompressed_length: 3,
..Header::test_dummy(BlockType::Data)
};
header.bind_checksum(payload, ChecksumAt::table(1, 100));
let mut frame = header.encode_into_vec();
frame.extend_from_slice(b"abc");
let len = frame.len();
Header::rebind_frame(
&mut frame,
ChecksumAt::table(1, 100),
ChecksumAt::table(2, 900),
)?;
assert_eq!(frame.len(), len);
assert_eq!(frame.get(len - 3..), Some(&b"abc"[..]));
let moved = Header::decode_from(&mut &frame[..])?;
assert_eq!(moved.payload_checksum(ChecksumAt::table(2, 900)), payload);
assert_ne!(moved.payload_checksum(ChecksumAt::table(1, 100)), payload);
Ok(())
}
#[test]
fn a_verified_rebind_refuses_a_payload_past_the_frame() {
let header = Header {
data_length: u32::MAX,
uncompressed_length: 3,
..Header::test_dummy(BlockType::Data)
};
let mut frame = header.encode_into_vec();
frame.extend_from_slice(b"abc");
assert!(
Header::rebind_verified_frame(
&mut frame,
ChecksumAt::table(1, 100),
ChecksumAt::table(2, 900),
)
.is_err()
);
}
#[test]
fn a_verified_rebind_refuses_a_damaged_payload() -> crate::Result<()> {
let from = ChecksumAt::table(1, 100);
let to = ChecksumAt::table(2, 900);
let mut header = Header {
data_length: 3,
uncompressed_length: 3,
..Header::test_dummy(BlockType::Data)
};
header.bind_checksum(Checksum::from_raw(crate::hash::hash128(b"abc")), from);
let mut intact = header.encode_into_vec();
intact.extend_from_slice(b"abc");
let mut damaged = intact.clone();
if let Some(byte) = damaged.last_mut() {
*byte ^= 0x01;
}
let before = damaged.clone();
assert!(Header::rebind_verified_frame(&mut damaged, from, to).is_err());
assert_eq!(damaged, before, "a refused frame is left as it was");
Header::rebind_verified_frame(&mut intact, from, to)?;
let moved = Header::decode_from(&mut &intact[..])?;
assert_eq!(
moved.payload_checksum(to),
Checksum::from_raw(crate::hash::hash128(b"abc"))
);
Ok(())
}