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