622 lines
15 KiB
Go
622 lines
15 KiB
Go
package processor
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"cook/utils"
|
|
)
|
|
|
|
func TestParseXMLWithPositions(t *testing.T) {
|
|
xml := `<root><item name="test">Hello</item></root>`
|
|
|
|
elem, err := parseXMLWithPositions(xml)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse XML: %v", err)
|
|
}
|
|
|
|
if elem.Tag != "root" {
|
|
t.Errorf("Expected root tag 'root', got '%s'", elem.Tag)
|
|
}
|
|
|
|
if len(elem.Children) != 1 {
|
|
t.Fatalf("Expected 1 child, got %d", len(elem.Children))
|
|
}
|
|
|
|
child := elem.Children[0]
|
|
if child.Tag != "item" {
|
|
t.Errorf("Expected child tag 'item', got '%s'", child.Tag)
|
|
}
|
|
|
|
if child.Attributes["name"].Value != "test" {
|
|
t.Errorf("Expected attribute 'name' to be 'test', got '%s'", child.Attributes["name"].Value)
|
|
}
|
|
|
|
if child.Text != "Hello" {
|
|
t.Errorf("Expected text 'Hello', got '%s'", child.Text)
|
|
}
|
|
}
|
|
|
|
func TestSurgicalTextChange(t *testing.T) {
|
|
original := `<root>
|
|
<item name="sword" weight="10">A sword</item>
|
|
</root>`
|
|
|
|
expected := `<root>
|
|
<item name="sword" weight="10">A modified sword</item>
|
|
</root>`
|
|
|
|
// Parse original
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse original XML: %v", err)
|
|
}
|
|
|
|
// Create modified version
|
|
modElem := deepCopyXMLElement(origElem)
|
|
modElem.Children[0].Text = "A modified sword"
|
|
|
|
// Find changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
if len(changes) != 1 {
|
|
t.Fatalf("Expected 1 change, got %d", len(changes))
|
|
}
|
|
|
|
if changes[0].Type != "text" {
|
|
t.Errorf("Expected change type 'text', got '%s'", changes[0].Type)
|
|
}
|
|
|
|
// Apply changes
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
if result != expected {
|
|
t.Errorf("Text change failed.\nExpected:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestSurgicalAttributeChange(t *testing.T) {
|
|
original := `<root>
|
|
<item name="sword" weight="10" />
|
|
</root>`
|
|
|
|
expected := `<root>
|
|
<item name="sword" weight="20" />
|
|
</root>`
|
|
|
|
// Parse original
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse original XML: %v", err)
|
|
}
|
|
|
|
// Create modified version
|
|
modElem := deepCopyXMLElement(origElem)
|
|
attr := modElem.Children[0].Attributes["weight"]
|
|
attr.Value = "20"
|
|
modElem.Children[0].Attributes["weight"] = attr
|
|
|
|
// Find changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
if len(changes) != 1 {
|
|
t.Fatalf("Expected 1 change, got %d", len(changes))
|
|
}
|
|
|
|
if changes[0].Type != "attribute" {
|
|
t.Errorf("Expected change type 'attribute', got '%s'", changes[0].Type)
|
|
}
|
|
|
|
// Apply changes
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
if result != expected {
|
|
t.Errorf("Attribute change failed.\nExpected:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestSurgicalMultipleAttributeChanges(t *testing.T) {
|
|
original := `<item name="sword" weight="10" damage="5" />`
|
|
|
|
expected := `<item name="greatsword" weight="20" damage="15" />`
|
|
|
|
// Parse original
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse original XML: %v", err)
|
|
}
|
|
|
|
// Create modified version
|
|
modElem := deepCopyXMLElement(origElem)
|
|
|
|
nameAttr := modElem.Attributes["name"]
|
|
nameAttr.Value = "greatsword"
|
|
modElem.Attributes["name"] = nameAttr
|
|
|
|
weightAttr := modElem.Attributes["weight"]
|
|
weightAttr.Value = "20"
|
|
modElem.Attributes["weight"] = weightAttr
|
|
|
|
damageAttr := modElem.Attributes["damage"]
|
|
damageAttr.Value = "15"
|
|
modElem.Attributes["damage"] = damageAttr
|
|
|
|
// Find changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
if len(changes) != 3 {
|
|
t.Fatalf("Expected 3 changes, got %d", len(changes))
|
|
}
|
|
|
|
// Apply changes
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
if result != expected {
|
|
t.Errorf("Multiple attribute changes failed.\nExpected:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestSurgicalAddAttribute(t *testing.T) {
|
|
original := `<item name="sword" />`
|
|
|
|
// Parse original
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse original XML: %v", err)
|
|
}
|
|
|
|
// Create modified version with new attribute
|
|
modElem := deepCopyXMLElement(origElem)
|
|
modElem.Attributes["weight"] = XMLAttribute{
|
|
Value: "10",
|
|
}
|
|
|
|
// Find changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
if len(changes) != 1 {
|
|
t.Fatalf("Expected 1 change, got %d", len(changes))
|
|
}
|
|
|
|
if changes[0].Type != "add_attribute" {
|
|
t.Errorf("Expected change type 'add_attribute', got '%s'", changes[0].Type)
|
|
}
|
|
|
|
// Apply changes
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Should contain the new attribute
|
|
if !strings.Contains(result, `weight="10"`) {
|
|
t.Errorf("Add attribute failed. Result doesn't contain weight=\"10\":\n%s", result)
|
|
}
|
|
}
|
|
|
|
func TestSurgicalRemoveAttribute(t *testing.T) {
|
|
original := `<item name="sword" weight="10" damage="5" />`
|
|
|
|
// Parse original
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse original XML: %v", err)
|
|
}
|
|
|
|
// Create modified version without weight attribute
|
|
modElem := deepCopyXMLElement(origElem)
|
|
delete(modElem.Attributes, "weight")
|
|
|
|
// Find changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
if len(changes) != 1 {
|
|
t.Fatalf("Expected 1 change, got %d", len(changes))
|
|
}
|
|
|
|
if changes[0].Type != "remove_attribute" {
|
|
t.Errorf("Expected change type 'remove_attribute', got '%s'", changes[0].Type)
|
|
}
|
|
|
|
// Apply changes
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Should not contain weight attribute
|
|
if strings.Contains(result, "weight=") {
|
|
t.Errorf("Remove attribute failed. Result still contains 'weight=':\n%s", result)
|
|
}
|
|
|
|
// Should still contain other attributes
|
|
if !strings.Contains(result, `name="sword"`) {
|
|
t.Errorf("Remove attribute incorrectly removed other attributes:\n%s", result)
|
|
}
|
|
}
|
|
|
|
func TestSurgicalAddElement(t *testing.T) {
|
|
original := `<root>
|
|
<item name="sword" />
|
|
</root>`
|
|
|
|
// Parse original
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse original XML: %v", err)
|
|
}
|
|
|
|
// Create modified version with new child
|
|
modElem := deepCopyXMLElement(origElem)
|
|
newChild := &XMLElement{
|
|
Tag: "item",
|
|
Attributes: map[string]XMLAttribute{
|
|
"name": {Value: "shield"},
|
|
},
|
|
Children: []*XMLElement{},
|
|
}
|
|
modElem.Children = append(modElem.Children, newChild)
|
|
|
|
// Find changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
if len(changes) != 1 {
|
|
t.Fatalf("Expected 1 change, got %d", len(changes))
|
|
}
|
|
|
|
if changes[0].Type != "add_element" {
|
|
t.Errorf("Expected change type 'add_element', got '%s'", changes[0].Type)
|
|
}
|
|
|
|
// Apply changes
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Should contain the new element
|
|
if !strings.Contains(result, `<item name="shield"`) {
|
|
t.Errorf("Add element failed. Result doesn't contain new item:\n%s", result)
|
|
}
|
|
}
|
|
|
|
func TestSurgicalRemoveElement(t *testing.T) {
|
|
original := `<root>
|
|
<item name="sword" />
|
|
<item name="shield" />
|
|
</root>`
|
|
|
|
expected := `<root>
|
|
<item name="sword" />
|
|
|
|
</root>`
|
|
|
|
// Parse original
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse original XML: %v", err)
|
|
}
|
|
|
|
// Create modified version without second child
|
|
modElem := deepCopyXMLElement(origElem)
|
|
modElem.Children = modElem.Children[:1]
|
|
|
|
// Find changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
if len(changes) != 1 {
|
|
t.Fatalf("Expected 1 change, got %d", len(changes))
|
|
}
|
|
|
|
if changes[0].Type != "remove_element" {
|
|
t.Errorf("Expected change type 'remove_element', got '%s'", changes[0].Type)
|
|
}
|
|
|
|
// Apply changes
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Should not contain shield
|
|
if strings.Contains(result, "shield") {
|
|
t.Errorf("Remove element failed. Result still contains 'shield':\n%s", result)
|
|
}
|
|
|
|
// Should still contain sword
|
|
if !strings.Contains(result, "sword") {
|
|
t.Errorf("Remove element incorrectly removed other elements:\n%s", result)
|
|
}
|
|
|
|
// Normalize whitespace for comparison
|
|
resultNorm := strings.TrimSpace(result)
|
|
expectedNorm := strings.TrimSpace(expected)
|
|
|
|
if resultNorm != expectedNorm {
|
|
t.Errorf("Remove element result mismatch.\nExpected:\n%s\n\nGot:\n%s", expectedNorm, resultNorm)
|
|
}
|
|
}
|
|
|
|
func TestComplexNestedChanges(t *testing.T) {
|
|
original := `<root>
|
|
<inventory>
|
|
<item name="sword" weight="10">
|
|
<stats damage="5" speed="3" />
|
|
</item>
|
|
<item name="shield" weight="8">
|
|
<stats defense="7" />
|
|
</item>
|
|
</inventory>
|
|
</root>`
|
|
|
|
// Parse original
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse original XML: %v", err)
|
|
}
|
|
|
|
// Create modified version with multiple changes
|
|
modElem := deepCopyXMLElement(origElem)
|
|
|
|
// Change first item's weight
|
|
inventory := modElem.Children[0]
|
|
item1 := inventory.Children[0]
|
|
weightAttr := item1.Attributes["weight"]
|
|
weightAttr.Value = "20"
|
|
item1.Attributes["weight"] = weightAttr
|
|
|
|
// Change nested stats damage
|
|
stats := item1.Children[0]
|
|
damageAttr := stats.Attributes["damage"]
|
|
damageAttr.Value = "10"
|
|
stats.Attributes["damage"] = damageAttr
|
|
|
|
// Change second item's name
|
|
item2 := inventory.Children[1]
|
|
nameAttr := item2.Attributes["name"]
|
|
nameAttr.Value = "buckler"
|
|
item2.Attributes["name"] = nameAttr
|
|
|
|
// Find changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
// Should have 3 changes: weight, damage, name
|
|
if len(changes) != 3 {
|
|
t.Fatalf("Expected 3 changes, got %d: %+v", len(changes), changes)
|
|
}
|
|
|
|
// Apply changes
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Verify all changes were applied
|
|
if !strings.Contains(result, `weight="20"`) {
|
|
t.Errorf("Failed to update weight to 20:\n%s", result)
|
|
}
|
|
if !strings.Contains(result, `damage="10"`) {
|
|
t.Errorf("Failed to update damage to 10:\n%s", result)
|
|
}
|
|
if !strings.Contains(result, `name="buckler"`) {
|
|
t.Errorf("Failed to update name to buckler:\n%s", result)
|
|
}
|
|
|
|
// Verify unchanged elements remain
|
|
if !strings.Contains(result, `speed="3"`) {
|
|
t.Errorf("Incorrectly modified speed attribute:\n%s", result)
|
|
}
|
|
if !strings.Contains(result, `defense="7"`) {
|
|
t.Errorf("Incorrectly modified defense attribute:\n%s", result)
|
|
}
|
|
}
|
|
|
|
func TestFormattingPreservation(t *testing.T) {
|
|
original := `<root>
|
|
<item name="sword" weight="10">
|
|
<description>A sharp blade</description>
|
|
<stats damage="5" speed="3" />
|
|
</item>
|
|
</root>`
|
|
|
|
expected := `<root>
|
|
<item name="sword" weight="20">
|
|
<description>A sharp blade</description>
|
|
<stats damage="5" speed="3" />
|
|
</item>
|
|
</root>`
|
|
|
|
// Parse original
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse original XML: %v", err)
|
|
}
|
|
|
|
// Modify only weight
|
|
modElem := deepCopyXMLElement(origElem)
|
|
item := modElem.Children[0]
|
|
weightAttr := item.Attributes["weight"]
|
|
weightAttr.Value = "20"
|
|
item.Attributes["weight"] = weightAttr
|
|
|
|
// Find changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
// Apply changes
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
if result != expected {
|
|
t.Errorf("Formatting preservation failed.\nExpected:\n%s\n\nGot:\n%s", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestNumericHelpers(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
expected float64
|
|
isNum bool
|
|
}{
|
|
{"42", 42.0, true},
|
|
{"3.14", 3.14, true},
|
|
{"0", 0.0, true},
|
|
{"-5", -5.0, true},
|
|
{"abc", 0.0, false},
|
|
{"", 0.0, false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
val, ok := parseNumeric(tt.input)
|
|
if ok != tt.isNum {
|
|
t.Errorf("parseNumeric(%q) isNum = %v, expected %v", tt.input, ok, tt.isNum)
|
|
}
|
|
if ok && val != tt.expected {
|
|
t.Errorf("parseNumeric(%q) = %v, expected %v", tt.input, val, tt.expected)
|
|
}
|
|
}
|
|
|
|
// Test formatting
|
|
formatTests := []struct {
|
|
input float64
|
|
expected string
|
|
}{
|
|
{42.0, "42"},
|
|
{3.14, "3.14"},
|
|
{0.0, "0"},
|
|
{-5.0, "-5"},
|
|
{100.5, "100.5"},
|
|
}
|
|
|
|
for _, tt := range formatTests {
|
|
result := formatNumeric(tt.input)
|
|
if result != tt.expected {
|
|
t.Errorf("formatNumeric(%v) = %q, expected %q", tt.input, result, tt.expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDeepCopyXMLElement(t *testing.T) {
|
|
original := &XMLElement{
|
|
Tag: "item",
|
|
Text: "content",
|
|
Attributes: map[string]XMLAttribute{
|
|
"name": {Value: "sword"},
|
|
},
|
|
Children: []*XMLElement{
|
|
{Tag: "child", Text: "text"},
|
|
},
|
|
}
|
|
|
|
copied := deepCopyXMLElement(original)
|
|
|
|
// Verify copy is equal
|
|
if copied.Tag != original.Tag {
|
|
t.Errorf("Tag not copied correctly")
|
|
}
|
|
if copied.Text != original.Text {
|
|
t.Errorf("Text not copied correctly")
|
|
}
|
|
|
|
// Modify copy
|
|
copied.Tag = "modified"
|
|
copied.Attributes["name"] = XMLAttribute{Value: "shield"}
|
|
copied.Children[0].Text = "modified text"
|
|
|
|
// Verify original unchanged
|
|
if original.Tag != "item" {
|
|
t.Errorf("Original was modified")
|
|
}
|
|
if original.Attributes["name"].Value != "sword" {
|
|
t.Errorf("Original attributes were modified")
|
|
}
|
|
if original.Children[0].Text != "text" {
|
|
t.Errorf("Original children were modified")
|
|
}
|
|
}
|
|
|
|
func TestSerializeXMLElement(t *testing.T) {
|
|
elem := &XMLElement{
|
|
Tag: "item",
|
|
Attributes: map[string]XMLAttribute{
|
|
"name": {Value: "sword"},
|
|
"weight": {Value: "10"},
|
|
},
|
|
Children: []*XMLElement{
|
|
{
|
|
Tag: "stats",
|
|
Attributes: map[string]XMLAttribute{
|
|
"damage": {Value: "5"},
|
|
},
|
|
Children: []*XMLElement{},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := serializeXMLElement(elem, "")
|
|
|
|
// Check it contains expected parts
|
|
if !strings.Contains(result, "<item") {
|
|
t.Errorf("Missing opening tag")
|
|
}
|
|
if !strings.Contains(result, "</item>") {
|
|
t.Errorf("Missing closing tag")
|
|
}
|
|
if !strings.Contains(result, `name="sword"`) {
|
|
t.Errorf("Missing name attribute")
|
|
}
|
|
if !strings.Contains(result, `weight="10"`) {
|
|
t.Errorf("Missing weight attribute")
|
|
}
|
|
if !strings.Contains(result, "<stats") {
|
|
t.Errorf("Missing child element")
|
|
}
|
|
}
|
|
|
|
func TestEmptyElements(t *testing.T) {
|
|
original := `<root>
|
|
<item name="sword" />
|
|
<item name="shield"></item>
|
|
</root>`
|
|
|
|
// Parse
|
|
elem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse XML: %v", err)
|
|
}
|
|
|
|
if len(elem.Children) != 2 {
|
|
t.Errorf("Expected 2 children, got %d", len(elem.Children))
|
|
}
|
|
|
|
// Both should be parsed correctly
|
|
if elem.Children[0].Tag != "item" {
|
|
t.Errorf("First child tag incorrect")
|
|
}
|
|
if elem.Children[1].Tag != "item" {
|
|
t.Errorf("Second child tag incorrect")
|
|
}
|
|
}
|
|
|
|
func TestAttributeOrderPreservation(t *testing.T) {
|
|
original := `<item name="sword" weight="10" damage="5" speed="3" />`
|
|
|
|
// Parse original
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse original XML: %v", err)
|
|
}
|
|
|
|
// Modify just weight
|
|
modElem := deepCopyXMLElement(origElem)
|
|
weightAttr := modElem.Attributes["weight"]
|
|
weightAttr.Value = "20"
|
|
modElem.Attributes["weight"] = weightAttr
|
|
|
|
// Find and apply changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Verify attribute order is preserved (weight comes before damage and speed)
|
|
weightIdx := strings.Index(result, "weight=")
|
|
damageIdx := strings.Index(result, "damage=")
|
|
speedIdx := strings.Index(result, "speed=")
|
|
|
|
if weightIdx > damageIdx || damageIdx > speedIdx {
|
|
t.Errorf("Attribute order not preserved:\n%s", result)
|
|
}
|
|
}
|