Files
BigChef/processor/xml_integration_test.go
2025-12-19 11:54:20 +01:00

347 lines
9.5 KiB
Go

package processor
import (
"strings"
"testing"
"cook/utils"
)
// TestRealWorldGameXML tests with game-like XML structure
func TestRealWorldGameXML(t *testing.T) {
original := `<?xml version="1.0" encoding="utf-8"?>
<Items>
<Item name="Fiber" identifier="Item_Fiber" category="Resource">
<Icon texture="Items/Fiber.png" />
<Weight value="0.01" />
<MaxStack value="1000" />
<Description text="Soft plant fibers useful for crafting." />
</Item>
<Item name="Wood" identifier="Item_Wood" category="Resource">
<Icon texture="Items/Wood.png" />
<Weight value="0.05" />
<MaxStack value="500" />
<Description text="Basic building material." />
</Item>
</Items>`
// Parse
origElem, err := parseXMLWithPositions(original)
if err != nil {
t.Fatalf("Failed to parse: %v", err)
}
// Modify: Double all MaxStack values and change Wood weight
modElem := deepCopyXMLElement(origElem)
// Fiber MaxStack: 1000 → 2000
fiberItem := modElem.Children[0]
fiberMaxStack := fiberItem.Children[2]
valueAttr := fiberMaxStack.Attributes["value"]
valueAttr.Value = "2000"
fiberMaxStack.Attributes["value"] = valueAttr
// Wood MaxStack: 500 → 1000
woodItem := modElem.Children[1]
woodMaxStack := woodItem.Children[2]
valueAttr2 := woodMaxStack.Attributes["value"]
valueAttr2.Value = "1000"
woodMaxStack.Attributes["value"] = valueAttr2
// Wood Weight: 0.05 → 0.10
woodWeight := woodItem.Children[1]
weightAttr := woodWeight.Attributes["value"]
weightAttr.Value = "0.10"
woodWeight.Attributes["value"] = weightAttr
// Generate changes
changes := findXMLChanges(origElem, modElem, "")
if len(changes) != 3 {
t.Fatalf("Expected 3 changes, got %d", len(changes))
}
// Apply
commands := applyXMLChanges(changes)
result, _ := utils.ExecuteModifications(commands, original)
// Verify changes
if !strings.Contains(result, `<MaxStack value="2000"`) {
t.Errorf("Failed to update Fiber MaxStack")
}
if !strings.Contains(result, `<MaxStack value="1000"`) {
t.Errorf("Failed to update Wood MaxStack")
}
if !strings.Contains(result, `<Weight value="0.10"`) {
t.Errorf("Failed to update Wood Weight")
}
// Verify formatting preserved (check XML declaration and indentation)
if !strings.HasPrefix(result, `<?xml version="1.0" encoding="utf-8"?>`) {
t.Errorf("XML declaration not preserved")
}
if !strings.Contains(result, "\n <Item") {
t.Errorf("Indentation not preserved")
}
}
// TestAddRemoveMultipleChildren tests adding and removing multiple elements
func TestAddRemoveMultipleChildren(t *testing.T) {
original := `<inventory>
<item name="sword" />
<item name="shield" />
<item name="potion" />
<item name="scroll" />
</inventory>`
// Parse
origElem, err := parseXMLWithPositions(original)
if err != nil {
t.Fatalf("Failed to parse: %v", err)
}
// Remove middle two items, add a new one
modElem := deepCopyXMLElement(origElem)
// Remove shield and potion (indices 1 and 2)
modElem.Children = []*XMLElement{
modElem.Children[0], // sword
modElem.Children[3], // scroll
}
// Add a new item
newItem := &XMLElement{
Tag: "item",
Attributes: map[string]XMLAttribute{
"name": {Value: "helmet"},
},
Children: []*XMLElement{},
}
modElem.Children = append(modElem.Children, newItem)
// Generate changes
changes := findXMLChanges(origElem, modElem, "")
// The algorithm compares by matching indices:
// orig[0]=sword vs mod[0]=sword (no change)
// orig[1]=shield vs mod[1]=scroll (treated as replace - shows as attribute changes)
// orig[2]=potion vs mod[2]=helmet (treated as replace)
// orig[3]=scroll (removed)
// This is fine - the actual edits will be correct
if len(changes) == 0 {
t.Fatalf("Expected changes, got none")
}
// Apply
commands := applyXMLChanges(changes)
result, _ := utils.ExecuteModifications(commands, original)
// Verify
if strings.Contains(result, `name="shield"`) {
t.Errorf("Shield not removed")
}
if strings.Contains(result, `name="potion"`) {
t.Errorf("Potion not removed")
}
if !strings.Contains(result, `name="sword"`) {
t.Errorf("Sword incorrectly removed")
}
if !strings.Contains(result, `name="scroll"`) {
t.Errorf("Scroll incorrectly removed")
}
if !strings.Contains(result, `name="helmet"`) {
t.Errorf("Helmet not added")
}
}
// TestModifyAttributesAndText tests changing both attributes and text content
func TestModifyAttributesAndText(t *testing.T) {
original := `<weapon>
<item type="sword" damage="10">Iron Sword</item>
<item type="axe" damage="15">Battle Axe</item>
</weapon>`
// Parse
origElem, err := parseXMLWithPositions(original)
if err != nil {
t.Fatalf("Failed to parse: %v", err)
}
// Modify both items
modElem := deepCopyXMLElement(origElem)
// First item: change damage and text
item1 := modElem.Children[0]
dmgAttr := item1.Attributes["damage"]
dmgAttr.Value = "20"
item1.Attributes["damage"] = dmgAttr
item1.Text = "Steel Sword"
// Second item: change damage and type
item2 := modElem.Children[1]
dmgAttr2 := item2.Attributes["damage"]
dmgAttr2.Value = "30"
item2.Attributes["damage"] = dmgAttr2
typeAttr := item2.Attributes["type"]
typeAttr.Value = "greataxe"
item2.Attributes["type"] = typeAttr
// Generate and apply changes
changes := findXMLChanges(origElem, modElem, "")
commands := applyXMLChanges(changes)
result, _ := utils.ExecuteModifications(commands, original)
// Verify
if !strings.Contains(result, `damage="20"`) {
t.Errorf("First item damage not updated")
}
if !strings.Contains(result, "Steel Sword") {
t.Errorf("First item text not updated")
}
if !strings.Contains(result, `damage="30"`) {
t.Errorf("Second item damage not updated")
}
if !strings.Contains(result, `type="greataxe"`) {
t.Errorf("Second item type not updated")
}
if strings.Contains(result, "Iron Sword") {
t.Errorf("Old text still present")
}
}
// TestSelfClosingTagPreservation tests that self-closing tags work correctly
func TestSelfClosingTagPreservation(t *testing.T) {
original := `<root>
<item name="test" />
<empty></empty>
</root>`
// Parse
origElem, err := parseXMLWithPositions(original)
if err != nil {
t.Fatalf("Failed to parse: %v", err)
}
// Modify first item's attribute
modElem := deepCopyXMLElement(origElem)
item := modElem.Children[0]
nameAttr := item.Attributes["name"]
nameAttr.Value = "modified"
item.Attributes["name"] = nameAttr
// Generate and apply changes
changes := findXMLChanges(origElem, modElem, "")
commands := applyXMLChanges(changes)
result, _ := utils.ExecuteModifications(commands, original)
// Verify the change was made
if !strings.Contains(result, `name="modified"`) {
t.Errorf("Attribute not updated: %s", result)
}
}
// TestNumericAttributeModification tests numeric attribute changes
func TestNumericAttributeModification(t *testing.T) {
original := `<stats health="100" mana="50" stamina="75.5" />`
// Parse
origElem, err := parseXMLWithPositions(original)
if err != nil {
t.Fatalf("Failed to parse: %v", err)
}
// Double all numeric values
modElem := deepCopyXMLElement(origElem)
// Helper to modify numeric attributes
modifyNumericAttr := func(attrName string, multiplier float64) {
if attr, exists := modElem.Attributes[attrName]; exists {
if val, ok := parseNumeric(attr.Value); ok {
attr.Value = formatNumeric(val * multiplier)
modElem.Attributes[attrName] = attr
}
}
}
modifyNumericAttr("health", 2.0)
modifyNumericAttr("mana", 2.0)
modifyNumericAttr("stamina", 2.0)
// Generate and apply changes
changes := findXMLChanges(origElem, modElem, "")
if len(changes) != 3 {
t.Fatalf("Expected 3 changes, got %d", len(changes))
}
commands := applyXMLChanges(changes)
result, _ := utils.ExecuteModifications(commands, original)
// Verify numeric changes
if !strings.Contains(result, `health="200"`) {
t.Errorf("Health not doubled: %s", result)
}
if !strings.Contains(result, `mana="100"`) {
t.Errorf("Mana not doubled: %s", result)
}
if !strings.Contains(result, `stamina="151"`) {
t.Errorf("Stamina not doubled: %s", result)
}
}
// TestMinimalGitDiff verifies that only changed parts are modified
func TestMinimalGitDiff(t *testing.T) {
original := `<config>
<setting name="volume" value="50" />
<setting name="brightness" value="75" />
<setting name="contrast" value="100" />
</config>`
// Parse
origElem, err := parseXMLWithPositions(original)
if err != nil {
t.Fatalf("Failed to parse: %v", err)
}
// Change only brightness
modElem := deepCopyXMLElement(origElem)
brightnessItem := modElem.Children[1]
valueAttr := brightnessItem.Attributes["value"]
valueAttr.Value = "90"
brightnessItem.Attributes["value"] = valueAttr
// Generate changes
changes := findXMLChanges(origElem, modElem, "")
// Should be exactly 1 change
if len(changes) != 1 {
t.Fatalf("Expected exactly 1 change for minimal diff, got %d", len(changes))
}
if changes[0].OldValue != "75" || changes[0].NewValue != "90" {
t.Errorf("Wrong change detected: %v", changes[0])
}
// Apply
commands := applyXMLChanges(changes)
result, _ := utils.ExecuteModifications(commands, original)
// Calculate diff size (rough approximation)
diffChars := len(changes[0].OldValue) + len(changes[0].NewValue)
if diffChars > 10 {
t.Errorf("Diff too large: %d characters changed (expected < 10)", diffChars)
}
// Verify only brightness changed
if !strings.Contains(result, `value="50"`) {
t.Errorf("Volume incorrectly modified")
}
if !strings.Contains(result, `value="90"`) {
t.Errorf("Brightness not modified")
}
if !strings.Contains(result, `value="100"`) {
t.Errorf("Contrast incorrectly modified")
}
}