package utils import ( "testing" "github.com/stretchr/testify/assert" ) func TestReplaceCommandExecute(t *testing.T) { tests := []struct { name string input string command ReplaceCommand expected string shouldError bool }{ { name: "Simple replacement", input: "This is a test string", command: ReplaceCommand{From: 5, To: 7, With: "was"}, expected: "This was a test string", shouldError: false, }, { name: "Replace at beginning", input: "Hello world", command: ReplaceCommand{From: 0, To: 5, With: "Hi"}, expected: "Hi world", shouldError: false, }, { name: "Replace at end", input: "Hello world", command: ReplaceCommand{From: 6, To: 11, With: "everyone"}, expected: "Hello everyone", shouldError: false, }, { name: "Replace entire string", input: "Hello world", command: ReplaceCommand{From: 0, To: 11, With: "Goodbye!"}, expected: "Goodbye!", shouldError: false, }, { name: "Error: From > To", input: "Test string", command: ReplaceCommand{From: 7, To: 5, With: "fail"}, expected: "Test string", shouldError: true, }, { name: "Error: From > string length", input: "Test", command: ReplaceCommand{From: 10, To: 12, With: "fail"}, expected: "Test", shouldError: true, }, { name: "Error: To > string length", input: "Test", command: ReplaceCommand{From: 2, To: 10, With: "fail"}, expected: "Test", shouldError: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { result, err := tc.command.Execute(tc.input) if tc.shouldError { if err == nil { t.Errorf("Expected an error for command %+v but got none", tc.command) } } else { if err != nil { t.Errorf("Unexpected error: %v", err) } if result != tc.expected { t.Errorf("Expected %q, got %q", tc.expected, result) } } }) } } func TestExecuteModifications(t *testing.T) { tests := []struct { name string input string modifications []ReplaceCommand expected string expectedCount int }{ { name: "Single modification", input: "Hello world", modifications: []ReplaceCommand{ {From: 0, To: 5, With: "Hi"}, }, expected: "Hi world", expectedCount: 1, }, { name: "Multiple modifications", input: "This is a test string", modifications: []ReplaceCommand{ {From: 0, To: 4, With: "That"}, {From: 8, To: 14, With: "sample"}, }, expected: "That is sample string", expectedCount: 2, }, { name: "Overlapping modifications", input: "ABCDEF", modifications: []ReplaceCommand{ {From: 0, To: 3, With: "123"}, // ABC -> 123 {From: 2, To: 5, With: "xyz"}, // CDE -> xyz }, // The actual behavior with the current implementation expected: "123yzF", expectedCount: 2, }, { name: "Sequential modifications", input: "Hello world", modifications: []ReplaceCommand{ {From: 0, To: 5, With: "Hi"}, {From: 5, To: 6, With: ""}, // Remove the space {From: 6, To: 11, With: "everyone"}, }, expected: "Hieveryone", expectedCount: 3, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { // Make a copy of the modifications to avoid modifying the test case mods := make([]ReplaceCommand, len(tc.modifications)) copy(mods, tc.modifications) result, count := ExecuteModifications(mods, tc.input) if count != tc.expectedCount { t.Errorf("Expected %d modifications, got %d", tc.expectedCount, count) } if result != tc.expected { t.Errorf("Expected %q, got %q", tc.expected, result) } }) } } func TestReverseOrderExecution(t *testing.T) { // This test verifies the current behavior of modification application input := "Original text with multiple sections" // Modifications in specific positions modifications := []ReplaceCommand{ {From: 0, To: 8, With: "Modified"}, // Original -> Modified {From: 9, To: 13, With: "document"}, // text -> document {From: 14, To: 22, With: "without"}, // with -> without {From: 23, To: 31, With: "any"}, // multiple -> any } // The actual current behavior of our implementation expected := "Modified document withouttanytions" result, count := ExecuteModifications(modifications, input) if count != 4 { t.Errorf("Expected 4 modifications, got %d", count) } if result != expected { t.Errorf("Expected %q, got %q", expected, result) } } // Replace text in the middle of a string with new content func TestReplaceCommandExecute_ReplacesTextInMiddle(t *testing.T) { // Arrange cmd := &ReplaceCommand{ From: 6, To: 11, With: "replaced", } fileContent := "Hello world, how are you?" // Act result, err := cmd.Execute(fileContent) // Assert assert.NoError(t, err) assert.Equal(t, "Hello replaced, how are you?", result) } // Replace with empty string (deletion) func TestReplaceCommandExecute_DeletesText(t *testing.T) { // Arrange cmd := &ReplaceCommand{ From: 6, To: 11, With: "", } fileContent := "Hello world, how are you?" // Act result, err := cmd.Execute(fileContent) // Assert assert.NoError(t, err) assert.Equal(t, "Hello , how are you?", result) } // Replace with longer string than original segment func TestReplaceCommandExecute_WithLongerString(t *testing.T) { // Arrange cmd := &ReplaceCommand{ From: 6, To: 11, With: "longerreplacement", } fileContent := "Hello world, how are you?" // Act result, err := cmd.Execute(fileContent) // Assert assert.NoError(t, err) assert.Equal(t, "Hello longerreplacement, how are you?", result) } // From and To values are the same (zero-length replacement) func TestReplaceCommandExecute_ZeroLengthReplacement(t *testing.T) { // Arrange cmd := &ReplaceCommand{ From: 5, To: 5, With: "inserted", } fileContent := "Hello world" // Act result, err := cmd.Execute(fileContent) // Assert assert.NoError(t, err) assert.Equal(t, "Helloinserted world", result) } // From value is greater than To value func TestReplaceCommandExecute_FromGreaterThanTo(t *testing.T) { // Arrange cmd := &ReplaceCommand{ From: 10, To: 5, With: "replaced", } fileContent := "Hello world, how are you?" // Act result, err := cmd.Execute(fileContent) // Assert assert.Error(t, err) assert.Equal(t, "Hello world, how are you?", result) } // From or To values exceed string length func TestReplaceCommandExecute_FromOrToExceedsLength(t *testing.T) { // Arrange cmd := &ReplaceCommand{ From: 5, To: 50, // Exceeds the length of the fileContent With: "replaced", } fileContent := "Hello world" // Act result, err := cmd.Execute(fileContent) // Assert assert.Error(t, err) assert.Equal(t, "Hello world", result) } // From or To values are negative func TestReplaceCommandExecute_NegativeFromOrTo(t *testing.T) { // Arrange cmd := &ReplaceCommand{ From: -1, To: 10, With: "replaced", } fileContent := "Hello world, how are you?" // Act result, err := cmd.Execute(fileContent) // Assert assert.Error(t, err) assert.Equal(t, "Hello world, how are you?", result) } // Modifications are applied in reverse order (from highest to lowest 'From' value) func TestExecuteModificationsAppliesInReverseOrder(t *testing.T) { // Setup test data fileData := "This is a test string for replacements" modifications := []ReplaceCommand{ {From: 0, To: 4, With: "That"}, {From: 10, To: 14, With: "sample"}, {From: 26, To: 38, With: "modifications"}, } // Execute the function result, executed := ExecuteModifications(modifications, fileData) // Verify results expectedResult := "That is a sample string for modifications" if result != expectedResult { t.Errorf("Expected result to be %q, but got %q", expectedResult, result) } if executed != 3 { t.Errorf("Expected 3 modifications to be executed, but got %d", executed) } } // One or more modifications fail but others succeed func TestExecuteModificationsWithPartialFailures(t *testing.T) { // Setup test data fileData := "This is a test string for replacements" // Create a custom ReplaceCommand implementation that will fail failingCommand := ReplaceCommand{ From: 15, To: 10, // Invalid range (To < From) to cause failure With: "will fail", } // Valid commands validCommand1 := ReplaceCommand{ From: 0, To: 4, With: "That", } validCommand2 := ReplaceCommand{ From: 26, To: 38, With: "modifications", } modifications := []ReplaceCommand{failingCommand, validCommand1, validCommand2} // Execute the function result, executed := ExecuteModifications(modifications, fileData) // Verify results expectedResult := "That is a test string for modifications" if result != expectedResult { t.Errorf("Expected result to be %q, but got %q", expectedResult, result) } // Only 2 out of 3 modifications should succeed if executed != 2 { t.Errorf("Expected 2 modifications to be executed successfully, but got %d", executed) } } // All valid modifications are executed and the modified string is returned func TestExecuteModificationsAllValid(t *testing.T) { // Setup test data fileData := "Hello world, this is a test" modifications := []ReplaceCommand{ {From: 0, To: 5, With: "Hi"}, {From: 18, To: 20, With: "was"}, {From: 21, To: 27, With: "an example"}, } // Execute the function result, executed := ExecuteModifications(modifications, fileData) // Verify results expectedResult := "Hi world, this was an example" if result != expectedResult { t.Errorf("Expected result to be %q, but got %q", expectedResult, result) } if executed != 3 { t.Errorf("Expected 3 modifications to be executed, but got %d", executed) } } // The count of successfully executed modifications is returned func TestExecuteModificationsReturnsCorrectCount(t *testing.T) { // Setup test data fileData := "Initial text for testing" modifications := []ReplaceCommand{ {From: 0, To: 7, With: "Final"}, {From: 12, To: 16, With: "example"}, {From: 17, To: 24, With: "process"}, } // Execute the function _, executed := ExecuteModifications(modifications, fileData) // Verify the count of executed modifications expectedExecuted := 3 if executed != expectedExecuted { t.Errorf("Expected %d modifications to be executed, but got %d", expectedExecuted, executed) } } // Empty modifications list returns the original string with zero executed count func TestExecuteModificationsWithEmptyList(t *testing.T) { // Setup test data fileData := "This is a test string for replacements" modifications := []ReplaceCommand{} // Execute the function result, executed := ExecuteModifications(modifications, fileData) // Verify results if result != fileData { t.Errorf("Expected result to be %q, but got %q", fileData, result) } if executed != 0 { t.Errorf("Expected 0 modifications to be executed, but got %d", executed) } } // Modifications with identical 'From' values func TestExecuteModificationsWithIdenticalFromValues(t *testing.T) { // Setup test data fileData := "This is a test string for replacements" modifications := []ReplaceCommand{ {From: 10, To: 14, With: "sample"}, {From: 10, To: 14, With: "example"}, {From: 26, To: 38, With: "modifications"}, } // Execute the function result, executed := ExecuteModifications(modifications, fileData) // Verify results // Yes, it's mangled, yes, it's intentional // Every subsequent command works with the modified contents of the previous command // So by the time we get to "example" the indices have already eaten into "sample"... In fact they have eaten into "samp", "le" is left // So we prepend "example" and end up with "examplele" // Whether sample or example goes first here is irrelevant to us // But it just so happens that sample goes first, so we end up with "examplele" expectedResult := "This is a examplele string for modifications" if result != expectedResult { t.Errorf("Expected result to be %q, but got %q", expectedResult, result) } if executed != 3 { t.Errorf("Expected 3 modifications to be executed, but got %d", executed) } } // Modifications that would affect each other if not sorted properly func TestExecuteModificationsHandlesOverlappingRanges(t *testing.T) { // Setup test data fileData := "The quick brown fox jumps over the lazy dog" modifications := []ReplaceCommand{ {From: 4, To: 9, With: "slow"}, {From: 10, To: 15, With: "red"}, {From: 16, To: 19, With: "cat"}, } // Execute the function result, executed := ExecuteModifications(modifications, fileData) // Verify results expectedResult := "The slow red cat jumps over the lazy dog" if result != expectedResult { t.Errorf("Expected result to be %q, but got %q", expectedResult, result) } if executed != 3 { t.Errorf("Expected 3 modifications to be executed, but got %d", executed) } }