1020 lines
22 KiB
Go
1020 lines
22 KiB
Go
package processor
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/PaesslerAG/jsonpath"
|
|
)
|
|
|
|
// fi ndMatchingPaths finds nodes in a JSON document that match the given JSONPath
|
|
func findMatchingPaths(jsonDoc interface{}, path string) ([]interface{}, error) {
|
|
// Use the existing jsonpath library to extract values
|
|
result, err := jsonpath.Get(path, jsonDoc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Convert the result to a slice
|
|
var values []interface{}
|
|
switch v := result.(type) {
|
|
case []interface{}:
|
|
values = v
|
|
default:
|
|
values = []interface{}{v}
|
|
}
|
|
|
|
return values, nil
|
|
}
|
|
|
|
// TestJSONProcessor_Process_NumericValues tests processing numeric JSON values
|
|
func TestJSONProcessor_Process_NumericValues(t *testing.T) {
|
|
content := `{
|
|
"books": [
|
|
{
|
|
"title": "The Go Programming Language",
|
|
"price": 44.95
|
|
},
|
|
{
|
|
"title": "Go in Action",
|
|
"price": 5.95
|
|
}
|
|
]
|
|
}`
|
|
|
|
expected := `{
|
|
"books": [
|
|
{
|
|
"title": "The Go Programming Language",
|
|
"price": 89.9
|
|
},
|
|
{
|
|
"title": "Go in Action",
|
|
"price": 11.9
|
|
}
|
|
]
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.books[*]", "v.price=v.price*2")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 2 {
|
|
t.Errorf("Expected 2 matches, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 2 {
|
|
t.Errorf("Expected 2 modifications, got %d", modCount)
|
|
}
|
|
|
|
// Compare parsed JSON objects instead of formatted strings
|
|
var resultObj map[string]interface{}
|
|
if err := json.Unmarshal([]byte(result), &resultObj); err != nil {
|
|
t.Fatalf("Failed to parse result JSON: %v", err)
|
|
}
|
|
|
|
var expectedObj map[string]interface{}
|
|
if err := json.Unmarshal([]byte(expected), &expectedObj); err != nil {
|
|
t.Fatalf("Failed to parse expected JSON: %v", err)
|
|
}
|
|
|
|
// Compare the first book's price
|
|
resultBooks, ok := resultObj["books"].([]interface{})
|
|
if !ok || len(resultBooks) < 1 {
|
|
t.Fatalf("Expected books array in result")
|
|
}
|
|
resultBook1, ok := resultBooks[0].(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("Expected first book to be an object")
|
|
}
|
|
resultPrice1, ok := resultBook1["price"].(float64)
|
|
if !ok {
|
|
t.Fatalf("Expected numeric price in first book")
|
|
}
|
|
if resultPrice1 != 89.9 {
|
|
t.Errorf("Expected first book price to be 89.9, got %v", resultPrice1)
|
|
}
|
|
|
|
// Compare the second book's price
|
|
resultBook2, ok := resultBooks[1].(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("Expected second book to be an object")
|
|
}
|
|
resultPrice2, ok := resultBook2["price"].(float64)
|
|
if !ok {
|
|
t.Fatalf("Expected numeric price in second book")
|
|
}
|
|
if resultPrice2 != 11.9 {
|
|
t.Errorf("Expected second book price to be 11.9, got %v", resultPrice2)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_Process_StringValues tests processing string JSON values
|
|
func TestJSONProcessor_Process_StringValues(t *testing.T) {
|
|
content := `{
|
|
"config": {
|
|
"maxItems": "100",
|
|
"itemTimeoutSecs": "30",
|
|
"retryCount": "5"
|
|
}
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.config", "for k,vi in pairs(v) do v[k]=vi*2 end")
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
// Debug info
|
|
t.Logf("Result: %s", result)
|
|
t.Logf("Match count: %d, Mod count: %d", matchCount, modCount)
|
|
|
|
if matchCount != 3 {
|
|
t.Errorf("Expected 3 matches, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 3 {
|
|
t.Errorf("Expected 3 modifications, got %d", modCount)
|
|
}
|
|
|
|
// Check that all expected values are in the result
|
|
if !strings.Contains(result, `"maxItems": "200"`) {
|
|
t.Errorf("Result missing expected value: maxItems=200")
|
|
}
|
|
|
|
if !strings.Contains(result, `"itemTimeoutSecs": "60"`) {
|
|
t.Errorf("Result missing expected value: itemTimeoutSecs=60")
|
|
}
|
|
|
|
if !strings.Contains(result, `"retryCount": "10"`) {
|
|
t.Errorf("Result missing expected value: retryCount=10")
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_FindNodes tests the JSONPath implementation
|
|
func TestJSONProcessor_FindNodes(t *testing.T) {
|
|
// Test cases for JSONPath implementation
|
|
testCases := []struct {
|
|
name string
|
|
jsonData string
|
|
path string
|
|
expectLen int
|
|
expectErr bool
|
|
}{
|
|
{
|
|
name: "Root element",
|
|
jsonData: `{"name": "root", "value": 100}`,
|
|
path: "$",
|
|
expectLen: 1,
|
|
expectErr: false,
|
|
},
|
|
{
|
|
name: "Direct property",
|
|
jsonData: `{"name": "test", "value": 100}`,
|
|
path: "$.value",
|
|
expectLen: 1,
|
|
expectErr: false,
|
|
},
|
|
{
|
|
name: "Array access",
|
|
jsonData: `{"items": [10, 20, 30]}`,
|
|
path: "$.items[1]",
|
|
expectLen: 1,
|
|
expectErr: false,
|
|
},
|
|
{
|
|
name: "All array elements",
|
|
jsonData: `{"items": [10, 20, 30]}`,
|
|
path: "$.items[*]",
|
|
expectLen: 3,
|
|
expectErr: false,
|
|
},
|
|
{
|
|
name: "Nested property",
|
|
jsonData: `{"user": {"name": "John", "age": 30}}`,
|
|
path: "$.user.age",
|
|
expectLen: 1,
|
|
expectErr: false,
|
|
},
|
|
{
|
|
name: "Array of objects",
|
|
jsonData: `{"users": [{"name": "John"}, {"name": "Jane"}]}`,
|
|
path: "$.users[*].name",
|
|
expectLen: 2,
|
|
expectErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Parse the JSON data
|
|
var jsonDoc interface{}
|
|
if err := json.Unmarshal([]byte(tc.jsonData), &jsonDoc); err != nil {
|
|
t.Fatalf("Failed to parse test JSON: %v", err)
|
|
}
|
|
|
|
// Find nodes with the given path
|
|
nodes, err := findMatchingPaths(jsonDoc, tc.path)
|
|
|
|
// Check error expectation
|
|
if tc.expectErr && err == nil {
|
|
t.Errorf("Expected error but got none")
|
|
}
|
|
if !tc.expectErr && err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
// Skip further checks if we expected an error
|
|
if tc.expectErr {
|
|
return
|
|
}
|
|
|
|
// Check the number of nodes found
|
|
if len(nodes) != tc.expectLen {
|
|
t.Errorf("Expected %d nodes, got %d", tc.expectLen, len(nodes))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_NestedModifications tests modifying nested JSON objects
|
|
func TestJSONProcessor_NestedModifications(t *testing.T) {
|
|
content := `{
|
|
"store": {
|
|
"book": [
|
|
{
|
|
"category": "reference",
|
|
"title": "Learn Go in 24 Hours",
|
|
"price": 10.99
|
|
},
|
|
{
|
|
"category": "fiction",
|
|
"title": "The Go Developer",
|
|
"price": 8.99
|
|
}
|
|
]
|
|
}
|
|
}`
|
|
|
|
expected := `{
|
|
"store": {
|
|
"book": [
|
|
{
|
|
"category": "reference",
|
|
"title": "Learn Go in 24 Hours",
|
|
"price": 13.188
|
|
},
|
|
{
|
|
"category": "fiction",
|
|
"title": "The Go Developer",
|
|
"price": 10.788
|
|
}
|
|
]
|
|
}
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.store.book[*].price", "v=v*1.2")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 2 {
|
|
t.Errorf("Expected 2 matches, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 2 {
|
|
t.Errorf("Expected 2 modifications, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_StringManipulation tests string manipulation
|
|
func TestJSONProcessor_StringManipulation(t *testing.T) {
|
|
content := `{
|
|
"users": [
|
|
{
|
|
"name": "john",
|
|
"role": "admin"
|
|
},
|
|
{
|
|
"name": "alice",
|
|
"role": "user"
|
|
}
|
|
]
|
|
}`
|
|
|
|
expected := `{
|
|
"users": [
|
|
{
|
|
"name": "JOHN",
|
|
"role": "admin"
|
|
},
|
|
{
|
|
"name": "ALICE",
|
|
"role": "user"
|
|
}
|
|
]
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.users[*].name", "v = string.upper(v)")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 2 {
|
|
t.Errorf("Expected 2 matches, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 2 {
|
|
t.Errorf("Expected 2 modifications, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_ComplexScript tests using more complex Lua scripts
|
|
func TestJSONProcessor_ComplexScript(t *testing.T) {
|
|
content := `{
|
|
"products": [
|
|
{
|
|
"name": "Basic Widget",
|
|
"price": 9.99,
|
|
"discount": 0.1
|
|
},
|
|
{
|
|
"name": "Premium Widget",
|
|
"price": 19.99,
|
|
"discount": 0.05
|
|
}
|
|
]
|
|
}`
|
|
|
|
expected := `{
|
|
"products": [
|
|
{
|
|
"name": "Basic Widget",
|
|
"price": 8.991,
|
|
"discount": 0.1
|
|
},
|
|
{
|
|
"name": "Premium Widget",
|
|
"price": 18.9905,
|
|
"discount": 0.05
|
|
}
|
|
]
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.products[*]", "v.price = v.price * (1 - v.discount)")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 2 {
|
|
t.Errorf("Expected 2 matches, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 2 {
|
|
t.Errorf("Expected 2 modifications, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_SpecificItemUpdate tests updating a specific item in an array
|
|
func TestJSONProcessor_SpecificItemUpdate(t *testing.T) {
|
|
content := `{
|
|
"items": [
|
|
{"id": 1, "name": "Item 1", "stock": 10},
|
|
{"id": 2, "name": "Item 2", "stock": 5},
|
|
{"id": 3, "name": "Item 3", "stock": 0}
|
|
]
|
|
}`
|
|
|
|
expected := `{
|
|
"items": [
|
|
{"id": 1, "name": "Item 1", "stock": 10},
|
|
{"id": 2, "name": "Item 2", "stock": 15},
|
|
{"id": 3, "name": "Item 3", "stock": 0}
|
|
]
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.items[1].stock", "v=v+10")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_RootElementUpdate tests updating the root element
|
|
func TestJSONProcessor_RootElementUpdate(t *testing.T) {
|
|
content := `{"value": 100}`
|
|
expected := `{"value": 200}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.value", "v=v*2")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", modCount)
|
|
}
|
|
|
|
if result != expected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_AddNewField tests adding a new field to a JSON object
|
|
func TestJSONProcessor_AddNewField(t *testing.T) {
|
|
content := `{
|
|
"user": {
|
|
"name": "John",
|
|
"age": 30
|
|
}
|
|
}`
|
|
|
|
expected := `{
|
|
"user": {
|
|
"name": "John",
|
|
"age": 30,
|
|
"email": "john@example.com"
|
|
}
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.user", "v.email = 'john@example.com'")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_RemoveField tests removing a field from a JSON object
|
|
func TestJSONProcessor_RemoveField(t *testing.T) {
|
|
content := `{
|
|
"user": {
|
|
"name": "John",
|
|
"age": 30,
|
|
"email": "john@example.com"
|
|
}
|
|
}`
|
|
|
|
expected := `{
|
|
"user": {
|
|
"name": "John",
|
|
"email": "john@example.com"
|
|
}
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.user", "v.age = nil")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_ArrayManipulation tests adding and manipulating array elements
|
|
func TestJSONProcessor_ArrayManipulation(t *testing.T) {
|
|
content := `{
|
|
"tags": ["go", "json", "lua"]
|
|
}`
|
|
|
|
expected := `{
|
|
"tags": ["GO", "JSON", "LUA", "testing"]
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.tags", `
|
|
-- Convert existing tags to uppercase
|
|
for i=1, #v do
|
|
v[i] = string.upper(v[i])
|
|
end
|
|
-- Add a new tag
|
|
v[#v+1] = "testing"
|
|
`)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_ConditionalModification tests conditionally modifying values
|
|
func TestJSONProcessor_ConditionalModification(t *testing.T) {
|
|
content := `{
|
|
"products": [
|
|
{
|
|
"name": "Product A",
|
|
"price": 10.99,
|
|
"inStock": true
|
|
},
|
|
{
|
|
"name": "Product B",
|
|
"price": 5.99,
|
|
"inStock": false
|
|
},
|
|
{
|
|
"name": "Product C",
|
|
"price": 15.99,
|
|
"inStock": true
|
|
}
|
|
]
|
|
}`
|
|
|
|
expected := `{
|
|
"products": [
|
|
{
|
|
"name": "Product A",
|
|
"price": 9.891,
|
|
"inStock": true
|
|
},
|
|
{
|
|
"name": "Product B",
|
|
"price": 5.99,
|
|
"inStock": false
|
|
},
|
|
{
|
|
"name": "Product C",
|
|
"price": 14.391,
|
|
"inStock": true
|
|
}
|
|
]
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.products[*]", `
|
|
if v.inStock then
|
|
v.price = v.price * 0.9
|
|
end
|
|
`)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 3 {
|
|
t.Errorf("Expected 3 matches, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 2 {
|
|
t.Errorf("Expected 2 modifications, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_DeepNesting tests manipulating deeply nested JSON structures
|
|
func TestJSONProcessor_DeepNesting(t *testing.T) {
|
|
content := `{
|
|
"company": {
|
|
"departments": {
|
|
"engineering": {
|
|
"teams": {
|
|
"frontend": {
|
|
"members": 12,
|
|
"projects": 5
|
|
},
|
|
"backend": {
|
|
"members": 8,
|
|
"projects": 3
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`
|
|
|
|
expected := `{
|
|
"company": {
|
|
"departments": {
|
|
"engineering": {
|
|
"teams": {
|
|
"frontend": {
|
|
"members": 12,
|
|
"projects": 5,
|
|
"status": "active"
|
|
},
|
|
"backend": {
|
|
"members": 8,
|
|
"projects": 3,
|
|
"status": "active"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.company.departments.engineering.teams.*", `
|
|
v.status = "active"
|
|
`)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 2 {
|
|
t.Errorf("Expected 2 matches, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 2 {
|
|
t.Errorf("Expected 2 modifications, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_ComplexTransformation tests a complex transformation involving
|
|
// multiple fields and calculations
|
|
func TestJSONProcessor_ComplexTransformation(t *testing.T) {
|
|
content := `{
|
|
"order": {
|
|
"items": [
|
|
{
|
|
"product": "Widget A",
|
|
"quantity": 5,
|
|
"price": 10.0
|
|
},
|
|
{
|
|
"product": "Widget B",
|
|
"quantity": 3,
|
|
"price": 15.0
|
|
}
|
|
],
|
|
"customer": {
|
|
"name": "John Smith",
|
|
"tier": "gold"
|
|
}
|
|
}
|
|
}`
|
|
|
|
expected := `{
|
|
"order": {
|
|
"items": [
|
|
{
|
|
"product": "Widget A",
|
|
"quantity": 5,
|
|
"price": 10.0,
|
|
"total": 50.0,
|
|
"discounted_total": 45.0
|
|
},
|
|
{
|
|
"product": "Widget B",
|
|
"quantity": 3,
|
|
"price": 15.0,
|
|
"total": 45.0,
|
|
"discounted_total": 40.5
|
|
}
|
|
],
|
|
"customer": {
|
|
"name": "John Smith",
|
|
"tier": "gold"
|
|
},
|
|
"summary": {
|
|
"total_items": 8,
|
|
"subtotal": 95.0,
|
|
"discount": 9.5,
|
|
"total": 85.5
|
|
}
|
|
}
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.order", `
|
|
-- Calculate item totals and apply discounts
|
|
local discount_rate = 0.1 -- 10% discount for gold tier
|
|
local subtotal = 0
|
|
local total_items = 0
|
|
|
|
for i, item in ipairs(v.items) do
|
|
-- Calculate item total
|
|
item.total = item.quantity * item.price
|
|
|
|
-- Apply discount
|
|
item.discounted_total = item.total * (1 - discount_rate)
|
|
|
|
-- Add to running totals
|
|
subtotal = subtotal + item.total
|
|
total_items = total_items + item.quantity
|
|
end
|
|
|
|
-- Add order summary
|
|
v.summary = {
|
|
total_items = total_items,
|
|
subtotal = subtotal,
|
|
discount = subtotal * discount_rate,
|
|
total = subtotal * (1 - discount_rate)
|
|
}
|
|
`)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_HandlingNullValues tests handling of null values in JSON
|
|
func TestJSONProcessor_HandlingNullValues(t *testing.T) {
|
|
content := `{
|
|
"data": {
|
|
"value1": null,
|
|
"value2": 42,
|
|
"value3": "hello"
|
|
}
|
|
}`
|
|
|
|
expected := `{
|
|
"data": {
|
|
"value1": 0,
|
|
"value2": 42,
|
|
"value3": "hello"
|
|
}
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.data.value1", `
|
|
v = 0
|
|
`)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_RestructuringData tests completely restructuring JSON data
|
|
func TestJSONProcessor_RestructuringData(t *testing.T) {
|
|
content := `{
|
|
"people": [
|
|
{
|
|
"id": 1,
|
|
"name": "Alice",
|
|
"attributes": {
|
|
"age": 25,
|
|
"role": "developer"
|
|
}
|
|
},
|
|
{
|
|
"id": 2,
|
|
"name": "Bob",
|
|
"attributes": {
|
|
"age": 30,
|
|
"role": "manager"
|
|
}
|
|
}
|
|
]
|
|
}`
|
|
|
|
expected := `{
|
|
"people": {
|
|
"developers": [
|
|
{
|
|
"id": 1,
|
|
"name": "Alice",
|
|
"age": 25
|
|
}
|
|
],
|
|
"managers": [
|
|
{
|
|
"id": 2,
|
|
"name": "Bob",
|
|
"age": 30
|
|
}
|
|
]
|
|
}
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$", `
|
|
-- Restructure the data
|
|
local old_people = v.people
|
|
local new_structure = {
|
|
developers = {},
|
|
managers = {}
|
|
}
|
|
|
|
for _, person in ipairs(old_people) do
|
|
local role = person.attributes.role
|
|
local new_person = {
|
|
id = person.id,
|
|
name = person.name,
|
|
age = person.attributes.age
|
|
}
|
|
|
|
if role == "developer" then
|
|
table.insert(new_structure.developers, new_person)
|
|
elseif role == "manager" then
|
|
table.insert(new_structure.managers, new_person)
|
|
end
|
|
end
|
|
|
|
v.people = new_structure
|
|
`)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// TestJSONProcessor_FilteringArrayElements tests filtering elements from an array
|
|
func TestJSONProcessor_FilteringArrayElements(t *testing.T) {
|
|
content := `{
|
|
"numbers": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
}`
|
|
|
|
expected := `{
|
|
"numbers": [2, 4, 6, 8, 10]
|
|
}`
|
|
|
|
p := &JSONProcessor{}
|
|
result, modCount, matchCount, err := p.ProcessContent(content, "$.numbers", `
|
|
-- Filter to keep only even numbers
|
|
local filtered = {}
|
|
for _, num in ipairs(v) do
|
|
if num % 2 == 0 then
|
|
table.insert(filtered, num)
|
|
end
|
|
end
|
|
v = filtered
|
|
`)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matchCount != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matchCount)
|
|
}
|
|
|
|
if modCount != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", modCount)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
normalizedResult := normalizeWhitespace(result)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
|
|
if normalizedResult != normalizedExpected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|