nitai/projects
noxstrap / src / crates / nox-json / tests / parse.rs
109 lines · 4.0 KB Raw
1use nox_json::{parse, Json};
2
3#[test]
4fn parses_a_roblox_client_version_response() {
5 let doc = parse(r#"{"version":"version-4a1d0c9f7b2e4d88","clientVersionUpload":"version-2e3f1a","bootstrapperVersion":"2.688.1092","nextClientVersionUpload":null,"IsRolloutComplete":true}"#).unwrap();
6 assert_eq!(doc.str_at("version"), Some("version-4a1d0c9f7b2e4d88"));
7 assert_eq!(doc.str_at("clientVersionUpload"), Some("version-2e3f1a"));
8 assert_eq!(doc.str_at("bootstrapperVersion"), Some("2.688.1092"));
9 assert!(doc.get("nextClientVersionUpload").unwrap().is_null());
10 assert_eq!(doc.path("IsRolloutComplete").unwrap().as_bool(), Some(true));
11 assert_eq!(doc.str_at("nope"), None);
12}
13
14#[test]
15fn handles_escapes_and_unicode() {
16 let doc = parse(r#"{"a":"line\nbreak\ttab \"q\" \\ \/ \u00e9 \u5b57 \ud83d\ude00","b":"plain é字"}"#).unwrap();
17 assert_eq!(doc.str_at("a"), Some("line\nbreak\ttab \"q\" \\ / é 字 😀"));
18 assert_eq!(doc.str_at("b"), Some("plain é字"));
19}
20
21#[test]
22fn numbers_match_expectations() {
23 let doc = parse(r#"[0,-1,3.5,1e3,-2.5E-2,12345678901,0.0]"#).unwrap();
24 let a = doc.as_array().unwrap();
25 assert_eq!(a[0].as_f64(), Some(0.0));
26 assert_eq!(a[1].as_f64(), Some(-1.0));
27 assert_eq!(a[2].as_f64(), Some(3.5));
28 assert_eq!(a[3].as_f64(), Some(1000.0));
29 assert_eq!(a[4].as_f64(), Some(-0.025));
30 assert_eq!(a[5].as_u64(), Some(12345678901));
31 assert_eq!(a[1].as_u64(), None, "negative must not become u64");
32}
33
34#[test]
35fn nested_structures() {
36 let doc = parse(r#"{"a":{"b":{"c":[1,{"d":"deep"}]}}}"#).unwrap();
37 assert_eq!(doc.path("a.b.c").unwrap().at(0).unwrap().as_f64(), Some(1.0));
38 assert_eq!(doc.path("a.b.c").unwrap().at(1).unwrap().str_at("d"), Some("deep"));
39 assert_eq!(doc.path("a.b.zzz"), None);
40}
41
42#[test]
43fn rejects_malformed_input() {
44 for bad in [
45 "", "{", "}", "[", "[1,", "[1,]", "{\"a\"}", "{\"a\":}", "{a:1}",
46 "\"unterminated", "tru", "nul", "01x", "--1", "{\"a\":1}x", "[1] [2]",
47 "\"\\q\"", "\"\\u12\"", "\"\\ud800\"", "\"\\ud800\\u0041\"",
48 ] {
49 assert!(parse(bad).is_err(), "{bad:?} should not parse");
50 }
51}
52
53#[test]
54fn depth_is_bounded() {
55 let deep = format!("{}1{}", "[".repeat(500), "]".repeat(500));
56 assert!(parse(&deep).is_err(), "runaway nesting must be rejected");
57 let ok = format!("{}1{}", "[".repeat(20), "]".repeat(20));
58 assert!(parse(&ok).is_ok());
59}
60
61#[test]
62fn survives_random_junk_without_panicking() {
63 let mut seed = 7u64;
64 let alphabet = b"{}[]\":,0123456789.eE-+ truefalsn\\/\x00\xff";
65 for _ in 0..4000 {
66 let mut s = String::new();
67 seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1);
68 let n = (seed >> 33) as usize % 40;
69 for _ in 0..n {
70 seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1);
71 s.push(alphabet[(seed >> 33) as usize % alphabet.len()] as char);
72 }
73 let _ = parse(&s);
74 }
75}
76
77#[test]
78fn duplicate_keys_keep_the_first() {
79 let doc = parse(r#"{"k":"first","k":"second"}"#).unwrap();
80 assert_eq!(doc.str_at("k"), Some("first"));
81 match doc {
82 Json::Obj(kv) => assert_eq!(kv.len(), 2, "both pairs are retained"),
83 _ => panic!(),
84 }
85}
86
87#[test]
88fn writes_json_that_parses_back_identically() {
89 let src = r#"{"a":1,"b":[true,false,null,"x\ny"],"c":{"d":-2.5,"e":"é字\"q\""},"f":[],"g":{}}"#;
90 let doc = parse(src).unwrap();
91 let text = doc.to_string();
92 let back = parse(&text).unwrap_or_else(|e| panic!("re-parse failed: {e}\n{text}"));
93 assert_eq!(doc, back, "round trip changed the document");
94 assert!(text.contains("\\n"), "control characters must stay escaped");
95}
96
97#[test]
98fn set_updates_or_appends() {
99 let mut o = Json::obj();
100 o.set("a", Json::Num(1.0));
101 o.set("b", Json::Str("x".into()));
102 o.set("a", Json::Num(2.0));
103 assert_eq!(o.path("a").unwrap().as_f64(), Some(2.0));
104 assert_eq!(o.str_at("b"), Some("x"));
105 match &o {
106 Json::Obj(kv) => assert_eq!(kv.len(), 2),
107 _ => panic!(),
108 }
109}