use crate::{DecodeOptions, Error, Float, Format, Strictness, Value};
fn lenient() -> DecodeOptions {
DecodeOptions::new().strictness(Strictness::LENIENT)
}
#[test]
fn non_shortest_unsigned_strict_rejects() {
assert_eq!(Value::decode(&[0x19, 0x00, 0xff]), Err(Error::NonDeterministic));
}
#[test]
fn non_shortest_unsigned_lenient_normalizes() {
let v = lenient().decode(&[0x19, 0x00, 0xff]).unwrap();
assert_eq!(v, Value::from(255));
assert_eq!(v.encode(), vec![0x18, 0xff]);
}
#[test]
fn non_shortest_negative_lenient_normalizes() {
let v = lenient().decode(&[0x39, 0x00, 0x00]).unwrap();
assert_eq!(v, Value::from(-1));
assert_eq!(v.encode(), vec![0x20]);
}
#[test]
fn non_shortest_array_length_lenient_normalizes() {
let v = lenient().decode(&[0x98, 0x02, 0x04, 0x05]).unwrap();
assert_eq!(v, Value::from([Value::from(4), Value::from(5)]));
assert_eq!(v.encode(), vec![0x82, 0x04, 0x05]);
}
#[test]
fn non_shortest_text_length_lenient_normalizes() {
let v = lenient().decode(&[0x78, 0x00]).unwrap();
assert_eq!(v, Value::from(""));
assert_eq!(v.encode(), vec![0x60]);
}
#[test]
fn non_shortest_tag_number_lenient_normalizes() {
let v = lenient().decode(&[0xd8, 0x00, 0x60]).unwrap();
assert_eq!(v.encode(), vec![0xc0, 0x60]);
}
#[test]
fn non_shortest_float_strict_rejects() {
assert_eq!(
Value::decode(&[0xfa, 0x41, 0x28, 0x00, 0x00]),
Err(Error::NonDeterministic)
);
}
#[test]
fn non_shortest_float_lenient_normalizes() {
let v = lenient().decode(&[0xfa, 0x41, 0x28, 0x00, 0x00]).unwrap();
assert_eq!(v, Value::from(10.5));
assert_eq!(v.encode(), vec![0xf9, 0x49, 0x40]);
}
#[test]
fn non_shortest_f64_lenient_normalizes() {
let v = lenient()
.decode(&[0xfb, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
.unwrap();
assert_eq!(v, Value::from(1.0));
assert_eq!(v.encode(), vec![0xf9, 0x3c, 0x00]);
}
#[test]
fn non_shortest_nan_lenient_normalizes() {
let v = lenient().decode(&[0xfa, 0x7f, 0xc0, 0x00, 0x00]).unwrap();
assert_eq!(v.encode(), vec![0xf9, 0x7e, 0x00]);
}
#[test]
fn bigint_fits_in_u64_strict_rejects() {
assert_eq!(
Value::decode(&[0xc2, 0x43, 0x01, 0x00, 0x00]),
Err(Error::NonDeterministic)
);
}
#[test]
fn bigint_fits_in_u64_lenient_downcasts() {
let v = lenient().decode(&[0xc2, 0x43, 0x01, 0x00, 0x00]).unwrap();
assert_eq!(v, Value::from(65536));
assert_eq!(v.encode(), vec![0x1a, 0x00, 0x01, 0x00, 0x00]);
}
#[test]
fn neg_bigint_fits_in_u64_lenient_downcasts() {
let v = lenient().decode(&[0xc3, 0x41, 0x00]).unwrap();
assert_eq!(v, Value::from(-1));
assert_eq!(v.encode(), vec![0x20]);
}
#[test]
fn bigint_leading_zero_strict_rejects() {
assert_eq!(
Value::decode(&[0xc3, 0x4a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
Err(Error::NonDeterministic)
);
}
#[test]
fn bigint_leading_zero_lenient_strips() {
let bytes = [0xc3, 0x4a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let v = lenient().decode(&bytes).unwrap();
let expected = Value::decode(&[0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).unwrap();
assert_eq!(v, expected);
assert_eq!(
v.encode(),
vec![0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
);
}
#[test]
fn bigint_eight_bytes_strict_rejects() {
assert_eq!(
Value::decode(&[0xc2, 0x48, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
Err(Error::NonDeterministic)
);
}
#[test]
fn bigint_eight_bytes_lenient_downcasts() {
let v = lenient()
.decode(&[0xc2, 0x48, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
.unwrap();
assert_eq!(v, Value::from(u64::MAX));
assert_eq!(v.encode(), vec![0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
}
#[test]
fn unsorted_map_keys_strict_rejects() {
assert_eq!(
Value::decode(&[0xa2, 0x61, 0x62, 0x01, 0x61, 0x61, 0x00]),
Err(Error::NonDeterministic)
);
}
#[test]
fn unsorted_map_keys_lenient_sorts() {
let v = lenient().decode(&[0xa2, 0x61, 0x62, 0x01, 0x61, 0x61, 0x00]).unwrap();
assert_eq!(v.encode(), vec![0xa2, 0x61, 0x61, 0x00, 0x61, 0x62, 0x01],);
}
#[test]
fn unsorted_map_keys_only_does_not_allow_duplicates() {
let opts = DecodeOptions::new().strictness(Strictness {
allow_unsorted_map_keys: true,
..Strictness::STRICT
});
assert_eq!(
opts.decode(&[0xa2, 0x61, 0x61, 0x00, 0x61, 0x61, 0x01]),
Err(Error::NonDeterministic),
);
}
#[test]
fn duplicate_map_keys_strict_rejects() {
assert_eq!(
Value::decode(&[0xa2, 0x61, 0x61, 0x00, 0x61, 0x61, 0x01]),
Err(Error::NonDeterministic)
);
}
#[test]
fn duplicate_map_keys_lenient_last_wins() {
let v = lenient().decode(&[0xa2, 0x61, 0x61, 0x00, 0x61, 0x61, 0x01]).unwrap();
assert_eq!(v.encode(), vec![0xa1, 0x61, 0x61, 0x01]);
}
#[test]
fn duplicate_map_keys_diagnostic_lenient_last_wins() {
let opts = DecodeOptions::new()
.format(Format::Diagnostic)
.strictness(Strictness::LENIENT);
let v = opts.decode(r#"{"a": 0, "a": 1}"#).unwrap();
assert_eq!(v.encode(), vec![0xa1, 0x61, 0x61, 0x01]);
}
#[test]
fn duplicate_map_keys_diagnostic_strict_rejects() {
let opts = DecodeOptions::new().format(Format::Diagnostic);
assert!(matches!(
opts.decode(r#"{"a": 0, "a": 1}"#),
Err(Error::NonDeterministic),
));
}
#[test]
fn lenient_decode_then_encode_is_canonical() {
let mut bytes = Vec::new();
bytes.push(0xa3);
bytes.extend_from_slice(&[0x61, b'z']);
bytes.extend_from_slice(&[0xfa, 0x41, 0x28, 0x00, 0x00]);
bytes.push(0x01);
bytes.extend_from_slice(&[0xc2, 0x43, 0x01, 0x00, 0x00]);
bytes.extend_from_slice(&[0x61, b'a']);
bytes.push(0x01);
let v = lenient().decode(&bytes).unwrap();
let canonical = Value::map([
(Value::from(1), Value::from(65536)),
(Value::from("a"), Value::from(1)),
(Value::from("z"), Value::from(10.5)),
]);
assert_eq!(v, canonical);
assert_eq!(v.encode(), canonical.encode());
}
#[test]
fn float_is_deterministic_for_canonical_constructors() {
assert!(Float::new(0.0_f64).is_deterministic());
assert!(Float::new(1.5_f64).is_deterministic());
assert!(Float::new(f64::NAN).is_deterministic());
assert!(Float::new(f64::INFINITY).is_deterministic());
assert!(Float::with_payload(0).is_deterministic());
assert!(Float::with_payload(0x7fffff).is_deterministic());
}
#[test]
fn indefinite_bytes_strict_rejects() {
assert_eq!(
Value::decode(&[0x5f, 0x41, 0x01, 0x42, 0x02, 0x03, 0xff]),
Err(Error::NonDeterministic),
);
}
#[test]
fn indefinite_bytes_lenient_normalizes() {
let v = lenient().decode(&[0x5f, 0x41, 0x01, 0x42, 0x02, 0x03, 0xff]).unwrap();
assert_eq!(v, Value::from(&b"\x01\x02\x03"[..]));
assert_eq!(v.encode(), vec![0x43, 0x01, 0x02, 0x03]);
}
#[test]
fn indefinite_bytes_empty_lenient() {
let v = lenient().decode(&[0x5f, 0xff]).unwrap();
assert_eq!(v, Value::from(b""));
assert_eq!(v.encode(), vec![0x40]);
}
#[test]
fn indefinite_bytes_mixed_major_chunk_is_malformed() {
assert_eq!(
lenient().decode(&[0x5f, 0x41, 0x01, 0x61, b'a', 0xff]),
Err(Error::Malformed),
);
}
#[test]
fn indefinite_bytes_nested_indefinite_chunk_is_malformed() {
assert_eq!(lenient().decode(&[0x5f, 0x5f, 0xff, 0xff]), Err(Error::Malformed),);
}
#[test]
fn indefinite_text_strict_rejects() {
assert_eq!(Value::decode(b"\x7f\x62ab\x62cd\xff"), Err(Error::NonDeterministic),);
}
#[test]
fn indefinite_text_lenient_normalizes() {
let v = lenient().decode(b"\x7f\x62ab\x62cd\xff").unwrap();
assert_eq!(v, Value::from("abcd"));
assert_eq!(v.encode(), b"\x64abcd");
}
#[test]
fn indefinite_text_chunk_invalid_utf8_rejected() {
assert_eq!(
lenient().decode(&[0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff]),
Err(Error::InvalidUtf8),
);
}
#[test]
fn indefinite_array_strict_rejects() {
assert_eq!(
Value::decode(&[0x9f, 0x01, 0x02, 0x03, 0xff]),
Err(Error::NonDeterministic),
);
}
#[test]
fn indefinite_array_lenient_normalizes() {
let v = lenient().decode(&[0x9f, 0x01, 0x02, 0x03, 0xff]).unwrap();
assert_eq!(v, Value::from([Value::from(1), Value::from(2), Value::from(3)]));
assert_eq!(v.encode(), vec![0x83, 0x01, 0x02, 0x03]);
}
#[test]
fn indefinite_array_empty_lenient() {
let v = lenient().decode(&[0x9f, 0xff]).unwrap();
assert_eq!(v, Value::from(Vec::<Value>::new()));
assert_eq!(v.encode(), vec![0x80]);
}
#[test]
fn indefinite_array_nested_in_definite_lenient() {
let v = lenient().decode(&[0x81, 0x9f, 0x01, 0x02, 0xff]).unwrap();
assert_eq!(v.encode(), vec![0x81, 0x82, 0x01, 0x02]);
}
#[test]
fn indefinite_map_strict_rejects() {
assert_eq!(
Value::decode(b"\xbf\x61a\x01\x61b\x02\xff"),
Err(Error::NonDeterministic),
);
}
#[test]
fn indefinite_map_lenient_normalizes() {
let v = lenient().decode(b"\xbf\x61a\x01\x61b\x02\xff").unwrap();
assert_eq!(v.encode(), b"\xa2\x61a\x01\x61b\x02");
}
#[test]
fn indefinite_map_unsorted_keys_strict_rejects() {
let opts = DecodeOptions::new().strictness(Strictness {
allow_indefinite_length: true,
..Strictness::STRICT
});
assert_eq!(opts.decode(b"\xbf\x61b\x02\x61a\x01\xff"), Err(Error::NonDeterministic),);
}
#[test]
fn indefinite_map_odd_count_is_malformed() {
assert_eq!(lenient().decode(b"\xbf\x61a\x01\x61b\xff"), Err(Error::Malformed),);
}
#[test]
fn indefinite_map_empty_lenient() {
let v = lenient().decode(&[0xbf, 0xff]).unwrap();
assert_eq!(v.encode(), vec![0xa0]);
}
#[test]
fn break_at_top_level_is_malformed() {
assert_eq!(Value::decode(&[0xff]), Err(Error::Malformed));
assert_eq!(lenient().decode(&[0xff]), Err(Error::Malformed));
}
fn lenient_diag() -> DecodeOptions {
DecodeOptions::new()
.format(Format::Diagnostic)
.strictness(Strictness::LENIENT)
}
fn strict_diag() -> DecodeOptions {
DecodeOptions::new().format(Format::Diagnostic)
}
#[test]
fn diag_indefinite_array_strict_rejects() {
assert_eq!(
strict_diag().decode("[_ 1, 2, 3]"),
Err(Error::NonDeterministic),
);
}
#[test]
fn diag_indefinite_array_lenient_normalizes() {
let v = lenient_diag().decode("[_ 1, 2, 3]").unwrap();
assert_eq!(v.encode(), vec![0x83, 0x01, 0x02, 0x03]);
}
#[test]
fn diag_indefinite_array_empty_lenient() {
let v = lenient_diag().decode("[_]").unwrap();
assert_eq!(v.encode(), vec![0x80]);
}
#[test]
fn diag_indefinite_map_strict_rejects() {
assert_eq!(
strict_diag().decode(r#"{_ "a": 1, "b": 2}"#),
Err(Error::NonDeterministic),
);
}
#[test]
fn diag_indefinite_map_lenient_normalizes() {
let v = lenient_diag().decode(r#"{_ "b": 2, "a": 1}"#).unwrap();
assert_eq!(v.encode(), b"\xa2\x61a\x01\x61b\x02");
}
#[test]
fn diag_chunked_text_strict_rejects() {
assert_eq!(
strict_diag().decode(r#"(_ "ab", "cd")"#),
Err(Error::NonDeterministic),
);
}
#[test]
fn diag_chunked_text_lenient_normalizes() {
let v = lenient_diag().decode(r#"(_ "ab", "cd")"#).unwrap();
assert_eq!(v, Value::from("abcd"));
assert_eq!(v.encode(), b"\x64abcd");
}
#[test]
fn diag_chunked_bytes_lenient_normalizes() {
let v = lenient_diag().decode("(_ h'01', h'0203')").unwrap();
assert_eq!(v, Value::from(&b"\x01\x02\x03"[..]));
assert_eq!(v.encode(), vec![0x43, 0x01, 0x02, 0x03]);
}
#[test]
fn diag_chunked_string_mixed_types_rejected() {
assert!(matches!(
lenient_diag().decode(r#"(_ "ab", h'01')"#),
Err(Error::InvalidFormat),
));
}
#[test]
fn diag_chunked_string_empty_rejected() {
assert!(matches!(
lenient_diag().decode("(_ )"),
Err(Error::InvalidFormat),
));
}
#[test]
fn diag_indefinite_marker_outside_container_rejected() {
assert!(matches!(
lenient_diag().decode("_"),
Err(Error::InvalidFormat),
));
}
#[test]
fn diag_chunked_text_nested_indefinite_rejected() {
assert!(matches!(
lenient_diag().decode(r#"(_ "ab", (_ "cd", "ef"), "gh")"#),
Err(Error::InvalidFormat),
));
}
#[test]
fn diag_chunked_bytes_nested_indefinite_rejected() {
assert!(matches!(
lenient_diag().decode("(_ h'01', (_ h'02', h'03'), h'04')"),
Err(Error::InvalidFormat),
));
}
#[test]
fn diag_chunked_first_chunk_indefinite_rejected() {
assert!(matches!(
lenient_diag().decode(r#"(_ (_ "ab", "cd"), "ef")"#),
Err(Error::InvalidFormat),
));
}
#[test]
fn diag_chunked_array_chunk_rejected() {
assert!(matches!(
lenient_diag().decode(r#"(_ "ab", [1, 2])"#),
Err(Error::InvalidFormat),
));
}
#[test]
fn diag_chunked_number_chunk_rejected() {
assert!(matches!(
lenient_diag().decode("(_ h'01', 42)"),
Err(Error::InvalidFormat),
));
}