use std::collections::{BTreeMap, HashMap};
use pack_io::{Config, Decoder, SerialError, decode, decode_view};
const VARINT_U64_MAX: [u8; 10] = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01];
#[test]
fn string_with_u64_max_length_prefix_is_rejected() {
let err = decode::<String>(&VARINT_U64_MAX).expect_err("u64::MAX length rejected");
assert!(matches!(
err,
SerialError::InvalidLength { .. } | SerialError::UnexpectedEof { .. }
));
}
#[test]
fn vec_u8_with_u64_max_length_prefix_is_rejected() {
let err = decode::<Vec<u8>>(&VARINT_U64_MAX).expect_err("u64::MAX length rejected");
assert!(matches!(err, SerialError::InvalidLength { .. }));
}
#[test]
fn hashmap_with_u64_max_count_is_rejected() {
let err = decode::<HashMap<String, u64>>(&VARINT_U64_MAX).expect_err("u64::MAX count rejected");
assert!(matches!(err, SerialError::InvalidLength { .. }));
}
#[test]
fn nested_vec_with_u64_max_count_is_rejected() {
let err = decode::<Vec<Vec<u8>>>(&VARINT_U64_MAX).expect_err("u64::MAX count rejected");
assert!(matches!(err, SerialError::InvalidLength { .. }));
}
#[test]
fn varint_with_length_above_tight_max_alloc_is_rejected_before_allocation() {
let cfg = Config::new().with_max_alloc(64);
let bytes = [0x80_u8, 0x08];
let mut dec = Decoder::with_config(&bytes, cfg).unwrap();
let err = dec
.read::<String>()
.expect_err("length > tight cap rejected");
assert!(matches!(
err,
SerialError::InvalidLength { declared: 1024, .. }
));
}
#[test]
fn overlong_u64_varint_is_rejected() {
let overlong = [0xff_u8; 11];
let err = decode::<u64>(&overlong).expect_err("overlong varint rejected");
assert!(matches!(err, SerialError::VarintOverflow));
}
#[test]
fn tenth_varint_byte_with_high_bits_is_rejected() {
let mut bytes = [0xff_u8; 10];
bytes[9] = 0x02;
let err = decode::<u64>(&bytes).expect_err("u64 overflow rejected");
assert!(matches!(err, SerialError::VarintOverflow));
}
#[test]
fn overlong_u128_varint_is_rejected() {
let overlong = [0xff_u8; 20];
let err = decode::<u128>(&overlong).expect_err("overlong u128 varint rejected");
assert!(matches!(err, SerialError::VarintOverflow));
}
fn deeply_nested_some_payload(n: usize) -> Vec<u8> {
vec![0x01; n]
}
#[test]
fn nested_option_to_modest_depth_decodes_or_errors_without_panic() {
type T = Option<Option<Option<Option<Option<Option<Option<Option<()>>>>>>>>;
let payload = deeply_nested_some_payload(8);
let _ = decode::<T>(&payload);
}
#[test]
fn nested_option_with_pathological_payload_is_rejected() {
type T = Option<Option<Option<()>>>;
let too_many_tags = deeply_nested_some_payload(64);
let err = decode::<T>(&too_many_tags).expect_err("trailing bytes rejected");
assert!(matches!(err, SerialError::TrailingBytes { .. }));
}
#[test]
fn decode_view_str_with_u64_max_length_is_rejected() {
let err = decode_view::<&str>(&VARINT_U64_MAX).expect_err("u64::MAX length rejected");
assert!(matches!(
err,
SerialError::InvalidLength { .. } | SerialError::UnexpectedEof { .. }
));
}
#[test]
fn decode_view_str_with_invalid_utf8_is_rejected() {
let bytes = [0x02, 0xff, 0xff];
let err = decode_view::<&str>(&bytes).expect_err("invalid UTF-8 rejected");
assert!(matches!(err, SerialError::InvalidUtf8));
}
#[test]
fn truncation_of_nested_struct_never_panics() {
let bytes = pack_io::encode(&(
42_u64,
"hello".to_string(),
vec![1_u8, 2, 3],
Some(true),
BTreeMap::from([("k1".to_string(), 1_u32), ("k2".to_string(), 2)]),
))
.unwrap();
for prefix_len in 0..bytes.len() {
let _ = decode::<(u64, String, Vec<u8>, Option<bool>, BTreeMap<String, u32>)>(
&bytes[..prefix_len],
);
}
}
#[test]
fn strict_decode_rejects_trailing_garbage() {
let mut bytes = pack_io::encode(&"hello").unwrap();
bytes.extend_from_slice(&[0xff, 0xff, 0xff]);
let err = decode::<String>(&bytes).expect_err("trailing bytes rejected");
assert!(matches!(err, SerialError::TrailingBytes { remaining: 3 }));
}
#[test]
fn strict_decode_view_rejects_trailing_garbage() {
let mut bytes = pack_io::encode(&"hello").unwrap();
bytes.extend_from_slice(&[0x00]);
let err = decode_view::<&str>(&bytes).expect_err("trailing bytes rejected");
assert!(matches!(err, SerialError::TrailingBytes { remaining: 1 }));
}
#[test]
fn empty_input_for_required_value_is_unexpected_eof() {
let err = decode::<u64>(&[]).expect_err("empty input rejected");
assert!(matches!(err, SerialError::UnexpectedEof { .. }));
}
#[test]
fn empty_input_for_unit_type_succeeds() {
decode::<()>(&[]).unwrap();
}