...

Source file src/github.com/evanphx/json-patch/merge_test.go

Documentation: github.com/evanphx/json-patch

     1  package jsonpatch
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  )
     8  
     9  func mergePatch(doc, patch string) string {
    10  	out, err := MergePatch([]byte(doc), []byte(patch))
    11  
    12  	if err != nil {
    13  		panic(err)
    14  	}
    15  
    16  	return string(out)
    17  }
    18  
    19  func TestMergePatchReplaceKey(t *testing.T) {
    20  	doc := `{ "title": "hello" }`
    21  	pat := `{ "title": "goodbye" }`
    22  
    23  	res := mergePatch(doc, pat)
    24  
    25  	if !compareJSON(pat, res) {
    26  		t.Fatalf("Key was not replaced")
    27  	}
    28  }
    29  
    30  func TestMergePatchIgnoresOtherValues(t *testing.T) {
    31  	doc := `{ "title": "hello", "age": 18 }`
    32  	pat := `{ "title": "goodbye" }`
    33  
    34  	res := mergePatch(doc, pat)
    35  
    36  	exp := `{ "title": "goodbye", "age": 18 }`
    37  
    38  	if !compareJSON(exp, res) {
    39  		t.Fatalf("Key was not replaced")
    40  	}
    41  }
    42  
    43  func TestMergePatchNilDoc(t *testing.T) {
    44  	doc := `{ "title": null }`
    45  	pat := `{ "title": {"foo": "bar"} }`
    46  
    47  	res := mergePatch(doc, pat)
    48  
    49  	exp := `{ "title": {"foo": "bar"} }`
    50  
    51  	if !compareJSON(exp, res) {
    52  		t.Fatalf("Key was not replaced")
    53  	}
    54  }
    55  
    56  type arrayCases struct {
    57  	original, patch, res string
    58  }
    59  
    60  func TestMergePatchNilArray(t *testing.T) {
    61  
    62  	cases := []arrayCases {
    63  		{`{"a": [ {"b":"c"} ] }`, `{"a": [1]}`, `{"a": [1]}`},
    64  		{`{"a": [ {"b":"c"} ] }`, `{"a": [null, 1]}`, `{"a": [null, 1]}`},
    65  		{`["a",null]`, `[null]`, `[null]`},
    66  		{`["a"]`, `[null]`, `[null]`},
    67  		{`["a", "b"]`, `["a", null]`, `["a", null]`},
    68  		{`{"a":["b"]}`, `{"a": ["b", null]}`, `{"a":["b", null]}`},
    69  		{`{"a":[]}`, `{"a": ["b", null, null, "a"]}`, `{"a":["b", null, null, "a"]}`},
    70  	}
    71  
    72  	for _, c := range cases {
    73  		act := mergePatch(c.original, c.patch)
    74  
    75  		if !compareJSON(c.res, act) {
    76  			t.Errorf("null values not preserved in array")
    77  		}
    78  	}
    79  }
    80  
    81  func TestMergePatchRecursesIntoObjects(t *testing.T) {
    82  	doc := `{ "person": { "title": "hello", "age": 18 } }`
    83  	pat := `{ "person": { "title": "goodbye" } }`
    84  
    85  	res := mergePatch(doc, pat)
    86  
    87  	exp := `{ "person": { "title": "goodbye", "age": 18 } }`
    88  
    89  	if !compareJSON(exp, res) {
    90  		t.Fatalf("Key was not replaced")
    91  	}
    92  }
    93  
    94  type nonObjectCases struct {
    95  	doc, pat, res string
    96  }
    97  
    98  func TestMergePatchReplacesNonObjectsWholesale(t *testing.T) {
    99  	a1 := `[1]`
   100  	a2 := `[2]`
   101  	o1 := `{ "a": 1 }`
   102  	o2 := `{ "a": 2 }`
   103  	o3 := `{ "a": 1, "b": 1 }`
   104  	o4 := `{ "a": 2, "b": 1 }`
   105  
   106  	cases := []nonObjectCases{
   107  		{a1, a2, a2},
   108  		{o1, a2, a2},
   109  		{a1, o1, o1},
   110  		{o3, o2, o4},
   111  	}
   112  
   113  	for _, c := range cases {
   114  		act := mergePatch(c.doc, c.pat)
   115  
   116  		if !compareJSON(c.res, act) {
   117  			t.Errorf("whole object replacement failed")
   118  		}
   119  	}
   120  }
   121  
   122  func TestMergePatchReturnsErrorOnBadJSON(t *testing.T) {
   123  	_, err := MergePatch([]byte(`[[[[`), []byte(`1`))
   124  
   125  	if err == nil {
   126  		t.Errorf("Did not return an error for bad json: %s", err)
   127  	}
   128  
   129  	_, err = MergePatch([]byte(`1`), []byte(`[[[[`))
   130  
   131  	if err == nil {
   132  		t.Errorf("Did not return an error for bad json: %s", err)
   133  	}
   134  }
   135  
   136  func TestMergePatchReturnsEmptyArrayOnEmptyArray(t *testing.T) {
   137  	doc := `{ "array": ["one", "two"] }`
   138  	pat := `{ "array": [] }`
   139  
   140  	exp := `{ "array": [] }`
   141  
   142  	res, err := MergePatch([]byte(doc), []byte(pat))
   143  
   144  	if err != nil {
   145  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   146  	}
   147  
   148  	if !compareJSON(exp, string(res)) {
   149  		t.Fatalf("Emtpy array did not return not return as empty array")
   150  	}
   151  }
   152  
   153  var rfcTests = []struct {
   154  	target   string
   155  	patch    string
   156  	expected string
   157  }{
   158  	// test cases from https://tools.ietf.org/html/rfc7386#appendix-A
   159  	{target: `{"a":"b"}`, patch: `{"a":"c"}`, expected: `{"a":"c"}`},
   160  	{target: `{"a":"b"}`, patch: `{"b":"c"}`, expected: `{"a":"b","b":"c"}`},
   161  	{target: `{"a":"b"}`, patch: `{"a":null}`, expected: `{}`},
   162  	{target: `{"a":"b","b":"c"}`, patch: `{"a":null}`, expected: `{"b":"c"}`},
   163  	{target: `{"a":["b"]}`, patch: `{"a":"c"}`, expected: `{"a":"c"}`},
   164  	{target: `{"a":"c"}`, patch: `{"a":["b"]}`, expected: `{"a":["b"]}`},
   165  	{target: `{"a":{"b": "c"}}`, patch: `{"a": {"b": "d","c": null}}`, expected: `{"a":{"b":"d"}}`},
   166  	{target: `{"a":[{"b":"c"}]}`, patch: `{"a":[1]}`, expected: `{"a":[1]}`},
   167  	{target: `["a","b"]`, patch: `["c","d"]`, expected: `["c","d"]`},
   168  	{target: `{"a":"b"}`, patch: `["c"]`, expected: `["c"]`},
   169  	// {target: `{"a":"foo"}`, patch: `null`, expected: `null`},
   170  	// {target: `{"a":"foo"}`, patch: `"bar"`, expected: `"bar"`},
   171  	{target: `{"e":null}`, patch: `{"a":1}`, expected: `{"a":1,"e":null}`},
   172  	{target: `[1,2]`, patch: `{"a":"b","c":null}`, expected: `{"a":"b"}`},
   173  	{target: `{}`, patch: `{"a":{"bb":{"ccc":null}}}`, expected: `{"a":{"bb":{}}}`},
   174  }
   175  
   176  func TestMergePatchRFCCases(t *testing.T) {
   177  	for i, c := range rfcTests {
   178  		out := mergePatch(c.target, c.patch)
   179  
   180  		if !compareJSON(out, c.expected) {
   181  			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)
   182  		}
   183  	}
   184  }
   185  
   186  var rfcFailTests = `
   187       {"a":"foo"}  |   null
   188       {"a":"foo"}  |   "bar"
   189  `
   190  
   191  func TestMergePatchFailRFCCases(t *testing.T) {
   192  	tests := strings.Split(rfcFailTests, "\n")
   193  
   194  	for _, c := range tests {
   195  		if strings.TrimSpace(c) == "" {
   196  			continue
   197  		}
   198  
   199  		parts := strings.SplitN(c, "|", 2)
   200  
   201  		doc := strings.TrimSpace(parts[0])
   202  		pat := strings.TrimSpace(parts[1])
   203  
   204  		out, err := MergePatch([]byte(doc), []byte(pat))
   205  
   206  		if err != ErrBadJSONPatch {
   207  			t.Errorf("error not returned properly: %s, %s", err, string(out))
   208  		}
   209  	}
   210  
   211  }
   212  
   213  func TestResembleJSONArray(t *testing.T) {
   214  	testCases := []struct {
   215  		input    []byte
   216  		expected bool
   217  	}{
   218  		// Failure cases
   219  		{input: []byte(``), expected: false},
   220  		{input: []byte(`not an array`), expected: false},
   221  		{input: []byte(`{"foo": "bar"}`), expected: false},
   222  		{input: []byte(`{"fizz": ["buzz"]}`), expected: false},
   223  		{input: []byte(`[bad suffix`), expected: false},
   224  		{input: []byte(`bad prefix]`), expected: false},
   225  		{input: []byte(`][`), expected: false},
   226  
   227  		// Valid cases
   228  		{input: []byte(`[]`), expected: true},
   229  		{input: []byte(`["foo", "bar"]`), expected: true},
   230  		{input: []byte(`[["foo", "bar"]]`), expected: true},
   231  		{input: []byte(`[not valid syntax]`), expected: true},
   232  
   233  		// Valid cases with whitespace
   234  		{input: []byte(`      []`), expected: true},
   235  		{input: []byte(`[]      `), expected: true},
   236  		{input: []byte(`      []      `), expected: true},
   237  		{input: []byte(`      [        ]      `), expected: true},
   238  		{input: []byte("\t[]"), expected: true},
   239  		{input: []byte("[]\n"), expected: true},
   240  		{input: []byte("\n\t\r[]"), expected: true},
   241  	}
   242  
   243  	for _, test := range testCases {
   244  		result := resemblesJSONArray(test.input)
   245  		if result != test.expected {
   246  			t.Errorf(
   247  				`expected "%t" but received "%t" for case: "%s"`,
   248  				test.expected,
   249  				result,
   250  				string(test.input),
   251  			)
   252  		}
   253  	}
   254  }
   255  
   256  func TestCreateMergePatchReplaceKey(t *testing.T) {
   257  	doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }`
   258  	pat := `{ "title": "goodbye", "nested": {"one": 2, "two": 2}  }`
   259  
   260  	exp := `{ "title": "goodbye", "nested": {"one": 2}  }`
   261  
   262  	res, err := CreateMergePatch([]byte(doc), []byte(pat))
   263  
   264  	if err != nil {
   265  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   266  	}
   267  
   268  	if !compareJSON(exp, string(res)) {
   269  		t.Fatalf("Key was not replaced")
   270  	}
   271  }
   272  
   273  func TestCreateMergePatchGetArray(t *testing.T) {
   274  	doc := `{ "title": "hello", "array": ["one", "two"], "notmatch": [1, 2, 3] }`
   275  	pat := `{ "title": "hello", "array": ["one", "two", "three"], "notmatch": [1, 2, 3]  }`
   276  
   277  	exp := `{ "array": ["one", "two", "three"] }`
   278  
   279  	res, err := CreateMergePatch([]byte(doc), []byte(pat))
   280  
   281  	if err != nil {
   282  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   283  	}
   284  
   285  	if !compareJSON(exp, string(res)) {
   286  		t.Fatalf("Array was not added")
   287  	}
   288  }
   289  
   290  func TestCreateMergePatchGetObjArray(t *testing.T) {
   291  	doc := `{ "title": "hello", "array": [{"banana": true}, {"evil": false}], "notmatch": [{"one":1}, {"two":2}, {"three":3}] }`
   292  	pat := `{ "title": "hello", "array": [{"banana": false}, {"evil": true}], "notmatch": [{"one":1}, {"two":2}, {"three":3}] }`
   293  
   294  	exp := `{  "array": [{"banana": false}, {"evil": true}] }`
   295  
   296  	res, err := CreateMergePatch([]byte(doc), []byte(pat))
   297  
   298  	if err != nil {
   299  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   300  	}
   301  
   302  	if !compareJSON(exp, string(res)) {
   303  		t.Fatalf("Object array was not added")
   304  	}
   305  }
   306  
   307  func TestCreateMergePatchDeleteKey(t *testing.T) {
   308  	doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }`
   309  	pat := `{ "title": "hello", "nested": {"one": 1}  }`
   310  
   311  	exp := `{"nested":{"two":null}}`
   312  
   313  	res, err := CreateMergePatch([]byte(doc), []byte(pat))
   314  
   315  	if err != nil {
   316  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   317  	}
   318  
   319  	// We cannot use "compareJSON", since Equals does not report a difference if the value is null
   320  	if exp != string(res) {
   321  		t.Fatalf("Key was not removed")
   322  	}
   323  }
   324  
   325  func TestCreateMergePatchEmptyArray(t *testing.T) {
   326  	doc := `{ "array": null }`
   327  	pat := `{ "array": [] }`
   328  
   329  	exp := `{"array":[]}`
   330  
   331  	res, err := CreateMergePatch([]byte(doc), []byte(pat))
   332  
   333  	if err != nil {
   334  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   335  	}
   336  
   337  	// We cannot use "compareJSON", since Equals does not report a difference if the value is null
   338  	if exp != string(res) {
   339  		t.Fatalf("Key was not removed")
   340  	}
   341  }
   342  
   343  func TestCreateMergePatchNil(t *testing.T) {
   344  	doc := `{ "title": "hello", "nested": {"one": 1, "two": [{"one":null}, {"two":null}, {"three":null}]} }`
   345  	pat := doc
   346  
   347  	exp := `{}`
   348  
   349  	res, err := CreateMergePatch([]byte(doc), []byte(pat))
   350  
   351  	if err != nil {
   352  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   353  	}
   354  
   355  	if !compareJSON(exp, string(res)) {
   356  		t.Fatalf("Object array was not added")
   357  	}
   358  }
   359  
   360  func TestCreateMergePatchObjArray(t *testing.T) {
   361  	doc := `{ "array": [ {"a": {"b": 2}}, {"a": {"b": 3}} ]}`
   362  	exp := `{}`
   363  
   364  	res, err := CreateMergePatch([]byte(doc), []byte(doc))
   365  
   366  	if err != nil {
   367  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   368  	}
   369  
   370  	// We cannot use "compareJSON", since Equals does not report a difference if the value is null
   371  	if exp != string(res) {
   372  		t.Fatalf("Array was not empty, was " + string(res))
   373  	}
   374  }
   375  
   376  func TestCreateMergePatchSameOuterArray(t *testing.T) {
   377  	doc := `[{"foo": "bar"}]`
   378  	pat := doc
   379  	exp := `[{}]`
   380  
   381  	res, err := CreateMergePatch([]byte(doc), []byte(pat))
   382  
   383  	if err != nil {
   384  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   385  	}
   386  
   387  	if !compareJSON(exp, string(res)) {
   388  		t.Fatalf("Outer array was not unmodified")
   389  	}
   390  }
   391  
   392  func TestCreateMergePatchModifiedOuterArray(t *testing.T) {
   393  	doc := `[{"name": "John"}, {"name": "Will"}]`
   394  	pat := `[{"name": "Jane"}, {"name": "Will"}]`
   395  	exp := `[{"name": "Jane"}, {}]`
   396  
   397  	res, err := CreateMergePatch([]byte(doc), []byte(pat))
   398  
   399  	if err != nil {
   400  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   401  	}
   402  
   403  	if !compareJSON(exp, string(res)) {
   404  		t.Fatalf("Expected %s but received %s", exp, res)
   405  	}
   406  }
   407  
   408  func TestCreateMergePatchMismatchedOuterArray(t *testing.T) {
   409  	doc := `[{"name": "John"}, {"name": "Will"}]`
   410  	pat := `[{"name": "Jane"}]`
   411  
   412  	_, err := CreateMergePatch([]byte(doc), []byte(pat))
   413  
   414  	if err == nil {
   415  		t.Errorf("Expected error due to array length differences but received none")
   416  	}
   417  }
   418  
   419  func TestCreateMergePatchMismatchedOuterTypes(t *testing.T) {
   420  	doc := `[{"name": "John"}]`
   421  	pat := `{"name": "Jane"}`
   422  
   423  	_, err := CreateMergePatch([]byte(doc), []byte(pat))
   424  
   425  	if err == nil {
   426  		t.Errorf("Expected error due to mismatched types but received none")
   427  	}
   428  }
   429  
   430  func TestCreateMergePatchNoDifferences(t *testing.T) {
   431  	doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }`
   432  	pat := doc
   433  
   434  	exp := `{}`
   435  
   436  	res, err := CreateMergePatch([]byte(doc), []byte(pat))
   437  
   438  	if err != nil {
   439  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   440  	}
   441  
   442  	if !compareJSON(exp, string(res)) {
   443  		t.Fatalf("Key was not replaced")
   444  	}
   445  }
   446  
   447  func TestCreateMergePatchComplexMatch(t *testing.T) {
   448  	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]} }`
   449  	empty := `{}`
   450  	res, err := CreateMergePatch([]byte(doc), []byte(doc))
   451  
   452  	if err != nil {
   453  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   454  	}
   455  
   456  	// We cannot use "compareJSON", since Equals does not report a difference if the value is null
   457  	if empty != string(res) {
   458  		t.Fatalf("Did not get empty result, was:%s", string(res))
   459  	}
   460  }
   461  
   462  func TestCreateMergePatchComplexAddAll(t *testing.T) {
   463  	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]} }`
   464  	empty := `{}`
   465  	res, err := CreateMergePatch([]byte(empty), []byte(doc))
   466  
   467  	if err != nil {
   468  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   469  	}
   470  
   471  	if !compareJSON(doc, string(res)) {
   472  		t.Fatalf("Did not get everything as, it was:\n%s", string(res))
   473  	}
   474  }
   475  
   476  // createNestedMap created a series of nested map objects such that the number of
   477  // objects is roughly 2^n (precisely, 2^(n+1)-1).
   478  func createNestedMap(m map[string]interface{}, depth int, objectCount *int) {
   479  	if depth == 0 {
   480  		return
   481  	}
   482  	for i := 0; i < 2; i++ {
   483  		nested := map[string]interface{}{}
   484  		*objectCount += 1
   485  		createNestedMap(nested, depth-1, objectCount)
   486  		m[fmt.Sprintf("key-%v", i)] = nested
   487  	}
   488  }
   489  
   490  func TestMatchesValue(t *testing.T) {
   491  	testcases := []struct {
   492  		name string
   493  		a    interface{}
   494  		b    interface{}
   495  		want bool
   496  	}{
   497  		{
   498  			name: "map empty",
   499  			a:    map[string]interface{}{},
   500  			b:    map[string]interface{}{},
   501  			want: true,
   502  		},
   503  		{
   504  			name: "map equal keys, equal non-nil value",
   505  			a:    map[string]interface{}{"1": true},
   506  			b:    map[string]interface{}{"1": true},
   507  			want: true,
   508  		},
   509  		{
   510  			name: "map equal keys, equal nil value",
   511  			a:    map[string]interface{}{"1": nil},
   512  			b:    map[string]interface{}{"1": nil},
   513  			want: true,
   514  		},
   515  
   516  		{
   517  			name: "map different value",
   518  			a:    map[string]interface{}{"1": true},
   519  			b:    map[string]interface{}{"1": false},
   520  			want: false,
   521  		},
   522  		{
   523  			name: "map different key, matching non-nil value",
   524  			a:    map[string]interface{}{"1": true},
   525  			b:    map[string]interface{}{"2": true},
   526  			want: false,
   527  		},
   528  		{
   529  			name: "map different key, matching nil value",
   530  			a:    map[string]interface{}{"1": nil},
   531  			b:    map[string]interface{}{"2": nil},
   532  			want: false,
   533  		},
   534  		{
   535  			name: "map different key, first nil value",
   536  			a:    map[string]interface{}{"1": true},
   537  			b:    map[string]interface{}{"2": nil},
   538  			want: false,
   539  		},
   540  		{
   541  			name: "map different key, second nil value",
   542  			a:    map[string]interface{}{"1": nil},
   543  			b:    map[string]interface{}{"2": true},
   544  			want: false,
   545  		},
   546  	}
   547  	for _, tc := range testcases {
   548  		t.Run(tc.name, func(t *testing.T) {
   549  			got := matchesValue(tc.a, tc.b)
   550  			if got != tc.want {
   551  				t.Fatalf("want %v, got %v", tc.want, got)
   552  			}
   553  		})
   554  	}
   555  }
   556  
   557  func benchmarkMatchesValueWithDeeplyNestedFields(depth int, b *testing.B) {
   558  	a := map[string]interface{}{}
   559  	objCount := 1
   560  	createNestedMap(a, depth, &objCount)
   561  	b.ResetTimer()
   562  	b.Run(fmt.Sprintf("objectCount=%v", objCount), func(b *testing.B) {
   563  		for i := 0; i < b.N; i++ {
   564  			if !matchesValue(a, a) {
   565  				b.Errorf("Should be equal")
   566  			}
   567  		}
   568  	})
   569  }
   570  
   571  func BenchmarkMatchesValue1(b *testing.B)  { benchmarkMatchesValueWithDeeplyNestedFields(1, b) }
   572  func BenchmarkMatchesValue2(b *testing.B)  { benchmarkMatchesValueWithDeeplyNestedFields(2, b) }
   573  func BenchmarkMatchesValue3(b *testing.B)  { benchmarkMatchesValueWithDeeplyNestedFields(3, b) }
   574  func BenchmarkMatchesValue4(b *testing.B)  { benchmarkMatchesValueWithDeeplyNestedFields(4, b) }
   575  func BenchmarkMatchesValue5(b *testing.B)  { benchmarkMatchesValueWithDeeplyNestedFields(5, b) }
   576  func BenchmarkMatchesValue6(b *testing.B)  { benchmarkMatchesValueWithDeeplyNestedFields(6, b) }
   577  func BenchmarkMatchesValue7(b *testing.B)  { benchmarkMatchesValueWithDeeplyNestedFields(7, b) }
   578  func BenchmarkMatchesValue8(b *testing.B)  { benchmarkMatchesValueWithDeeplyNestedFields(8, b) }
   579  func BenchmarkMatchesValue9(b *testing.B)  { benchmarkMatchesValueWithDeeplyNestedFields(9, b) }
   580  func BenchmarkMatchesValue10(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(10, b) }
   581  
   582  func TestCreateMergePatchComplexRemoveAll(t *testing.T) {
   583  	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]} }`
   584  	exp := `{"a":null,"f":null,"hello":null,"i":null,"n":null,"nested":null,"pi":null,"t":null}`
   585  	empty := `{}`
   586  	res, err := CreateMergePatch([]byte(doc), []byte(empty))
   587  
   588  	if err != nil {
   589  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   590  	}
   591  
   592  	if exp != string(res) {
   593  		t.Fatalf("Did not get result, was:%s", string(res))
   594  	}
   595  
   596  	// FIXME: Crashes if using compareJSON like this:
   597  	/*
   598  		if !compareJSON(doc, string(res)) {
   599  			t.Fatalf("Did not get everything as, it was:\n%s", string(res))
   600  		}
   601  	*/
   602  }
   603  
   604  func TestCreateMergePatchObjectWithInnerArray(t *testing.T) {
   605  	stateString := `{
   606  	  "OuterArray": [
   607  	    {
   608  		  "InnerArray": [
   609  	        {
   610  	          "StringAttr": "abc123"
   611  	        }
   612  	      ],
   613  	      "StringAttr": "def456"
   614  	    }
   615  	  ]
   616  	}`
   617  
   618  	patch, err := CreateMergePatch([]byte(stateString), []byte(stateString))
   619  	if err != nil {
   620  		t.Fatal(err)
   621  	}
   622  
   623  	if string(patch) != "{}" {
   624  		t.Fatalf("Patch should have been {} but was: %v", string(patch))
   625  	}
   626  }
   627  
   628  func TestCreateMergePatchReplaceKeyNotEscape(t *testing.T) {
   629  	doc := `{ "title": "hello", "nested": {"title/escaped": 1, "two": 2} }`
   630  	pat := `{ "title": "goodbye", "nested": {"title/escaped": 2, "two": 2}  }`
   631  
   632  	exp := `{ "title": "goodbye", "nested": {"title/escaped": 2}  }`
   633  
   634  	res, err := CreateMergePatch([]byte(doc), []byte(pat))
   635  
   636  	if err != nil {
   637  		t.Errorf("Unexpected error: %s, %s", err, string(res))
   638  	}
   639  
   640  	if !compareJSON(exp, string(res)) {
   641  		t.Log(string(res))
   642  		t.Fatalf("Key was not replaced")
   643  	}
   644  }
   645  
   646  func TestMergePatchReplaceKeyNotEscaping(t *testing.T) {
   647  	doc := `{ "obj": { "title/escaped": "hello" } }`
   648  	pat := `{ "obj": { "title/escaped": "goodbye" } }`
   649  	exp := `{ "obj": { "title/escaped": "goodbye" } }`
   650  
   651  	res := mergePatch(doc, pat)
   652  
   653  	if !compareJSON(exp, res) {
   654  		t.Fatalf("Key was not replaced")
   655  	}
   656  }
   657  
   658  func TestMergeMergePatches(t *testing.T) {
   659  	cases := []struct {
   660  		demonstrates string
   661  		p1           string
   662  		p2           string
   663  		exp          string
   664  	}{
   665  		{
   666  			demonstrates: "simple patches are merged normally",
   667  			p1:           `{"add1": 1}`,
   668  			p2:           `{"add2": 2}`,
   669  			exp:          `{"add1": 1, "add2": 2}`,
   670  		},
   671  		{
   672  			demonstrates: "nulls are kept",
   673  			p1:           `{"del1": null}`,
   674  			p2:           `{"del2": null}`,
   675  			exp:          `{"del1": null, "del2": null}`,
   676  		},
   677  		{
   678  			demonstrates: "nulls are kept in complex objects",
   679  			p1:           `{}`,
   680  			p2:           `{"request":{"object":{"complex_object_array":["value1","value2","value3"],"complex_object_map":{"key1":"value1","key2":"value2","key3":"value3"},"simple_object_bool":false,"simple_object_float":-5.5,"simple_object_int":5,"simple_object_null":null,"simple_object_string":"example"}}}`,
   681  			exp:          `{"request":{"object":{"complex_object_array":["value1","value2","value3"],"complex_object_map":{"key1":"value1","key2":"value2","key3":"value3"},"simple_object_bool":false,"simple_object_float":-5.5,"simple_object_int":5,"simple_object_null":null,"simple_object_string":"example"}}}`,
   682  		},
   683  		{
   684  			demonstrates: "a key added then deleted is kept deleted",
   685  			p1:           `{"add_then_delete": "atd"}`,
   686  			p2:           `{"add_then_delete": null}`,
   687  			exp:          `{"add_then_delete": null}`,
   688  		},
   689  		{
   690  			demonstrates: "a key deleted then added is kept added",
   691  			p1:           `{"delete_then_add": null}`,
   692  			p2:           `{"delete_then_add": "dta"}`,
   693  			exp:          `{"delete_then_add": "dta"}`,
   694  		},
   695  		{
   696  			demonstrates: "object overrides array",
   697  			p1:           `[]`,
   698  			p2:           `{"del": null, "add": "a"}`,
   699  			exp:          `{"del": null, "add": "a"}`,
   700  		},
   701  		{
   702  			demonstrates: "array overrides object",
   703  			p1:           `{"del": null, "add": "a"}`,
   704  			p2:           `[]`,
   705  			exp:          `[]`,
   706  		},
   707  	}
   708  
   709  	for _, c := range cases {
   710  		out, err := MergeMergePatches([]byte(c.p1), []byte(c.p2))
   711  
   712  		if err != nil {
   713  			panic(err)
   714  		}
   715  
   716  		if !compareJSON(c.exp, string(out)) {
   717  			t.Logf("Error while trying to demonstrate: %v", c.demonstrates)
   718  			t.Logf("Got %v", string(out))
   719  			t.Logf("Expected %v", c.exp)
   720  			t.Fatalf("Merged merge patch is incorrect")
   721  		}
   722  	}
   723  }
   724  

View as plain text