Files
jdescopingtool/NEW/tests/JdeScoping.ConfigManager.Tests/ViewModels/MainWindowViewModelTests.cs
T
Joseph Doherty 6642c83cdb feat(configmanager): add runtime config validation using Infrastructure validators
Enable ConfigManager to validate runtime configuration (SecureStore secrets,
connection strings, LDAP) using the same validators as the Host application.
Adds AddInfrastructureValidators() extension for shared validator registration.
2026-01-21 18:31:42 -05:00

327 lines
11 KiB
C#

using JdeScoping.ConfigManager.Models;
using JdeScoping.ConfigManager.Services;
using JdeScoping.ConfigManager.Services.SecureStore;
using JdeScoping.ConfigManager.ViewModels;
using JdeScoping.ConfigManager.ViewModels.Forms;
using Microsoft.Extensions.Logging;
using NSubstitute;
namespace JdeScoping.ConfigManager.Tests.ViewModels;
public class MainWindowViewModelTests
{
private readonly IFileSystem _fileSystem;
private readonly IConfigFileService _configFileService;
private readonly IValidationService _validationService;
private readonly IBackupService _backupService;
private readonly IAutoDiscoveryService _autoDiscoveryService;
private readonly IDialogService _dialogService;
private readonly ISecureStoreManager _secureStoreManager;
private readonly IClipboardService _clipboardService;
private readonly IRuntimeConfigValidationService _runtimeValidationService;
private readonly ILogger<MainWindowViewModel> _logger;
public MainWindowViewModelTests()
{
_fileSystem = Substitute.For<IFileSystem>();
_configFileService = Substitute.For<IConfigFileService>();
_validationService = Substitute.For<IValidationService>();
_backupService = Substitute.For<IBackupService>();
_autoDiscoveryService = Substitute.For<IAutoDiscoveryService>();
_dialogService = Substitute.For<IDialogService>();
_secureStoreManager = Substitute.For<ISecureStoreManager>();
_clipboardService = Substitute.For<IClipboardService>();
_runtimeValidationService = Substitute.For<IRuntimeConfigValidationService>();
_logger = Substitute.For<ILogger<MainWindowViewModel>>();
_validationService.ValidateAppSettings(Arg.Any<ConfigModel>())
.Returns(new ValidationResult());
_validationService.ValidatePipelines(Arg.Any<PipelinesConfigModel>())
.Returns(new ValidationResult());
}
[Fact]
public void SelectingDataSyncNode_LoadsDataSyncFormViewModel()
{
// Arrange
var config = new ConfigModel { DataSync = new DataSyncSection { MaxDegreeOfParallelism = 8 } };
var sut = CreateViewModel();
sut.LoadConfigForTesting(config, null);
var dataSyncNode = sut.TreeNodes
.SelectMany(n => n.Children)
.First(n => n.SectionKey == "DataSync");
// Act
sut.SelectedNode = dataSyncNode;
// Assert
sut.SelectedFormViewModel.ShouldBeOfType<DataSyncFormViewModel>();
((DataSyncFormViewModel)sut.SelectedFormViewModel!).MaxDegreeOfParallelism.ShouldBe(8);
}
[Fact]
public void SelectingDataAccessNode_LoadsDataAccessFormViewModel()
{
// Arrange
var config = new ConfigModel { DataAccess = new DataAccessSection { ProductionSchema = "custom" } };
var sut = CreateViewModel();
sut.LoadConfigForTesting(config, null);
var dataAccessNode = sut.TreeNodes
.SelectMany(n => n.Children)
.First(n => n.SectionKey == "DataAccess");
// Act
sut.SelectedNode = dataAccessNode;
// Assert
sut.SelectedFormViewModel.ShouldBeOfType<DataAccessFormViewModel>();
((DataAccessFormViewModel)sut.SelectedFormViewModel!).ProductionSchema.ShouldBe("custom");
}
[Fact]
public void SelectingAuthNode_LoadsAuthFormViewModel()
{
// Arrange
var config = new ConfigModel { Auth = new AuthSection { CookieName = "TestCookie" } };
var sut = CreateViewModel();
sut.LoadConfigForTesting(config, null);
var authNode = sut.TreeNodes
.SelectMany(n => n.Children)
.First(n => n.SectionKey == "Auth");
// Act
sut.SelectedNode = authNode;
// Assert
sut.SelectedFormViewModel.ShouldBeOfType<AuthFormViewModel>();
((AuthFormViewModel)sut.SelectedFormViewModel!).CookieName.ShouldBe("TestCookie");
}
[Fact]
public void SelectingLdapNode_LoadsLdapFormViewModel()
{
// Arrange
var config = new ConfigModel { Ldap = new LdapSection { GroupDn = "CN=TestGroup" } };
var sut = CreateViewModel();
sut.LoadConfigForTesting(config, null);
var ldapNode = sut.TreeNodes
.SelectMany(n => n.Children)
.First(n => n.SectionKey == "Ldap");
// Act
sut.SelectedNode = ldapNode;
// Assert
sut.SelectedFormViewModel.ShouldBeOfType<LdapFormViewModel>();
((LdapFormViewModel)sut.SelectedFormViewModel!).GroupDn.ShouldBe("CN=TestGroup");
}
[Fact]
public void SelectingSearchNode_LoadsSearchFormViewModel()
{
// Arrange
var config = new ConfigModel { Search = new SearchSection { MaxResultRows = 50000 } };
var sut = CreateViewModel();
sut.LoadConfigForTesting(config, null);
var searchNode = sut.TreeNodes
.SelectMany(n => n.Children)
.First(n => n.SectionKey == "Search");
// Act
sut.SelectedNode = searchNode;
// Assert
sut.SelectedFormViewModel.ShouldBeOfType<SearchFormViewModel>();
((SearchFormViewModel)sut.SelectedFormViewModel!).MaxResultRows.ShouldBe(50000);
}
[Fact]
public void SelectingExcelExportNode_LoadsExcelExportFormViewModel()
{
// Arrange
var config = new ConfigModel { ExcelExport = new ExcelExportSection { TimezoneId = "America/New_York" } };
var sut = CreateViewModel();
sut.LoadConfigForTesting(config, null);
var excelNode = sut.TreeNodes
.SelectMany(n => n.Children)
.First(n => n.SectionKey == "ExcelExport");
// Act
sut.SelectedNode = excelNode;
// Assert
sut.SelectedFormViewModel.ShouldBeOfType<ExcelExportFormViewModel>();
((ExcelExportFormViewModel)sut.SelectedFormViewModel!).TimezoneId.ShouldBe("America/New_York");
}
[Fact]
public void SelectingPipelineNode_LoadsPipelineEditorViewModel()
{
// Arrange
var config = new ConfigModel();
var pipelines = new PipelinesConfigModel
{
Pipelines = new Dictionary<string, PipelineModel>
{
["WorkOrders"] = new PipelineModel
{
Source = new PipelineSource { Connection = "jde", Query = "SELECT * FROM WO" },
Destination = new PipelineDestination { Table = "WorkOrder_Curr" }
}
}
};
var sut = CreateViewModel();
sut.LoadConfigForTesting(config, pipelines);
var pipelineNode = sut.TreeNodes
.SelectMany(n => n.Children)
.First(n => n.SectionKey == "WorkOrders");
// Act
sut.SelectedNode = pipelineNode;
// Assert
sut.SelectedFormViewModel.ShouldBeOfType<PipelineEditorViewModel>();
var pipelineEditor = (PipelineEditorViewModel)sut.SelectedFormViewModel!;
pipelineEditor.Name.ShouldBe("WorkOrders");
pipelineEditor.Source.Connection.ShouldBe("jde");
}
[Fact]
public void ModifyingFormProperty_SetsHasUnsavedChanges()
{
// Arrange
var config = new ConfigModel();
var sut = CreateViewModel();
sut.LoadConfigForTesting(config, null);
var dataSyncNode = sut.TreeNodes
.SelectMany(n => n.Children)
.First(n => n.SectionKey == "DataSync");
sut.SelectedNode = dataSyncNode;
// Act
((DataSyncFormViewModel)sut.SelectedFormViewModel!).BatchSize = 10000;
// Assert
sut.HasUnsavedChanges.ShouldBeTrue();
}
[Fact]
public void ModifyingFormProperty_MarksNodeAsModified()
{
// Arrange
var config = new ConfigModel();
var sut = CreateViewModel();
sut.LoadConfigForTesting(config, null);
var dataSyncNode = sut.TreeNodes
.SelectMany(n => n.Children)
.First(n => n.SectionKey == "DataSync");
sut.SelectedNode = dataSyncNode;
// Act
((DataSyncFormViewModel)sut.SelectedFormViewModel!).MaxDegreeOfParallelism = 16;
// Assert
dataSyncNode.IsModified.ShouldBeTrue();
}
[Fact]
public void SelectingFolderNode_SetsSelectedFormViewModelToNull()
{
// Arrange
var config = new ConfigModel();
var sut = CreateViewModel();
sut.LoadConfigForTesting(config, null);
var folderNode = sut.TreeNodes.First(); // Settings folder
// Act
sut.SelectedNode = folderNode;
// Assert
sut.SelectedFormViewModel.ShouldBeNull();
}
[Fact]
public void SelectingNull_SetsSelectedFormViewModelToNull()
{
// Arrange
var config = new ConfigModel();
var sut = CreateViewModel();
sut.LoadConfigForTesting(config, null);
var dataSyncNode = sut.TreeNodes
.SelectMany(n => n.Children)
.First(n => n.SectionKey == "DataSync");
sut.SelectedNode = dataSyncNode;
// Act
sut.SelectedNode = null;
// Assert
sut.SelectedFormViewModel.ShouldBeNull();
}
[Fact]
public void LoadConfigForTesting_BuildsTreeNodes()
{
// Arrange
var config = new ConfigModel();
var sut = CreateViewModel();
// Act
sut.LoadConfigForTesting(config, null);
// Assert
sut.TreeNodes.Count.ShouldBe(3); // Settings, Pipelines, and Secure Stores folders
sut.TreeNodes[0].Name.ShouldBe("Settings");
sut.TreeNodes[0].Children.Count.ShouldBe(6); // DataSync, DataAccess, Auth, Ldap, Search, ExcelExport
}
[Fact]
public void LoadConfigForTesting_WithPipelines_BuildsPipelineNodes()
{
// Arrange
var config = new ConfigModel();
var pipelines = new PipelinesConfigModel
{
Pipelines = new Dictionary<string, PipelineModel>
{
["Pipeline1"] = new PipelineModel(),
["Pipeline2"] = new PipelineModel()
}
};
var sut = CreateViewModel();
// Act
sut.LoadConfigForTesting(config, pipelines);
// Assert
sut.TreeNodes[1].Name.ShouldBe("Pipelines");
sut.TreeNodes[1].Children.Count.ShouldBe(2);
}
private MainWindowViewModel CreateViewModel()
{
return new MainWindowViewModel(
_fileSystem,
_configFileService,
_validationService,
_backupService,
_autoDiscoveryService,
_dialogService,
_secureStoreManager,
_clipboardService,
_runtimeValidationService,
_logger);
}
}