Refactor everything to processors and implement json and xml processors such as they are
This commit is contained in:
345
processor/xml_test.go
Normal file
345
processor/xml_test.go
Normal file
@@ -0,0 +1,345 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/antchfx/xmlquery"
|
||||
)
|
||||
|
||||
func TestXMLProcessor_Process_TextNodes(t *testing.T) {
|
||||
// Test XML file with price tags that we want to modify
|
||||
testXML := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<catalog>
|
||||
<book id="bk101">
|
||||
<author>Gambardella, Matthew</author>
|
||||
<title>XML Developer's Guide</title>
|
||||
<genre>Computer</genre>
|
||||
<price>44.95</price>
|
||||
<publish_date>2000-10-01</publish_date>
|
||||
</book>
|
||||
<book id="bk102">
|
||||
<author>Ralls, Kim</author>
|
||||
<title>Midnight Rain</title>
|
||||
<genre>Fantasy</genre>
|
||||
<price>5.95</price>
|
||||
<publish_date>2000-12-16</publish_date>
|
||||
</book>
|
||||
</catalog>`
|
||||
|
||||
// Create an XML processor
|
||||
processor := NewXMLProcessor(&TestLogger{})
|
||||
|
||||
// Process the XML content directly to double all prices
|
||||
xpathExpr := "//price/text()"
|
||||
modifiedXML, modCount, matchCount, err := processor.ProcessContent(testXML, xpathExpr, "v1 = v1 * 2", "*2")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to process XML content: %v", err)
|
||||
}
|
||||
|
||||
// Check that we found and modified the correct number of nodes
|
||||
if matchCount != 2 {
|
||||
t.Errorf("Expected to match 2 nodes, got %d", matchCount)
|
||||
}
|
||||
if modCount != 2 {
|
||||
t.Errorf("Expected to modify 2 nodes, got %d", modCount)
|
||||
}
|
||||
|
||||
// Check that prices were doubled
|
||||
if !strings.Contains(modifiedXML, "<price>89.9</price>") {
|
||||
t.Errorf("Modified content does not contain doubled price 89.9")
|
||||
}
|
||||
if !strings.Contains(modifiedXML, "<price>11.9</price>") {
|
||||
t.Errorf("Modified content does not contain doubled price 11.9")
|
||||
}
|
||||
|
||||
// Verify we can parse the XML after modification
|
||||
_, err = xmlquery.Parse(strings.NewReader(modifiedXML))
|
||||
if err != nil {
|
||||
t.Errorf("Modified XML is not valid: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestXMLProcessor_Process_Elements(t *testing.T) {
|
||||
// Test XML file with elements that we want to modify attributes of
|
||||
testXML := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<items>
|
||||
<item id="1" value="100" />
|
||||
<item id="2" value="200" />
|
||||
<item id="3" value="300" />
|
||||
</items>`
|
||||
|
||||
// Create an XML processor
|
||||
processor := NewXMLProcessor(&TestLogger{})
|
||||
|
||||
// Process the file to modify the value attribute
|
||||
// We'll create a more complex Lua script that deals with the node table
|
||||
luaScript := `
|
||||
-- Get the current value attribute
|
||||
local valueAttr = node.attributes.value
|
||||
if valueAttr then
|
||||
-- Convert to number and add 50
|
||||
local numValue = tonumber(valueAttr)
|
||||
if numValue then
|
||||
-- Update the value in the attributes table
|
||||
node.attributes.value = tostring(numValue + 50)
|
||||
end
|
||||
end
|
||||
`
|
||||
|
||||
// Process the XML content directly
|
||||
xpathExpr := "//item"
|
||||
modifiedXML, modCount, matchCount, err := processor.ProcessContent(testXML, xpathExpr, luaScript, "Add 50 to values")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to process XML content: %v", err)
|
||||
}
|
||||
|
||||
// Check that we found and modified the correct number of nodes
|
||||
if matchCount != 3 {
|
||||
t.Errorf("Expected to match 3 item nodes, got %d", matchCount)
|
||||
}
|
||||
if modCount != 3 {
|
||||
t.Errorf("Expected to modify 3 nodes, got %d", modCount)
|
||||
}
|
||||
|
||||
// Check that values were increased by 50
|
||||
if !strings.Contains(modifiedXML, `value="150"`) {
|
||||
t.Errorf("Modified content does not contain updated value 150")
|
||||
}
|
||||
if !strings.Contains(modifiedXML, `value="250"`) {
|
||||
t.Errorf("Modified content does not contain updated value 250")
|
||||
}
|
||||
if !strings.Contains(modifiedXML, `value="350"`) {
|
||||
t.Errorf("Modified content does not contain updated value 350")
|
||||
}
|
||||
|
||||
// Verify we can parse the XML after modification
|
||||
_, err = xmlquery.Parse(strings.NewReader(modifiedXML))
|
||||
if err != nil {
|
||||
t.Errorf("Modified XML is not valid: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// New test for adding attributes to XML elements
|
||||
func TestXMLProcessor_AddAttributes(t *testing.T) {
|
||||
testXML := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<element>Content</element>
|
||||
<element>Another</element>
|
||||
</root>`
|
||||
|
||||
processor := NewXMLProcessor(&TestLogger{})
|
||||
|
||||
// Add a new attribute to each element
|
||||
luaScript := `
|
||||
-- Add a new attribute
|
||||
node.attributes.status = "active"
|
||||
-- Also add another attribute with a sequential number
|
||||
node.attributes.index = tostring(_POSITION)
|
||||
`
|
||||
|
||||
xpathExpr := "//element"
|
||||
modifiedXML, modCount, matchCount, err := processor.ProcessContent(testXML, xpathExpr, luaScript, "Add attributes")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to process XML content: %v", err)
|
||||
}
|
||||
|
||||
// Check counts
|
||||
if matchCount != 2 {
|
||||
t.Errorf("Expected to match 2 nodes, got %d", matchCount)
|
||||
}
|
||||
if modCount != 2 {
|
||||
t.Errorf("Expected to modify 2 nodes, got %d", modCount)
|
||||
}
|
||||
|
||||
// Verify the new attributes
|
||||
if !strings.Contains(modifiedXML, `status="active"`) {
|
||||
t.Errorf("Modified content does not contain added status attribute")
|
||||
}
|
||||
|
||||
if !strings.Contains(modifiedXML, `index="1"`) && !strings.Contains(modifiedXML, `index="2"`) {
|
||||
t.Errorf("Modified content does not contain added index attributes")
|
||||
}
|
||||
|
||||
// Verify the XML is valid
|
||||
_, err = xmlquery.Parse(strings.NewReader(modifiedXML))
|
||||
if err != nil {
|
||||
t.Errorf("Modified XML is not valid: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test for adding new child elements
|
||||
func TestXMLProcessor_AddChildElements(t *testing.T) {
|
||||
testXML := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<products>
|
||||
<product id="1">
|
||||
<name>Product One</name>
|
||||
<price>10.99</price>
|
||||
</product>
|
||||
<product id="2">
|
||||
<name>Product Two</name>
|
||||
<price>20.99</price>
|
||||
</product>
|
||||
</products>`
|
||||
|
||||
processor := NewXMLProcessor(&TestLogger{})
|
||||
|
||||
// Add a new child element to each product
|
||||
luaScript := `
|
||||
-- Create a new "discount" child element
|
||||
local discount = create_node("discount")
|
||||
-- Calculate discount as 10% of price
|
||||
local priceText = ""
|
||||
for _, child in ipairs(node.children) do
|
||||
if child.name == "price" and child.children[1] then
|
||||
priceText = child.children[1].data
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local price = tonumber(priceText) or 0
|
||||
local discountValue = price * 0.1
|
||||
|
||||
-- Add text content to the discount element
|
||||
discount.children[1] = {type="text", data=string.format("%.2f", discountValue)}
|
||||
|
||||
-- Add the new element as a child
|
||||
add_child(node, discount)
|
||||
`
|
||||
|
||||
xpathExpr := "//product"
|
||||
modifiedXML, modCount, matchCount, err := processor.ProcessContent(testXML, xpathExpr, luaScript, "Add discount elements")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to process XML content: %v", err)
|
||||
}
|
||||
|
||||
// Check counts
|
||||
if matchCount != 2 {
|
||||
t.Errorf("Expected to match 2 nodes, got %d", matchCount)
|
||||
}
|
||||
if modCount != 2 {
|
||||
t.Errorf("Expected to modify 2 nodes, got %d", modCount)
|
||||
}
|
||||
|
||||
// Verify the new elements
|
||||
if !strings.Contains(modifiedXML, "<discount>1.10</discount>") {
|
||||
t.Errorf("Modified content does not contain first discount element")
|
||||
}
|
||||
|
||||
if !strings.Contains(modifiedXML, "<discount>2.10</discount>") {
|
||||
t.Errorf("Modified content does not contain second discount element")
|
||||
}
|
||||
|
||||
// Verify the XML is valid
|
||||
_, err = xmlquery.Parse(strings.NewReader(modifiedXML))
|
||||
if err != nil {
|
||||
t.Errorf("Modified XML is not valid: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test for complex XML transformations
|
||||
func TestXMLProcessor_ComplexTransformation(t *testing.T) {
|
||||
testXML := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config>
|
||||
<settings>
|
||||
<setting name="timeout" value="30" />
|
||||
<setting name="retries" value="3" />
|
||||
<setting name="enabled" value="true" />
|
||||
</settings>
|
||||
</config>`
|
||||
|
||||
processor := NewXMLProcessor(&TestLogger{})
|
||||
|
||||
// Complex transformation that changes attributes based on name
|
||||
luaScript := `
|
||||
local name = node.attributes.name
|
||||
local value = node.attributes.value
|
||||
|
||||
if name == "timeout" then
|
||||
-- Double the timeout
|
||||
node.attributes.value = tostring(tonumber(value) * 2)
|
||||
-- Add a unit attribute
|
||||
node.attributes.unit = "seconds"
|
||||
elseif name == "retries" then
|
||||
-- Increase retries by 2
|
||||
node.attributes.value = tostring(tonumber(value) + 2)
|
||||
-- Add a comment element as sibling
|
||||
local comment = create_node("comment")
|
||||
comment.children[1] = {type="text", data="Increased for reliability"}
|
||||
|
||||
-- We can't directly add siblings in this implementation
|
||||
-- But this would be the place to do it if supported
|
||||
elseif name == "enabled" and value == "true" then
|
||||
-- Add a priority attribute for enabled settings
|
||||
node.attributes.priority = "high"
|
||||
end
|
||||
`
|
||||
|
||||
xpathExpr := "//setting"
|
||||
modifiedXML, _, matchCount, err := processor.ProcessContent(testXML, xpathExpr, luaScript, "Transform settings")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to process XML content: %v", err)
|
||||
}
|
||||
|
||||
// Check counts
|
||||
if matchCount != 3 {
|
||||
t.Errorf("Expected to match 3 nodes, got %d", matchCount)
|
||||
}
|
||||
|
||||
// Verify the transformed attributes
|
||||
if !strings.Contains(modifiedXML, `value="60"`) {
|
||||
t.Errorf("Modified content does not have doubled timeout value")
|
||||
}
|
||||
|
||||
if !strings.Contains(modifiedXML, `unit="seconds"`) {
|
||||
t.Errorf("Modified content does not have added unit attribute")
|
||||
}
|
||||
|
||||
if !strings.Contains(modifiedXML, `value="5"`) {
|
||||
t.Errorf("Modified content does not have increased retries value")
|
||||
}
|
||||
|
||||
if !strings.Contains(modifiedXML, `priority="high"`) {
|
||||
t.Errorf("Modified content does not have added priority attribute")
|
||||
}
|
||||
|
||||
// Verify the XML is valid
|
||||
_, err = xmlquery.Parse(strings.NewReader(modifiedXML))
|
||||
if err != nil {
|
||||
t.Errorf("Modified XML is not valid: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test for handling special XML characters
|
||||
func TestXMLProcessor_SpecialCharacters(t *testing.T) {
|
||||
testXML := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<data>
|
||||
<item><![CDATA[Special & <characters> "here"]]></item>
|
||||
<item>Regular & text with <b>markup</b></item>
|
||||
</data>`
|
||||
|
||||
processor := NewXMLProcessor(&TestLogger{})
|
||||
|
||||
// Process text nodes, being careful with special characters
|
||||
luaScript := `
|
||||
-- For text nodes, replace & with &
|
||||
s1 = string.gsub(s1, "&([^;])", "&%1")
|
||||
`
|
||||
|
||||
xpathExpr := "//item/text()"
|
||||
modifiedXML, _, _, err := processor.ProcessContent(testXML, xpathExpr, luaScript, "Handle special chars")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to process XML content: %v", err)
|
||||
}
|
||||
|
||||
// CDATA sections should be preserved
|
||||
if !strings.Contains(modifiedXML, "<![CDATA[") {
|
||||
t.Errorf("CDATA section was not preserved")
|
||||
}
|
||||
|
||||
// Verify the XML is valid
|
||||
_, err = xmlquery.Parse(strings.NewReader(modifiedXML))
|
||||
if err != nil {
|
||||
t.Errorf("Modified XML is not valid: %v", err)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user