549 lines
13 KiB
Go
549 lines
13 KiB
Go
package processor
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// 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 ...interface{}) {
|
|
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 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"},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
result := BuildLuaScript(c.input)
|
|
if result != c.expected {
|
|
t.Errorf("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>`
|
|
|
|
p := &RegexProcessor{}
|
|
result, mods, matches, err := p.ProcessContent(content, `(?s)<value>(\d+)</value>`, "v1 = v1*1.5")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matches != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matches)
|
|
}
|
|
|
|
if mods != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", mods)
|
|
}
|
|
|
|
if result != expected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestShorthandNotation(t *testing.T) {
|
|
content := `<config>
|
|
<item>
|
|
<value>100</value>
|
|
</item>
|
|
</config>`
|
|
|
|
expected := `<config>
|
|
<item>
|
|
<value>150</value>
|
|
</item>
|
|
</config>`
|
|
|
|
p := &RegexProcessor{}
|
|
result, mods, matches, err := p.ProcessContent(content, `(?s)<value>(\d+)</value>`, "v1*1.5")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matches != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matches)
|
|
}
|
|
|
|
if mods != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", mods)
|
|
}
|
|
|
|
if result != expected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestShorthandNotationFloats(t *testing.T) {
|
|
content := `<config>
|
|
<item>
|
|
<value>10.5</value>
|
|
</item>
|
|
</config>`
|
|
|
|
expected := `<config>
|
|
<item>
|
|
<value>15.75</value>
|
|
</item>
|
|
</config>`
|
|
|
|
p := &RegexProcessor{}
|
|
result, mods, matches, err := p.ProcessContent(content, `(?s)<value>(\d+\.\d+)</value>`, "v1*1.5")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matches != 1 {
|
|
t.Errorf("Expected 1 match, got %d", matches)
|
|
}
|
|
|
|
if mods != 1 {
|
|
t.Errorf("Expected 1 modification, got %d", mods)
|
|
}
|
|
|
|
if result != expected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
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>`
|
|
|
|
p := &RegexProcessor{}
|
|
result, mods, matches, err := p.ProcessContent(content, `(?s)<price>(\d+)</price>`, "v1*2")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matches != 3 {
|
|
t.Errorf("Expected 3 matches, got %d", matches)
|
|
}
|
|
|
|
if mods != 3 {
|
|
t.Errorf("Expected 3 modifications, got %d", mods)
|
|
}
|
|
|
|
if result != expected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
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>`
|
|
|
|
p := &RegexProcessor{}
|
|
result, mods, matches, err := p.ProcessContent(content, `<entry>(\d+)</entry>`, "v1*2")
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matches != 3 {
|
|
t.Errorf("Expected 3 matches, got %d", matches)
|
|
}
|
|
|
|
if mods != 3 {
|
|
t.Errorf("Expected 3 modifications, got %d", mods)
|
|
}
|
|
|
|
if result != expected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
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>`
|
|
|
|
p := &RegexProcessor{}
|
|
result, mods, matches, err := p.ProcessContent(content, `<name>([A-Za-z]+)</name>`, `s1 = s1 .. "_modified"`)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matches != 2 {
|
|
t.Errorf("Expected 2 matches, got %d", matches)
|
|
}
|
|
|
|
if mods != 2 {
|
|
t.Errorf("Expected 2 modifications, got %d", mods)
|
|
}
|
|
|
|
if result != expected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestStringUpperCase(t *testing.T) {
|
|
content := `<users>
|
|
<user>John</user>
|
|
<user>Mary</user>
|
|
</users>`
|
|
|
|
expected := `<users>
|
|
<user>JOHN</user>
|
|
<user>MARY</user>
|
|
</users>`
|
|
|
|
p := &RegexProcessor{}
|
|
// Convert names to uppercase using Lua string function
|
|
result, mods, matches, err := p.ProcessContent(content, `<user>([A-Za-z]+)</user>`, `s1 = string.upper(s1)`)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matches != 2 {
|
|
t.Errorf("Expected 2 matches, got %d", matches)
|
|
}
|
|
|
|
if mods != 2 {
|
|
t.Errorf("Expected 2 modifications, got %d", mods)
|
|
}
|
|
|
|
if result != expected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
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>`
|
|
|
|
p := &RegexProcessor{}
|
|
result, mods, matches, err := p.ProcessContent(content, `<product>([A-Za-z]+)</product>`, `s1 = s1 .. "_fruit"`)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
if matches != 2 {
|
|
t.Errorf("Expected 2 matches, got %d", matches)
|
|
}
|
|
|
|
if mods != 2 {
|
|
t.Errorf("Expected 2 modifications, got %d", mods)
|
|
}
|
|
|
|
if result != expected {
|
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
// 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>`)
|
|
p := &RegexProcessor{}
|
|
luaExpr := BuildLuaScript("v1 = v1 * v2")
|
|
|
|
modifiedContent, _, _, err := p.ProcessContent(content, regex.String(), luaExpr)
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
normalizedModified := normalizeWhitespace(modifiedContent)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
if normalizedModified != normalizedExpected {
|
|
t.Fatalf("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>`)
|
|
p := &RegexProcessor{}
|
|
luaExpr := BuildLuaScript("v1 = math.sqrt(v1)")
|
|
|
|
modifiedContent, _, _, err := p.ProcessContent(content, regex.String(), luaExpr)
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
normalizedModified := normalizeWhitespace(modifiedContent)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
if normalizedModified != normalizedExpected {
|
|
t.Fatalf("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>`)
|
|
p := &RegexProcessor{}
|
|
luaExpr := BuildLuaScript("=0")
|
|
|
|
modifiedContent, _, _, err := p.ProcessContent(content, regex.String(), luaExpr)
|
|
if err != nil {
|
|
t.Fatalf("Error processing content: %v", err)
|
|
}
|
|
|
|
normalizedModified := normalizeWhitespace(modifiedContent)
|
|
normalizedExpected := normalizeWhitespace(expected)
|
|
if normalizedModified != normalizedExpected {
|
|
t.Fatalf("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
|
|
p := &RegexProcessor{}
|
|
luaExpr := BuildLuaScript(tt.luaExpression)
|
|
|
|
// Process with our function
|
|
result, modCount, _, err := p.ProcessContent(tt.input, pattern, luaExpr)
|
|
if err != nil {
|
|
t.Fatalf("Process function failed: %v", err)
|
|
}
|
|
|
|
// Check results
|
|
if result != tt.expectedOutput {
|
|
t.Errorf("Expected output: %s, got: %s", tt.expectedOutput, result)
|
|
}
|
|
|
|
if modCount != tt.expectedMods {
|
|
t.Errorf("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
|
|
p := &RegexProcessor{}
|
|
luaExpr := BuildLuaScript(tt.luaExpression)
|
|
|
|
// Process with our function
|
|
result, modCount, _, err := p.ProcessContent(tt.input, pattern, luaExpr)
|
|
if err != nil {
|
|
t.Fatalf("Process function failed: %v", err)
|
|
}
|
|
|
|
// Check results
|
|
if result != tt.expectedOutput {
|
|
t.Errorf("Expected output: %s, got: %s", tt.expectedOutput, result)
|
|
}
|
|
|
|
if modCount != tt.expectedMods {
|
|
t.Errorf("Expected %d modifications, got %d", tt.expectedMods, modCount)
|
|
}
|
|
})
|
|
}
|
|
}
|