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) } }