use dever_runtime::wire::{document, json::Json}; #[path = "json/encode.rs"] mod encode; #[path = "json/input.rs"] mod input; #[test] fn fixed_wire_keeps_syntax_and_byte_limits_without_a_second_parse() { use dever_runtime::wire; for source in [r#""\ud800""#, r#""\udc00""#, r#""x" null"#] { assert!(wire::decode_fixed::(source).is_err(), "{source}"); } let source = format!("\"{}\"", "x".repeat(document::MAX_BYTES - 2)); assert_eq!( wire::decode_fixed::(&source).unwrap().len(), document::MAX_BYTES - 2 ); assert!(wire::decode_fixed::(&(source + " ")).is_err()); let overhead = wire::response_fixed(&"").unwrap().len(); let value = "x".repeat(document::MAX_BYTES - overhead); assert_eq!( wire::response_fixed(&value).unwrap().len(), document::MAX_BYTES ); assert_eq!( wire::response_fixed_bytes(&value).unwrap().len(), document::MAX_BYTES ); let oversized = value + "x"; assert!(wire::response_fixed(&oversized).is_err()); assert!(wire::response_fixed_bytes(&oversized).is_err()); let value = "雪\"\\\n"; assert_eq!(wire::response_fixed(&value), wire::response(&value)); assert_eq!( wire::response_fixed_bytes(&value).unwrap(), wire::response(&value).unwrap().into_bytes() ); } #[test] fn parser_keeps_numbers_order_and_node_ids() { let source = r#"{"b":1e+03,"a":[null,"雪",-0]}"#; let document = document::parse(source).unwrap(); assert_eq!(document.root, 5); assert_eq!(document::stringify(&document).unwrap(), source); assert_eq!(document::property(&document, document.root, "b"), Some(0)); assert_eq!(document::element(&document, 4, 1), Some(2)); for source in ["{}", "[{},[]]", r#"{"":{}}"#] { let parsed = document::parse(source).unwrap(); assert_eq!(document::stringify(&parsed).unwrap(), source); assert_eq!(document::property(&parsed, parsed.root, "missing"), None); } } #[test] fn parser_rejects_duplicates_surrogates_and_trailing_content() { for source in [ r#"{"name":1,"name":2}"#, r#""\ud800""#, r#"[1,]"#, "null false", r#""\udc00""#, r#""\ud800\u0061""#, r#""\u00""#, "\"雪\n\"", ] { assert!(document::parse(source).is_err(), "{source}"); } let duplicate = r#"{"雪":0,"\u96ea":[}"#; let error = document::parse(duplicate).unwrap_err(); assert_eq!(error.message, "duplicate JSON object key"); assert_eq!( error.position, duplicate[..duplicate.rfind(':').unwrap()].chars().count() ); assert!( Json::parse(duplicate) .unwrap_err() .to_string() .contains("duplicate JSON object key") ); assert_eq!( document::parse(r#""\ud83d\ude42""#).unwrap().nodes, vec![document::Value::String("🙂".into())] ); for text in ["", "plain text", "雪🙂", "雪\"\\\n\0🙂", "\u{08}\u{0c}\r\t"] { let source = serde_json::to_string(text).unwrap(); let parsed = document::parse(&source).unwrap(); assert_eq!(parsed.nodes, vec![document::Value::String(text.into())]); assert_eq!(document::stringify(&parsed).unwrap(), source); assert_eq!(Json::parse(&source).unwrap().as_text().unwrap(), text); } } #[test] fn document_string_escaping_matches_serde_for_unicode_and_control_boundaries() { let encode = |text: String| { let expected = serde_json::to_string(&text).unwrap(); let value = document::Document { root: 0, nodes: vec![document::Value::String(text)], }; assert_eq!(document::stringify(&value).unwrap(), expected); assert_eq!(document::parse(&expected).unwrap().nodes, value.nodes); }; encode( (0..=u32::from(char::MAX)) .filter_map(char::from_u32) .collect(), ); encode( (128..=u32::from(char::MAX)) .filter_map(char::from_u32) .collect(), ); for character in (0..32).map(char::from).chain(['"', '\\']) { for text in [ format!("雪🙂{character}𐀀a{character}"), format!("{character}{character}雪🙂"), ] { encode(text); } } encode(String::new()); } #[test] fn json_property_update_and_scalar_reads_preserve_wire_text() { let value = Json::parse(r#"{"a":1,"b":[true]}"#).unwrap(); assert_eq!(value.property("a").unwrap().unwrap().as_integer(), Ok(1)); assert_eq!(value.property("missing").unwrap(), None); let updated = value .set_property("a", &Json::parse("1.000e2").unwrap()) .unwrap(); assert_eq!(updated.as_str(), r#"{"a":1.000e2,"b":[true]}"#); assert_eq!( Json::parse(r#""hello""#).unwrap().as_text(), Ok("hello".into()) ); } #[test] fn parser_enforces_depth_and_value_budgets() { let nested = "[".repeat(document::MAX_DEPTH) + &"]".repeat(document::MAX_DEPTH); assert!(document::parse(&nested).is_ok()); let too_deep = "[".repeat(document::MAX_DEPTH + 1) + &"]".repeat(document::MAX_DEPTH + 1); assert!(document::parse(&too_deep).is_err()); let allowed = format!("[{}0]", "0,".repeat(document::MAX_VALUES - 2)); assert!(document::parse(&allowed).is_ok()); let many = format!("[{}0]", "0,".repeat(document::MAX_VALUES - 1)); assert!(document::parse(&many).is_err()); } #[test] fn invalid_utf8_escape_reports_a_codepoint_position_without_panicking() { for source in [r#""\雪""#, r#""\u0雪雪""#] { assert!(document::parse(source).is_err()); } assert_eq!(document::parse("[\"雪\",]").unwrap_err().position, 5); for source in ["\"plain", "\"雪🙂", "{\"雪🙂"] { let error = document::parse(source).unwrap_err(); assert_eq!(error.message, "unterminated JSON string"); assert_eq!(error.position, source.chars().count()); } } #[test] fn manual_documents_reject_forward_references() { let document = document::Document { root: 1, nodes: vec![document::Value::Null, document::Value::Array(vec![1])], }; assert!(document::stringify(&document).is_err()); } #[test] fn manual_document_numbers_preserve_json_lexical_rules() { let encode = |raw: &str| { document::stringify(&document::Document { root: 0, nodes: vec![document::Value::Number(raw.into())], }) }; for raw in [ "0", "-0", "1.00", "-0.125E+03", "1e9999", "1234567890123456789012345678901234e-12", ] { assert_eq!(encode(raw).unwrap(), raw); } for raw in [ "", "-", "+1", "01", "-01", "0x1", "NaN", "1.", ".1", "1e", "1e+", "1_0", " 1", "1 ", "1\n", "null", "[]", "1true", "1雪", "0],\"x\":true", ] { let error = encode(raw).unwrap_err(); assert_eq!(error.node, 0, "{raw}"); assert_eq!(error.message, "invalid JSON number", "{raw}"); } } #[test] fn scalar_and_array_encoding_reuses_the_same_budgeted_parser() { assert_eq!(Json::boolean(true).unwrap().as_str(), "true"); assert_eq!(Json::integer(42).unwrap().as_str(), "42"); assert_eq!(Json::string("雪").unwrap().as_str(), "\"雪\""); assert!(Json::float(f64::NAN).is_err()); let array = Json::array(&[Json::integer(1).unwrap(), Json::boolean(false).unwrap()]).unwrap(); assert_eq!(array.as_str(), "[1,false]"); } #[test] fn source_document_keeps_the_256_container_contract() { let nested = "[".repeat(256) + &"]".repeat(256); let parsed = document::parse_with_depth(&nested, document::DOCUMENT_MAX_DEPTH).unwrap(); assert_eq!( document::stringify_with_depth(&parsed, document::DOCUMENT_MAX_DEPTH).unwrap(), nested ); assert!(document::stringify_with_depth(&parsed, 255).is_err()); let too_deep = "[".repeat(257) + &"]".repeat(257); assert!(document::parse_with_depth(&too_deep, document::DOCUMENT_MAX_DEPTH).is_err()); } #[test] fn raw_properties_preserve_subtree_bytes_and_distinguish_null_from_missing() { let value = Json::parse(r#"{"payload": [ 1e9999, -0, "\u96ea" ], "empty": null}"#).unwrap(); assert_eq!( value.property("payload").unwrap().unwrap().as_str(), r#"[ 1e9999, -0, "\u96ea" ]"# ); assert_eq!(value.property("empty").unwrap().unwrap().as_str(), "null"); assert_eq!(value.property("missing").unwrap(), None); assert!(Json::integer(1).unwrap().property("key").is_err()); assert!( Json::parse("[]") .unwrap() .set_property("key", &value) .is_err() ); let updated = value .set_property("empty", &Json::boolean(true).unwrap()) .unwrap(); assert!(updated.as_str().contains(r#"[ 1e9999, -0, "\u96ea" ]"#)); assert_eq!(value.property("empty").unwrap().unwrap().as_str(), "null"); } #[test] fn property_keys_compare_decoded_text_without_changing_subtree_spelling() { let source = r#" {"\u0061": [ 1e9999, -0 ], "雪\"\\\n": "\u96ea", "\ud83d\ude42": null, "last": true} "#; let value = Json::parse(source).unwrap(); for (key, expected) in [ ("a", Some("[ 1e9999, -0 ]")), ("雪\"\\\n", Some(r#""\u96ea""#)), ("🙂", Some("null")), ("last", Some("true")), (r"\u0061", None), ("missing", None), ] { assert_eq!( value.property(key).unwrap().as_ref().map(Json::as_str), expected, "{key}" ); } assert_eq!(value.as_str(), source); for source in [r#"{"a":1,"\u0061":2}"#, r#"{"a":1,"tail":[}"#] { assert!(Json::parse(source).is_err()); } } #[test] fn shared_wire_checks_duplicate_names_and_all_three_budgets() { use dever_runtime::wire; for source in [r#"{"a":1,"\u0061":2}"#, r#"[{"a":1,"a":2}]"#, r#""\ud800""#] { assert!(Json::parse(source).is_err(), "{source}"); assert!( wire::decode::(source).is_err(), "{source}" ); } let maximum = format!("[{}0]", "0,".repeat(document::MAX_VALUES - 2)); assert!(Json::parse(&maximum).is_ok()); assert!(wire::decode::>(&maximum).is_ok()); let excessive = format!("[{}0]", "0,".repeat(document::MAX_VALUES - 1)); assert!(Json::parse(&excessive).is_err()); assert!(wire::decode::>(&excessive).is_err()); let nested = "[".repeat(document::MAX_DEPTH) + &"]".repeat(document::MAX_DEPTH); assert!(Json::parse(&nested).is_ok()); assert!(Json::parse(&format!("[{nested}]")).is_err()); assert!(wire::response(&serde_json::from_str::(&nested).unwrap()).is_err()); assert!(Json::string(&"x".repeat(document::MAX_BYTES - 2)).is_ok()); assert!(Json::string(&"x".repeat(document::MAX_BYTES - 1)).is_err()); } #[test] fn manual_document_shared_subgraphs_are_bounded_before_expansion() { let mut graph = document::Document { root: 0, nodes: vec![document::Value::Null], }; for index in 1..40 { graph.nodes.push(document::Value::Array(vec![ index as i64 - 1, index as i64 - 1, ])); graph.root = index; } let error = document::stringify(&graph).unwrap_err(); assert!(error.node < 20, "{error}"); assert!(error.message.contains("65536"), "{error}"); let oversized = document::Document { root: 1, nodes: vec![ document::Value::String("x".repeat(document::MAX_BYTES / 2)), document::Value::Array(vec![0, 0]), ], }; assert!( document::stringify(&oversized) .unwrap_err() .message .contains("16 MiB") ); } #[test] fn replacing_a_field_does_not_accumulate_obsolete_document_nodes() { let source = format!( r#"{{"count":0,"values":[{}0]}}"#, "0,".repeat(document::MAX_VALUES - 4) ); let original = Json::parse(&source).unwrap(); let updated = original .set_property("count", &Json::integer(1).unwrap()) .unwrap(); assert_eq!( updated.property("count").unwrap().unwrap().as_integer(), Ok(1) ); assert_eq!( original.property("count").unwrap().unwrap().as_integer(), Ok(0) ); assert!( updated .set_property("extra", &Json::null().unwrap()) .is_err() ); } #[test] fn fixed_record_encoding_keeps_byte_limits_and_escaping() { #[derive(serde::Serialize)] struct Record<'value> { text: &'value str, } for text in ["", "雪\"\\\n"] { let value = Record { text }; assert_eq!(Json::encode_proven(&value, 2, 1), Json::encode(&value)); } let text = "x".repeat(document::MAX_BYTES - r#"{"text":""}"#.len()); assert_eq!( Json::encode(&Record { text: &text }) .unwrap() .as_str() .len(), document::MAX_BYTES ); assert!( Json::encode(&Record { text: &(text + "x") }) .is_err() ); } #[test] fn decimal_wire_is_an_exact_string_not_a_floating_point_value() { use dever_runtime::{value::decimal::Decimal, wire}; let value = wire::decode::(r#""1234567890123456789012345678901234""#).unwrap(); assert_eq!( Json::encode(&value).unwrap().as_str(), r#""1234567890123456789012345678901234""# ); assert!(wire::decode::("1.25").is_err()); assert!(wire::decode::(r#""NaN""#).is_err()); assert!(wire::decode::(r#""12345678901234567890123456789012345""#).is_err()); } #[test] fn decimal_nullable_lists_preserve_extremes_and_normalized_output() { use dever_runtime::{value::decimal::Decimal, wire}; let values = [ "-0.00", "1.2300", "1E-6176", "9.999999999999999999999999999999999E6144", ]; let mut numbers = values .map(|value| Some(Decimal::parse(value).unwrap())) .to_vec(); numbers.push(None); let encoded = Json::encode(&numbers).unwrap(); let expected = [ Some("0"), Some("1.23"), Some("1E-6176"), Some("9.999999999999999999999999999999999E+6144"), None, ]; assert_eq!(encoded.as_str(), serde_json::to_string(&expected).unwrap()); assert_eq!( wire::decode::>>(encoded.as_str()).unwrap(), numbers ); assert!(wire::decode::>(encoded.as_str()).is_err()); } #[test] fn raw_deserialization_preserves_subtree_text_and_enforces_bounds() { let source = r#" {"huge": 1e9999, "text": "\u96ea", "array": [ 1, 2 ]} "#; let value: Json = serde_json::from_str(source).unwrap(); assert_eq!(value.as_str(), source.trim()); assert_eq!(Json::parse(source).unwrap().as_str(), source); let parsed = Json::parse(source.trim()).unwrap(); assert_eq!(value, parsed); for value in [value, parsed] { assert_eq!(format!("{value:?}"), format!("Json({:?})", source.trim())); assert_eq!( Json::encode_with(|output| output.json(&value)) .unwrap() .as_str(), source.trim() ); assert_eq!(value.clone().into_text(), source.trim()); let child = value.property("array").unwrap().unwrap(); assert_eq!( Json::encode_with(|output| output.json(&child)) .unwrap() .as_str(), "[ 1, 2 ]" ); let updated = value.set_property("array", &child).unwrap(); assert_eq!(updated.property("array").unwrap().as_ref(), Some(&child)); assert_eq!( updated.property("huge").unwrap().unwrap().as_str(), "1e9999" ); } for source in [ r#"{"a":1,"\u0061":2}"#.to_owned(), r#"["\ud800"]"#.to_owned(), "[".repeat(65) + &"]".repeat(65), format!("[{}0]", "0,".repeat(document::MAX_VALUES - 1)), format!("\"{}\"", "x".repeat(document::MAX_BYTES - 1)), ] { assert!(serde_json::from_str::(&source).is_err()); } }