Add more tests (and fix some things) for replacecommand
This commit is contained in:
@@ -36,7 +36,7 @@ func ExecuteModifications(modifications []ReplaceCommand, fileData string) (stri
|
|||||||
func (m *ReplaceCommand) Execute(fileDataStr string) (string, error) {
|
func (m *ReplaceCommand) Execute(fileDataStr string) (string, error) {
|
||||||
err := m.Validate(len(fileDataStr))
|
err := m.Validate(len(fileDataStr))
|
||||||
if err != nil {
|
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)
|
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 {
|
if m.From > maxsize || m.To > maxsize {
|
||||||
return fmt.Errorf("command from or to is greater than replacement length: %v", m)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,8 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReplaceCommandExecute(t *testing.T) {
|
func TestReplaceCommandExecute(t *testing.T) {
|
||||||
@@ -178,3 +180,325 @@ func TestReverseOrderExecution(t *testing.T) {
|
|||||||
t.Errorf("Expected %q, got %q", expected, result)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user