Implement Set and SetAll (and Get)
This commit is contained in:
432
processor/jsonpath/jsonpath_get_set_test.go
Normal file
432
processor/jsonpath/jsonpath_get_set_test.go
Normal file
@@ -0,0 +1,432 @@
|
||||
package jsonpath
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data map[string]interface{}
|
||||
path string
|
||||
expected []interface{}
|
||||
}{
|
||||
{
|
||||
name: "simple property",
|
||||
data: map[string]interface{}{
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
},
|
||||
path: "$.name",
|
||||
expected: []interface{}{"John"},
|
||||
},
|
||||
{
|
||||
name: "nested property",
|
||||
data: map[string]interface{}{
|
||||
"user": map[string]interface{}{
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
},
|
||||
},
|
||||
path: "$.user.name",
|
||||
expected: []interface{}{"John"},
|
||||
},
|
||||
{
|
||||
name: "array access",
|
||||
data: map[string]interface{}{
|
||||
"users": []interface{}{
|
||||
map[string]interface{}{"name": "John", "age": 30},
|
||||
map[string]interface{}{"name": "Jane", "age": 25},
|
||||
},
|
||||
},
|
||||
path: "$.users[1].name",
|
||||
expected: []interface{}{"Jane"},
|
||||
},
|
||||
{
|
||||
name: "wildcard",
|
||||
data: map[string]interface{}{
|
||||
"users": []interface{}{
|
||||
map[string]interface{}{"name": "John", "age": 30},
|
||||
map[string]interface{}{"name": "Jane", "age": 25},
|
||||
},
|
||||
},
|
||||
path: "$.users[*].name",
|
||||
expected: []interface{}{"John", "Jane"},
|
||||
},
|
||||
{
|
||||
name: "recursive descent",
|
||||
data: map[string]interface{}{
|
||||
"user": map[string]interface{}{
|
||||
"name": "John",
|
||||
"profile": map[string]interface{}{
|
||||
"email": "john@example.com",
|
||||
},
|
||||
},
|
||||
"admin": map[string]interface{}{
|
||||
"email": "admin@example.com",
|
||||
},
|
||||
},
|
||||
path: "$..email",
|
||||
expected: []interface{}{"john@example.com", "admin@example.com"},
|
||||
},
|
||||
{
|
||||
name: "nonexistent path",
|
||||
data: map[string]interface{}{
|
||||
"user": map[string]interface{}{
|
||||
"name": "John",
|
||||
},
|
||||
},
|
||||
path: "$.user.email",
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := Get(tt.data, tt.path)
|
||||
|
||||
// For nonexistent path, we expect either nil or empty slice
|
||||
if tt.name == "nonexistent path" {
|
||||
if len(result) > 0 {
|
||||
t.Errorf("Get() returned %v, expected empty result", result)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check if lengths match
|
||||
if len(result) != len(tt.expected) {
|
||||
t.Errorf("Get() returned %d items, expected %d", len(result), len(tt.expected))
|
||||
return
|
||||
}
|
||||
|
||||
// For wildcard results, we need to check containment rather than exact order
|
||||
if tt.name == "wildcard" || tt.name == "recursive descent" {
|
||||
expectedMap := make(map[interface{}]bool)
|
||||
for _, v := range tt.expected {
|
||||
expectedMap[v] = true
|
||||
}
|
||||
|
||||
for _, v := range result {
|
||||
if !expectedMap[v] {
|
||||
t.Errorf("Get() result contains unexpected value: %v", v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise check exact equality
|
||||
if !reflect.DeepEqual(result, tt.expected) {
|
||||
t.Errorf("Get() = %v, expected %v", result, tt.expected)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
t.Run("simple property", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
}
|
||||
success := Set(data, "$.name", "Jane")
|
||||
|
||||
if !success {
|
||||
t.Errorf("Set() returned false, expected true")
|
||||
}
|
||||
if data["name"] != "Jane" {
|
||||
t.Errorf("Set() failed: expected name to be 'Jane', got %v", data["name"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nested property", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"user": map[string]interface{}{
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
},
|
||||
}
|
||||
success := Set(data, "$.user.name", "Jane")
|
||||
|
||||
if !success {
|
||||
t.Errorf("Set() returned false, expected true")
|
||||
}
|
||||
user, ok := data["user"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("User is not a map")
|
||||
}
|
||||
if user["name"] != "Jane" {
|
||||
t.Errorf("Set() failed: expected user.name to be 'Jane', got %v", user["name"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("array element", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"users": []interface{}{
|
||||
map[string]interface{}{"name": "John", "age": 30},
|
||||
map[string]interface{}{"name": "Jane", "age": 25},
|
||||
},
|
||||
}
|
||||
success := Set(data, "$.users[0].name", "Bob")
|
||||
|
||||
if !success {
|
||||
t.Errorf("Set() returned false, expected true")
|
||||
}
|
||||
users, ok := data["users"].([]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Users is not a slice")
|
||||
}
|
||||
user0, ok := users[0].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("User is not a map")
|
||||
}
|
||||
if user0["name"] != "Bob" {
|
||||
t.Errorf("Set() failed: expected users[0].name to be 'Bob', got %v", user0["name"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("complex value", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"user": map[string]interface{}{
|
||||
"name": "John",
|
||||
"profile": map[string]interface{}{
|
||||
"email": "john@example.com",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
newProfile := map[string]interface{}{
|
||||
"email": "john.doe@example.com",
|
||||
"phone": "123-456-7890",
|
||||
}
|
||||
|
||||
success := Set(data, "$.user.profile", newProfile)
|
||||
|
||||
if !success {
|
||||
t.Errorf("Set() returned false, expected true")
|
||||
}
|
||||
userMap, ok := data["user"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("User is not a map")
|
||||
}
|
||||
|
||||
profile, ok := userMap["profile"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Profile is not a map")
|
||||
}
|
||||
|
||||
if profile["email"] != "john.doe@example.com" || profile["phone"] != "123-456-7890" {
|
||||
t.Errorf("Set() failed: expected profile to be updated with new values")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create new property", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"user": map[string]interface{}{
|
||||
"name": "John",
|
||||
},
|
||||
}
|
||||
|
||||
success := Set(data, "$.user.email", "john@example.com")
|
||||
|
||||
if !success {
|
||||
t.Errorf("Set() returned false, expected true")
|
||||
}
|
||||
userMap, ok := data["user"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("User is not a map")
|
||||
}
|
||||
|
||||
if email, exists := userMap["email"]; !exists || email != "john@example.com" {
|
||||
t.Errorf("Set() failed: expected user.email to be 'john@example.com', got %v", userMap["email"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create nested properties", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"user": map[string]interface{}{
|
||||
"name": "John",
|
||||
},
|
||||
}
|
||||
|
||||
success := Set(data, "$.user.contact.email", "john@example.com")
|
||||
|
||||
if !success {
|
||||
t.Errorf("Set() returned false, expected true")
|
||||
}
|
||||
userMap, ok := data["user"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("User is not a map")
|
||||
}
|
||||
|
||||
contact, ok := userMap["contact"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Contact is not a map")
|
||||
}
|
||||
|
||||
if email, exists := contact["email"]; !exists || email != "john@example.com" {
|
||||
t.Errorf("Set() failed: expected user.contact.email to be 'john@example.com', got %v", contact["email"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create array and element", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"user": map[string]interface{}{
|
||||
"name": "John",
|
||||
},
|
||||
}
|
||||
|
||||
// This should create an empty addresses array, but won't be able to set index 0
|
||||
// since the array is empty
|
||||
success := Set(data, "$.user.addresses[0].street", "123 Main St")
|
||||
|
||||
// This shouldn't succeed because we can't create array elements that don't exist
|
||||
if success {
|
||||
t.Errorf("Set() returned true, expected false for out-of-bounds array index")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("multiple targets (should only update first)", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"users": []interface{}{
|
||||
map[string]interface{}{"active": true},
|
||||
map[string]interface{}{"active": true},
|
||||
},
|
||||
}
|
||||
|
||||
success := Set(data, "$.users[*].active", false)
|
||||
|
||||
if !success {
|
||||
t.Errorf("Set() returned false, expected true")
|
||||
}
|
||||
|
||||
users, ok := data["users"].([]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Users is not a slice")
|
||||
}
|
||||
|
||||
user0, ok := users[0].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("User0 is not a map")
|
||||
}
|
||||
|
||||
user1, ok := users[1].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("User1 is not a map")
|
||||
}
|
||||
|
||||
// Only the first one should be changed
|
||||
if active, exists := user0["active"]; !exists || active != false {
|
||||
t.Errorf("Set() failed: expected users[0].active to be false, got %v", user0["active"])
|
||||
}
|
||||
|
||||
// The second one should remain unchanged
|
||||
if active, exists := user1["active"]; !exists || active != true {
|
||||
t.Errorf("Set() incorrectly modified users[1].active: expected true, got %v", user1["active"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("setting on root should fail", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"name": "John",
|
||||
}
|
||||
|
||||
success := Set(data, "$", "Jane")
|
||||
|
||||
if success {
|
||||
t.Errorf("Set() returned true, expected false for setting on root")
|
||||
}
|
||||
|
||||
// Data should be unchanged
|
||||
if data["name"] != "John" {
|
||||
t.Errorf("Data was modified when setting on root")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetAll(t *testing.T) {
|
||||
t.Run("simple property", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
}
|
||||
success := SetAll(data, "$.name", "Jane")
|
||||
|
||||
if !success {
|
||||
t.Errorf("SetAll() returned false, expected true")
|
||||
}
|
||||
if data["name"] != "Jane" {
|
||||
t.Errorf("SetAll() failed: expected name to be 'Jane', got %v", data["name"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("all array elements", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"users": []interface{}{
|
||||
map[string]interface{}{"active": true},
|
||||
map[string]interface{}{"active": true},
|
||||
},
|
||||
}
|
||||
|
||||
success := SetAll(data, "$.users[*].active", false)
|
||||
|
||||
if !success {
|
||||
t.Errorf("SetAll() returned false, expected true")
|
||||
}
|
||||
|
||||
users, ok := data["users"].([]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Users is not a slice")
|
||||
}
|
||||
|
||||
// Both elements should be updated
|
||||
for i, user := range users {
|
||||
userMap, ok := user.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("User%d is not a map", i)
|
||||
}
|
||||
|
||||
if active, exists := userMap["active"]; !exists || active != false {
|
||||
t.Errorf("SetAll() failed: expected users[%d].active to be false, got %v", i, userMap["active"])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("recursive descent", func(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"user": map[string]interface{}{
|
||||
"profile": map[string]interface{}{
|
||||
"active": true,
|
||||
},
|
||||
},
|
||||
"admin": map[string]interface{}{
|
||||
"profile": map[string]interface{}{
|
||||
"active": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
success := SetAll(data, "$..active", false)
|
||||
|
||||
if !success {
|
||||
t.Errorf("SetAll() returned false, expected true")
|
||||
}
|
||||
|
||||
// Check user profile
|
||||
userProfile, ok := data["user"].(map[string]interface{})["profile"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Failed to access user.profile")
|
||||
}
|
||||
if active, exists := userProfile["active"]; !exists || active != false {
|
||||
t.Errorf("SetAll() didn't update user.profile.active, got: %v", active)
|
||||
}
|
||||
|
||||
// Check admin profile
|
||||
adminProfile, ok := data["admin"].(map[string]interface{})["profile"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Failed to access admin.profile")
|
||||
}
|
||||
if active, exists := adminProfile["active"]; !exists || active != false {
|
||||
t.Errorf("SetAll() didn't update admin.profile.active, got: %v", active)
|
||||
}
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user