func Test_jsonpatch_Add_WithNonExistentParent_is_error(t *testing.T) {
	g := NewWithT(t)
	patch1, _ := jsonpatch.DecodePatch([]byte(`
		[{"op":"add", "path":"/level1/level2/test_key", "value":"qwe"}]
	`))
	origDoc := []byte(`{"bar":"foo"}`)
	_, err := patch1.Apply(origDoc)
	g.Expect(err).Should(HaveOccurred(), "patch apply")
}
Applying op:add of path with non-existent parents should give error.
No auto instantiating!
package utils
import (
	"encoding/json"
	"reflect"
	"strings"
	"testing"
	jsonpatch "github.com/evanphx/json-patch"
	"github.com/stretchr/testify/assert"
	. "github.com/onsi/gomega"
)
func Test_JsonPatchFromString(t *testing.T) {
	g := NewWithT(t)
	input := `{"op":"add","path":"/key","value":"foobar"}
 [{"op":"remove","path":"/key"},
   {"op":"add","path":"/key","value":"bazzz"}]
{"op":"remove","path":"/key2"}
`
	patch, err := JsonPatchFromString(input)
	g.Expect(err).ShouldNot(HaveOccurred())
	g.Expect(patch).Should(HaveLen(4))
}
func Test_ValuesPatchFromBytes(t *testing.T) {
	g := NewWithT(t)
	input := `{"op":"add","path":"/key","value":"foobar"}
 [{"op":"remove","path":"/key"},
   {"op":"add","path":"/key","value":"bazzz"}]
{"op":"remove","path":"/key2"}
`
	vp, err := ValuesPatchFromBytes([]byte(input))
	g.Expect(err).ShouldNot(HaveOccurred())
	g.Expect(vp.Operations).Should(HaveLen(4))
}
func Test_ApplyValuesPatch(t *testing.T) {
	tests := []struct {
		name                  string
		operations            ValuesPatch
		values                Values
		expectedValues        Values
		expectedValuesChanged bool
	}{
		{
			"add",
			ValuesPatch{
				[]*ValuesPatchOperation{
					{
						Op:    "add",
						Path:  "/test_key_3",
						Value: "baz",
					},
				},
			},
			Values{
				"test_key_1": "foo",
				"test_key_2": "bar",
			},
			Values{
				"test_key_1": "foo",
				"test_key_2": "bar",
				"test_key_3": "baz",
			},
			true,
		},
		{
			"remove",
			ValuesPatch{
				[]*ValuesPatchOperation{
					{
						Op:    "remove",
						Path:  "/test_key_3",
						Value: "baz",
					},
				},
			},
			Values{
				"test_key_1": "foo",
				"test_key_2": "bar",
			},
			Values{
				"test_key_1": "foo",
				"test_key_2": "bar",
			},
			false,
		},
		{
			"add+remove+add+remove+add",
			ValuesPatch{
				[]*ValuesPatchOperation{
					{
						Op:    "add",
						Path:  "/test_key_3",
						Value: "baz",
					},
					{
						Op:   "remove",
						Path: "/test_key_3",
					},
					{
						Op:    "add",
						Path:  "/test_key_3",
						Value: "baz",
					},
					{
						Op:   "remove",
						Path: "/test-parent/test_key_nonexist",
					},
					{
						Op:    "add",
						Path:  "/test_key_3",
						Value: "baz",
					},
				},
			},
			Values{
				"test_key_1": "foo",
				"test_key_2": "bar",
			},
			Values{
				"test_key_1": "foo",
				"test_key_2": "bar",
				"test_key_3": "baz",
			},
			true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			newValues, changed, err := ApplyValuesPatch(tt.values, tt.operations, IgnoreNonExistentPaths)
			if err != nil {
				t.Errorf("ApplyValuesPatch error: %s", err)
				return
			}
			if !reflect.DeepEqual(tt.expectedValues, newValues) {
				t.Errorf("\n[EXPECTED]: %#v\n[GOT]: %#v", tt.expectedValues, newValues)
			}
			if changed != tt.expectedValuesChanged {
				t.Errorf("\n[EXPECTED]: %#v\n[GOT]: %#v", tt.expectedValuesChanged, changed)
			}
		})
	}
}
func Test_ApplyValuesPatch_Strict(t *testing.T) {
	tests := []struct {
		name       string
		operations ValuesPatch
		values     Values
	}{
		{
			"remove",
			ValuesPatch{
				[]*ValuesPatchOperation{
					{
						Op:    "remove",
						Path:  "/test_key_3",
						Value: "baz",
					},
				},
			},
			Values{
				"test_key_1": "foo",
				"test_key_2": "bar",
			},
		},
		{
			"add+remove+add+remove+add",
			ValuesPatch{
				[]*ValuesPatchOperation{
					{
						Op:    "add",
						Path:  "/test_key_3",
						Value: "baz",
					},
					{
						Op:   "remove",
						Path: "/test_key_3",
					},
					{
						Op:    "add",
						Path:  "/test_key_3",
						Value: "baz",
					},
					{
						Op:   "remove",
						Path: "/test-parent/test_key_nonexist",
					},
					{
						Op:    "add",
						Path:  "/test_key_3",
						Value: "baz",
					},
				},
			},
			Values{
				"test_key_1": "foo",
				"test_key_2": "bar",
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, _, err := ApplyValuesPatch(tt.values, tt.operations, Strict)
			if err == nil {
				t.Errorf("ApplyValuesPatch in Strict mode should return error (%s)", tt.name)
			}
		})
	}
}
func Test_CompactPatches_Result(t *testing.T) {
	tests := []struct {
		name                     string
		valuesPatchOperations    []string
		newValuesPatchOperations []string
		expected                 string
	}{
		{
			"remove+add == add",
			[]string{
				`{"op":"remove", "path":"/test_key"}`,
			},
			[]string{
				`{"op":"add", "path":"/test_key", "value":"foo"}`,
			},
			`[{"op":"add", "path":"/test_key", "value":"foo"}]`,
		},
		{
			"add+remove == remove",
			[]string{
				`{"op":"add", "path":"/test_key", "value":"foo"}`,
			},
			[]string{
				`{"op":"remove", "path":"/test_key"}`,
			},
			`[{"op":"remove", "path":"/test_key"}]`,
		},
		{
			"add+remove+add == add",
			[]string{
				`{"op":"add", "path":"/test_key", "value":"foo"}`,
				`{"op":"remove", "path":"/test_key"}`,
			},
			[]string{
				`{"op":"add", "path":"/test_key", "value":"barbaz"}`,
			},
			`[{"op":"add", "path":"/test_key", "value":"barbaz"}]`,
		},
		{
			"add_1+add_2",
			[]string{
				`{"op":"add", "path":"/test_key", "value":"foo"}`,
			},
			[]string{
				`{"op":"add", "path":"/test_key_2", "value": "qwe"}`,
			},
			`[{"op":"add", "path":"/test_key", "value":"foo"}, {"op":"add", "path":"/test_key_2", "value": "qwe"}]`,
		},
		{
			"add_1+add_2+remove_1 == add_2+remove_1",
			[]string{
				`{"op":"add", "path":"/test_key", "value":"foo"}`,
				`{"op":"add", "path":"/test_key_2", "value": "qwe"}`,
			},
			[]string{
				`{"op":"remove", "path":"/test_key"}`,
			},
			`[{"op":"remove", "path":"/test_key"}, {"op":"add", "path":"/test_key_2", "value": "qwe"}]`,
		},
		{
			"add object + remove parent == remove parent",
			[]string{
				`{"op":"add", "path":"/test_obj", "value":{}}`,
				`{"op":"add", "path":"/test_obj/key1", "value":"foo"}`,
				`{"op":"add", "path":"/test_obj/key2", "value":"bar"}`,
			},
			[]string{
				`{"op":"remove", "path":"/test_obj"}`,
			},
			`[{"op":"remove", "path":"/test_obj"}]`,
		},
		{
			"add parent with keys + remove parent + add new object == add new object",
			[]string{
				`{"op":"add", "path":"/test_obj", "value":{}}`,
				`{"op":"add", "path":"/test_obj/key1", "value":"foo"}`,
			},
			[]string{
				`{"op":"add", "path":"/test_obj/key2", "value":"bar"}`,
				`{"op":"remove", "path":"/test_obj"}`,
				`{"op":"add", "path":"/test_obj", "value":{}}`,
				`{"op":"add", "path":"/test_obj/key3", "value":"foo"}`,
			},
			`[{"op":"add", "path":"/test_obj", "value":{}},{"op":"add", "path":"/test_obj/key3", "value":"foo"}]`,
		},
		{
			"add parent with keys + remove parent + add new array == add new array",
			[]string{
				`{"op":"add", "path":"/test_obj", "value":{}}`,
				`{"op":"add", "path":"/test_obj/key1", "value":"foo"}`,
			},
			[]string{
				`{"op":"add", "path":"/test_obj/key2", "value":"bar"}`,
				`{"op":"remove", "path":"/test_obj"}`,
				`{"op":"add", "path":"/test_obj", "value":[]}`,
				`{"op":"add", "path":"/test_obj/0", "value":"0"}`,
			},
			`[{"op":"add", "path":"/test_obj", "value":[]},{"op":"add", "path":"/test_obj/0", "value":"0"}]`,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			valuesPatch, _ := ValuesPatchFromBytes([]byte("[" + strings.Join(tt.valuesPatchOperations, ", ") + "]"))
			newValuesPatch, _ := ValuesPatchFromBytes([]byte(strings.Join(tt.newValuesPatchOperations, "")))
			newPatch := CompactValuesPatches([]ValuesPatch{*valuesPatch}, *newValuesPatch)
			newPatchBytes, err := json.Marshal(newPatch[0].Operations)
			if assert.NoError(t, err) {
				assert.True(t, jsonpatch.Equal(newPatchBytes, []byte(tt.expected)), "%s should be equal to %s", newPatchBytes, tt.expected)
			}
		})
	}
}
func Test_CompactPatches_Apply(t *testing.T) {
	tests := []struct {
		name     string
		patches  []string
		input    string
		expected string
	}{
		{
			"remove+add",
			[]string{
				`[{"op":"remove", "path":"/test_key"}]`,
				`[{"op":"add", "path":"/test_key", "value":"foo"}]`,
			},
			`{"test_key":"wqe"}`,
			`{"test_key":"foo"}`,
		},
		{
			"add+remove",
			[]string{
				`[{"op":"add", "path":"/test_key", "value":"foo"}]`,
				`[{"op":"remove", "path":"/test_key"}]`,
			},
			`{"test_key":"wqe"}`,
			`{}`,
		},
		{
			"add+add",
			[]string{
				`[{"op":"add", "path":"/test_key", "value":"foo"}]`,
				`[{"op":"add", "path":"/test_key_2", "value": "qwe"}]`,
			},
			`{"test_key":"wqe"}`,
			`{"test_key":"foo", "test_key_2":"qwe"}`,
		},
		{
			"add+add+remove",
			[]string{
				`[{"op":"add", "path":"/test_key", "value":"foo"}]`,
				`[{"op":"add", "path":"/test_key_2", "value": "qwe"}]`,
				`[{"op":"remove", "path":"/test_key"}]`,
			},
			`{"test_key":"/123"}`,
			`{"test_key_2":"qwe"}`,
		},
		{
			"add object + remove parent == remove parent",
			[]string{
				`[{"op":"add", "path":"/test_obj", "value":{}}]`,
				`[{"op":"add", "path":"/test_obj/key1", "value":"foo"}]`,
				`[{"op":"add", "path":"/test_obj/key2", "value":"bar"}]`,
				`[{"op":"remove", "path":"/test_obj"}]`,
			},
			`{"test_obj":{}}`,
			`{}`,
		},
		{
			"add parent with keys + remove parent + add new object == add new object",
			[]string{
				`[{"op":"add", "path":"/test_obj", "value":{}}]`,
				`[{"op":"add", "path":"/test_obj/key1", "value":"foo"}]`,
				`[{"op":"add", "path":"/test_obj/key2", "value":"bar"}]`,
				`[{"op":"remove", "path":"/test_obj"}]`,
				`[{"op":"add", "path":"/test_obj", "value":{}}]`,
				`[{"op":"add", "path":"/test_obj/key3", "value":"foo"}]`,
			},
			`{"test_obj":{}}`,
			`{"test_obj":{"key3":"foo"}}`,
		},
		{
			"add parent + remove parent + add new object == add new object",
			[]string{
				`[{"op":"add", "path":"/test_obj", "value":{}}]`,
				`[{"op":"add", "path":"/test_obj/key1", "value":"foo"}]`,
				`[{"op":"add", "path":"/test_obj/key2", "value":"bar"}]`,
				`[{"op":"remove", "path":"/test_obj"}]`,
				`[{"op":"add", "path":"/test_obj", "value":[]}]`,
				`[{"op":"add", "path":"/test_obj/0", "value":"foo"}]`,
			},
			`{"test_obj":{}}`,
			`{"test_obj":["foo"]}`,
		},
		{
			"add parent + remove parent + add new object == add new object",
			[]string{
				`[{"op":"add", "path":"/test_obj", "value":{}}]`,
				`[{"op":"add", "path":"/test_obj/key1", "value":"foo"}]`,
				`[{"op":"add", "path":"/test_obj/key2", "value":"bar"}]`,
				`[{"op":"remove", "path":"/test_obj"}]`,
				`[{"op":"add", "path":"/test_obj", "value":[]}]`,
				`[{"op":"add", "path":"/test_obj/0", "value":"foo"}]`,
			},
			`{"test_ob":{"foo":"bar"}, "test_object":{"foo":"bar"}, "test_obj":{}}`,
			`{"test_ob":{"foo":"bar"}, "test_object":{"foo":"bar"}, "test_obj":["foo"]}`,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var origPatchedDoc []byte = []byte(tt.input)
			var err error
			operations := []*ValuesPatchOperation{}
			for _, patch := range tt.patches {
				vp, _ := ValuesPatchFromBytes([]byte(patch))
				operations = append(operations, vp.Operations...)
				patchedDoc, err := vp.ApplyIgnoreNonExistentPaths([]byte(origPatchedDoc))
				if !assert.NoError(t, err) {
					t.Logf("%s should apply on %s", patch, origPatchedDoc)
					t.FailNow()
				}
				origPatchedDoc = patchedDoc
			}
			assert.True(t, jsonpatch.Equal(origPatchedDoc, []byte(tt.expected)), "%s should be equal to %s", origPatchedDoc, tt.expected)
			newPatch := CompactPatches(nil, operations)
			if !assert.NoError(t, err) {
				t.FailNow()
			}
			patched, err := newPatch.ApplyIgnoreNonExistentPaths([]byte(tt.input))
			if assert.NoError(t, err) {
				assert.True(t, jsonpatch.Equal(patched, []byte(tt.expected)), "%s should be equal to %s", patched, tt.expected)
			}
		})
	}
}
func Test_CompactPatches_add_empty_object(t *testing.T) {
	valuesPatchOperations := []string{
		`{"op":"add", "path":"/test_obj", "value":{"parent_1":{"key":"val"}, "parent_2":{"key":"val"}}}`,
		`{"op":"add", "path":"/test_obj/parent_1/key1", "value":"foo"}`,
		`{"op":"add", "path":"/test_obj/parent_2/key1", "value":"foo"}`,
	}
	newValuesPatchOperations := []string{
		`{"op":"add", "path":"/test_obj", "value":{}}`,
	}
	expected := `[{"op":"add", "path":"/test_obj", "value":{}}]`
	valuesPatch, _ := ValuesPatchFromBytes([]byte("[" + strings.Join(valuesPatchOperations, ", ") + "]"))
	newValuesPatch, _ := ValuesPatchFromBytes([]byte(strings.Join(newValuesPatchOperations, "")))
	newPatch := CompactValuesPatches([]ValuesPatch{*valuesPatch}, *newValuesPatch)
	newPatchBytes, err := json.Marshal(newPatch[0].Operations)
	if assert.NoError(t, err) {
		assert.True(t, jsonpatch.Equal(newPatchBytes, []byte(expected)), "%s should be equal to %s", newPatchBytes, expected)
	}
}
func Test_jsonpatch_Remove_NonExistent_IsError(t *testing.T) {
	g := NewWithT(t)
	origDoc := []byte(`{"foo":"bar"}`)
	rootKeyPatch := []byte(`[{"op":"remove", "path":"/test_key"}]`)
	withParentsPatch := []byte(`[{"op":"remove", "path":"/test_parent/test_sub/test_key"}]`)
	patch1, _ := jsonpatch.DecodePatch(rootKeyPatch)
	_, err := patch1.Apply(origDoc)
	g.Expect(err).Should(HaveOccurred(), "jsonpatch Apply should return error")
	rootKeyPrefix := NonExistentPathErrorMsg
	g.Expect(err.Error()).Should(HavePrefix(rootKeyPrefix), "json-patch Apply should return error with prefix '%s'. Consider checking source code if it fails after upgrade.", rootKeyPrefix)
	g.Expect(IsNonExistentPathError(err)).Should(BeTrue())
	patch2, _ := jsonpatch.DecodePatch(withParentsPatch)
	_, err = patch2.Apply(origDoc)
	g.Expect(err).Should(HaveOccurred(), "jsonpatch Apply should return error")
	withParentsPrefix := MissingPathErrorMsg
	g.Expect(err.Error()).Should(HavePrefix(withParentsPrefix), "json-patch Apply should return error with prefix '%s'. Consider checking source code if it fails after upgrade.", withParentsPrefix)
	g.Expect(IsNonExistentPathError(err)).Should(BeTrue())
	vp1, _ := ValuesPatchFromBytes(rootKeyPatch)
	_, err = vp1.ApplyIgnoreNonExistentPaths(origDoc)
	g.Expect(err).ShouldNot(HaveOccurred(), "values_patch Apply should not return error")
	vp2, _ := ValuesPatchFromBytes(withParentsPatch)
	_, err = vp2.ApplyIgnoreNonExistentPaths(origDoc)
	g.Expect(err).ShouldNot(HaveOccurred(), "values_patch Apply should not return error")
	vp3, _ := ValuesPatchFromBytes([]byte(`[{"op":"remove"}]`))
	_, err = vp3.ApplyIgnoreNonExistentPaths(origDoc)
	g.Expect(err).Should(HaveOccurred(), "values_patch Apply should return error on invalid path")
}
func Test_jsonpatch_Remove_ObjectAndArray(t *testing.T) {
	g := NewWithT(t)
	origDoc := []byte(`{
		"asd":"foof",
		"test_obj":{"color":"red", "model":"sedan"},
		"test_array":["uno", "deux", "three"]
    }`)
	patch1, _ := jsonpatch.DecodePatch([]byte(`[
		{"op":"remove", "path":"/test_obj"},
		{"op":"remove", "path":"/test_array"}
	]`))
	expectNewDoc := []byte(`{"asd":"foof"}`)
	newDoc, err := patch1.Apply(origDoc)
	g.Expect(err).ShouldNot(HaveOccurred(), "patch apply")
	g.Expect(jsonpatch.Equal(newDoc, expectNewDoc)).Should(BeTrue(), "%v is not equal to %v", string(newDoc), string(expectNewDoc))
}
func Test_jsonpatch_Replace_NonExistent_IsError(t *testing.T) {
	t.SkipNow()
	g := NewWithT(t)
	origDoc := []byte(`{"foo":"bar"}`)
	patch1, _ := jsonpatch.DecodePatch([]byte(`[{"op":"replace", "path":"/test_key", "value":"qwe"}]`))
	expectNewDoc := []byte(`{"foo":"bar"}`)
	newDoc, err := patch1.Apply(origDoc)
	g.Expect(err).Should(HaveOccurred(), "replace operation should ")
	g.Expect(jsonpatch.Equal(newDoc, expectNewDoc)).Should(BeTrue(), "%v is not equal to %v", string(newDoc), string(expectNewDoc))
}
func Test_jsonpatch_Replace_NonExistent_v4_9_0_behaviour_is_incorrect(t *testing.T) {
	g := NewWithT(t)
	origDoc := []byte(`{"foo":"bar"}`)
	patch1, _ := jsonpatch.DecodePatch([]byte(`[{"op":"replace", "path":"/test_key", "value":"qwe"}]`))
	expectNewDoc := []byte(`{"foo":"bar", "test_key":"qwe"}`)
	newDoc, err := patch1.Apply(origDoc)
	g.Expect(err).ShouldNot(HaveOccurred(), "replace operation should ")
	g.Expect(jsonpatch.Equal(newDoc, expectNewDoc)).Should(BeTrue(), "%v is not equal to %v", string(newDoc), string(expectNewDoc))
}