test(configmanager): expand unit test coverage to 451 tests

Add comprehensive tests for services (ConnectionTestService, RuntimeConfigValidation),
ViewModels (PipelineEditor, dialogs, transformers), and Avalonia headless UI tests
for views and forms.
This commit is contained in:
Joseph Doherty
2026-01-27 07:24:55 -05:00
parent 227a749cdf
commit 937eb66ac8
14 changed files with 4053 additions and 62 deletions
@@ -0,0 +1,756 @@
using System.Text.Json;
using JdeScoping.ConfigManager.ViewModels.PipelineSteps;
using JdeScoping.DataSync.Configuration;
namespace JdeScoping.ConfigManager.Tests.ViewModels.PipelineSteps;
public class ColumnDropTransformerViewModelTests
{
[Fact]
public void Constructor_WithElement_ParsesColumnsFromConfig()
{
// Arrange
var element = CreateColumnDropElement("Col1", "Col2", "Col3");
// Act
var sut = new ColumnDropTransformerViewModel(element, () => { });
// Assert
var columns = sut.GetColumns();
columns.ShouldContain("Col1");
columns.ShouldContain("Col2");
columns.ShouldContain("Col3");
}
[Fact]
public void Constructor_WithEmptyElement_InitializesEmpty()
{
// Arrange
var element = new TransformElement { TransformType = "ColumnDrop" };
// Act
var sut = new ColumnDropTransformerViewModel(element, () => { });
// Assert
sut.GetColumns().ShouldBeEmpty();
}
[Fact]
public void Constructor_Default_InitializesEmpty()
{
// Act
var sut = new ColumnDropTransformerViewModel(() => { });
// Assert
sut.GetColumns().ShouldBeEmpty();
sut.ColumnsText.ShouldBe(string.Empty);
}
[Fact]
public void ColumnsText_Setter_InvokesOnChanged()
{
// Arrange
var onChangedCalled = false;
var sut = new ColumnDropTransformerViewModel(() => onChangedCalled = true);
// Act
sut.ColumnsText = "Column1\nColumn2";
// Assert
onChangedCalled.ShouldBeTrue();
}
[Fact]
public void GetColumns_ReturnsNewlineSeparatedColumns()
{
// Arrange
var sut = new ColumnDropTransformerViewModel(() => { });
sut.ColumnsText = "Col1\nCol2\nCol3";
// Act
var columns = sut.GetColumns();
// Assert
columns.Count.ShouldBe(3);
columns[0].ShouldBe("Col1");
columns[1].ShouldBe("Col2");
columns[2].ShouldBe("Col3");
}
[Fact]
public void GetColumns_TrimsWhitespace()
{
// Arrange
var sut = new ColumnDropTransformerViewModel(() => { });
sut.ColumnsText = " Col1 \n Col2 ";
// Act
var columns = sut.GetColumns();
// Assert
columns[0].ShouldBe("Col1");
columns[1].ShouldBe("Col2");
}
[Fact]
public void GetColumns_IgnoresEmptyLines()
{
// Arrange
var sut = new ColumnDropTransformerViewModel(() => { });
sut.ColumnsText = "Col1\n\n\nCol2";
// Act
var columns = sut.GetColumns();
// Assert
columns.Count.ShouldBe(2);
}
[Fact]
public void ToModel_ReturnsCorrectTransformElement()
{
// Arrange
var sut = new ColumnDropTransformerViewModel(() => { });
sut.ColumnsText = "DropMe\nAlsoDropMe";
// Act
var model = sut.ToModel();
// Assert
model.TransformType.ShouldBe("ColumnDrop");
model.Config.ShouldNotBeNull();
}
[Fact]
public void TransformerType_ReturnsColumnDrop()
{
// Arrange
var sut = new ColumnDropTransformerViewModel(() => { });
// Assert
sut.TransformerType.ShouldBe("ColumnDrop");
}
[Fact]
public void DisplayName_ReturnsColumnDrop()
{
// Arrange
var sut = new ColumnDropTransformerViewModel(() => { });
// Assert
sut.DisplayName.ShouldBe("Column Drop");
}
[Fact]
public void Summary_WithColumns_ShowsCount()
{
// Arrange
var sut = new ColumnDropTransformerViewModel(() => { });
sut.ColumnsText = "Col1\nCol2\nCol3";
// Assert
sut.Summary.ShouldBe("Drop 3 columns");
}
[Fact]
public void Summary_WithNoColumns_ShowsNoColumns()
{
// Arrange
var sut = new ColumnDropTransformerViewModel(() => { });
// Assert
sut.Summary.ShouldBe("No columns");
}
private static TransformElement CreateColumnDropElement(params string[] columns)
{
var config = new { columns };
var json = JsonSerializer.Serialize(config, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
using var doc = JsonDocument.Parse(json);
return new TransformElement
{
TransformType = "ColumnDrop",
Config = doc.RootElement.Clone()
};
}
}
public class ColumnRenameTransformerViewModelTests
{
[Fact]
public void Constructor_WithElement_ParsesMappingsFromConfig()
{
// Arrange
var element = CreateColumnRenameElement(("OldName", "NewName"), ("Source", "Target"));
// Act
var sut = new ColumnRenameTransformerViewModel(element, () => { });
// Assert
sut.Mappings.Count.ShouldBe(2);
sut.Mappings.ShouldContain(m => m.OldName == "OldName" && m.NewName == "NewName");
sut.Mappings.ShouldContain(m => m.OldName == "Source" && m.NewName == "Target");
}
[Fact]
public void Constructor_WithEmptyElement_InitializesEmpty()
{
// Arrange
var element = new TransformElement { TransformType = "ColumnRename" };
// Act
var sut = new ColumnRenameTransformerViewModel(element, () => { });
// Assert
sut.Mappings.ShouldBeEmpty();
}
[Fact]
public void Constructor_Default_InitializesEmpty()
{
// Act
var sut = new ColumnRenameTransformerViewModel(() => { });
// Assert
sut.Mappings.ShouldBeEmpty();
}
[Fact]
public void AddMapping_AddsNewMapping()
{
// Arrange
var sut = new ColumnRenameTransformerViewModel(() => { });
// Act
sut.AddMapping();
// Assert
sut.Mappings.Count.ShouldBe(1);
sut.Mappings[0].OldName.ShouldBe("");
sut.Mappings[0].NewName.ShouldBe("");
}
[Fact]
public void AddMapping_InvokesOnChanged()
{
// Arrange
var onChangedCalled = false;
var sut = new ColumnRenameTransformerViewModel(() => onChangedCalled = true);
// Act
sut.AddMapping();
// Assert
onChangedCalled.ShouldBeTrue();
}
[Fact]
public void RemoveMapping_RemovesMapping()
{
// Arrange
var sut = new ColumnRenameTransformerViewModel(() => { });
sut.AddMapping();
var mapping = sut.Mappings[0];
// Act
sut.RemoveMapping(mapping);
// Assert
sut.Mappings.ShouldBeEmpty();
}
[Fact]
public void RemoveMapping_InvokesOnChanged()
{
// Arrange
var onChangedCount = 0;
var sut = new ColumnRenameTransformerViewModel(() => onChangedCount++);
sut.AddMapping();
var mapping = sut.Mappings[0];
onChangedCount = 0;
// Act
sut.RemoveMapping(mapping);
// Assert
onChangedCount.ShouldBe(1);
}
[Fact]
public void ToModel_ReturnsCorrectTransformElement()
{
// Arrange
var sut = new ColumnRenameTransformerViewModel(() => { });
sut.AddMapping();
sut.Mappings[0].OldName = "OldCol";
sut.Mappings[0].NewName = "NewCol";
// Act
var model = sut.ToModel();
// Assert
model.TransformType.ShouldBe("ColumnRename");
model.Config.ShouldNotBeNull();
}
[Fact]
public void TransformerType_ReturnsColumnRename()
{
// Arrange
var sut = new ColumnRenameTransformerViewModel(() => { });
// Assert
sut.TransformerType.ShouldBe("ColumnRename");
}
[Fact]
public void DisplayName_ReturnsColumnRename()
{
// Arrange
var sut = new ColumnRenameTransformerViewModel(() => { });
// Assert
sut.DisplayName.ShouldBe("Column Rename");
}
[Fact]
public void Summary_WithMappings_ShowsCount()
{
// Arrange
var sut = new ColumnRenameTransformerViewModel(() => { });
sut.AddMapping();
sut.AddMapping();
// Assert
sut.Summary.ShouldBe("Rename 2 columns");
}
[Fact]
public void Summary_WithNoMappings_ShowsNoMappings()
{
// Arrange
var sut = new ColumnRenameTransformerViewModel(() => { });
// Assert
sut.Summary.ShouldBe("No mappings");
}
private static TransformElement CreateColumnRenameElement(params (string oldName, string newName)[] mappings)
{
var dict = mappings.ToDictionary(m => m.oldName, m => m.newName);
var config = new { mappings = dict };
var json = JsonSerializer.Serialize(config, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
using var doc = JsonDocument.Parse(json);
return new TransformElement
{
TransformType = "ColumnRename",
Config = doc.RootElement.Clone()
};
}
}
public class ColumnMappingViewModelTests
{
[Fact]
public void Constructor_SetsProperties()
{
// Act
var sut = new ColumnMappingViewModel("OldName", "NewName", () => { });
// Assert
sut.OldName.ShouldBe("OldName");
sut.NewName.ShouldBe("NewName");
}
[Fact]
public void OldName_Setter_InvokesOnChanged()
{
// Arrange
var onChangedCalled = false;
var sut = new ColumnMappingViewModel("Old", "New", () => onChangedCalled = true);
// Act
sut.OldName = "Updated";
// Assert
onChangedCalled.ShouldBeTrue();
}
[Fact]
public void NewName_Setter_InvokesOnChanged()
{
// Arrange
var onChangedCalled = false;
var sut = new ColumnMappingViewModel("Old", "New", () => onChangedCalled = true);
// Act
sut.NewName = "Updated";
// Assert
onChangedCalled.ShouldBeTrue();
}
[Fact]
public void OldName_Setter_HandlesNull()
{
// Arrange
var sut = new ColumnMappingViewModel("Old", "New", () => { });
// Act
sut.OldName = null!;
// Assert
sut.OldName.ShouldBe(string.Empty);
}
[Fact]
public void NewName_Setter_HandlesNull()
{
// Arrange
var sut = new ColumnMappingViewModel("Old", "New", () => { });
// Act
sut.NewName = null!;
// Assert
sut.NewName.ShouldBe(string.Empty);
}
}
public class JdeDateTransformerViewModelTests
{
[Fact]
public void Constructor_WithElement_ParsesPropertiesFromConfig()
{
// Arrange
var element = CreateJdeDateElement("WADDJ", "WADTM", "CompletionDate");
// Act
var sut = new JdeDateTransformerViewModel(element, () => { });
// Assert
sut.DateColumn.ShouldBe("WADDJ");
sut.TimeColumn.ShouldBe("WADTM");
sut.OutputColumn.ShouldBe("CompletionDate");
}
[Fact]
public void Constructor_WithEmptyElement_InitializesNull()
{
// Arrange
var element = new TransformElement { TransformType = "JdeDate" };
// Act
var sut = new JdeDateTransformerViewModel(element, () => { });
// Assert
sut.DateColumn.ShouldBeNull();
sut.TimeColumn.ShouldBeNull();
sut.OutputColumn.ShouldBeNull();
}
[Fact]
public void Constructor_Default_InitializesNull()
{
// Act
var sut = new JdeDateTransformerViewModel(() => { });
// Assert
sut.DateColumn.ShouldBeNull();
sut.TimeColumn.ShouldBeNull();
sut.OutputColumn.ShouldBeNull();
}
[Fact]
public void DateColumn_Setter_InvokesOnChanged()
{
// Arrange
var onChangedCalled = false;
var sut = new JdeDateTransformerViewModel(() => onChangedCalled = true);
// Act
sut.DateColumn = "WADDJ";
// Assert
onChangedCalled.ShouldBeTrue();
}
[Fact]
public void TimeColumn_Setter_InvokesOnChanged()
{
// Arrange
var onChangedCalled = false;
var sut = new JdeDateTransformerViewModel(() => onChangedCalled = true);
// Act
sut.TimeColumn = "WADTM";
// Assert
onChangedCalled.ShouldBeTrue();
}
[Fact]
public void OutputColumn_Setter_InvokesOnChanged()
{
// Arrange
var onChangedCalled = false;
var sut = new JdeDateTransformerViewModel(() => onChangedCalled = true);
// Act
sut.OutputColumn = "ResultDate";
// Assert
onChangedCalled.ShouldBeTrue();
}
[Fact]
public void ToModel_ReturnsCorrectTransformElement()
{
// Arrange
var sut = new JdeDateTransformerViewModel(() => { });
sut.DateColumn = "DateCol";
sut.TimeColumn = "TimeCol";
sut.OutputColumn = "Output";
// Act
var model = sut.ToModel();
// Assert
model.TransformType.ShouldBe("JdeDate");
model.Config.ShouldNotBeNull();
}
[Fact]
public void TransformerType_ReturnsJdeDate()
{
// Arrange
var sut = new JdeDateTransformerViewModel(() => { });
// Assert
sut.TransformerType.ShouldBe("JdeDate");
}
[Fact]
public void DisplayName_ReturnsJdeDateConvert()
{
// Arrange
var sut = new JdeDateTransformerViewModel(() => { });
// Assert
sut.DisplayName.ShouldBe("JDE Date Convert");
}
[Fact]
public void Summary_WithOutputColumn_ShowsOutputColumn()
{
// Arrange
var sut = new JdeDateTransformerViewModel(() => { });
sut.OutputColumn = "CompletionDate";
// Assert
sut.Summary.ShouldContain("CompletionDate");
}
[Fact]
public void Summary_WithNoOutputColumn_ShowsConfigure()
{
// Arrange
var sut = new JdeDateTransformerViewModel(() => { });
// Assert
sut.Summary.ShouldBe("Configure...");
}
private static TransformElement CreateJdeDateElement(string? dateColumn, string? timeColumn, string? outputColumn)
{
var config = new { dateColumn, timeColumn, outputColumn };
var json = JsonSerializer.Serialize(config, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
using var doc = JsonDocument.Parse(json);
return new TransformElement
{
TransformType = "JdeDate",
Config = doc.RootElement.Clone()
};
}
}
public class TransformerFactoryTests
{
[Fact]
public void Create_ColumnDrop_ReturnsColumnDropViewModel()
{
// Arrange
var element = new TransformElement { TransformType = "ColumnDrop" };
// Act
var result = TransformerFactory.Create(element, () => { });
// Assert
result.ShouldBeOfType<ColumnDropTransformerViewModel>();
}
[Fact]
public void Create_ColumnRename_ReturnsColumnRenameViewModel()
{
// Arrange
var element = new TransformElement { TransformType = "ColumnRename" };
// Act
var result = TransformerFactory.Create(element, () => { });
// Assert
result.ShouldBeOfType<ColumnRenameTransformerViewModel>();
}
[Fact]
public void Create_JdeDate_ReturnsJdeDateViewModel()
{
// Arrange
var element = new TransformElement { TransformType = "JdeDate" };
// Act
var result = TransformerFactory.Create(element, () => { });
// Assert
result.ShouldBeOfType<JdeDateTransformerViewModel>();
}
[Fact]
public void Create_Regex_ReturnsRegexViewModel()
{
// Arrange
var element = new TransformElement { TransformType = "Regex" };
// Act
var result = TransformerFactory.Create(element, () => { });
// Assert
result.ShouldBeOfType<RegexTransformerViewModel>();
}
[Fact]
public void Create_CaseInsensitive_WorksCorrectly()
{
// Arrange
var element = new TransformElement { TransformType = "COLUMNDROP" };
// Act
var result = TransformerFactory.Create(element, () => { });
// Assert
result.ShouldBeOfType<ColumnDropTransformerViewModel>();
}
[Fact]
public void Create_UnknownType_ReturnsNull()
{
// Arrange
var element = new TransformElement { TransformType = "UnknownType" };
// Act
var result = TransformerFactory.Create(element, () => { });
// Assert
result.ShouldBeNull();
}
[Fact]
public void Create_NullType_ReturnsNull()
{
// Arrange
var element = new TransformElement { TransformType = null };
// Act
var result = TransformerFactory.Create(element, () => { });
// Assert
result.ShouldBeNull();
}
[Fact]
public void CreateNew_ColumnDrop_ReturnsNewColumnDropViewModel()
{
// Act
var result = TransformerFactory.CreateNew("ColumnDrop", () => { });
// Assert
result.ShouldBeOfType<ColumnDropTransformerViewModel>();
}
[Fact]
public void CreateNew_ColumnRename_ReturnsNewColumnRenameViewModel()
{
// Act
var result = TransformerFactory.CreateNew("ColumnRename", () => { });
// Assert
result.ShouldBeOfType<ColumnRenameTransformerViewModel>();
}
[Fact]
public void CreateNew_JdeDate_ReturnsNewJdeDateViewModel()
{
// Act
var result = TransformerFactory.CreateNew("JdeDate", () => { });
// Assert
result.ShouldBeOfType<JdeDateTransformerViewModel>();
}
[Fact]
public void CreateNew_Regex_ReturnsNewRegexViewModel()
{
// Act
var result = TransformerFactory.CreateNew("Regex", () => { });
// Assert
result.ShouldBeOfType<RegexTransformerViewModel>();
}
[Fact]
public void CreateNew_CaseInsensitive_WorksCorrectly()
{
// Act
var result = TransformerFactory.CreateNew("columndrop", () => { });
// Assert
result.ShouldBeOfType<ColumnDropTransformerViewModel>();
}
[Fact]
public void CreateNew_UnknownType_ReturnsNull()
{
// Act
var result = TransformerFactory.CreateNew("UnknownType", () => { });
// Assert
result.ShouldBeNull();
}
[Fact]
public void CreateNew_NullType_ReturnsNull()
{
// Act
var result = TransformerFactory.CreateNew(null!, () => { });
// Assert
result.ShouldBeNull();
}
[Fact]
public void AvailableTypes_ContainsExpectedTypes()
{
// Assert
TransformerFactory.AvailableTypes.ShouldContain("ColumnDrop");
TransformerFactory.AvailableTypes.ShouldContain("ColumnRename");
TransformerFactory.AvailableTypes.ShouldContain("JdeDate");
TransformerFactory.AvailableTypes.ShouldContain("Regex");
}
[Fact]
public void AvailableTypes_HasFourTypes()
{
// Assert
TransformerFactory.AvailableTypes.Count.ShouldBe(4);
}
}