#[cfg(test)] mod tests { use anyhow::anyhow; use anyhow::Result; use jsonpatch_rs::{JsonPatch, JsonPatchError, PatchElem}; use serde_json::Value; #[test] fn add_simple_key() -> Result<()> { let data = r#" { "foo": { "bar": 2 } } "#; let patches_str = r#" [ { "op": "add", "path": "/foo/baz", "value": "hello" } ] "#; let expected_str = r#" { "foo": { "bar": 2, "baz": "hello" } } "#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } #[test] fn add_simple_index_key() -> Result<()> { let data = r#" { "foo": { "bar": 2, "baz": [1, 3] } } "#; let patches_str = r#" [ { "op": "add", "path": "/foo/baz/1", "value": 2 } ] "#; let expected_str = r#" { "foo": { "bar": 2, "baz": [1, 2, 3] } } "#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } #[test] fn add_an_array_element() -> Result<()> { let data = r#"{ "foo": [ "bar", "baz" ] }"#; let patches_str = r#" [ { "op": "add", "path": "/foo/1", "value": "qux" } ] "#; let expected_str = r#"{ "foo": [ "bar", "qux", "baz" ] }"#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } #[test] fn add_an_object_member() -> Result<()> { let data = r#"{ "foo": "bar"}"#; let patches_str = r#" [ { "op": "add", "path": "/baz", "value": "qux" } ]"#; let expected_str = r#"{ "baz": "qux", "foo": "bar" }"#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } #[test] fn add_a_nested_member_object() -> Result<()> { let data = r#"{ "foo": "bar"}"#; let patches_str = r#" [ { "op": "add", "path": "/child", "value": { "grandchild": { } } } ] "#; let expected_str = r#" { "foo": "bar", "child": { "grandchild": {} } } "#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } #[test] fn remove_simple_key() -> Result<()> { let data = r#" { "foo": { "bar": 2, "baz": "hello" } } "#; let patches_str = r#" [ { "op": "remove", "path": "/foo/baz"} ] "#; let expected_str = r#" { "foo": { "bar": 2 } } "#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } #[test] fn remove_simple_index_key() -> Result<()> { let data = r#" { "foo": { "bar": 2, "baz": [1, 2, 3] } } "#; let patches_str = r#" [ { "op": "remove", "path": "/foo/baz/1"} ] "#; let expected_str = r#" { "foo": { "bar": 2, "baz": [1, 3] } } "#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } #[test] fn remove_an_object_member() -> Result<()> { let data = r#"{ "baz": "qux", "foo": "bar" }"#; let patches_str = r#" [ { "op": "remove", "path": "/baz" } ] "#; let expected_str = r#"{ "foo": "bar" }"#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } #[test] fn remove_an_array_element() -> Result<()> { let data = r#"{ "foo": [ "bar", "qux", "baz" ] }"#; let patches_str = r#" [ { "op": "remove", "path": "/foo/1" } ] "#; let expected_str = r#"{ "foo": [ "bar", "baz" ] }"#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } #[test] fn replace_simple_key() -> Result<()> { let data = r#" { "foo": { "bar": 2, "baz": "world" } } "#; let patches_str = r#" [ { "op": "replace", "path": "/foo/baz", "value": "hello" } ] "#; let expected_str = r#" { "foo": { "bar": 2, "baz": "hello" } } "#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } #[test] fn replace_simple_index_key() -> Result<()> { let data = r#" { "foo": { "bar": 2, "baz": [1, 2, 3] } } "#; let patches_str = r#" [ { "op": "replace", "path": "/foo/baz/1", "value": "hello" } ] "#; let expected_str = r#" { "foo": { "bar": 2, "baz": [1, "hello", 3] } } "#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } #[test] fn replace_a_value() -> Result<()> { let data = r#" { "baz": "qux", "foo": "bar" }"#; let patches_str = r#" [ { "op": "replace", "path": "/baz", "value": "boo" } ] "#; let expected_str = r#" { "baz": "boo", "foo": "bar" } "#; test_json_patch_arr(data, patches_str, expected_str)?; Ok(()) } fn test_json_patch(json: &str, patch_str: &str, expected_json_str: &str) -> Result<()> { let patch: PatchElem = PatchElem::try_from(patch_str)?; let jp = JsonPatch { patches: vec![patch], }; let res = jp.apply(&serde_json::from_str(json)?)?; let expected: Value = serde_json::from_str(expected_json_str)?; assert_eq!(res, expected); Ok(()) } fn test_json_patch_arr(json: &str, patches_str: &str, expected_json_str: &str) -> Result<()> { let jp: JsonPatch = JsonPatch::try_from(patches_str)?; let res = jp.apply(&serde_json::from_str(json)?)?; let expected: Value = serde_json::from_str(expected_json_str)?; assert_eq!(res, expected); Ok(()) } #[test] fn move_a_value() -> Result<()> { let data = r#" { "foo": { "bar": "baz", "waldo": "fred" }, "qux": { "corge": "grault" } }"#; let patch_str = r#"{ "op": "move", "from": "/foo/waldo", "path": "/qux/thud" }"#; let expected_str = r#" { "foo": { "bar": "baz" }, "qux": { "corge": "grault", "thud": "fred" } }"#; test_json_patch(data, patch_str, expected_str)?; Ok(()) } #[test] fn move_an_array_element() -> Result<()> { let data = r#"{ "foo": [ "all", "grass", "cows", "eat" ] }"#; let patch_str = r#"{ "op": "move", "from": "/foo/1", "path": "/foo/3" }"#; let expected_str = r#"{ "foo": [ "all", "cows", "eat", "grass" ] }"#; test_json_patch(data, patch_str, expected_str)?; Ok(()) } #[test] fn copy_a_value() -> Result<()> { let data = r#" { "foo": { "bar": "baz", "waldo": "fred" }, "qux": { "corge": "grault" } }"#; let patch_str = r#"{ "op": "copy", "from": "/foo/waldo", "path": "/qux/thud" }"#; let expected_str = r#" { "foo": { "bar": "baz", "waldo": "fred" }, "qux": { "corge": "grault", "thud": "fred" } }"#; test_json_patch(data, patch_str, expected_str)?; Ok(()) } #[test] fn copy_an_array_element() -> Result<()> { let data = r#"{ "foo": [ "all", "grass", "cows", "eat" ] }"#; let patch_str = r#"{ "op": "copy", "from": "/foo/1", "path": "/foo/3" }"#; let expected_str = r#"{ "foo": [ "all", "grass", "cows", "grass", "eat" ] }"#; test_json_patch(data, patch_str, expected_str)?; Ok(()) } #[test] fn test_a_value_success() -> Result<()> { let data = r#" { "baz": "qux", "foo": [ "a", 2, "c" ] }"#; let patches_str = r#" [ { "op": "test", "path": "/baz", "value": "qux" }, { "op": "test", "path": "/foo/1", "value": 2 } ]"#; test_json_patch_arr(data, patches_str, data)?; Ok(()) } #[test] fn test_a_value_error() -> Result<()> { let data = r#"{ "baz": "qux" }"#; let patches_str = r#" [ { "op": "test", "path": "/baz", "value": "bar" } ] "#; match test_json_patch_arr(data, patches_str, data) { Ok(_) => Err(anyhow!("not get test error")), Err(e) => match e.downcast_ref::() { Some(JsonPatchError::TestFail { json_ptr, expected, actual, }) => { if json_ptr.to_escaped_string() == "/baz" && expected.to_string() == "\"bar\"" && actual.to_string() == "\"qux\"" { Ok(()) } else { Err(anyhow!("Wrong test fail error: {}", e)) } } None => Err(anyhow!("Not get JsonPatchError, get {}", e)), _ => Err(anyhow!("Get the wrong JsonPatchError {}", e)), }, } } #[test] fn ignore_unrecognized_elements() -> Result<()> { let data = r#"{ "foo": "bar" }"#; let patch_str = r#" [ { "op": "add", "path": "/baz", "value": "qux", "xyz": 123 } ] "#; let expected_str = r#" { "foo": "bar", "baz": "qux" } "#; test_json_patch_arr(data, patch_str, expected_str)?; Ok(()) } #[test] fn add_to_nonexistent_target() -> Result<()> { let data = r#"{ "foo": "bar" }"#; let patch_str = r#" [ { "op": "add", "path": "/baz/bat", "value": "qux" } ] "#; match test_json_patch_arr(data, patch_str, data) { Ok(_) => Err(anyhow!("not get test error")), Err(e) => Ok(()), } } }