1238 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1238 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package processor
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"io"
 | 
						|
	"modify/utils"
 | 
						|
	"os"
 | 
						|
	"regexp"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/stretchr/testify/assert"
 | 
						|
)
 | 
						|
 | 
						|
// TestLogger implements the Logger interface for testing
 | 
						|
type TestLogger struct {
 | 
						|
	T *testing.T // Reference to the test's *testing.T
 | 
						|
}
 | 
						|
 | 
						|
func (l *TestLogger) Printf(format string, v ...any) {
 | 
						|
	if l.T != nil {
 | 
						|
		l.T.Logf(format, v...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Helper function to normalize whitespace for comparison
 | 
						|
func normalizeWhitespace(s string) string {
 | 
						|
	// Replace all whitespace with a single space
 | 
						|
	re := regexp.MustCompile(`\s+`)
 | 
						|
	return re.ReplaceAllString(strings.TrimSpace(s), " ")
 | 
						|
}
 | 
						|
 | 
						|
func ApiAdaptor(content string, regex string, lua string) (string, int, int, error) {
 | 
						|
	command := utils.ModifyCommand{
 | 
						|
		Regex:    regex,
 | 
						|
		Lua:      lua,
 | 
						|
		LogLevel: "TRACE",
 | 
						|
	}
 | 
						|
 | 
						|
	commands, err := ProcessRegex(content, command, "test")
 | 
						|
	if err != nil {
 | 
						|
		return "", 0, 0, err
 | 
						|
	}
 | 
						|
 | 
						|
	result, modifications := utils.ExecuteModifications(commands, content)
 | 
						|
	return result, modifications, len(commands), nil
 | 
						|
}
 | 
						|
 | 
						|
func TestBuildLuaScript(t *testing.T) {
 | 
						|
	cases := []struct {
 | 
						|
		input    string
 | 
						|
		expected string
 | 
						|
	}{
 | 
						|
		{"s1 .. '_suffix'", "v1 = s1 .. '_suffix'"},
 | 
						|
		{"v1 * 1.5", "v1 = v1 * 1.5"},
 | 
						|
		{"v1 + 10", "v1 = v1 + 10"},
 | 
						|
		{"v1 * 2", "v1 = v1 * 2"},
 | 
						|
		{"v1 * v2", "v1 = v1 * v2"},
 | 
						|
		{"v1 / v2", "v1 = v1 / v2"},
 | 
						|
		{"12", "v1 = 12"},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, c := range cases {
 | 
						|
		result := PrependLuaAssignment(c.input)
 | 
						|
		assert.Equal(t, c.expected, result, "BuildLuaScript(%q): expected %q, got %q", c.input, c.expected, result)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSimpleValueMultiplication(t *testing.T) {
 | 
						|
	content := `<config>
 | 
						|
    <item>
 | 
						|
        <value>100</value>
 | 
						|
    </item>
 | 
						|
</config>`
 | 
						|
 | 
						|
	expected := `<config>
 | 
						|
    <item>
 | 
						|
        <value>150</value>
 | 
						|
    </item>
 | 
						|
</config>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content, `(?s)<value>(\d+)</value>`, "v1 = v1*1.5")
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestShorthandNotation(t *testing.T) {
 | 
						|
	content := `<config>
 | 
						|
    <item>
 | 
						|
        <value>100</value>
 | 
						|
    </item>
 | 
						|
</config>`
 | 
						|
 | 
						|
	expected := `<config>
 | 
						|
    <item>
 | 
						|
        <value>150</value>
 | 
						|
    </item>
 | 
						|
</config>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content, `(?s)<value>(\d+)</value>`, "v1*1.5")
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestShorthandNotationFloats(t *testing.T) {
 | 
						|
	content := `<config>
 | 
						|
    <item>
 | 
						|
        <value>10.5</value>
 | 
						|
    </item>
 | 
						|
</config>`
 | 
						|
 | 
						|
	expected := `<config>
 | 
						|
    <item>
 | 
						|
        <value>15.75</value>
 | 
						|
    </item>
 | 
						|
</config>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content, `(?s)<value>(\d+\.\d+)</value>`, "v1*1.5")
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestArrayNotation(t *testing.T) {
 | 
						|
	content := `<config>
 | 
						|
    <prices>
 | 
						|
        <price>10</price>
 | 
						|
        <price>20</price>
 | 
						|
        <price>30</price>
 | 
						|
    </prices>
 | 
						|
</config>`
 | 
						|
 | 
						|
	expected := `<config>
 | 
						|
    <prices>
 | 
						|
        <price>20</price>
 | 
						|
        <price>40</price>
 | 
						|
        <price>60</price>
 | 
						|
    </prices>
 | 
						|
</config>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content, `(?s)<price>(\d+)</price>`, "v1*2")
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 3, matches, "Expected 3 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 3, mods, "Expected 3 modifications, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestMultipleNumericMatches(t *testing.T) {
 | 
						|
	content := `<data>
 | 
						|
    <entry>50</entry>
 | 
						|
    <entry>100</entry>
 | 
						|
    <entry>200</entry>
 | 
						|
</data>`
 | 
						|
 | 
						|
	expected := `<data>
 | 
						|
    <entry>100</entry>
 | 
						|
    <entry>200</entry>
 | 
						|
    <entry>400</entry>
 | 
						|
</data>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content, `<entry>(\d+)</entry>`, "v1*2")
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 3, matches, "Expected 3 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 3, mods, "Expected 3 modifications, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestMultipleStringMatches(t *testing.T) {
 | 
						|
	content := `<data>
 | 
						|
    <name>John</name>
 | 
						|
    <name>Mary</name>
 | 
						|
</data>`
 | 
						|
 | 
						|
	expected := `<data>
 | 
						|
    <name>John_modified</name>
 | 
						|
    <name>Mary_modified</name>
 | 
						|
</data>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content, `<name>([A-Za-z]+)</name>`, `s1 = s1 .. "_modified"`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 2, matches, "Expected 2 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 2, mods, "Expected 2 modifications, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestStringUpperCase(t *testing.T) {
 | 
						|
	content := `<users>
 | 
						|
    <user>John</user>
 | 
						|
    <user>Mary</user>
 | 
						|
</users>`
 | 
						|
 | 
						|
	expected := `<users>
 | 
						|
    <user>JOHN</user>
 | 
						|
    <user>MARY</user>
 | 
						|
</users>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content, `<user>([A-Za-z]+)</user>`, `s1 = string.upper(s1)`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 2, matches, "Expected 2 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 2, mods, "Expected 2 modifications, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestStringConcatenation(t *testing.T) {
 | 
						|
	content := `<products>
 | 
						|
    <product>Apple</product>
 | 
						|
    <product>Banana</product>
 | 
						|
</products>`
 | 
						|
 | 
						|
	expected := `<products>
 | 
						|
    <product>Apple_fruit</product>
 | 
						|
    <product>Banana_fruit</product>
 | 
						|
</products>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content, `<product>([A-Za-z]+)</product>`, `s1 = s1 .. "_fruit"`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 2, matches, "Expected 2 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 2, mods, "Expected 2 modifications, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
// Added from main_test.go
 | 
						|
func TestDecimalValues(t *testing.T) {
 | 
						|
	content := `
 | 
						|
		<config>
 | 
						|
			<item>
 | 
						|
				<value>10.5</value>
 | 
						|
				<multiplier>2.5</multiplier>
 | 
						|
			</item>
 | 
						|
		</config>
 | 
						|
	`
 | 
						|
	expected := `
 | 
						|
		<config>
 | 
						|
			<item>
 | 
						|
				<value>26.25</value>
 | 
						|
				<multiplier>2.5</multiplier>
 | 
						|
			</item>
 | 
						|
		</config>
 | 
						|
	`
 | 
						|
 | 
						|
	regex := regexp.MustCompile(`(?s)<value>([0-9.]+)</value>.*?<multiplier>([0-9.]+)</multiplier>`)
 | 
						|
	luaExpr := BuildLuaScript("v1 = v1 * v2")
 | 
						|
 | 
						|
	result, _, _, err := ApiAdaptor(content, regex.String(), luaExpr)
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
 | 
						|
	normalizedModified := normalizeWhitespace(result)
 | 
						|
	normalizedExpected := normalizeWhitespace(expected)
 | 
						|
	assert.Equal(t, normalizedExpected, normalizedModified, "Expected modified content to be %q, but got %q", normalizedExpected, normalizedModified)
 | 
						|
}
 | 
						|
 | 
						|
// Added from main_test.go
 | 
						|
func TestLuaMathFunctions(t *testing.T) {
 | 
						|
	content := `
 | 
						|
		<config>
 | 
						|
			<item>
 | 
						|
				<value>16</value>
 | 
						|
			</item>
 | 
						|
		</config>
 | 
						|
	`
 | 
						|
	expected := `
 | 
						|
		<config>
 | 
						|
			<item>
 | 
						|
				<value>4</value>
 | 
						|
			</item>
 | 
						|
		</config>
 | 
						|
	`
 | 
						|
 | 
						|
	regex := regexp.MustCompile(`(?s)<value>(\d+)</value>`)
 | 
						|
	luaExpr := BuildLuaScript("v1 = math.sqrt(v1)")
 | 
						|
 | 
						|
	modifiedContent, _, _, err := ApiAdaptor(content, regex.String(), luaExpr)
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
 | 
						|
	normalizedModified := normalizeWhitespace(modifiedContent)
 | 
						|
	normalizedExpected := normalizeWhitespace(expected)
 | 
						|
	assert.Equal(t, normalizedExpected, normalizedModified, "Expected modified content to be %q, but got %q", normalizedExpected, normalizedModified)
 | 
						|
}
 | 
						|
 | 
						|
// Added from main_test.go
 | 
						|
func TestDirectAssignment(t *testing.T) {
 | 
						|
	content := `
 | 
						|
		<config>
 | 
						|
			<item>
 | 
						|
				<value>100</value>
 | 
						|
			</item>
 | 
						|
		</config>
 | 
						|
	`
 | 
						|
	expected := `
 | 
						|
		<config>
 | 
						|
			<item>
 | 
						|
				<value>0</value>
 | 
						|
			</item>
 | 
						|
		</config>
 | 
						|
	`
 | 
						|
 | 
						|
	regex := regexp.MustCompile(`(?s)<value>(\d+)</value>`)
 | 
						|
	luaExpr := BuildLuaScript("=0")
 | 
						|
 | 
						|
	modifiedContent, _, _, err := ApiAdaptor(content, regex.String(), luaExpr)
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
 | 
						|
	normalizedModified := normalizeWhitespace(modifiedContent)
 | 
						|
	normalizedExpected := normalizeWhitespace(expected)
 | 
						|
	assert.Equal(t, normalizedExpected, normalizedModified, "Expected modified content to be %q, but got %q", normalizedExpected, normalizedModified)
 | 
						|
}
 | 
						|
 | 
						|
// Added from main_test.go
 | 
						|
func TestStringAndNumericOperations(t *testing.T) {
 | 
						|
	tests := []struct {
 | 
						|
		name           string
 | 
						|
		input          string
 | 
						|
		regexPattern   string
 | 
						|
		luaExpression  string
 | 
						|
		expectedOutput string
 | 
						|
		expectedMods   int
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name:           "Basic numeric multiplication",
 | 
						|
			input:          "<value>42</value>",
 | 
						|
			regexPattern:   "<value>(\\d+)</value>",
 | 
						|
			luaExpression:  "v1 = v1 * 2",
 | 
						|
			expectedOutput: "<value>84</value>",
 | 
						|
			expectedMods:   1,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "Basic string manipulation",
 | 
						|
			input:          "<name>test</name>",
 | 
						|
			regexPattern:   "<name>(.*?)</name>",
 | 
						|
			luaExpression:  "s1 = string.upper(s1)",
 | 
						|
			expectedOutput: "<name>TEST</name>",
 | 
						|
			expectedMods:   1,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "String concatenation",
 | 
						|
			input:          "<id>abc123</id>",
 | 
						|
			regexPattern:   "<id>(.*?)</id>",
 | 
						|
			luaExpression:  "s1 = s1 .. '_modified'",
 | 
						|
			expectedOutput: "<id>abc123_modified</id>",
 | 
						|
			expectedMods:   1,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "Numeric value from string using num()",
 | 
						|
			input:          "<price>19.99</price>",
 | 
						|
			regexPattern:   "<price>(.*?)</price>",
 | 
						|
			luaExpression:  "v1 = num(s1) * 1.2",
 | 
						|
			expectedOutput: "<price>23.987999999999996</price>",
 | 
						|
			expectedMods:   1,
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tt := range tests {
 | 
						|
		t.Run(tt.name, func(t *testing.T) {
 | 
						|
			// Compile the regex pattern with multiline support
 | 
						|
			pattern := "(?s)" + tt.regexPattern
 | 
						|
			luaExpr := BuildLuaScript(tt.luaExpression)
 | 
						|
 | 
						|
			// Process with our function
 | 
						|
			result, modCount, _, err := ApiAdaptor(tt.input, pattern, luaExpr)
 | 
						|
			assert.NoError(t, err, "Process function failed: %v", err)
 | 
						|
 | 
						|
			// Check results
 | 
						|
			assert.Equal(t, tt.expectedOutput, result, "Expected output: %s, got: %s", tt.expectedOutput, result)
 | 
						|
			assert.Equal(t, tt.expectedMods, modCount, "Expected %d modifications, got %d", tt.expectedMods, modCount)
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Added from main_test.go
 | 
						|
func TestEdgeCases(t *testing.T) {
 | 
						|
	tests := []struct {
 | 
						|
		name           string
 | 
						|
		input          string
 | 
						|
		regexPattern   string
 | 
						|
		luaExpression  string
 | 
						|
		expectedOutput string
 | 
						|
		expectedMods   int
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name:           "Empty capture group",
 | 
						|
			input:          "<value></value>",
 | 
						|
			regexPattern:   "<value>(.*?)</value>",
 | 
						|
			luaExpression:  "s1 = 'filled'",
 | 
						|
			expectedOutput: "<value>filled</value>",
 | 
						|
			expectedMods:   1,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "Non-numeric string with numeric operation",
 | 
						|
			input:          "<value>abc</value>",
 | 
						|
			regexPattern:   "<value>(.*?)</value>",
 | 
						|
			luaExpression:  "v1 = v1 * 2",        // This would fail if we didn't handle strings properly
 | 
						|
			expectedOutput: "<value>abc</value>", // Should remain unchanged
 | 
						|
			expectedMods:   0,                    // No modifications
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "Invalid number conversion",
 | 
						|
			input:          "<value>abc</value>",
 | 
						|
			regexPattern:   "<value>(.*?)</value>",
 | 
						|
			luaExpression:  "v1 = num(s1) + 10", // num(s1) should return 0
 | 
						|
			expectedOutput: "<value>10</value>",
 | 
						|
			expectedMods:   1,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:           "Multiline string",
 | 
						|
			input:          "<text>Line 1\nLine 2</text>",
 | 
						|
			regexPattern:   "<text>(.*?)</text>",
 | 
						|
			luaExpression:  "s1 = string.gsub(s1, '\\n', ' - ')",
 | 
						|
			expectedOutput: "<text>Line 1 - Line 2</text>",
 | 
						|
			expectedMods:   1,
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tt := range tests {
 | 
						|
		t.Run(tt.name, func(t *testing.T) {
 | 
						|
			// Make sure the regex can match across multiple lines
 | 
						|
			pattern := "(?s)" + tt.regexPattern
 | 
						|
			luaExpr := BuildLuaScript(tt.luaExpression)
 | 
						|
 | 
						|
			// Process with our function
 | 
						|
			result, modCount, _, err := ApiAdaptor(tt.input, pattern, luaExpr)
 | 
						|
			assert.NoError(t, err, "Process function failed: %v", err)
 | 
						|
 | 
						|
			// Check results
 | 
						|
			assert.Equal(t, tt.expectedOutput, result, "Expected output: %s, got: %s", tt.expectedOutput, result)
 | 
						|
			assert.Equal(t, tt.expectedMods, modCount, "Expected %d modifications, got %d", tt.expectedMods, modCount)
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestNamedCaptureGroups(t *testing.T) {
 | 
						|
	content := `<config>
 | 
						|
    <item>
 | 
						|
        <value>100</value>
 | 
						|
    </item>
 | 
						|
</config>`
 | 
						|
 | 
						|
	expected := `<config>
 | 
						|
    <item>
 | 
						|
        <value>200</value>
 | 
						|
    </item>
 | 
						|
</config>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content, `(?s)<value>(?<amount>\d+)</value>`, "amount = amount * 2")
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestNamedCaptureGroupsNum(t *testing.T) {
 | 
						|
	content := `<config>
 | 
						|
    <item>
 | 
						|
        <value>100</value>
 | 
						|
    </item>
 | 
						|
</config>`
 | 
						|
 | 
						|
	expected := `<config>
 | 
						|
    <item>
 | 
						|
        <value>200</value>
 | 
						|
    </item>
 | 
						|
</config>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content, `(?s)<value>(?<amount>!num)</value>`, "amount = amount * 2")
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestMultipleNamedCaptureGroups(t *testing.T) {
 | 
						|
	content := `<product>
 | 
						|
    <name>Widget</name>
 | 
						|
    <price>15.99</price>
 | 
						|
    <quantity>10</quantity>
 | 
						|
</product>`
 | 
						|
 | 
						|
	expected := `<product>
 | 
						|
    <name>WIDGET</name>
 | 
						|
    <price>23.99</price>
 | 
						|
    <quantity>15</quantity>
 | 
						|
</product>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`(?s)<name>(?<prodName>[^<]+)</name>.*?<price>(?<prodPrice>\d+\.\d+)</price>.*?<quantity>(?<prodQty>\d+)</quantity>`,
 | 
						|
		`prodName = string.upper(prodName)
 | 
						|
		 prodPrice = round(prodPrice + 8, 2)
 | 
						|
		 prodQty = prodQty + 5`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 3, matches, "Expected 3 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 3, mods, "Expected 3 modifications, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestMixedIndexedAndNamedCaptures(t *testing.T) {
 | 
						|
	content := `<entry>
 | 
						|
    <id>12345</id>
 | 
						|
    <data>value</data>
 | 
						|
</entry>`
 | 
						|
 | 
						|
	expected := `<entry>
 | 
						|
    <id>24690</id>
 | 
						|
    <data>VALUE</data>
 | 
						|
</entry>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`(?s)<id>(\d+)</id>.*?<data>(?<dataField>[^<]+)</data>`,
 | 
						|
		`v1 = v1 * 2
 | 
						|
		 dataField = string.upper(dataField)`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 2, matches, "Expected 2 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 2, mods, "Expected 2 modifications, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestComplexNestedNamedCaptures(t *testing.T) {
 | 
						|
	content := `<person>
 | 
						|
		<details>
 | 
						|
			<name>John Smith</name>
 | 
						|
			<age>32</age>
 | 
						|
		</details>
 | 
						|
		<contact>
 | 
						|
			<email>john@example.com</email>
 | 
						|
		</contact>
 | 
						|
	</person>`
 | 
						|
 | 
						|
	expected := `<person>
 | 
						|
		<details>
 | 
						|
			<name>JOHN SMITH (32)</name>
 | 
						|
			<age>32</age>
 | 
						|
		</details>
 | 
						|
		<contact>
 | 
						|
			<email>john@example.com</email>
 | 
						|
		</contact>
 | 
						|
	</person>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`(?s)<details>.*?<name>(?<fullName>[^<]+)</name>.*?<age>(?<age>\d+)</age>`,
 | 
						|
		`fullName = string.upper(fullName) .. " (" .. age .. ")"`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestNamedCaptureWithVariableReadback(t *testing.T) {
 | 
						|
	content := `<stats>
 | 
						|
    <health>100</health>
 | 
						|
    <mana>200</mana>
 | 
						|
</stats>`
 | 
						|
 | 
						|
	expected := `<stats>
 | 
						|
    <health>150</health>
 | 
						|
    <mana>300</mana>
 | 
						|
</stats>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`(?s)<health>(?<hp>\d+)</health>.*?<mana>(?<mp>\d+)</mana>`,
 | 
						|
		`hp = hp * 1.5
 | 
						|
		 mp = mp * 1.5`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 2, matches, "Expected 2 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 2, mods, "Expected 2 modifications, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestNamedCaptureWithSpecialCharsInName(t *testing.T) {
 | 
						|
	content := `<data value="42" min="10" max="100" />`
 | 
						|
 | 
						|
	expected := `<data value="84" min="10" max="100" />`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`<data value="(?<val_1>\d+)"`,
 | 
						|
		`val_1 = val_1 * 2`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestEmptyNamedCapture(t *testing.T) {
 | 
						|
	content := `<tag attr="" />`
 | 
						|
 | 
						|
	expected := `<tag attr="default" />`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`attr="(?<value>.*?)"`,
 | 
						|
		`value = value == "" and "default" or value`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestMultipleNamedCapturesInSameLine(t *testing.T) {
 | 
						|
	content := `<rect x="10" y="20" width="100" height="50" />`
 | 
						|
 | 
						|
	expected := `<rect x="20" y="40" width="200" height="100" />`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`x="(?<x>\d+)" y="(?<y>\d+)" width="(?<w>\d+)" height="(?<h>\d+)"`,
 | 
						|
		`x = x * 2
 | 
						|
		 y = y * 2
 | 
						|
		 w = w * 2
 | 
						|
		 h = h * 2`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 4, matches, "Expected 4 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 4, mods, "Expected 4 modifications, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestConditionalNamedCapture(t *testing.T) {
 | 
						|
	content := `
 | 
						|
	<item status="active" count="5" />
 | 
						|
	<item status="inactive" count="10" />
 | 
						|
	`
 | 
						|
 | 
						|
	expected := `
 | 
						|
	<item status="active" count="10" />
 | 
						|
	<item status="inactive" count="10" />
 | 
						|
	`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`<item status="(?<status>[^"]+)" count="(?<count>\d+)"`,
 | 
						|
		`count = status == "active" and count * 2 or count`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestLuaFunctionsOnNamedCaptures(t *testing.T) {
 | 
						|
	content := `
 | 
						|
	<user name="john doe" role="user" />
 | 
						|
	<user name="jane smith" role="admin" />
 | 
						|
	`
 | 
						|
 | 
						|
	expected := `
 | 
						|
	<user name="John Doe" role="user" />
 | 
						|
	<user name="JANE SMITH" role="admin" />
 | 
						|
	`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`<user name="(?<name>[^"]+)" role="(?<role>[^"]+)"`,
 | 
						|
		`-- Capitalize first letters for regular users
 | 
						|
		 if role == "user" then
 | 
						|
		   name = name:gsub("(%w)(%w*)", function(first, rest) return first:upper()..rest end):gsub(" (%w)(%w*)", " %1%2")
 | 
						|
		 else
 | 
						|
		   -- Uppercase for admins
 | 
						|
		   name = string.upper(name)
 | 
						|
		 end`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 2, matches, "Expected 2 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 2, mods, "Expected 2 modifications, got %d", mods)
 | 
						|
 | 
						|
	// For simpler tests, we can use this. More complex string modifications
 | 
						|
	// might need additional transformations before comparison
 | 
						|
	normalizedResult := normalizeWhitespace(result)
 | 
						|
	normalizedExpected := normalizeWhitespace(expected)
 | 
						|
	assert.Equal(t, normalizedExpected, normalizedResult, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestNamedCaptureWithMath(t *testing.T) {
 | 
						|
	content := `
 | 
						|
	<item price="19.99" quantity="3" />
 | 
						|
	`
 | 
						|
 | 
						|
	expected := `
 | 
						|
	<item price="19.99" quantity="3" total="59.97" />
 | 
						|
	`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`<item price="(?<price>\d+\.\d+)" quantity="(?<qty>\d+)"!any$`,
 | 
						|
		`-- Calculate and add total
 | 
						|
		 replacement = string.format('<item price="%s" quantity="%s" total="%.2f" />',
 | 
						|
			price, qty, price * qty)`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
 | 
						|
	result = normalizeWhitespace(result)
 | 
						|
	expected = normalizeWhitespace(expected)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestNamedCaptureWithGlobals(t *testing.T) {
 | 
						|
	content := `<temp unit="C">25</temp>`
 | 
						|
 | 
						|
	expected := `<temp unit="F">77</temp>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`<temp unit="(?<unit>[CF]?)">(?<value>\d+)</temp>`,
 | 
						|
		`if unit == "C" then
 | 
						|
		  value = value * 9/5 + 32
 | 
						|
		  unit = "F"
 | 
						|
		elseif unit == "F" then
 | 
						|
		  value = (value - 32) * 5/9
 | 
						|
		  unit = "C"
 | 
						|
		end`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 2, matches, "Expected 2 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 2, mods, "Expected 2 modifications, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestMixedDynamicAndNamedCaptures(t *testing.T) {
 | 
						|
	content := `
 | 
						|
	<color rgb="255,0,0" name="red" />
 | 
						|
	<color rgb="0,255,0" name="green" />
 | 
						|
	`
 | 
						|
 | 
						|
	expected := `
 | 
						|
	<color rgb="255,0,0" name="RED" hex="#FF0000" />
 | 
						|
	<color rgb="0,255,0" name="GREEN" hex="#00FF00" />
 | 
						|
	`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`<color rgb="(?<r>\d+),(?<g>\d+),(?<b>\d+)" name="(?<colorName>[^"]+)" />`,
 | 
						|
		`-- Uppercase the name
 | 
						|
		 colorName = string.upper(colorName)
 | 
						|
 | 
						|
		 -- Create hex color
 | 
						|
		 local hex = string.format("#%02X%02X%02X", tonumber(r), tonumber(g), tonumber(b))
 | 
						|
 | 
						|
		 -- Replace the entire match
 | 
						|
		 replacement = string.format('<color rgb="%s,%s,%s" name="%s" hex="%s" />',
 | 
						|
		                            r, g, b, colorName, hex)`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 2, matches, "Expected 2 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 2, mods, "Expected 2 modifications, got %d", mods)
 | 
						|
 | 
						|
	result = normalizeWhitespace(result)
 | 
						|
	expected = normalizeWhitespace(expected)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestNamedCapturesWithMultipleReferences(t *testing.T) {
 | 
						|
	content := `<text>Hello world</text>`
 | 
						|
 | 
						|
	expected := `<text format="uppercase" length="11">HELLO WORLD</text>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`<text>(?<content>[^<]+)</text>`,
 | 
						|
		`local uppercaseContent = string.upper(content)
 | 
						|
		 local contentLength = string.len(content)
 | 
						|
		 replacement = string.format('<text format="uppercase" length="%d">%s</text>',
 | 
						|
		                            contentLength, uppercaseContent)`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestNamedCaptureWithJsonData(t *testing.T) {
 | 
						|
	content := `<data>{"name":"John","age":30}</data>`
 | 
						|
 | 
						|
	expected := `<data>{"name":"JOHN","age":30}</data>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`<data>(?<json>\{.*?\})</data>`,
 | 
						|
		`-- Parse JSON (simplified, assumes valid JSON)
 | 
						|
		 local name = json:match('"name":"([^"]+)"')
 | 
						|
		 local upperName = string.upper(name)
 | 
						|
		 json = json:gsub('"name":"([^"]+)"', '"name":"' .. upperName .. '"')`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestNamedCaptureInXML(t *testing.T) {
 | 
						|
	content := `
 | 
						|
	<product>
 | 
						|
		<sku>ABC-123</sku>
 | 
						|
		<price currency="USD">19.99</price>
 | 
						|
		<stock>25</stock>
 | 
						|
	</product>
 | 
						|
	`
 | 
						|
 | 
						|
	expected := `
 | 
						|
	<product>
 | 
						|
		<sku>ABC-123</sku>
 | 
						|
		<price currency="USD">23.99</price>
 | 
						|
		<stock>20</stock>
 | 
						|
	</product>
 | 
						|
	`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`(?s)<price currency="(?<currency>[^"]+)">(?<price>\d+\.\d+)</price>.*?<stock>(?<stock>\d+)</stock>`,
 | 
						|
		`-- Add 20% to price if USD
 | 
						|
		 if currency == "USD" then
 | 
						|
		   price = round(price * 1.20, 2)
 | 
						|
		 end
 | 
						|
 | 
						|
		 -- Reduce stock by 5
 | 
						|
		 stock = stock - 5`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 2, matches, "Expected 2 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 2, mods, "Expected 2 modifications, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestComprehensiveNamedCaptures(t *testing.T) {
 | 
						|
	content := `
 | 
						|
	<products>
 | 
						|
		<product sku="AB-123" status="in-stock">
 | 
						|
			<name>Widget A</name>
 | 
						|
			<price currency="USD">19.99</price>
 | 
						|
			<quantity>15</quantity>
 | 
						|
		</product>
 | 
						|
		<product sku="CD-456" status="out-of-stock">
 | 
						|
			<name>Widget B</name>
 | 
						|
			<price currency="EUR">29.99</price>
 | 
						|
			<quantity>0</quantity>
 | 
						|
		</product>
 | 
						|
		<product sku="EF-789" status="in-stock">
 | 
						|
			<name>Widget C</name>
 | 
						|
			<price currency="GBP">39.99</price>
 | 
						|
			<quantity>5</quantity>
 | 
						|
		</product>
 | 
						|
	</products>
 | 
						|
	`
 | 
						|
 | 
						|
	expected := `
 | 
						|
	<products>
 | 
						|
		<product sku="AB-123" status="in-stock" discounted="true">
 | 
						|
			<name>WIDGET A</name>
 | 
						|
			<price currency="USD">15.99</price>
 | 
						|
			<quantity>15</quantity>
 | 
						|
		</product>
 | 
						|
		<product sku="CD-456" status="out-of-stock" discounted="false">
 | 
						|
			<name>Widget B</name>
 | 
						|
			<price currency="EUR">29.99</price>
 | 
						|
			<quantity>0</quantity>
 | 
						|
		</product>
 | 
						|
		<product sku="EF-789" status="in-stock" discounted="true">
 | 
						|
			<name>WIDGET C</name>
 | 
						|
			<price currency="GBP">39.99</price>
 | 
						|
			<quantity>5</quantity>
 | 
						|
		</product>
 | 
						|
	</products>
 | 
						|
	`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`(?s)<product sku="(?<sku>[^"]+)" status="(?<status>[^"]+)"[^>]*>\s*<name>(?<product_name>[^<]+)</name>\s*<price currency="(?<currency>[^"]+)">(?<price>\d+\.\d+)</price>\s*<quantity>(?<qty>\d+)</quantity>`,
 | 
						|
		`-- Only process in-stock items
 | 
						|
		if status == "in-stock" then
 | 
						|
			-- Transform name to uppercase
 | 
						|
			product_name = string.upper(product_name)
 | 
						|
 | 
						|
			-- Apply discount based on currency
 | 
						|
			local discounted = true
 | 
						|
			if currency == "USD" then
 | 
						|
				price = round(price * 0.8, 2)  -- 20% discount for USD
 | 
						|
			elseif currency == "GBP" then
 | 
						|
				price = round(price * 0.8, 2)  -- 20% discount for GBP
 | 
						|
				price = price + 8  -- Add shipping cost for GBP
 | 
						|
			else
 | 
						|
				discounted = false
 | 
						|
			end
 | 
						|
 | 
						|
			-- Add discounted attribute
 | 
						|
			replacement = string.format('<product sku="%s" status="%s" discounted="%s">\n\t\t\t<name>%s</name>\n\t\t\t<price currency="%s">%.2f</price>\n\t\t\t<quantity>%s</quantity>',
 | 
						|
				sku, status, tostring(discounted), product_name, currency, price, qty)
 | 
						|
		else
 | 
						|
			-- Add discounted attribute for out-of-stock items (always false)
 | 
						|
			replacement = string.format('<product sku="%s" status="%s" discounted="false">\n\t\t\t<name>%s</name>\n\t\t\t<price currency="%s">%s</price>\n\t\t\t<quantity>%s</quantity>',
 | 
						|
				sku, status, product_name, currency, price, qty)
 | 
						|
		end`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 3, matches, "Expected 3 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 3, mods, "Expected 3 modifications, got %d", mods)
 | 
						|
 | 
						|
	// Normalize whitespace for comparison
 | 
						|
	normalizedResult := normalizeWhitespace(result)
 | 
						|
	normalizedExpected := normalizeWhitespace(expected)
 | 
						|
	assert.Equal(t, normalizedExpected, normalizedResult, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestVariousNamedCaptureFormats(t *testing.T) {
 | 
						|
	content := `
 | 
						|
	<data>
 | 
						|
		<entry id="1" value="100" />
 | 
						|
		<entry id="2" value="200" status="active" />
 | 
						|
		<entry id="3" value="300" status="inactive" />
 | 
						|
	</data>
 | 
						|
	`
 | 
						|
 | 
						|
	expected := `
 | 
						|
	<data>
 | 
						|
		<entry id="ID-1" value="200" />
 | 
						|
		<entry id="ID-2" value="400" status="ACTIVE" />
 | 
						|
		<entry id="ID-3" value="300" status="inactive" />
 | 
						|
	</data>
 | 
						|
	`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`<entry id="(?<id_num>\d+)" value="(?<val>\d+)"(?: status="(?<status>[^"]*)")? />`,
 | 
						|
		`-- Prefix the ID with "ID-"
 | 
						|
		id_num = "ID-" .. id_num
 | 
						|
		print(id_num)
 | 
						|
		print(val)
 | 
						|
		print(status)
 | 
						|
 | 
						|
		-- Double the value except for inactive status
 | 
						|
		if not status or status ~= "inactive" then
 | 
						|
			val = val * 2
 | 
						|
		end
 | 
						|
 | 
						|
		-- Convert status to uppercase if present and active
 | 
						|
		if status and status == "active" then
 | 
						|
			status = string.upper(status)
 | 
						|
		end
 | 
						|
 | 
						|
		-- Build the replacement based on whether status exists
 | 
						|
		if status then
 | 
						|
			replacement = string.format('<entry id="%s" value="%s" status="%s" />', id_num, val, status)
 | 
						|
		else
 | 
						|
			replacement = string.format('<entry id="%s" value="%s" />', id_num, val)
 | 
						|
		end`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 3, matches, "Expected 3 matches, got %d", matches)
 | 
						|
	assert.Equal(t, 3, mods, "Expected 3 modifications, got %d", mods)
 | 
						|
 | 
						|
	normalizedResult := normalizeWhitespace(result)
 | 
						|
	normalizedExpected := normalizeWhitespace(expected)
 | 
						|
	assert.Equal(t, normalizedExpected, normalizedResult, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
func TestSimpleNamedCapture(t *testing.T) {
 | 
						|
	content := `<product name="Widget" price="19.99"/>`
 | 
						|
 | 
						|
	expected := `<product name="WIDGET" price="19.99"/>`
 | 
						|
 | 
						|
	result, mods, matches, err := ApiAdaptor(content,
 | 
						|
		`name="(?<product_name>[^"]+)"`,
 | 
						|
		`product_name = string.upper(product_name)`)
 | 
						|
 | 
						|
	assert.NoError(t, err, "Error processing content: %v", err)
 | 
						|
	assert.Equal(t, 1, matches, "Expected 1 match, got %d", matches)
 | 
						|
	assert.Equal(t, 1, mods, "Expected 1 modification, got %d", mods)
 | 
						|
	assert.Equal(t, expected, result, "Expected content to be different")
 | 
						|
}
 | 
						|
 | 
						|
// Pattern without "(?s)" prefix gets modified to include it
 | 
						|
func TestPatternWithoutPrefixGetsModified(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	pattern := "some.*pattern"
 | 
						|
 | 
						|
	// Execute function
 | 
						|
	result := resolveRegexPlaceholders(pattern)
 | 
						|
 | 
						|
	// Verify results
 | 
						|
	expectedPattern := "(?s)some.*pattern"
 | 
						|
	assert.Equal(t, expectedPattern, result, "Expected pattern to be %q, got %q", expectedPattern, result)
 | 
						|
}
 | 
						|
 | 
						|
// Empty string input returns "(?s)"
 | 
						|
func TestEmptyStringReturnsWithPrefix(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	pattern := ""
 | 
						|
 | 
						|
	// Execute function
 | 
						|
	result := resolveRegexPlaceholders(pattern)
 | 
						|
 | 
						|
	// Verify results
 | 
						|
	expectedPattern := "(?s)"
 | 
						|
	assert.Equal(t, expectedPattern, result, "Expected pattern to be %q, got %q", expectedPattern, result)
 | 
						|
}
 | 
						|
 | 
						|
// Named group with "!num" pattern gets replaced with proper regex for numbers
 | 
						|
func TestNamedGroupNumPatternReplacement(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	pattern := "(?<group>!num)"
 | 
						|
 | 
						|
	// Execute function
 | 
						|
	result := resolveRegexPlaceholders(pattern)
 | 
						|
 | 
						|
	// Verify results
 | 
						|
	expectedPattern := "(?s)(?<group>-?\\d*\\.?\\d+)"
 | 
						|
	assert.Equal(t, expectedPattern, result, "Expected pattern to be %q, got %q", expectedPattern, result)
 | 
						|
}
 | 
						|
 | 
						|
// "!any" placeholder gets replaced with ".*?" pattern
 | 
						|
func TestAnyPlaceholderReplacement(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	pattern := "start!anyend"
 | 
						|
 | 
						|
	// Execute function
 | 
						|
	result := resolveRegexPlaceholders(pattern)
 | 
						|
 | 
						|
	// Verify results
 | 
						|
	expectedPattern := "(?s)start.*?end"
 | 
						|
	assert.Equal(t, expectedPattern, result, "Expected pattern to be %q, got %q", expectedPattern, result)
 | 
						|
}
 | 
						|
 | 
						|
// "!rep(pattern, count)" correctly repeats the pattern with separators
 | 
						|
func TestRepPatternRepetition(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	pattern := "!rep(a, 3)"
 | 
						|
 | 
						|
	// Execute function
 | 
						|
	result := resolveRegexPlaceholders(pattern)
 | 
						|
 | 
						|
	// Verify results
 | 
						|
	expectedPattern := "(?s)a.*?a.*?a"
 | 
						|
	assert.Equal(t, expectedPattern, result, "Expected pattern to be %q, got %q", expectedPattern, result)
 | 
						|
}
 | 
						|
 | 
						|
// Multiple different placeholders in the same pattern are all correctly replaced
 | 
						|
func TestMultiplePlaceholdersReplacedCorrectly(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	pattern := "(?s)some(?<num1>!num)text!anymore!rep(!num, 3)"
 | 
						|
 | 
						|
	// Execute function
 | 
						|
	result := resolveRegexPlaceholders(pattern)
 | 
						|
 | 
						|
	// Verify results
 | 
						|
	expectedPattern := "(?s)some(?<num1>-?\\d*\\.?\\d+)text.*?more(-?\\d*\\.?\\d+).*?(-?\\d*\\.?\\d+).*?(-?\\d*\\.?\\d+)"
 | 
						|
	assert.Equal(t, expectedPattern, result, "Expected pattern to be %q, got %q", expectedPattern, result)
 | 
						|
}
 | 
						|
 | 
						|
// Pattern already containing "(?s)" prefix remains unchanged
 | 
						|
func TestPatternWithPrefixRemainsUnchanged(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	pattern := "(?s)some.*pattern"
 | 
						|
 | 
						|
	// Redirect stdout to capture fmt.Printf output
 | 
						|
	oldStdout := os.Stdout
 | 
						|
	r, w, _ := os.Pipe()
 | 
						|
	os.Stdout = w
 | 
						|
 | 
						|
	// Execute function
 | 
						|
	result := resolveRegexPlaceholders(pattern)
 | 
						|
 | 
						|
	// Restore stdout
 | 
						|
	w.Close()
 | 
						|
	os.Stdout = oldStdout
 | 
						|
 | 
						|
	// Read captured output
 | 
						|
	var buf bytes.Buffer
 | 
						|
	io.Copy(&buf, r)
 | 
						|
	output := buf.String()
 | 
						|
 | 
						|
	// Verify results
 | 
						|
	expectedPattern := "(?s)some.*pattern"
 | 
						|
	assert.Equal(t, expectedPattern, result, "Expected pattern to remain %q, got %q", expectedPattern, result)
 | 
						|
 | 
						|
	expectedOutput := ""
 | 
						|
	assert.Equal(t, expectedOutput, output, "Expected no output message, got %q", output)
 | 
						|
}
 | 
						|
 | 
						|
// Malformed "!rep" pattern without proper parameters returns unchanged match
 | 
						|
func TestMalformedRepPatternReturnsUnchanged(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	pattern := "!rep(somepattern)" // Malformed !rep pattern without count
 | 
						|
 | 
						|
	// Execute function
 | 
						|
	result := resolveRegexPlaceholders(pattern)
 | 
						|
 | 
						|
	// Verify results
 | 
						|
	expectedPattern := "(?s)!rep(somepattern)"
 | 
						|
	assert.Equal(t, expectedPattern, result, "Expected pattern to be %q, got %q", expectedPattern, result)
 | 
						|
}
 | 
						|
 | 
						|
// Nested placeholder patterns (e.g., "!rep(!num, 3)")
 | 
						|
func TestNestedPlaceholderPatterns(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	pattern := "!rep(!num, 3)"
 | 
						|
 | 
						|
	// Execute function
 | 
						|
	result := resolveRegexPlaceholders(pattern)
 | 
						|
 | 
						|
	// Verify results
 | 
						|
	expectedPattern := `(?s)(-?\d*\.?\d+).*?(-?\d*\.?\d+).*?(-?\d*\.?\d+)`
 | 
						|
	assert.Equal(t, expectedPattern, result, "Expected pattern to be %q, got %q", expectedPattern, result)
 | 
						|
}
 | 
						|
 | 
						|
// Returns empty slice when input is empty
 | 
						|
func TestDeduplicateGroupsWithEmptyInput(t *testing.T) {
 | 
						|
	// Arrange
 | 
						|
	var captureGroups []*CaptureGroup
 | 
						|
 | 
						|
	// Act
 | 
						|
	result := deduplicateGroups(captureGroups)
 | 
						|
 | 
						|
	// Assert
 | 
						|
	assert.Empty(t, result, "Expected empty slice when input is empty")
 | 
						|
}
 | 
						|
 | 
						|
// Input with all overlapping groups returns empty slice
 | 
						|
func TestDeduplicateGroupsWithAllOverlappingGroups(t *testing.T) {
 | 
						|
	// Arrange
 | 
						|
	captureGroups := []*CaptureGroup{
 | 
						|
		{
 | 
						|
			Name:  "group1",
 | 
						|
			Range: [2]int{10, 20},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:  "group2",
 | 
						|
			Range: [2]int{15, 25},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:  "group3",
 | 
						|
			Range: [2]int{5, 15},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	// Act
 | 
						|
	result := deduplicateGroups(captureGroups)
 | 
						|
 | 
						|
	// Assert
 | 
						|
	assert.Len(t, result, 1, "Expected only one group to remain after deduplication")
 | 
						|
	assert.Equal(t, "group1", result[0].Name, "Expected first group to be kept")
 | 
						|
}
 | 
						|
 | 
						|
// Successfully adds non-overlapping capture groups to result
 | 
						|
func TestDeduplicateGroupsWithNonOverlappingInput(t *testing.T) {
 | 
						|
	// Arrange
 | 
						|
	captureGroups := []*CaptureGroup{
 | 
						|
		{Name: "Group1", Range: [2]int{0, 5}},
 | 
						|
		{Name: "Group2", Range: [2]int{6, 10}},
 | 
						|
		{Name: "Group3", Range: [2]int{11, 15}},
 | 
						|
	}
 | 
						|
 | 
						|
	// Act
 | 
						|
	result := deduplicateGroups(captureGroups)
 | 
						|
 | 
						|
	// Assert
 | 
						|
	assert.Equal(t, 3, len(result), "Expected all non-overlapping groups to be added")
 | 
						|
	assert.Equal(t, "Group1", result[0].Name, "Expected Group1 to be in the result")
 | 
						|
	assert.Equal(t, "Group2", result[1].Name, "Expected Group2 to be in the result")
 | 
						|
	assert.Equal(t, "Group3", result[2].Name, "Expected Group3 to be in the result")
 | 
						|
}
 | 
						|
 | 
						|
// Correctly identifies and filters out overlapping capture groups
 | 
						|
func TestDeduplicateGroupsWithOverlappingInput(t *testing.T) {
 | 
						|
	// Arrange
 | 
						|
	captureGroups := []*CaptureGroup{
 | 
						|
		{Name: "group1", Range: [2]int{0, 5}},
 | 
						|
		{Name: "group2", Range: [2]int{3, 8}}, // Overlaps with group1
 | 
						|
		{Name: "group3", Range: [2]int{10, 15}},
 | 
						|
	}
 | 
						|
 | 
						|
	// Act
 | 
						|
	result := deduplicateGroups(captureGroups)
 | 
						|
 | 
						|
	// Assert
 | 
						|
	assert.Len(t, result, 2, "Expected two non-overlapping capture groups")
 | 
						|
	assert.Equal(t, "group1", result[0].Name, "Expected group1 to be in the result")
 | 
						|
	assert.Equal(t, "group3", result[1].Name, "Expected group3 to be in the result")
 | 
						|
}
 | 
						|
 | 
						|
// Handles multiple non-overlapping groups correctly
 | 
						|
func TestDeduplicateGroupsWithNonOverlappingGroups(t *testing.T) {
 | 
						|
	// Arrange
 | 
						|
	captureGroups := []*CaptureGroup{
 | 
						|
		{Name: "Group1", Range: [2]int{0, 5}},
 | 
						|
		{Name: "Group2", Range: [2]int{6, 10}},
 | 
						|
		{Name: "Group3", Range: [2]int{11, 15}},
 | 
						|
	}
 | 
						|
 | 
						|
	// Act
 | 
						|
	result := deduplicateGroups(captureGroups)
 | 
						|
 | 
						|
	// Assert
 | 
						|
	assert.Equal(t, 3, len(result), "Expected all groups to be included as they do not overlap")
 | 
						|
	assert.Equal(t, "Group1", result[0].Name, "Expected Group1 to be the first in the result")
 | 
						|
	assert.Equal(t, "Group2", result[1].Name, "Expected Group2 to be the second in the result")
 | 
						|
	assert.Equal(t, "Group3", result[2].Name, "Expected Group3 to be the third in the result")
 | 
						|
}
 | 
						|
 | 
						|
// Groups with identical ranges are considered overlapping
 | 
						|
func TestDeduplicateGroupsWithIdenticalRanges(t *testing.T) {
 | 
						|
	// Arrange
 | 
						|
	captureGroups := []*CaptureGroup{
 | 
						|
		{Name: "Group1", Range: [2]int{0, 5}},
 | 
						|
		{Name: "Group2", Range: [2]int{0, 5}}, // Identical range to Group1
 | 
						|
	}
 | 
						|
 | 
						|
	// Act
 | 
						|
	result := deduplicateGroups(captureGroups)
 | 
						|
 | 
						|
	// Assert
 | 
						|
	assert.Len(t, result, 1, "Expected only one group in the result due to identical ranges")
 | 
						|
	assert.Equal(t, "Group1", result[0].Name, "Expected Group1 to be in the result")
 | 
						|
}
 | 
						|
 | 
						|
// Groups with adjacent but non-overlapping ranges (end of one = start of another)
 | 
						|
func TestDeduplicateGroupsWithAdjacentNonOverlappingRanges(t *testing.T) {
 | 
						|
	// Arrange
 | 
						|
	captureGroups := []*CaptureGroup{
 | 
						|
		{Name: "Group1", Range: [2]int{0, 5}},
 | 
						|
		{Name: "Group2", Range: [2]int{5, 10}},
 | 
						|
		{Name: "Group3", Range: [2]int{10, 15}},
 | 
						|
	}
 | 
						|
 | 
						|
	// Act
 | 
						|
	result := deduplicateGroups(captureGroups)
 | 
						|
 | 
						|
	// Assert
 | 
						|
	assert.Equal(t, 3, len(result), "Expected all groups to be included as they are adjacent but non-overlapping")
 | 
						|
	assert.Equal(t, "Group1", result[0].Name, "Expected Group1 to be the first in the result")
 | 
						|
	assert.Equal(t, "Group2", result[1].Name, "Expected Group2 to be the second in the result")
 | 
						|
	assert.Equal(t, "Group3", result[2].Name, "Expected Group3 to be the third in the result")
 | 
						|
}
 |