Files
event-driven-shoppinglist/json_patch_test.go
2025-09-29 08:22:38 +02:00

561 lines
12 KiB
Go

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)
}
})
}
}