use nextjson::formats;
use nextjson::Value;
fn pickle_payload(n_tuples: usize) -> Vec<u8> {
let mut payload = vec![0x80, 0x02, 0x4E];
payload.extend(vec![0x85u8; n_tuples]);
payload.push(0x2E);
payload
}
#[test]
fn pickle_tuple_chain_below_limit_roundtrips() {
let v: Value = formats::decode_with(&pickle_payload(100), formats::Pickle).unwrap();
let mut depth = 0u32;
let mut cur = &v;
while let Value::Array(a) = cur {
depth += 1;
assert_eq!(a.len(), 1);
cur = &a[0];
}
assert_eq!(cur, &Value::Null);
assert_eq!(depth, 100);
}
#[test]
fn pickle_tuple_chain_over_limit_is_an_error_not_a_crash() {
let err = formats::decode_with::<Value, _>(&pickle_payload(200_000), formats::Pickle)
.expect_err("deep pickle chain must be rejected, not crash the process");
assert!(
err.to_string().contains("recursion"),
"unexpected error: {err}"
);
}
#[test]
fn pickle_mark_chain_is_still_bounded() {
let mut payload = vec![0x80, 0x02];
payload.extend(vec![0x28; 200]); payload.push(0x2E); let err = formats::decode_with::<Value, _>(&payload, formats::Pickle)
.expect_err("MARK-chain beyond 128 must be rejected");
assert!(
err.to_string().contains("recursion"),
"unexpected error: {err}"
);
}
#[test]
fn from_value_bounds_hand_built_deep_trees() {
let mut v = Value::Null;
for _ in 0..5000 {
v = Value::Array(vec![v]);
}
let err = nextjson::from_value::<Value>(v).expect_err("deep hand-built tree must be rejected");
assert!(err.to_string().contains("depth"), "unexpected error: {err}");
}
#[test]
fn hjson_unquoted_unicode_key_is_preserved() {
let key = "\u{43A}\u{43B}\u{44E}\u{447}"; let input = format!("{{{key}: 1}}");
let v: Value = formats::decode_with(input.as_bytes(), formats::Hjson).unwrap();
assert_eq!(v.get(key), Some(&Value::from(1_i64)));
let bytes = formats::encode_with(&v, formats::Hjson).unwrap();
let back: Value = formats::decode_with(&bytes, formats::Hjson).unwrap();
assert_eq!(back, v);
}
#[test]
fn text_decoders_reject_overflowing_floats() {
assert!(formats::decode_with::<Value, _>(b"x = 1e999", formats::Toml).is_err());
assert!(formats::decode_with::<Value, _>(b"a: 1e999", formats::Yaml).is_err());
assert!(formats::decode_with::<Value, _>(b"{a: 1e999}", formats::Hjson).is_err());
assert!(formats::decode_with::<Value, _>(b"(a: 1e999)", formats::Ron).is_err());
assert!(formats::decode_with::<Value, _>(b"1e999", formats::Sexpr).is_err());
assert!(formats::decode_with::<f64, _>(b"a=1e999", formats::UrlForm).is_err());
let url: Value = formats::decode_with(b"a=1e999", formats::UrlForm).unwrap();
assert_eq!(url.get("a"), Some(&Value::from("1e999")));
let csv: Value = formats::decode_with(b"1e999", formats::Csv).unwrap();
assert!(
csv.as_str().is_some(),
"expected a string cell, got {csv:?}"
);
assert!(formats::decode_with::<Value, _>(b"a: .inf", formats::Yaml).is_err());
assert!(formats::decode_with::<Value, _>(b"a: .nan", formats::Yaml).is_err());
}
#[test]
fn json5_keeps_its_documented_infinity_nan_support() {
let v: Value = formats::decode_with(b"Infinity", formats::Json5).unwrap();
assert!(v.as_number().is_some());
assert!(formats::encode_with(&v, formats::Json5).is_err());
}
#[test]
fn typed_f64_decode_rejects_non_finite_from_binary_wire() {
let err = formats::decode_with::<f64, _>(
&[0xcb, 0x7f, 0xf0, 0, 0, 0, 0, 0, 0], formats::MsgPack,
)
.expect_err("typed f64 must reject +inf from the wire");
assert!(
err.to_string().contains("out of range"),
"unexpected: {err}"
);
}
#[test]
fn text_encoders_reject_non_finite_values() {
let mut map = nextjson::Map::new();
map.insert("value".to_string(), Value::from(f64::NAN));
assert!(formats::encode_with(&map, formats::Toml).is_err());
assert!(formats::encode_with(&map, formats::Yaml).is_err());
assert!(formats::encode_with(&map, formats::Ron).is_err());
assert!(formats::encode_with(&map, formats::Sexpr).is_err());
assert!(formats::encode_with(&map, formats::UrlForm).is_err());
assert!(formats::encode_with(&map, formats::Csv).is_err());
assert!(formats::encode_with(&map, formats::Bson).is_err());
assert!(formats::encode_with(&map, formats::Pickle).is_err());
assert!(formats::encode_with(&map, formats::Json).is_err());
}
#[test]
fn max_str_len_u64_max_does_not_overflow() {
use nextjson::{FieldSchema, Policy, StructSchema, TypeSchema};
const S: TypeSchema = TypeSchema::Struct(&StructSchema {
name: "S",
transparent: false,
max_depth: None,
deny_unknown_fields: false,
fields: &[FieldSchema {
name: "s",
orig: "s",
required: true,
flattened: false,
policy: Policy {
max_str_len: Some(u64::MAX),
max_items: None,
min: None,
max: None,
sensitive: false,
},
ty: TypeSchema::Str,
}],
});
let v = nextjson::json!({ "s": "hello" });
assert!(nextjson::validate(S, &v).is_ok());
let _ = S;
}
#[test]
fn validate_bounds_hand_built_deep_trees() {
use nextjson::TypeSchema;
let mut v = Value::Null;
for _ in 0..5000 {
v = Value::Array(vec![v]);
}
let schema = TypeSchema::Seq(&TypeSchema::Opaque);
let report = nextjson::validate(schema, &v);
assert!(
report.is_ok(),
"walk must terminate and accept a shallow schema: {report:?}"
);
}
#[test]
fn container_max_depth_attribute_overflow_is_safe() {
use nextjson::{StructSchema, TypeSchema};
const S: TypeSchema = TypeSchema::Struct(&StructSchema {
name: "S",
transparent: false,
max_depth: Some(u64::MAX),
deny_unknown_fields: false,
fields: &[],
});
let v = nextjson::json!({});
assert!(nextjson::validate(S, &v).is_ok());
}
#[test]
fn forged_binary_container_lengths_do_not_drive_allocation() {
use nextjson::formats::{Format, MsgPack, MsgPackDecoder, Postcard, PostcardDecoder};
use nextjson::FormatDecoder;
let msgpack = [0xdd, 0xff, 0xff, 0xff, 0xff];
assert!(MsgPack.decode::<Vec<u8>>(&msgpack).is_err());
let postcard = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01];
assert!(Postcard.decode::<Vec<u8>>(&postcard).is_err());
let mut padded_msgpack = vec![0xdd, 0xff, 0xff, 0xff, 0xff];
padded_msgpack.resize(16_384, 0);
let mut decoder = MsgPackDecoder::new(&padded_msgpack);
decoder.begin_array().unwrap();
assert_eq!(decoder.array_len_hint(), Some(4_096));
let mut padded_postcard = postcard.to_vec();
padded_postcard.resize(16_384, 0);
let mut decoder = PostcardDecoder::new(&padded_postcard);
decoder.begin_array().unwrap();
assert_eq!(decoder.array_len_hint(), Some(4_096));
}