Files
jdescopingtool/NEW/tests/Utils/JdeScoping.ConfigManager.Ui.Tests/ViewModels/Forms/PipelineEditorViewModelTests.cs
T
Joseph Doherty 1fc7792cd1 refactor(configmanager): rename UI project and split test projects
Rename ConfigManager to ConfigManager.Ui to match the Core/CLI/UI project
structure, and split the monolithic test project into Core.Tests,
Cli.Tests, and Ui.Tests to align with the source project organization.
2026-01-28 10:24:36 -05:00

631 lines
20 KiB
C#

using JdeScoping.ConfigManager.Ui.Services;
using JdeScoping.ConfigManager.Ui.ViewModels.Forms;
using JdeScoping.ConfigManager.Ui.ViewModels.PipelineSteps;
using JdeScoping.DataSync.Configuration;
namespace JdeScoping.ConfigManager.Ui.Tests.ViewModels.Forms;
public class PipelineEditorViewModelTests
{
private readonly IDialogService _dialogService;
public PipelineEditorViewModelTests()
{
_dialogService = Substitute.For<IDialogService>();
}
[Fact]
public void Constructor_InitializesPropertiesCorrectly()
{
// Arrange
var model = CreateDefaultModel();
var connections = new List<string> { "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<string> { "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<ArgumentNullException>(() =>
new PipelineEditorViewModel(null!, model, [], _dialogService, () => { }));
}
[Fact]
public void Constructor_ThrowsOnNullModel()
{
// Act & Assert
Should.Throw<ArgumentNullException>(() =>
new PipelineEditorViewModel("Test", null!, [], _dialogService, () => { }));
}
[Fact]
public void Constructor_ThrowsOnNullDialogService()
{
// Arrange
var model = CreateDefaultModel();
// Act & Assert
Should.Throw<ArgumentNullException>(() =>
new PipelineEditorViewModel("Test", model, [], null!, () => { }));
}
[Fact]
public void Constructor_ThrowsOnNullOnChanged()
{
// Arrange
var model = CreateDefaultModel();
// Act & Assert
Should.Throw<ArgumentNullException>(() =>
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<ColumnDropTransformerViewModel>();
sut.Transformers[1].ShouldBeOfType<ColumnRenameTransformerViewModel>();
}
[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<PreScriptStepViewModel>();
allSteps[1].ShouldBeOfType<SourceStepViewModel>();
allSteps[2].ShouldBeOfType<ColumnDropTransformerViewModel>();
allSteps[3].ShouldBeOfType<DestinationStepViewModel>();
allSteps[4].ShouldBeOfType<PostScriptStepViewModel>();
}
[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<JdeDateTransformerViewModel>();
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 }
};
}
}