use beve::{Error, MAX_RECURSION_DEPTH, Value};
fn nested_arrays(values: usize) -> Vec<u8> {
let mut bytes = Vec::with_capacity(values * 2);
for _ in 0..values - 1 {
bytes.extend_from_slice(&[0x05, 0x04]);
}
bytes.extend_from_slice(&[0x05, 0x00]);
bytes
}
fn nested_objects(values: usize) -> Vec<u8> {
let mut bytes = Vec::with_capacity(values * 4);
for _ in 0..values - 1 {
bytes.extend_from_slice(&[0x03, 0x04, 0x04, b'a']);
}
bytes.extend_from_slice(&[0x03, 0x00]);
bytes
}
fn nested_json_arrays(values: usize) -> Vec<u8> {
let mut bytes = Vec::with_capacity(values * 2);
bytes.extend(std::iter::repeat_n(b'[', values));
bytes.extend(std::iter::repeat_n(b']', values));
bytes
}
fn is_too_deep(err: &Error) -> bool {
matches!(err, Error::RecursionLimitExceeded)
}
#[test]
fn a_depth_bomb_is_refused_rather_than_aborting_the_process() {
let err = beve::from_slice::<Value>(&nested_arrays(20_000))
.expect_err("a 20,000-deep array must be refused");
assert!(is_too_deep(&err), "{err}");
}
#[test]
fn the_refusal_is_an_ordinary_error_a_caller_can_act_on() {
let err = beve::from_slice::<Value>(&nested_arrays(20_000)).unwrap_err();
assert!(err.to_string().contains("nests deeper"), "{err}");
}
#[test]
fn nesting_at_the_limit_still_decodes() {
let decoded = beve::from_slice::<Value>(&nested_arrays(MAX_RECURSION_DEPTH))
.expect("nesting at the limit is legal input");
assert!(decoded.is_array());
}
#[test]
fn nesting_one_past_the_limit_is_refused() {
let err = beve::from_slice::<Value>(&nested_arrays(MAX_RECURSION_DEPTH + 1))
.expect_err("one level past the limit must be refused");
assert!(is_too_deep(&err), "{err}");
}
#[test]
fn objects_are_bounded_too() {
assert!(beve::from_slice::<Value>(&nested_objects(MAX_RECURSION_DEPTH)).is_ok());
let err = beve::from_slice::<Value>(&nested_objects(MAX_RECURSION_DEPTH + 1))
.expect_err("nested objects are bounded on the same counter");
assert!(is_too_deep(&err), "{err}");
}
#[test]
fn siblings_do_not_accumulate_depth() {
let wide: Vec<Vec<u8>> = (0..10_000u32).map(|_| vec![1u8, 2, 3]).collect();
let bytes = beve::to_vec(&wide).unwrap();
let decoded: Vec<Vec<u8>> = beve::from_slice(&bytes).expect("width is not depth");
assert_eq!(decoded.len(), 10_000);
}
#[test]
fn a_refused_document_does_not_poison_a_later_one() {
assert!(beve::from_slice::<Value>(&nested_arrays(MAX_RECURSION_DEPTH + 1)).is_err());
assert!(beve::from_slice::<Value>(&nested_arrays(MAX_RECURSION_DEPTH)).is_ok());
}
#[test]
fn the_streaming_decoder_is_bounded() {
let bomb = nested_arrays(20_000);
let err = beve::from_reader_streaming::<_, Value>(bomb.as_slice())
.expect_err("the streaming decoder must be bounded too");
assert!(is_too_deep(&err), "{err}");
assert!(
beve::from_reader_streaming::<_, Value>(nested_arrays(MAX_RECURSION_DEPTH).as_slice())
.is_ok()
);
}
#[test]
fn skipping_a_value_is_bounded() {
let bomb = nested_arrays(20_000);
let mut pos = 0;
let err = beve::skip_value(&bomb, &mut pos).expect_err("skipping must be bounded too");
assert!(is_too_deep(&err), "{err}");
let shallow = nested_arrays(MAX_RECURSION_DEPTH);
let mut pos = 0;
beve::skip_value(&shallow, &mut pos).expect("nesting at the limit still skips");
assert_eq!(pos, shallow.len());
}
#[test]
fn every_entry_point_draws_the_line_in_the_same_place() {
let ok = nested_arrays(MAX_RECURSION_DEPTH);
let too_deep = nested_arrays(MAX_RECURSION_DEPTH + 1);
assert!(beve::from_slice::<Value>(&ok).is_ok());
assert!(beve::validate_slice(&ok).is_ok());
assert!(beve::from_reader_streaming::<_, Value>(ok.as_slice()).is_ok());
assert!(beve::skip_value(&ok, &mut 0).is_ok());
assert!(beve::from_slice::<Value>(&too_deep).is_err());
assert!(beve::validate_slice(&too_deep).is_err());
assert!(beve::from_reader_streaming::<_, Value>(too_deep.as_slice()).is_err());
assert!(beve::skip_value(&too_deep, &mut 0).is_err());
}
#[test]
fn json_conversion_is_bounded_on_the_same_constant() {
let err =
beve::beve_slice_to_json(&nested_arrays(20_000)).expect_err("conversion must be bounded");
assert!(is_too_deep(&err), "{err}");
assert!(beve::beve_slice_to_json(&nested_arrays(MAX_RECURSION_DEPTH)).is_ok());
}
#[test]
fn json_input_is_bounded_on_the_same_constant() {
let err = beve::json_slice_to_beve(&nested_json_arrays(20_000))
.expect_err("conversion must be bounded");
assert!(is_too_deep(&err), "{err}");
assert!(beve::json_slice_to_beve(&nested_json_arrays(MAX_RECURSION_DEPTH)).is_ok());
}
#[test]
fn validation_is_bounded() {
let err = beve::validate_slice(&nested_arrays(20_000))
.expect_err("validation must be bounded on the same counter");
assert!(is_too_deep(&err), "{err}");
}