package jsonpath import ( "reflect" "testing" ) func TestGetWithPathsBasic(t *testing.T) { tests := []struct { name string data map[string]interface{} path string expected []JSONNode error bool }{ { name: "simple property", data: map[string]interface{}{ "name": "John", "age": 30, }, path: "$.name", expected: []JSONNode{ {Value: "John", Path: "$.name"}, }, }, { name: "nested property", data: map[string]interface{}{ "user": map[string]interface{}{ "name": "John", "age": 30, }, }, path: "$.user.name", expected: []JSONNode{ {Value: "John", Path: "$.user.name"}, }, }, { 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: []JSONNode{ {Value: "Jane", Path: "$.users[1].name"}, }, }, { 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: []JSONNode{ {Value: "John", Path: "$.users[0].name"}, {Value: "Jane", Path: "$.users[1].name"}, }, }, { 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: []JSONNode{ {Value: "john@example.com", Path: "$.user.profile.email"}, {Value: "admin@example.com", Path: "$.admin.email"}, }, }, { name: "nonexistent path", data: map[string]interface{}{ "user": map[string]interface{}{ "name": "John", }, }, path: "$.user.email", expected: []JSONNode{}, error: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := Get(tt.data, tt.path) if err != nil { if !tt.error { t.Errorf("GetWithPaths() returned error: %v", err) } return } // For nonexistent path, we expect empty slice if tt.name == "nonexistent path" { if len(result) > 0 { t.Errorf("GetWithPaths() returned %v, expected empty result", result) } return } // Check if lengths match if len(result) != len(tt.expected) { t.Errorf("GetWithPaths() 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" { // For each expected item, check if it exists in the results by both value and path for _, expected := range tt.expected { found := false for _, r := range result { if reflect.DeepEqual(r.Value, expected.Value) && r.Path == expected.Path { found = true break } } if !found { t.Errorf("GetWithPaths() missing expected value: %v with path: %s", expected.Value, expected.Path) } } } else { // Otherwise check exact equality of both values and paths for i, expected := range tt.expected { if !reflect.DeepEqual(result[i].Value, expected.Value) { t.Errorf("GetWithPaths() value at [%d] = %v, expected %v", i, result[i].Value, expected.Value) } if result[i].Path != expected.Path { t.Errorf("GetWithPaths() path at [%d] = %s, expected %s", i, result[i].Path, expected.Path) } } } }) } } func TestSet(t *testing.T) { t.Run("simple property", func(t *testing.T) { data := map[string]interface{}{ "name": "John", "age": 30, } err := Set(data, "$.name", "Jane") if err != nil { t.Errorf("Set() returned error: %v", err) return } 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, }, } err := Set(data, "$.user.name", "Jane") if err != nil { t.Errorf("Set() returned error: %v", err) return } 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}, }, } err := Set(data, "$.users[0].name", "Bob") if err != nil { t.Errorf("Set() returned error: %v", err) return } 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", } err := Set(data, "$.user.profile", newProfile) if err != nil { t.Errorf("Set() returned error: %v", err) return } 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", }, } err := Set(data, "$.user.email", "john@example.com") if err != nil { t.Errorf("Set() returned error: %v", err) return } 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", }, } err := Set(data, "$.user.contact.email", "john@example.com") if err != nil { t.Errorf("Set() returned error: %v", err) return } 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 err := Set(data, "$.user.addresses[0].street", "123 Main St") if err != nil { t.Errorf("Set() returned error: %v", err) return } }) 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}, }, } err := Set(data, "$.users[*].active", false) if err != nil { t.Errorf("Set() returned error: %v", err) return } 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", } err := Set(data, "$", "Jane") if err == nil { t.Errorf("Set() returned no error, expected error for setting on root") return } // 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, } err := SetAll(data, "$.name", "Jane") if err != nil { t.Errorf("SetAll() returned error: %v", err) return } 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}, }, } err := SetAll(data, "$.users[*].active", false) if err != nil { t.Errorf("SetAll() returned error: %v", err) return } 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, }, }, } err := SetAll(data, "$..active", false) if err != nil { t.Errorf("SetAll() returned error: %v", err) return } // 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) } }) } func TestGetWithPathsExtended(t *testing.T) { tests := []struct { name string data map[string]interface{} path string expected []JSONNode }{ { name: "simple property", data: map[string]interface{}{ "name": "John", "age": 30, }, path: "$.name", expected: []JSONNode{ {Value: "John", Path: "$.name"}, }, }, { name: "nested property", data: map[string]interface{}{ "user": map[string]interface{}{ "name": "John", "age": 30, }, }, path: "$.user.name", expected: []JSONNode{ {Value: "John", Path: "$.user.name"}, }, }, { 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: []JSONNode{ {Value: "Jane", Path: "$.users[1].name"}, }, }, { 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: []JSONNode{ {Value: "John", Path: "$.users[0].name"}, {Value: "Jane", Path: "$.users[1].name"}, }, }, { 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: []JSONNode{ {Value: "john@example.com", Path: "$.user.profile.email"}, {Value: "admin@example.com", Path: "$.admin.email"}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := Get(tt.data, tt.path) if err != nil { t.Errorf("GetWithPaths() returned error: %v", err) return } // Check if lengths match if len(result) != len(tt.expected) { t.Errorf("GetWithPaths() returned %d items, expected %d", len(result), len(tt.expected)) return } // For each expected item, find its match in the results and verify both value and path for _, expected := range tt.expected { found := false for _, r := range result { // Check if value matches if reflect.DeepEqual(r.Value, expected.Value) { found = true // Check if path matches if r.Path != expected.Path { t.Errorf("Path mismatch for value %v: got %s, expected %s", r.Value, r.Path, expected.Path) } break } } if !found { t.Errorf("Expected node with value %v and path %s not found in results", expected.Value, expected.Path) } } }) } }