use super::*;
#[test]
fn test_merge_inplace() {
let mut a = serde_json::json!({"key1": "value1"});
let b = serde_json::json!({"key2": "value2"});
merge_inplace(&mut a, b);
let expected = serde_json::json!({"key1": "value1", "key2": "value2"});
assert_eq!(a, expected);
}
#[test]
fn text_that_states_no_object_has_none() {
for text in ["not json", "[1, 2", "\"str", "12"] {
assert_eq!(parse_partial_object(text), None, "{text}");
}
}
mod string_or_vec_shapes {
use serde::Deserialize;
use std::convert::Infallible;
use std::str::FromStr;
#[derive(Debug, Deserialize, PartialEq)]
struct Block {
text: String,
}
impl FromStr for Block {
type Err = Infallible;
fn from_str(value: &str) -> Result<Self, Self::Err> {
Ok(Block {
text: value.to_owned(),
})
}
}
#[derive(Debug, Deserialize, PartialEq)]
struct Holder {
#[serde(deserialize_with = "super::super::string_or_vec")]
content: Vec<Block>,
}
fn decode(json: serde_json::Value) -> Vec<Block> {
serde_json::from_value::<Holder>(json)
.expect("shape should decode")
.content
}
fn block(text: &str) -> Block {
Block {
text: text.to_owned(),
}
}
#[test]
fn a_bare_object_is_one_element() {
assert_eq!(
decode(serde_json::json!({"content": {"text": "hi"}})),
vec![block("hi")]
);
}
#[test]
fn a_bare_string_becomes_one_element_via_from_str() {
assert_eq!(
decode(serde_json::json!({"content": "hi"})),
vec![block("hi")]
);
}
#[test]
fn an_empty_sequence_is_an_empty_list() {
assert!(decode(serde_json::json!({"content": []})).is_empty());
}
#[test]
fn null_is_an_empty_list() {
assert!(decode(serde_json::json!({"content": null})).is_empty());
}
}
mod partial_arguments {
use super::super::parse_partial_arguments;
use serde_json::{Value, json};
fn parsed(text: &str) -> Value {
Value::Object(parse_partial_arguments(text))
}
#[test]
fn cut_off_text_keeps_what_it_states() {
for (text, expected) in [
("", json!({})),
(" ", json!({})),
("{", json!({})),
(r#"{"path"#, json!({})),
(r#"{"path":"#, json!({})),
(r#"{"path": "no"#, json!({"path": "no"})),
(r#"{"path": "notes.md","#, json!({"path": "notes.md"})),
(r#"{"path": "notes.md", "con"#, json!({"path": "notes.md"})),
(r#"{"a": {"b": [1, 2"#, json!({"a": {"b": [1, 2]}})),
(r#"{"a": [{"b": "c"}, {"d"#, json!({"a": [{"b": "c"}, {}]})),
(r#"{"a": 1, "b": 2,"#, json!({"a": 1, "b": 2})),
(r#"{"line": "one\ntw"#, json!({"line": "one\ntw"})),
(r#"{"quote": "say \"hi"#, json!({"quote": "say \"hi"})),
] {
assert_eq!(parsed(text), expected, "{text}");
}
}
#[test]
fn escapes_cut_short_keep_the_string_before_them() {
for (text, expected) in [
(r#"{"a": "x\"#, json!({"a": "x"})),
(r#"{"a": "x\u"#, json!({"a": "x"})),
(r#"{"a": "x\u00"#, json!({"a": "x"})),
(r#"{"a": "x\u00e"#, json!({"a": "x"})),
(r#"{"a": "x\u00e9"#, json!({"a": "xé"})),
(r#"{"a": "x\\u00"#, json!({"a": "x\\u00"})),
] {
assert_eq!(parsed(text), expected, "{text}");
}
}
#[test]
fn numbers_and_literals_cut_short_are_dropped_until_valid() {
for (text, expected) in [
(r#"{"n": 1"#, json!({"n": 1})),
(r#"{"n": 1."#, json!({})),
(r#"{"n": -"#, json!({})),
(r#"{"n": 1e"#, json!({})),
(r#"{"n": 1.5"#, json!({"n": 1.5})),
(r#"{"b": tru"#, json!({})),
(r#"{"b": true"#, json!({"b": true})),
(r#"{"v": nul"#, json!({})),
(r#"{"a": 1, "v": nul"#, json!({"a": 1})),
] {
assert_eq!(parsed(text), expected, "{text}");
}
}
#[test]
fn raw_control_characters_and_stray_backslashes_are_repaired() {
assert_eq!(
parsed("{\"text\": \"line one\nline two\ttabbed\"}"),
json!({"text": "line one\nline two\ttabbed"})
);
assert_eq!(
parsed(r#"{"path": "C:\Users\me"}"#),
json!({"path": "C:\\Users\\me"})
);
assert_eq!(
parsed("{\"text\": \"cut\nshort"),
json!({"text": "cut\nshort"})
);
}
#[test]
fn a_complete_object_parses_as_serde_does() {
let text = r#"{"path": "a.md", "content": "x\ny \"q\" \u00e9", "n": [1, 2.5, -3e2], "o": {"t": true, "f": null}}"#;
assert_eq!(
parsed(text),
serde_json::from_str::<Value>(text).expect("valid json")
);
}
#[test]
fn a_top_level_that_is_not_an_object_is_empty() {
for text in [
"[1, 2]", "[1, 2", "\"str\"", "\"str", "12", "true", "null", "not json",
] {
assert_eq!(parsed(text), json!({}), "{text}");
}
}
#[test]
fn every_prefix_parses_and_the_whole_text_parses_exactly() {
for whole in [
json!({"path": "notes/today.md", "content": "Line one.\nLine \"two\" \\ é ✓\n\tIndented."}),
json!({"x": 1, "y": -2.5, "z": 3e10, "flags": [true, false, null], "nested": {"a": [{"b": "c"}]}}),
json!({"query": "", "limit": 0, "filters": {}, "tags": []}),
] {
for text in [
whole.to_string(),
serde_json::to_string_pretty(&whole).expect("json"),
] {
for (cut, _) in text.char_indices() {
let _ = parse_partial_arguments(&text[..cut]);
}
assert_eq!(parsed(&text), whole);
}
}
}
}