408 lines
12 KiB
Go
408 lines
12 KiB
Go
|
// Copyright © 2016 Abcum Ltd
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
package json
|
||
|
|
||
|
// import (
|
||
|
// "strings"
|
||
|
// "testing"
|
||
|
// )
|
||
|
|
||
|
// func mergePatch(doc, patch string) string {
|
||
|
// out, err := MergePatch([]byte(doc), []byte(patch))
|
||
|
|
||
|
// if err != nil {
|
||
|
// panic(err)
|
||
|
// }
|
||
|
|
||
|
// return string(out)
|
||
|
// }
|
||
|
|
||
|
// func TestMergePatchReplaceKey(t *testing.T) {
|
||
|
// doc := `{ "title": "hello" }`
|
||
|
// pat := `{ "title": "goodbye" }`
|
||
|
|
||
|
// res := mergePatch(doc, pat)
|
||
|
|
||
|
// if !compareJSON(pat, res) {
|
||
|
// t.Fatalf("Key was not replaced")
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergePatchIgnoresOtherValues(t *testing.T) {
|
||
|
// doc := `{ "title": "hello", "age": 18 }`
|
||
|
// pat := `{ "title": "goodbye" }`
|
||
|
|
||
|
// res := mergePatch(doc, pat)
|
||
|
|
||
|
// exp := `{ "title": "goodbye", "age": 18 }`
|
||
|
|
||
|
// if !compareJSON(exp, res) {
|
||
|
// t.Fatalf("Key was not replaced")
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergePatchNilDoc(t *testing.T) {
|
||
|
// doc := `{ "title": null }`
|
||
|
// pat := `{ "title": {"foo": "bar"} }`
|
||
|
|
||
|
// res := mergePatch(doc, pat)
|
||
|
|
||
|
// exp := `{ "title": {"foo": "bar"} }`
|
||
|
|
||
|
// if !compareJSON(exp, res) {
|
||
|
// t.Fatalf("Key was not replaced")
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergePatchRecursesIntoObjects(t *testing.T) {
|
||
|
// doc := `{ "person": { "title": "hello", "age": 18 } }`
|
||
|
// pat := `{ "person": { "title": "goodbye" } }`
|
||
|
|
||
|
// res := mergePatch(doc, pat)
|
||
|
|
||
|
// exp := `{ "person": { "title": "goodbye", "age": 18 } }`
|
||
|
|
||
|
// if !compareJSON(exp, res) {
|
||
|
// t.Fatalf("Key was not replaced")
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// type nonObjectCases struct {
|
||
|
// doc, pat, res string
|
||
|
// }
|
||
|
|
||
|
// func TestMergePatchReplacesNonObjectsWholesale(t *testing.T) {
|
||
|
// a1 := `[1]`
|
||
|
// a2 := `[2]`
|
||
|
// o1 := `{ "a": 1 }`
|
||
|
// o2 := `{ "a": 2 }`
|
||
|
// o3 := `{ "a": 1, "b": 1 }`
|
||
|
// o4 := `{ "a": 2, "b": 1 }`
|
||
|
|
||
|
// cases := []nonObjectCases{
|
||
|
// {a1, a2, a2},
|
||
|
// {o1, a2, a2},
|
||
|
// {a1, o1, o1},
|
||
|
// {o3, o2, o4},
|
||
|
// }
|
||
|
|
||
|
// for _, c := range cases {
|
||
|
// act := mergePatch(c.doc, c.pat)
|
||
|
|
||
|
// if !compareJSON(c.res, act) {
|
||
|
// t.Errorf("whole object replacement failed")
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergePatchReturnsErrorOnBadJSON(t *testing.T) {
|
||
|
// _, err := MergePatch([]byte(`[[[[`), []byte(`1`))
|
||
|
|
||
|
// if err == nil {
|
||
|
// t.Errorf("Did not return an error for bad json: %s", err)
|
||
|
// }
|
||
|
|
||
|
// _, err = MergePatch([]byte(`1`), []byte(`[[[[`))
|
||
|
|
||
|
// if err == nil {
|
||
|
// t.Errorf("Did not return an error for bad json: %s", err)
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergePatchReturnsEmptyArrayOnEmptyArray(t *testing.T) {
|
||
|
// doc := `{ "array": ["one", "two"] }`
|
||
|
// pat := `{ "array": [] }`
|
||
|
|
||
|
// exp := `{ "array": [] }`
|
||
|
|
||
|
// res, err := MergePatch([]byte(doc), []byte(pat))
|
||
|
|
||
|
// if err != nil {
|
||
|
// t.Errorf("Unexpected error: %s, %s", err, string(res))
|
||
|
// }
|
||
|
|
||
|
// if !compareJSON(exp, string(res)) {
|
||
|
// t.Fatalf("Emtpy array did not return not return as empty array")
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// var rfcTests = []struct {
|
||
|
// target string
|
||
|
// patch string
|
||
|
// expected string
|
||
|
// }{
|
||
|
// // test cases from https://tools.ietf.org/html/rfc7386#appendix-A
|
||
|
// {target: `{"a":"b"}`, patch: `{"a":"c"}`, expected: `{"a":"c"}`},
|
||
|
// {target: `{"a":"b"}`, patch: `{"b":"c"}`, expected: `{"a":"b","b":"c"}`},
|
||
|
// {target: `{"a":"b"}`, patch: `{"a":null}`, expected: `{}`},
|
||
|
// {target: `{"a":"b","b":"c"}`, patch: `{"a":null}`, expected: `{"b":"c"}`},
|
||
|
// {target: `{"a":["b"]}`, patch: `{"a":"c"}`, expected: `{"a":"c"}`},
|
||
|
// {target: `{"a":"c"}`, patch: `{"a":["b"]}`, expected: `{"a":["b"]}`},
|
||
|
// {target: `{"a":{"b": "c"}}`, patch: `{"a": {"b": "d","c": null}}`, expected: `{"a":{"b":"d"}}`},
|
||
|
// {target: `{"a":[{"b":"c"}]}`, patch: `{"a":[1]}`, expected: `{"a":[1]}`},
|
||
|
// {target: `["a","b"]`, patch: `["c","d"]`, expected: `["c","d"]`},
|
||
|
// {target: `{"a":"b"}`, patch: `["c"]`, expected: `["c"]`},
|
||
|
// // {target: `{"a":"foo"}`, patch: `null`, expected: `null`},
|
||
|
// // {target: `{"a":"foo"}`, patch: `"bar"`, expected: `"bar"`},
|
||
|
// {target: `{"e":null}`, patch: `{"a":1}`, expected: `{"a":1,"e":null}`},
|
||
|
// {target: `[1,2]`, patch: `{"a":"b","c":null}`, expected: `{"a":"b"}`},
|
||
|
// {target: `{}`, patch: `{"a":{"bb":{"ccc":null}}}`, expected: `{"a":{"bb":{}}}`},
|
||
|
// }
|
||
|
|
||
|
// func TestMergePatchRFCCases(t *testing.T) {
|
||
|
// for i, c := range rfcTests {
|
||
|
// out := mergePatch(c.target, c.patch)
|
||
|
|
||
|
// if !compareJSON(out, c.expected) {
|
||
|
// t.Errorf("case[%d], patch '%s' did not apply properly to '%s'. expected:\n'%s'\ngot:\n'%s'", i, c.patch, c.target, c.expected, out)
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// var rfcFailTests = `
|
||
|
// {"a":"foo"} | null
|
||
|
// {"a":"foo"} | "bar"
|
||
|
// `
|
||
|
|
||
|
// func TestMergePatchFailRFCCases(t *testing.T) {
|
||
|
// tests := strings.Split(rfcFailTests, "\n")
|
||
|
|
||
|
// for _, c := range tests {
|
||
|
// if strings.TrimSpace(c) == "" {
|
||
|
// continue
|
||
|
// }
|
||
|
|
||
|
// parts := strings.SplitN(c, "|", 2)
|
||
|
|
||
|
// doc := strings.TrimSpace(parts[0])
|
||
|
// pat := strings.TrimSpace(parts[1])
|
||
|
|
||
|
// out, err := MergePatch([]byte(doc), []byte(pat))
|
||
|
|
||
|
// if err != errBadJSONPatch {
|
||
|
// t.Errorf("error not returned properly: %s, %s", err, string(out))
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// }
|
||
|
|
||
|
// func TestMergeReplaceKey(t *testing.T) {
|
||
|
// doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }`
|
||
|
// pat := `{ "title": "goodbye", "nested": {"one": 2, "two": 2} }`
|
||
|
|
||
|
// exp := `{ "title": "goodbye", "nested": {"one": 2} }`
|
||
|
|
||
|
// res, err := CreateMergePatch([]byte(doc), []byte(pat))
|
||
|
|
||
|
// if err != nil {
|
||
|
// t.Errorf("Unexpected error: %s, %s", err, string(res))
|
||
|
// }
|
||
|
|
||
|
// if !compareJSON(exp, string(res)) {
|
||
|
// t.Fatalf("Key was not replaced")
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergeGetArray(t *testing.T) {
|
||
|
// doc := `{ "title": "hello", "array": ["one", "two"], "notmatch": [1, 2, 3] }`
|
||
|
// pat := `{ "title": "hello", "array": ["one", "two", "three"], "notmatch": [1, 2, 3] }`
|
||
|
|
||
|
// exp := `{ "array": ["one", "two", "three"] }`
|
||
|
|
||
|
// res, err := CreateMergePatch([]byte(doc), []byte(pat))
|
||
|
|
||
|
// if err != nil {
|
||
|
// t.Errorf("Unexpected error: %s, %s", err, string(res))
|
||
|
// }
|
||
|
|
||
|
// if !compareJSON(exp, string(res)) {
|
||
|
// t.Fatalf("Array was not added")
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergeGetObjArray(t *testing.T) {
|
||
|
// doc := `{ "title": "hello", "array": [{"banana": true}, {"evil": false}], "notmatch": [{"one":1}, {"two":2}, {"three":3}] }`
|
||
|
// pat := `{ "title": "hello", "array": [{"banana": false}, {"evil": true}], "notmatch": [{"one":1}, {"two":2}, {"three":3}] }`
|
||
|
|
||
|
// exp := `{ "array": [{"banana": false}, {"evil": true}] }`
|
||
|
|
||
|
// res, err := CreateMergePatch([]byte(doc), []byte(pat))
|
||
|
|
||
|
// if err != nil {
|
||
|
// t.Errorf("Unexpected error: %s, %s", err, string(res))
|
||
|
// }
|
||
|
|
||
|
// if !compareJSON(exp, string(res)) {
|
||
|
// t.Fatalf("Object array was not added")
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergeDeleteKey(t *testing.T) {
|
||
|
// doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }`
|
||
|
// pat := `{ "title": "hello", "nested": {"one": 1} }`
|
||
|
|
||
|
// exp := `{"nested":{"two":null}}`
|
||
|
|
||
|
// res, err := CreateMergePatch([]byte(doc), []byte(pat))
|
||
|
|
||
|
// if err != nil {
|
||
|
// t.Errorf("Unexpected error: %s, %s", err, string(res))
|
||
|
// }
|
||
|
|
||
|
// // We cannot use "compareJSON", since Equals does not report a difference if the value is null
|
||
|
// if exp != string(res) {
|
||
|
// t.Fatalf("Key was not removed")
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergeEmptyArray(t *testing.T) {
|
||
|
// doc := `{ "array": null }`
|
||
|
// pat := `{ "array": [] }`
|
||
|
|
||
|
// exp := `{"array":[]}`
|
||
|
|
||
|
// res, err := CreateMergePatch([]byte(doc), []byte(pat))
|
||
|
|
||
|
// if err != nil {
|
||
|
// t.Errorf("Unexpected error: %s, %s", err, string(res))
|
||
|
// }
|
||
|
|
||
|
// // We cannot use "compareJSON", since Equals does not report a difference if the value is null
|
||
|
// if exp != string(res) {
|
||
|
// t.Fatalf("Key was not removed")
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergeObjArray(t *testing.T) {
|
||
|
// doc := `{ "array": [ {"a": {"b": 2}}, {"a": {"b": 3}} ]}`
|
||
|
// exp := `{}`
|
||
|
|
||
|
// res, err := CreateMergePatch([]byte(doc), []byte(doc))
|
||
|
|
||
|
// if err != nil {
|
||
|
// t.Errorf("Unexpected error: %s, %s", err, string(res))
|
||
|
// }
|
||
|
|
||
|
// // We cannot use "compareJSON", since Equals does not report a difference if the value is null
|
||
|
// if exp != string(res) {
|
||
|
// t.Fatalf("Array was not empty, was " + string(res))
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergeComplexMatch(t *testing.T) {
|
||
|
// doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }`
|
||
|
// empty := `{}`
|
||
|
// res, err := CreateMergePatch([]byte(doc), []byte(doc))
|
||
|
|
||
|
// if err != nil {
|
||
|
// t.Errorf("Unexpected error: %s, %s", err, string(res))
|
||
|
// }
|
||
|
|
||
|
// // We cannot use "compareJSON", since Equals does not report a difference if the value is null
|
||
|
// if empty != string(res) {
|
||
|
// t.Fatalf("Did not get empty result, was:%s", string(res))
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergeComplexAddAll(t *testing.T) {
|
||
|
// doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }`
|
||
|
// empty := `{}`
|
||
|
// res, err := CreateMergePatch([]byte(empty), []byte(doc))
|
||
|
|
||
|
// if err != nil {
|
||
|
// t.Errorf("Unexpected error: %s, %s", err, string(res))
|
||
|
// }
|
||
|
|
||
|
// if !compareJSON(doc, string(res)) {
|
||
|
// t.Fatalf("Did not get everything as, it was:\n%s", string(res))
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergeComplexRemoveAll(t *testing.T) {
|
||
|
// doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }`
|
||
|
// exp := `{"a":null,"f":null,"hello":null,"i":null,"n":null,"nested":null,"pi":null,"t":null}`
|
||
|
// empty := `{}`
|
||
|
// res, err := CreateMergePatch([]byte(doc), []byte(empty))
|
||
|
|
||
|
// if err != nil {
|
||
|
// t.Errorf("Unexpected error: %s, %s", err, string(res))
|
||
|
// }
|
||
|
|
||
|
// if exp != string(res) {
|
||
|
// t.Fatalf("Did not get result, was:%s", string(res))
|
||
|
// }
|
||
|
|
||
|
// // FIXME: Crashes if using compareJSON like this:
|
||
|
// /*
|
||
|
// if !compareJSON(doc, string(res)) {
|
||
|
// t.Fatalf("Did not get everything as, it was:\n%s", string(res))
|
||
|
// }
|
||
|
// */
|
||
|
// }
|
||
|
|
||
|
// func TestMergeObjectWithInnerArray(t *testing.T) {
|
||
|
// stateString := `{
|
||
|
// "OuterArray": [
|
||
|
// {
|
||
|
// "InnerArray": [
|
||
|
// {
|
||
|
// "StringAttr": "abc123"
|
||
|
// }
|
||
|
// ],
|
||
|
// "StringAttr": "def456"
|
||
|
// }
|
||
|
// ]
|
||
|
// }`
|
||
|
|
||
|
// patch, err := CreateMergePatch([]byte(stateString), []byte(stateString))
|
||
|
// if err != nil {
|
||
|
// t.Fatal(err)
|
||
|
// }
|
||
|
|
||
|
// if string(patch) != "{}" {
|
||
|
// t.Fatalf("Patch should have been {} but was: %v", string(patch))
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergeReplaceKeyRequiringEscape(t *testing.T) {
|
||
|
// doc := `{ "title": "hello", "nested": {"title/escaped": 1, "two": 2} }`
|
||
|
// pat := `{ "title": "goodbye", "nested": {"title/escaped": 2, "two": 2} }`
|
||
|
|
||
|
// exp := `{ "title": "goodbye", "nested": {"title~1escaped": 2} }`
|
||
|
|
||
|
// res, err := CreateMergePatch([]byte(doc), []byte(pat))
|
||
|
|
||
|
// if err != nil {
|
||
|
// t.Errorf("Unexpected error: %s, %s", err, string(res))
|
||
|
// }
|
||
|
|
||
|
// if !compareJSON(exp, string(res)) {
|
||
|
// t.Log(string(res))
|
||
|
// t.Fatalf("Key was not replaced")
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// func TestMergePatchReplaceKeyRequiringEscaping(t *testing.T) {
|
||
|
// doc := `{ "obj": { "title/escaped": "hello" } }`
|
||
|
// pat := `{ "obj": { "title~1escaped": "goodbye" } }`
|
||
|
// exp := `{ "obj": { "title/escaped": "goodbye" } }`
|
||
|
|
||
|
// res := mergePatch(doc, pat)
|
||
|
|
||
|
// if !compareJSON(exp, res) {
|
||
|
// t.Fatalf("Key was not replaced")
|
||
|
// }
|
||
|
// }
|