Files
event-driven-shoppinglist/json_patch_test.go
2025-09-29 12:03:28 +02:00

574 lines
12 KiB
Go

package main
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestJSONPatcher_Add(t *testing.T) {
tests := []struct {
name string
doc map[string]interface{}
patch PatchOperation
expected map[string]interface{}
wantErr bool
}{
{
name: "add simple field",
doc: map[string]interface{}{"id": "test"},
patch: PatchOperation{
Op: "add",
Path: "/content",
Value: "test content",
},
expected: map[string]interface{}{
"id": "test",
"content": "test content",
},
wantErr: false,
},
{
name: "add nested field",
doc: map[string]interface{}{"id": "test", "meta": map[string]interface{}{}},
patch: PatchOperation{
Op: "add",
Path: "/meta/priority",
Value: "high",
},
expected: map[string]interface{}{
"id": "test",
"meta": map[string]interface{}{"priority": "high"},
},
wantErr: false,
},
{
name: "add to existing field (overwrite)",
doc: map[string]interface{}{"id": "test", "content": "old"},
patch: PatchOperation{
Op: "add",
Path: "/content",
Value: "new",
},
expected: map[string]interface{}{
"id": "test",
"content": "new",
},
wantErr: false,
},
{
name: "add array field",
doc: map[string]interface{}{"id": "test"},
patch: PatchOperation{
Op: "add",
Path: "/tags",
Value: []interface{}{"tag1", "tag2"},
},
expected: map[string]interface{}{
"id": "test",
"tags": []interface{}{"tag1", "tag2"},
},
wantErr: false,
},
{
name: "add complex object",
doc: map[string]interface{}{"id": "test"},
patch: PatchOperation{
Op: "add",
Path: "/metadata",
Value: map[string]interface{}{
"created": "2025-01-01",
"version": 1,
},
},
expected: map[string]interface{}{
"id": "test",
"metadata": map[string]interface{}{
"created": "2025-01-01",
"version": 1,
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
patcher := &JSONPatcher{}
result, err := patcher.ApplyPatches(tt.doc, []PatchOperation{tt.patch})
if tt.wantErr {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, tt.expected, result)
}
})
}
}
func TestJSONPatcher_Remove(t *testing.T) {
tests := []struct {
name string
doc map[string]interface{}
patch PatchOperation
expected map[string]interface{}
wantErr bool
}{
{
name: "remove simple field",
doc: map[string]interface{}{
"id": "test",
"content": "test content",
},
patch: PatchOperation{
Op: "remove",
Path: "/content",
},
expected: map[string]interface{}{
"id": "test",
},
wantErr: false,
},
{
name: "remove nested field",
doc: map[string]interface{}{
"id": "test",
"meta": map[string]interface{}{"priority": "high", "tags": []string{"tag1"}},
},
patch: PatchOperation{
Op: "remove",
Path: "/meta/priority",
},
expected: map[string]interface{}{
"id": "test",
"meta": map[string]interface{}{"tags": []string{"tag1"}},
},
wantErr: false,
},
{
name: "remove non-existent field",
doc: map[string]interface{}{
"id": "test",
},
patch: PatchOperation{
Op: "remove",
Path: "/nonexistent",
},
expected: map[string]interface{}{
"id": "test",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
patcher := &JSONPatcher{}
result, err := patcher.ApplyPatches(tt.doc, []PatchOperation{tt.patch})
if tt.wantErr {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, tt.expected, result)
}
})
}
}
func TestJSONPatcher_Replace(t *testing.T) {
tests := []struct {
name string
doc map[string]interface{}
patch PatchOperation
expected map[string]interface{}
wantErr bool
}{
{
name: "replace simple field",
doc: map[string]interface{}{
"id": "test",
"content": "old content",
},
patch: PatchOperation{
Op: "replace",
Path: "/content",
Value: "new content",
},
expected: map[string]interface{}{
"id": "test",
"content": "new content",
},
wantErr: false,
},
{
name: "replace nested field",
doc: map[string]interface{}{
"id": "test",
"meta": map[string]interface{}{"priority": "low"},
},
patch: PatchOperation{
Op: "replace",
Path: "/meta/priority",
Value: "high",
},
expected: map[string]interface{}{
"id": "test",
"meta": map[string]interface{}{"priority": "high"},
},
wantErr: false,
},
{
name: "replace non-existent field",
doc: map[string]interface{}{
"id": "test",
},
patch: PatchOperation{
Op: "replace",
Path: "/nonexistent",
Value: "value",
},
expected: map[string]interface{}{
"id": "test",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
patcher := &JSONPatcher{}
result, err := patcher.ApplyPatches(tt.doc, []PatchOperation{tt.patch})
if tt.wantErr {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, tt.expected, result)
}
})
}
}
func TestJSONPatcher_Test(t *testing.T) {
tests := []struct {
name string
doc map[string]interface{}
patch PatchOperation
wantErr bool
}{
{
name: "test field success",
doc: map[string]interface{}{
"id": "test",
"content": "test content",
},
patch: PatchOperation{
Op: "test",
Path: "/content",
Value: "test content",
},
wantErr: false,
},
{
name: "test field failure",
doc: map[string]interface{}{
"id": "test",
"content": "test content",
},
patch: PatchOperation{
Op: "test",
Path: "/content",
Value: "different content",
},
wantErr: true,
},
{
name: "test non-existent field",
doc: map[string]interface{}{
"id": "test",
},
patch: PatchOperation{
Op: "test",
Path: "/nonexistent",
Value: "value",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
patcher := &JSONPatcher{}
_, err := patcher.ApplyPatches(tt.doc, []PatchOperation{tt.patch})
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestJSONPatcher_Move(t *testing.T) {
tests := []struct {
name string
doc map[string]interface{}
patch PatchOperation
expected map[string]interface{}
wantErr bool
}{
{
name: "move field",
doc: map[string]interface{}{
"id": "test",
"content": "test content",
},
patch: PatchOperation{
Op: "move",
From: "/content",
Path: "/title",
},
expected: map[string]interface{}{
"id": "test",
"title": "test content",
},
wantErr: false,
},
{
name: "move non-existent field",
doc: map[string]interface{}{
"id": "test",
},
patch: PatchOperation{
Op: "move",
From: "/nonexistent",
Path: "/title",
},
expected: map[string]interface{}{
"id": "test",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
patcher := &JSONPatcher{}
result, err := patcher.ApplyPatches(tt.doc, []PatchOperation{tt.patch})
if tt.wantErr {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, tt.expected, result)
}
})
}
}
func TestJSONPatcher_Copy(t *testing.T) {
tests := []struct {
name string
doc map[string]interface{}
patch PatchOperation
expected map[string]interface{}
wantErr bool
}{
{
name: "copy field",
doc: map[string]interface{}{
"id": "test",
"content": "test content",
},
patch: PatchOperation{
Op: "copy",
From: "/content",
Path: "/title",
},
expected: map[string]interface{}{
"id": "test",
"content": "test content",
"title": "test content",
},
wantErr: false,
},
{
name: "copy non-existent field",
doc: map[string]interface{}{
"id": "test",
},
patch: PatchOperation{
Op: "copy",
From: "/nonexistent",
Path: "/title",
},
expected: map[string]interface{}{
"id": "test",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
patcher := &JSONPatcher{}
result, err := patcher.ApplyPatches(tt.doc, []PatchOperation{tt.patch})
if tt.wantErr {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, tt.expected, result)
}
})
}
}
func TestJSONPatcher_Complex(t *testing.T) {
tests := []struct {
name string
doc map[string]interface{}
patches []PatchOperation
expected map[string]interface{}
wantErr bool
}{
{
name: "multiple operations",
doc: map[string]interface{}{"id": "test"},
patches: []PatchOperation{
{Op: "add", Path: "/content", Value: "test"},
{Op: "add", Path: "/priority", Value: "high"},
{Op: "replace", Path: "/content", Value: "updated test"},
},
expected: map[string]interface{}{
"id": "test",
"content": "updated test",
"priority": "high",
},
wantErr: false,
},
{
name: "test then modify",
doc: map[string]interface{}{"id": "test", "version": 1},
patches: []PatchOperation{
{Op: "test", Path: "/version", Value: 1},
{Op: "replace", Path: "/version", Value: 2},
},
expected: map[string]interface{}{
"id": "test",
"version": 2,
},
wantErr: false,
},
{
name: "failed test stops execution",
doc: map[string]interface{}{"id": "test", "version": 1},
patches: []PatchOperation{
{Op: "test", Path: "/version", Value: 2}, // This should fail
{Op: "replace", Path: "/version", Value: 3},
},
expected: map[string]interface{}{
"id": "test",
"version": 1,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
patcher := &JSONPatcher{}
result, err := patcher.ApplyPatches(tt.doc, tt.patches)
if tt.wantErr {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, tt.expected, result)
}
})
}
}
func TestJSONPatcher_ParsePath(t *testing.T) {
tests := []struct {
name string
path string
expected []string
}{
{
name: "empty path",
path: "",
expected: []string{},
},
{
name: "root path",
path: "/",
expected: []string{""},
},
{
name: "simple path",
path: "/content",
expected: []string{"content"},
},
{
name: "nested path",
path: "/meta/priority",
expected: []string{"meta", "priority"},
},
{
name: "path with escaped characters",
path: "/content~1title",
expected: []string{"content/title"},
},
{
name: "path with escaped slash",
path: "/content~0title",
expected: []string{"content~title"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
patcher := &JSONPatcher{}
result := patcher.parsePath(tt.path)
assert.Equal(t, tt.expected, result)
})
}
}
func TestJSONPatcher_InvalidOperations(t *testing.T) {
tests := []struct {
name string
patch PatchOperation
}{
{
name: "invalid operation",
patch: PatchOperation{
Op: "invalid",
Path: "/content",
},
},
{
name: "invalid path format",
patch: PatchOperation{
Op: "add",
Path: "content", // missing leading slash
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
patcher := &JSONPatcher{}
doc := map[string]interface{}{"id": "test"}
_, err := patcher.ApplyPatches(doc, []PatchOperation{tt.patch})
assert.Error(t, err)
})
}
}