From 9064a538201cd067b964797b0606df49e43081fd Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Fri, 28 Mar 2025 00:23:42 +0100 Subject: [PATCH] Add more tests (and fix some things) for replacecommand --- utils/replacecommand.go | 5 +- utils/replacecommand_test.go | 324 +++++++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+), 1 deletion(-) diff --git a/utils/replacecommand.go b/utils/replacecommand.go index c6273ce..59c8fe9 100644 --- a/utils/replacecommand.go +++ b/utils/replacecommand.go @@ -36,7 +36,7 @@ func ExecuteModifications(modifications []ReplaceCommand, fileData string) (stri func (m *ReplaceCommand) Execute(fileDataStr string) (string, error) { err := m.Validate(len(fileDataStr)) if err != nil { - return "", fmt.Errorf("failed to validate modification: %v", err) + return fileDataStr, fmt.Errorf("failed to validate modification: %v", err) } logger.Trace("Replace pos %d-%d with %q", m.From, m.To, m.With) @@ -50,5 +50,8 @@ func (m *ReplaceCommand) Validate(maxsize int) error { if m.From > maxsize || m.To > maxsize { return fmt.Errorf("command from or to is greater than replacement length: %v", m) } + if m.From < 0 || m.To < 0 { + return fmt.Errorf("command from or to is less than 0: %v", m) + } return nil } diff --git a/utils/replacecommand_test.go b/utils/replacecommand_test.go index 318e16c..1704aef 100644 --- a/utils/replacecommand_test.go +++ b/utils/replacecommand_test.go @@ -2,6 +2,8 @@ package utils import ( "testing" + + "github.com/stretchr/testify/assert" ) func TestReplaceCommandExecute(t *testing.T) { @@ -178,3 +180,325 @@ func TestReverseOrderExecution(t *testing.T) { 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) + } +}