package main import ( "reflect" "testing" ) func TestJSONPatcher_Add(t *testing.T) { patcher := &JSONPatcher{} tests := []struct { name string doc map[string]interface{} patches []PatchOperation expected map[string]interface{} wantErr bool }{ { name: "add simple field", doc: map[string]interface{}{}, patches: []PatchOperation{ {Op: "add", Path: "/name", Value: "John"}, }, expected: map[string]interface{}{ "name": "John", }, wantErr: false, }, { name: "add nested field", doc: map[string]interface{}{ "user": map[string]interface{}{}, }, patches: []PatchOperation{ {Op: "add", Path: "/user/name", Value: "Jane"}, }, expected: map[string]interface{}{ "user": map[string]interface{}{ "name": "Jane", }, }, wantErr: false, }, { name: "add to existing field (overwrite)", doc: map[string]interface{}{ "name": "Old Name", }, patches: []PatchOperation{ {Op: "add", Path: "/name", Value: "New Name"}, }, expected: map[string]interface{}{ "name": "New Name", }, wantErr: false, }, { name: "add array field", doc: map[string]interface{}{}, patches: []PatchOperation{ {Op: "add", Path: "/tags", Value: []interface{}{"tag1", "tag2"}}, }, expected: map[string]interface{}{ "tags": []interface{}{"tag1", "tag2"}, }, wantErr: false, }, { name: "add complex object", doc: map[string]interface{}{}, patches: []PatchOperation{ {Op: "add", Path: "/metadata", Value: map[string]interface{}{ "version": "1.0", "active": true, }}, }, expected: map[string]interface{}{ "metadata": map[string]interface{}{ "version": "1.0", "active": true, }, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := patcher.ApplyPatches(tt.doc, tt.patches) if (err != nil) != tt.wantErr { t.Errorf("ApplyPatches() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && !reflect.DeepEqual(result, tt.expected) { t.Errorf("ApplyPatches() = %v, want %v", result, tt.expected) } }) } } func TestJSONPatcher_Remove(t *testing.T) { patcher := &JSONPatcher{} tests := []struct { name string doc map[string]interface{} patches []PatchOperation expected map[string]interface{} wantErr bool }{ { name: "remove simple field", doc: map[string]interface{}{ "name": "John", "age": 30, }, patches: []PatchOperation{ {Op: "remove", Path: "/name"}, }, expected: map[string]interface{}{ "age": 30, }, wantErr: false, }, { name: "remove nested field", doc: map[string]interface{}{ "user": map[string]interface{}{ "name": "Jane", "age": 25, }, }, patches: []PatchOperation{ {Op: "remove", Path: "/user/name"}, }, expected: map[string]interface{}{ "user": map[string]interface{}{ "age": 25, }, }, wantErr: false, }, { name: "remove non-existent field", doc: map[string]interface{}{ "name": "John", }, patches: []PatchOperation{ {Op: "remove", Path: "/age"}, }, expected: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := patcher.ApplyPatches(tt.doc, tt.patches) if (err != nil) != tt.wantErr { t.Errorf("ApplyPatches() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && !reflect.DeepEqual(result, tt.expected) { t.Errorf("ApplyPatches() = %v, want %v", result, tt.expected) } }) } } func TestJSONPatcher_Replace(t *testing.T) { patcher := &JSONPatcher{} tests := []struct { name string doc map[string]interface{} patches []PatchOperation expected map[string]interface{} wantErr bool }{ { name: "replace simple field", doc: map[string]interface{}{ "name": "John", }, patches: []PatchOperation{ {Op: "replace", Path: "/name", Value: "Jane"}, }, expected: map[string]interface{}{ "name": "Jane", }, wantErr: false, }, { name: "replace nested field", doc: map[string]interface{}{ "user": map[string]interface{}{ "name": "John", }, }, patches: []PatchOperation{ {Op: "replace", Path: "/user/name", Value: "Jane"}, }, expected: map[string]interface{}{ "user": map[string]interface{}{ "name": "Jane", }, }, wantErr: false, }, { name: "replace non-existent field", doc: map[string]interface{}{ "name": "John", }, patches: []PatchOperation{ {Op: "replace", Path: "/age", Value: 30}, }, expected: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := patcher.ApplyPatches(tt.doc, tt.patches) if (err != nil) != tt.wantErr { t.Errorf("ApplyPatches() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && !reflect.DeepEqual(result, tt.expected) { t.Errorf("ApplyPatches() = %v, want %v", result, tt.expected) } }) } } func TestJSONPatcher_Test(t *testing.T) { patcher := &JSONPatcher{} tests := []struct { name string doc map[string]interface{} patches []PatchOperation expected map[string]interface{} wantErr bool }{ { name: "test field success", doc: map[string]interface{}{ "name": "John", }, patches: []PatchOperation{ {Op: "test", Path: "/name", Value: "John"}, }, expected: map[string]interface{}{ "name": "John", }, wantErr: false, }, { name: "test field failure", doc: map[string]interface{}{ "name": "John", }, patches: []PatchOperation{ {Op: "test", Path: "/name", Value: "Jane"}, }, expected: nil, wantErr: true, }, { name: "test non-existent field", doc: map[string]interface{}{ "name": "John", }, patches: []PatchOperation{ {Op: "test", Path: "/age", Value: 30}, }, expected: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := patcher.ApplyPatches(tt.doc, tt.patches) if (err != nil) != tt.wantErr { t.Errorf("ApplyPatches() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && !reflect.DeepEqual(result, tt.expected) { t.Errorf("ApplyPatches() = %v, want %v", result, tt.expected) } }) } } func TestJSONPatcher_Move(t *testing.T) { patcher := &JSONPatcher{} tests := []struct { name string doc map[string]interface{} patches []PatchOperation expected map[string]interface{} wantErr bool }{ { name: "move field", doc: map[string]interface{}{ "firstName": "John", "lastName": "Doe", }, patches: []PatchOperation{ {Op: "move", From: "/firstName", Path: "/name"}, }, expected: map[string]interface{}{ "lastName": "Doe", "name": "John", }, wantErr: false, }, { name: "move non-existent field", doc: map[string]interface{}{ "name": "John", }, patches: []PatchOperation{ {Op: "move", From: "/age", Path: "/userAge"}, }, expected: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := patcher.ApplyPatches(tt.doc, tt.patches) if (err != nil) != tt.wantErr { t.Errorf("ApplyPatches() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && !reflect.DeepEqual(result, tt.expected) { t.Errorf("ApplyPatches() = %v, want %v", result, tt.expected) } }) } } func TestJSONPatcher_Copy(t *testing.T) { patcher := &JSONPatcher{} tests := []struct { name string doc map[string]interface{} patches []PatchOperation expected map[string]interface{} wantErr bool }{ { name: "copy field", doc: map[string]interface{}{ "name": "John", }, patches: []PatchOperation{ {Op: "copy", From: "/name", Path: "/displayName"}, }, expected: map[string]interface{}{ "name": "John", "displayName": "John", }, wantErr: false, }, { name: "copy non-existent field", doc: map[string]interface{}{ "name": "John", }, patches: []PatchOperation{ {Op: "copy", From: "/age", Path: "/userAge"}, }, expected: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := patcher.ApplyPatches(tt.doc, tt.patches) if (err != nil) != tt.wantErr { t.Errorf("ApplyPatches() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && !reflect.DeepEqual(result, tt.expected) { t.Errorf("ApplyPatches() = %v, want %v", result, tt.expected) } }) } } func TestJSONPatcher_Complex(t *testing.T) { patcher := &JSONPatcher{} tests := []struct { name string doc map[string]interface{} patches []PatchOperation expected map[string]interface{} wantErr bool }{ { name: "multiple operations", doc: map[string]interface{}{ "name": "John", "age": 30, }, patches: []PatchOperation{ {Op: "replace", Path: "/name", Value: "Jane"}, {Op: "add", Path: "/email", Value: "jane@example.com"}, {Op: "remove", Path: "/age"}, }, expected: map[string]interface{}{ "name": "Jane", "email": "jane@example.com", }, wantErr: false, }, { name: "test then modify", doc: map[string]interface{}{ "version": "1.0", "name": "App", }, patches: []PatchOperation{ {Op: "test", Path: "/version", Value: "1.0"}, {Op: "replace", Path: "/version", Value: "1.1"}, {Op: "add", Path: "/updated", Value: true}, }, expected: map[string]interface{}{ "version": "1.1", "name": "App", "updated": true, }, wantErr: false, }, { name: "failed test stops execution", doc: map[string]interface{}{ "version": "1.0", "name": "App", }, patches: []PatchOperation{ {Op: "test", Path: "/version", Value: "2.0"}, // This will fail {Op: "replace", Path: "/version", Value: "1.1"}, }, expected: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := patcher.ApplyPatches(tt.doc, tt.patches) if (err != nil) != tt.wantErr { t.Errorf("ApplyPatches() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && !reflect.DeepEqual(result, tt.expected) { t.Errorf("ApplyPatches() = %v, want %v", result, tt.expected) } }) } } func TestJSONPatcher_ParsePath(t *testing.T) { patcher := &JSONPatcher{} tests := []struct { name string path string expected []string }{ { name: "empty path", path: "", expected: []string{}, }, { name: "root path", path: "/", expected: []string{""}, }, { name: "simple path", path: "/name", expected: []string{"name"}, }, { name: "nested path", path: "/user/name", expected: []string{"user", "name"}, }, { name: "path with escaped characters", path: "/user/first~0name", expected: []string{"user", "first~name"}, }, { name: "path with escaped slash", path: "/user/first~1name", expected: []string{"user", "first/name"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := patcher.parsePath(tt.path) if !reflect.DeepEqual(result, tt.expected) { t.Errorf("parsePath() = %v, want %v", result, tt.expected) } }) } } func TestJSONPatcher_InvalidOperations(t *testing.T) { patcher := &JSONPatcher{} tests := []struct { name string doc map[string]interface{} patches []PatchOperation wantErr bool }{ { name: "invalid operation", doc: map[string]interface{}{}, patches: []PatchOperation{ {Op: "invalid", Path: "/name", Value: "John"}, }, wantErr: true, }, { name: "invalid path format", doc: map[string]interface{}{}, patches: []PatchOperation{ {Op: "add", Path: "name", Value: "John"}, // Missing leading slash }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := patcher.ApplyPatches(tt.doc, tt.patches) if (err != nil) != tt.wantErr { t.Errorf("ApplyPatches() error = %v, wantErr %v", err, tt.wantErr) } }) } }