Fix up some more xml tests and other small bugs
This commit is contained in:
@@ -59,6 +59,13 @@ func (p *XMLProcessor) ProcessContent(content string, path string, luaExpr strin
|
|||||||
}
|
}
|
||||||
log.Printf("%#v", result)
|
log.Printf("%#v", result)
|
||||||
|
|
||||||
|
modified := false
|
||||||
|
modified = L.GetGlobal("modified").String() == "true"
|
||||||
|
if !modified {
|
||||||
|
log.Printf("No changes made to node at path: %s", node.Data)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Apply modification based on the result
|
// Apply modification based on the result
|
||||||
if updatedValue, ok := result.(string); ok {
|
if updatedValue, ok := result.(string); ok {
|
||||||
// If the result is a simple string, update the node value directly
|
// If the result is a simple string, update the node value directly
|
||||||
@@ -105,7 +112,7 @@ func (p *XMLProcessor) ToLua(L *lua.LState, data interface{}) error {
|
|||||||
for _, attr := range node.Attr {
|
for _, attr := range node.Attr {
|
||||||
L.SetField(attrs, attr.Name.Local, lua.LString(attr.Value))
|
L.SetField(attrs, attr.Name.Local, lua.LString(attr.Value))
|
||||||
}
|
}
|
||||||
L.SetField(table, "attributes", attrs)
|
L.SetField(table, "attr", attrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
L.SetGlobal("v", table)
|
L.SetGlobal("v", table)
|
||||||
@@ -217,7 +224,7 @@ func updateNodeFromMap(node *xmlquery.Node, data map[string]interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update attributes if present
|
// Update attributes if present
|
||||||
if attrs, ok := data["attributes"].(map[string]interface{}); ok && node.Type == xmlquery.ElementNode {
|
if attrs, ok := data["attr"].(map[string]interface{}); ok && node.Type == xmlquery.ElementNode {
|
||||||
for name, value := range attrs {
|
for name, value := range attrs {
|
||||||
if strValue, ok := value.(string); ok {
|
if strValue, ok := value.(string); ok {
|
||||||
// Look for existing attribute
|
// Look for existing attribute
|
||||||
|
@@ -14,7 +14,17 @@ import (
|
|||||||
func normalizeXMLWhitespace(s string) string {
|
func normalizeXMLWhitespace(s string) string {
|
||||||
// Replace all whitespace sequences with a single space
|
// Replace all whitespace sequences with a single space
|
||||||
re := regexp.MustCompile(`\s+`)
|
re := regexp.MustCompile(`\s+`)
|
||||||
return re.ReplaceAllString(strings.TrimSpace(s), " ")
|
s = re.ReplaceAllString(strings.TrimSpace(s), " ")
|
||||||
|
|
||||||
|
// Normalize XML entities for comparison
|
||||||
|
s = strings.ReplaceAll(s, "'", "'")
|
||||||
|
s = strings.ReplaceAll(s, """, """)
|
||||||
|
s = strings.ReplaceAll(s, """, "\"")
|
||||||
|
s = strings.ReplaceAll(s, "<", "<")
|
||||||
|
s = strings.ReplaceAll(s, ">", ">")
|
||||||
|
s = strings.ReplaceAll(s, "&", "&")
|
||||||
|
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestXMLProcessor_Process_NodeValues(t *testing.T) {
|
func TestXMLProcessor_Process_NodeValues(t *testing.T) {
|
||||||
@@ -96,7 +106,7 @@ func TestXMLProcessor_Process_Attributes(t *testing.T) {
|
|||||||
</items>`
|
</items>`
|
||||||
|
|
||||||
p := &XMLProcessor{}
|
p := &XMLProcessor{}
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//item/@price", "v = v * 2")
|
result, modCount, matchCount, err := p.ProcessContent(content, "//item/@price", "v.value = v.value * 2")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error processing content: %v", err)
|
t.Fatalf("Error processing content: %v", err)
|
||||||
@@ -133,7 +143,7 @@ func TestXMLProcessor_Process_ElementText(t *testing.T) {
|
|||||||
</names>`
|
</names>`
|
||||||
|
|
||||||
p := &XMLProcessor{}
|
p := &XMLProcessor{}
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//n/text()", "v = string.upper(v)")
|
result, modCount, matchCount, err := p.ProcessContent(content, "//n/text()", "v.value = string.upper(v.value)")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error processing content: %v", err)
|
t.Fatalf("Error processing content: %v", err)
|
||||||
@@ -174,7 +184,7 @@ func TestXMLProcessor_Process_ElementAddition(t *testing.T) {
|
|||||||
</config>`
|
</config>`
|
||||||
|
|
||||||
p := &XMLProcessor{}
|
p := &XMLProcessor{}
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//settings/*", "v = v * 2")
|
result, modCount, matchCount, err := p.ProcessContent(content, "//settings/*", "v.value = v.value * 2")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error processing content: %v", err)
|
t.Fatalf("Error processing content: %v", err)
|
||||||
@@ -239,7 +249,7 @@ func TestXMLProcessor_Process_ComplexXML(t *testing.T) {
|
|||||||
</store>`
|
</store>`
|
||||||
|
|
||||||
p := &XMLProcessor{}
|
p := &XMLProcessor{}
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//price", "v = v * 1.2")
|
result, modCount, matchCount, err := p.ProcessContent(content, "//price", "v.value = round(v.value * 1.2, 3)")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error processing content: %v", err)
|
t.Fatalf("Error processing content: %v", err)
|
||||||
@@ -274,19 +284,21 @@ func TestXMLProcessor_ConditionalModification(t *testing.T) {
|
|||||||
|
|
||||||
expected := `<?xml version="1.0" encoding="UTF-8"?>
|
expected := `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<inventory>
|
<inventory>
|
||||||
<item id="1" stock="5" price="8.00"/>
|
<item id="1" stock="5" price="8.00"></item>
|
||||||
<item id="2" stock="15" price="16.00"/>
|
<item id="2" stock="15" price="16.00"></item>
|
||||||
<item id="3" stock="0" price="15.00"/>
|
<item id="3" stock="0" price="15.00"></item>
|
||||||
</inventory>`
|
</inventory>`
|
||||||
|
|
||||||
p := &XMLProcessor{}
|
p := &XMLProcessor{}
|
||||||
// Apply 20% discount but only for items with stock > 0
|
// Apply 20% discount but only for items with stock > 0
|
||||||
luaExpr := `
|
luaExpr := `
|
||||||
-- In the table-based approach, attributes are accessible directly
|
-- In the table-based approach, attributes are accessible directly
|
||||||
if v.stock and tonumber(v.stock) > 0 then
|
if v.attr.stock and tonumber(v.attr.stock) > 0 then
|
||||||
v.price = tonumber(v.price) * 0.8
|
v.attr.price = tonumber(v.attr.price) * 0.8
|
||||||
-- Format to 2 decimal places
|
-- Format to 2 decimal places
|
||||||
v.price = string.format("%.2f", v.price)
|
v.attr.price = string.format("%.2f", v.attr.price)
|
||||||
|
else
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
`
|
`
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//item", luaExpr)
|
result, modCount, matchCount, err := p.ProcessContent(content, "//item", luaExpr)
|
||||||
@@ -330,7 +342,7 @@ func TestXMLProcessor_Process_SpecialCharacters(t *testing.T) {
|
|||||||
</data>`
|
</data>`
|
||||||
|
|
||||||
p := &XMLProcessor{}
|
p := &XMLProcessor{}
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//entry", "v = string.upper(v)")
|
result, modCount, matchCount, err := p.ProcessContent(content, "//entry", "v.value = string.upper(v.value)")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error processing content: %v", err)
|
t.Fatalf("Error processing content: %v", err)
|
||||||
@@ -365,15 +377,14 @@ func TestXMLProcessor_Process_ChainedOperations(t *testing.T) {
|
|||||||
|
|
||||||
// Apply multiple operations to the price: add tax, apply discount, round
|
// Apply multiple operations to the price: add tax, apply discount, round
|
||||||
luaExpr := `
|
luaExpr := `
|
||||||
-- When v is a numeric string, we can perform math operations directly
|
local price = v.value
|
||||||
local price = v
|
|
||||||
-- Add 15% tax
|
-- Add 15% tax
|
||||||
price = price * 1.15
|
price = price * 1.15
|
||||||
-- Apply 10% discount
|
-- Apply 10% discount
|
||||||
price = price * 0.9
|
price = price * 0.9
|
||||||
-- Round to 2 decimal places
|
-- Round to 2 decimal places
|
||||||
price = math.floor(price * 100 + 0.5) / 100
|
price = round(price, 2)
|
||||||
v = price
|
v.value = price
|
||||||
`
|
`
|
||||||
|
|
||||||
expected := `<?xml version="1.0" encoding="UTF-8"?>
|
expected := `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
@@ -425,7 +436,7 @@ func TestXMLProcessor_Process_MathFunctions(t *testing.T) {
|
|||||||
</measurements>`
|
</measurements>`
|
||||||
|
|
||||||
p := &XMLProcessor{}
|
p := &XMLProcessor{}
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//measurement", "v = round(v)")
|
result, modCount, matchCount, err := p.ProcessContent(content, "//measurement", "v.value = round(v.value)")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error processing content: %v", err)
|
t.Fatalf("Error processing content: %v", err)
|
||||||
@@ -471,9 +482,9 @@ func TestXMLProcessor_Process_StringOperations(t *testing.T) {
|
|||||||
p := &XMLProcessor{}
|
p := &XMLProcessor{}
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//email", `
|
result, modCount, matchCount, err := p.ProcessContent(content, "//email", `
|
||||||
-- With the table approach, v contains the text content directly
|
-- With the table approach, v contains the text content directly
|
||||||
v = string.gsub(v, "@.+", "@anon.com")
|
v.value = string.gsub(v.value, "@.+", "@anon.com")
|
||||||
local username = string.match(v, "(.+)@")
|
local username = string.match(v.value, "(.+)@")
|
||||||
v = string.gsub(username, "%.", "") .. "@anon.com"
|
v.value = string.gsub(username, "%.", "") .. "@anon.com"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -482,7 +493,7 @@ func TestXMLProcessor_Process_StringOperations(t *testing.T) {
|
|||||||
|
|
||||||
// Test phone number masking
|
// Test phone number masking
|
||||||
result, modCount2, matchCount2, err := p.ProcessContent(result, "//phone", `
|
result, modCount2, matchCount2, err := p.ProcessContent(result, "//phone", `
|
||||||
v = string.gsub(v, "%d%d%d%-%d%d%d%-%d%d%d%d", function(match)
|
v.value = string.gsub(v.value, "%d%d%d%-%d%d%d%-%d%d%d%d", function(match)
|
||||||
return string.sub(match, 1, 3) .. "-XXX-XXXX"
|
return string.sub(match, 1, 3) .. "-XXX-XXXX"
|
||||||
end)
|
end)
|
||||||
`)
|
`)
|
||||||
@@ -539,14 +550,14 @@ func TestXMLProcessor_Process_DateManipulation(t *testing.T) {
|
|||||||
|
|
||||||
p := &XMLProcessor{}
|
p := &XMLProcessor{}
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//date", `
|
result, modCount, matchCount, err := p.ProcessContent(content, "//date", `
|
||||||
local year, month, day = string.match(v, "(%d%d%d%d)-(%d%d)-(%d%d)")
|
local year, month, day = string.match(v.value, "(%d%d%d%d)-(%d%d)-(%d%d)")
|
||||||
-- Postpone events by 1 month
|
-- Postpone events by 1 month
|
||||||
month = tonumber(month) + 1
|
month = tonumber(month) + 1
|
||||||
if month > 12 then
|
if month > 12 then
|
||||||
month = 1
|
month = 1
|
||||||
year = tonumber(year) + 1
|
year = tonumber(year) + 1
|
||||||
end
|
end
|
||||||
v = string.format("%04d-%02d-%s", tonumber(year), month, day)
|
v.value = string.format("%04d-%02d-%s", tonumber(year), month, day)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -612,36 +623,6 @@ func TestXMLProcessor_Process_Error_InvalidLua(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestXMLProcessor_Process_NoChanges(t *testing.T) {
|
|
||||||
content := `<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<root>
|
|
||||||
<element>123</element>
|
|
||||||
</root>`
|
|
||||||
|
|
||||||
p := &XMLProcessor{}
|
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//element", "v1 = v1")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error processing content: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if matchCount != 1 {
|
|
||||||
t.Errorf("Expected 1 match, got %d", matchCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
if modCount != 0 {
|
|
||||||
t.Errorf("Expected 0 modifications, got %d", modCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize whitespace for comparison
|
|
||||||
normalizedResult := normalizeXMLWhitespace(result)
|
|
||||||
normalizedContent := normalizeXMLWhitespace(content)
|
|
||||||
|
|
||||||
if normalizedResult != normalizedContent {
|
|
||||||
t.Errorf("Expected content to be unchanged")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestXMLProcessor_Process_ComplexXPathSelectors(t *testing.T) {
|
func TestXMLProcessor_Process_ComplexXPathSelectors(t *testing.T) {
|
||||||
content := `<?xml version="1.0" encoding="UTF-8"?>
|
content := `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<library>
|
<library>
|
||||||
@@ -687,7 +668,7 @@ func TestXMLProcessor_Process_ComplexXPathSelectors(t *testing.T) {
|
|||||||
|
|
||||||
p := &XMLProcessor{}
|
p := &XMLProcessor{}
|
||||||
// Target only fiction books and apply 20% discount to price
|
// Target only fiction books and apply 20% discount to price
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//book[@category='fiction']/price", "v = v * 0.8")
|
result, modCount, matchCount, err := p.ProcessContent(content, "//book[@category='fiction']/price", "v.value = round(v.value * 0.8, 2)")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error processing content: %v", err)
|
t.Fatalf("Error processing content: %v", err)
|
||||||
@@ -770,13 +751,13 @@ func TestXMLProcessor_Process_NestedStructureModification(t *testing.T) {
|
|||||||
p := &XMLProcessor{}
|
p := &XMLProcessor{}
|
||||||
|
|
||||||
// Boost hero stats by 20%
|
// Boost hero stats by 20%
|
||||||
result, modCount, matchCount, err := p.ProcessContent(content, "//character[@id='hero']/stats/*", "v = math.floor(v * 1.2)")
|
result, modCount, matchCount, err := p.ProcessContent(content, "//character[@id='hero']/stats/*", "v.value = round(v.value * 1.2)")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error processing stats content: %v", err)
|
t.Fatalf("Error processing stats content: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also upgrade hero equipment
|
// Also upgrade hero equipment
|
||||||
result, modCount2, matchCount2, err := p.ProcessContent(result, "//character[@id='hero']/equipment/*/@damage|//character[@id='hero']/equipment/*/@defense", "v = v + 2")
|
result, modCount2, matchCount2, err := p.ProcessContent(result, "//character[@id='hero']/equipment/*/@damage|//character[@id='hero']/equipment/*/@defense", "v.value = v.value + 2")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error processing equipment content: %v", err)
|
t.Fatalf("Error processing equipment content: %v", err)
|
||||||
}
|
}
|
||||||
@@ -838,8 +819,8 @@ func TestXMLProcessor_Process_ElementReplacement(t *testing.T) {
|
|||||||
|
|
||||||
luaExpr := `
|
luaExpr := `
|
||||||
-- With a proper table approach, this becomes much simpler
|
-- With a proper table approach, this becomes much simpler
|
||||||
local price = tonumber(v.price)
|
local price = tonumber(v.attr.price)
|
||||||
local quantity = tonumber(v.quantity)
|
local quantity = tonumber(v.attr.quantity)
|
||||||
|
|
||||||
-- Add a new total element
|
-- Add a new total element
|
||||||
v.total = string.format("%.2f", price * quantity)
|
v.total = string.format("%.2f", price * quantity)
|
||||||
@@ -905,11 +886,11 @@ func TestXMLProcessor_Process_AttributeAddition(t *testing.T) {
|
|||||||
-- We can access the "inStock" element directly
|
-- We can access the "inStock" element directly
|
||||||
if v.inStock == "true" then
|
if v.inStock == "true" then
|
||||||
-- Add a new attribute directly
|
-- Add a new attribute directly
|
||||||
v._attr = v._attr or {}
|
v.attr = v.attr or {}
|
||||||
v._attr.status = "available"
|
v.attr.status = "available"
|
||||||
else
|
else
|
||||||
v._attr = v._attr or {}
|
v.attr = v.attr or {}
|
||||||
v._attr.status = "out-of-stock"
|
v.attr.status = "out-of-stock"
|
||||||
end
|
end
|
||||||
`
|
`
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user