feat(etl): add collision detection to ColumnRenameTransformer

This commit is contained in:
Joseph Doherty
2026-01-03 10:50:03 -05:00
parent ae84cb3d75
commit 0820a9b024
2 changed files with 53 additions and 1 deletions
@@ -30,10 +30,23 @@ public class ColumnRenameTransformer : DataTransformerBase
{
_outputNames = new string[source.FieldCount];
_nameToOrdinal = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < source.FieldCount; i++)
{
var originalName = source.GetName(i);
var outputName = _renames.TryGetValue(originalName, out var newName) ? newName : originalName;
var outputName = _renames.TryGetValue(originalName, out var newName)
? newName
: originalName;
if (_nameToOrdinal.TryGetValue(outputName, out var existingOrdinal))
{
var existingOriginal = source.GetName(existingOrdinal);
throw new InvalidOperationException(
$"Column name collision: '{originalName}' → '{outputName}' conflicts with " +
$"'{existingOriginal}' (already at ordinal {existingOrdinal}). " +
$"Each output column name must be unique.");
}
_outputNames[i] = outputName;
_nameToOrdinal[outputName] = i;
}
@@ -54,6 +54,33 @@ public class ColumnRenameTransformerTests
Assert.Equal("Charlie", reader.GetName(2));
}
[Fact]
public void OnInitialize_RenameCollision_ThrowsInvalidOperationException()
{
// Arrange - renaming A to B when B already exists
var transformer = new ColumnRenameTransformer(("A", "B"));
var source = CreateMockReader(new[] { "A", "B", "C" });
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() =>
transformer.Transform(source));
Assert.Contains("A", ex.Message);
Assert.Contains("B", ex.Message);
Assert.Contains("collision", ex.Message.ToLower());
}
[Fact]
public void OnInitialize_PreExistingDuplicates_ThrowsInvalidOperationException()
{
// Arrange - source has duplicate column names (case-insensitive)
var transformer = new ColumnRenameTransformer();
var source = CreateMockReaderWithDuplicates(new[] { "Name", "name" });
// Act & Assert
Assert.Throws<InvalidOperationException>(() =>
transformer.Transform(source));
}
private static IDataReader CreateMockReader(string[] columns)
{
var reader = Substitute.For<IDataReader>();
@@ -66,4 +93,16 @@ public class ColumnRenameTransformerTests
}
return reader;
}
private static IDataReader CreateMockReaderWithDuplicates(string[] columns)
{
var reader = Substitute.For<IDataReader>();
reader.FieldCount.Returns(columns.Length);
for (int i = 0; i < columns.Length; i++)
{
var index = i;
reader.GetName(index).Returns(columns[index]);
}
return reader;
}
}