using JdeScoping.ConfigManager.Services; using JdeScoping.ConfigManager.ViewModels.Forms; using JdeScoping.ConfigManager.ViewModels.PipelineSteps; using JdeScoping.DataSync.Configuration; namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms; public class PipelineEditorViewModelTests { private readonly IDialogService _dialogService; public PipelineEditorViewModelTests() { _dialogService = Substitute.For(); } [Fact] public void Constructor_InitializesPropertiesCorrectly() { // Arrange var model = CreateDefaultModel(); var connections = new List { "jde", "cms", "lotfinder" }; // Act var sut = new PipelineEditorViewModel("TestPipeline", model, connections, _dialogService, () => { }); // Assert sut.Name.ShouldBe("TestPipeline"); sut.AvailableConnections.ShouldBe(connections); sut.PreScripts.ShouldNotBeNull(); sut.Transformers.ShouldNotBeNull(); sut.PostScripts.ShouldNotBeNull(); sut.Source.ShouldNotBeNull(); sut.Destination.ShouldNotBeNull(); } [Fact] public void Constructor_BuildsSourceAndDestinationFromModel() { // Arrange var model = new EtlPipelineConfig { Source = new SourceElement { Connection = "jde", Query = "SELECT * FROM WO" }, Destination = new DestinationElement { Table = "WorkOrder_Curr" } }; var connections = new List { "jde" }; // Act var sut = new PipelineEditorViewModel("TestPipeline", model, connections, _dialogService, () => { }); // Assert sut.Source.Connection.ShouldBe("jde"); sut.Source.Query.ShouldBe("SELECT * FROM WO"); sut.Destination.Table.ShouldBe("WorkOrder_Curr"); } [Fact] public void IsEnabled_Setter_UpdatesModelAndInvokesOnChanged() { // Arrange var model = CreateDefaultModel(); model.IsEnabled = false; var changedInvoked = false; var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => changedInvoked = true); // Act sut.IsEnabled = true; // Assert sut.IsEnabled.ShouldBeTrue(); model.IsEnabled.ShouldBeTrue(); changedInvoked.ShouldBeTrue(); } [Fact] public void IsManualOnly_Setter_UpdatesModelAndInvokesOnChanged() { // Arrange var model = CreateDefaultModel(); model.IsManualOnly = false; var changedInvoked = false; var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => changedInvoked = true); // Act sut.IsManualOnly = true; // Assert sut.IsManualOnly.ShouldBeTrue(); model.IsManualOnly.ShouldBeTrue(); changedInvoked.ShouldBeTrue(); } [Fact] public void MassSyncEnabled_ToggleOn_SetsDefaultInterval() { // Arrange var model = CreateDefaultModel(); model.MassSyncIntervalMinutes = null; var changedInvoked = false; var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => changedInvoked = true); // Act sut.MassSyncEnabled = true; // Assert sut.MassSyncEnabled.ShouldBeTrue(); sut.MassSyncIntervalMinutes.ShouldBe(10080); // 1 week default model.MassSyncIntervalMinutes.ShouldBe(10080); changedInvoked.ShouldBeTrue(); } [Fact] public void MassSyncEnabled_ToggleOff_ClearsInterval() { // Arrange var model = CreateDefaultModel(); model.MassSyncIntervalMinutes = 10080; var changedInvoked = false; var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => changedInvoked = true); // Act sut.MassSyncEnabled = false; // Assert sut.MassSyncEnabled.ShouldBeFalse(); model.MassSyncIntervalMinutes.ShouldBeNull(); changedInvoked.ShouldBeTrue(); } [Fact] public void DailySyncEnabled_ToggleOn_SetsDefaultInterval() { // Arrange var model = CreateDefaultModel(); model.DailySyncIntervalMinutes = null; var changedInvoked = false; var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => changedInvoked = true); // Act sut.DailySyncEnabled = true; // Assert sut.DailySyncEnabled.ShouldBeTrue(); sut.DailySyncIntervalMinutes.ShouldBe(1440); // 1 day default model.DailySyncIntervalMinutes.ShouldBe(1440); changedInvoked.ShouldBeTrue(); } [Fact] public void HourlySyncEnabled_ToggleOn_SetsDefaultInterval() { // Arrange var model = CreateDefaultModel(); model.HourlySyncIntervalMinutes = null; var changedInvoked = false; var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => changedInvoked = true); // Act sut.HourlySyncEnabled = true; // Assert sut.HourlySyncEnabled.ShouldBeTrue(); sut.HourlySyncIntervalMinutes.ShouldBe(60); // 1 hour default model.HourlySyncIntervalMinutes.ShouldBe(60); changedInvoked.ShouldBeTrue(); } [Fact] public void SelectedStep_Setter_DeselectsPreviousAndSelectsNew() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); var source = sut.Source; var destination = sut.Destination; // Act - Select source sut.SelectedStep = source; // Assert source.IsSelected.ShouldBeTrue(); sut.SelectedStep.ShouldBe(source); // Act - Select destination (should deselect source) sut.SelectedStep = destination; // Assert source.IsSelected.ShouldBeFalse(); destination.IsSelected.ShouldBeTrue(); sut.SelectedStep.ShouldBe(destination); } [Fact] public void CanDeleteSelectedStep_ReturnsFalse_ForSourceStep() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Act sut.SelectedStep = sut.Source; // Assert sut.CanDeleteSelectedStep.ShouldBeFalse(); } [Fact] public void CanDeleteSelectedStep_ReturnsFalse_ForDestinationStep() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Act sut.SelectedStep = sut.Destination; // Assert sut.CanDeleteSelectedStep.ShouldBeFalse(); } [Fact] public void CanDeleteSelectedStep_ReturnsTrue_ForTransformerStep() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Add a transformer sut.AddTransformerCommand.Execute(null); var transformer = sut.Transformers[0]; // Act sut.SelectedStep = transformer; // Assert sut.CanDeleteSelectedStep.ShouldBeTrue(); } [Fact] public void CanDeleteSelectedStep_ReturnsTrue_ForPreScriptStep() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Add a pre-script sut.AddPreScriptCommand.Execute(null); var preScript = sut.PreScripts[0]; // Act sut.SelectedStep = preScript; // Assert sut.CanDeleteSelectedStep.ShouldBeTrue(); } [Fact] public void CanDeleteSelectedStep_ReturnsTrue_ForPostScriptStep() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Add a post-script sut.AddPostScriptCommand.Execute(null); var postScript = sut.PostScripts[0]; // Act sut.SelectedStep = postScript; // Assert sut.CanDeleteSelectedStep.ShouldBeTrue(); } [Fact] public void AddPreScriptCommand_AddsPreScriptAndSelectsIt() { // Arrange var model = CreateDefaultModel(); var changedInvoked = false; var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => changedInvoked = true); // Act sut.AddPreScriptCommand.Execute(null); // Assert sut.PreScripts.Count.ShouldBe(1); sut.SelectedStep.ShouldBe(sut.PreScripts[0]); sut.PreScripts[0].IsSelected.ShouldBeTrue(); changedInvoked.ShouldBeTrue(); } [Fact] public void AddTransformerCommand_AddsTransformerAndSelectsIt() { // Arrange var model = CreateDefaultModel(); var changedInvoked = false; var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => changedInvoked = true); // Act sut.AddTransformerCommand.Execute(null); // Assert sut.Transformers.Count.ShouldBe(1); sut.SelectedStep.ShouldBe(sut.Transformers[0]); sut.Transformers[0].IsSelected.ShouldBeTrue(); changedInvoked.ShouldBeTrue(); } [Fact] public void AddPostScriptCommand_AddsPostScriptAndSelectsIt() { // Arrange var model = CreateDefaultModel(); var changedInvoked = false; var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => changedInvoked = true); // Act sut.AddPostScriptCommand.Execute(null); // Assert sut.PostScripts.Count.ShouldBe(1); sut.SelectedStep.ShouldBe(sut.PostScripts[0]); sut.PostScripts[0].IsSelected.ShouldBeTrue(); changedInvoked.ShouldBeTrue(); } [Fact] public void RemoveStepCommand_RemovesSelectedStep() { // Arrange var model = CreateDefaultModel(); var changedInvoked = false; var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => changedInvoked = true); // Add a transformer first sut.AddTransformerCommand.Execute(null); var transformer = sut.Transformers[0]; changedInvoked = false; // Act sut.RemoveStepCommand.Execute(transformer); // Assert sut.Transformers.Count.ShouldBe(0); sut.SelectedStep.ShouldBeNull(); changedInvoked.ShouldBeTrue(); } [Fact] public void MoveStepUpCommand_MovesStepUpInCollection() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Add two transformers sut.AddTransformerCommand.Execute(null); sut.AddTransformerCommand.Execute(null); var firstTransformer = sut.Transformers[0]; var secondTransformer = sut.Transformers[1]; // Act - Move second up sut.MoveStepUpCommand.Execute(secondTransformer); // Assert sut.Transformers[0].ShouldBe(secondTransformer); sut.Transformers[1].ShouldBe(firstTransformer); } [Fact] public void MoveStepDownCommand_MovesStepDownInCollection() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Add two transformers sut.AddTransformerCommand.Execute(null); sut.AddTransformerCommand.Execute(null); var firstTransformer = sut.Transformers[0]; var secondTransformer = sut.Transformers[1]; // Act - Move first down sut.MoveStepDownCommand.Execute(firstTransformer); // Assert sut.Transformers[0].ShouldBe(secondTransformer); sut.Transformers[1].ShouldBe(firstTransformer); } [Fact] public void MoveStepUpCommand_DoesNotMove_WhenAtTop() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Add two transformers sut.AddTransformerCommand.Execute(null); sut.AddTransformerCommand.Execute(null); var firstTransformer = sut.Transformers[0]; var secondTransformer = sut.Transformers[1]; // Act - Try to move first up (should do nothing) sut.MoveStepUpCommand.Execute(firstTransformer); // Assert sut.Transformers[0].ShouldBe(firstTransformer); sut.Transformers[1].ShouldBe(secondTransformer); } [Fact] public void MoveStepDownCommand_DoesNotMove_WhenAtBottom() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Add two transformers sut.AddTransformerCommand.Execute(null); sut.AddTransformerCommand.Execute(null); var firstTransformer = sut.Transformers[0]; var secondTransformer = sut.Transformers[1]; // Act - Try to move second down (should do nothing) sut.MoveStepDownCommand.Execute(secondTransformer); // Assert sut.Transformers[0].ShouldBe(firstTransformer); sut.Transformers[1].ShouldBe(secondTransformer); } [Fact] public void Constructor_ThrowsOnNullName() { // Arrange var model = CreateDefaultModel(); // Act & Assert Should.Throw(() => new PipelineEditorViewModel(null!, model, [], _dialogService, () => { })); } [Fact] public void Constructor_ThrowsOnNullModel() { // Act & Assert Should.Throw(() => new PipelineEditorViewModel("Test", null!, [], _dialogService, () => { })); } [Fact] public void Constructor_ThrowsOnNullDialogService() { // Arrange var model = CreateDefaultModel(); // Act & Assert Should.Throw(() => new PipelineEditorViewModel("Test", model, [], null!, () => { })); } [Fact] public void Constructor_ThrowsOnNullOnChanged() { // Arrange var model = CreateDefaultModel(); // Act & Assert Should.Throw(() => new PipelineEditorViewModel("Test", model, [], _dialogService, null!)); } [Fact] public void Constructor_LoadsExistingTransformers() { // Arrange var model = new EtlPipelineConfig { Source = new SourceElement { Connection = "jde", Query = "SELECT * FROM WO" }, Destination = new DestinationElement { Table = "WorkOrder" }, Transforms = [ new TransformElement { TransformType = "ColumnDrop" }, new TransformElement { TransformType = "ColumnRename" } ] }; // Act var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Assert sut.Transformers.Count.ShouldBe(2); sut.Transformers[0].ShouldBeOfType(); sut.Transformers[1].ShouldBeOfType(); } [Fact] public void Constructor_LoadsExistingPreScripts() { // Arrange var model = new EtlPipelineConfig { Source = new SourceElement { Connection = "jde", Query = "SELECT * FROM WO" }, Destination = new DestinationElement { Table = "WorkOrder" }, PreScripts = [new ScriptElement { Connection = "lotfinder", Script = "TRUNCATE TABLE Test" }] }; // Act var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Assert sut.PreScripts.Count.ShouldBe(1); sut.PreScripts[0].Script.ShouldBe("TRUNCATE TABLE Test"); } [Fact] public void Constructor_LoadsExistingPostScripts() { // Arrange var model = new EtlPipelineConfig { Source = new SourceElement { Connection = "jde", Query = "SELECT * FROM WO" }, Destination = new DestinationElement { Table = "WorkOrder" }, PostScripts = [new ScriptElement { Connection = "lotfinder", Script = "UPDATE Stats SET LastRun = GETDATE()" }] }; // Act var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Assert sut.PostScripts.Count.ShouldBe(1); sut.PostScripts[0].Script.ShouldBe("UPDATE Stats SET LastRun = GETDATE()"); } [Fact] public void AllSteps_ReturnsAllStepsInOrder() { // Arrange var model = new EtlPipelineConfig { Source = new SourceElement { Connection = "jde", Query = "SELECT * FROM WO" }, Destination = new DestinationElement { Table = "WorkOrder" }, PreScripts = [new ScriptElement { Script = "pre" }], Transforms = [new TransformElement { TransformType = "ColumnDrop" }], PostScripts = [new ScriptElement { Script = "post" }] }; // Act var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); var allSteps = sut.AllSteps.ToList(); // Assert allSteps.Count.ShouldBe(5); allSteps[0].ShouldBeOfType(); allSteps[1].ShouldBeOfType(); allSteps[2].ShouldBeOfType(); allSteps[3].ShouldBeOfType(); allSteps[4].ShouldBeOfType(); } [Fact] public void SelectedStepEditor_UpdatesWhenSelectedStepChanges() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Act sut.SelectedStep = sut.Source; // Assert sut.SelectedStepEditor.ShouldBe(sut.Source); } [Fact] public void AddTransformerOfType_AddsSpecificTransformerType() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Act sut.AddTransformerOfType("JdeDate"); // Assert sut.Transformers.Count.ShouldBe(1); sut.Transformers[0].ShouldBeOfType(); sut.SelectedStep.ShouldBe(sut.Transformers[0]); } [Fact] public void SelectedTransformerType_SetterAndGetter_Work() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Act sut.SelectedTransformerType = "ColumnRename"; // Assert sut.SelectedTransformerType.ShouldBe("ColumnRename"); } [Fact] public void AvailableTransformerTypes_ReturnsExpectedTypes() { // Arrange var model = CreateDefaultModel(); var sut = new PipelineEditorViewModel("Test", model, [], _dialogService, () => { }); // Assert sut.AvailableTransformerTypes.ShouldContain("ColumnDrop"); sut.AvailableTransformerTypes.ShouldContain("ColumnRename"); sut.AvailableTransformerTypes.ShouldContain("JdeDate"); sut.AvailableTransformerTypes.ShouldContain("Regex"); } private static EtlPipelineConfig CreateDefaultModel() { return new EtlPipelineConfig { Source = new SourceElement { Connection = string.Empty, Query = string.Empty }, Destination = new DestinationElement { Table = string.Empty } }; } }