package processor
import (
"strings"
"testing"
"cook/utils"
)
func TestParseXMLWithPositions(t *testing.T) {
xml := `- Hello
`
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 := `
- A sword
`
expected := `
- A modified sword
`
// 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 := `
`
expected := `
`
// 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 := ` `
expected := ` `
// 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 := ` `
// 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 := ` `
// 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 := `
`
// 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, `-
`
expected := `
`
// 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 := `
-
-
`
// 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 := `
-
A sharp blade
`
expected := `
-
A sharp blade
`
// 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, "- ") {
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, "
`
// 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 := ` `
// 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)
}
}