e5fe2f06e9
Add ConfigurationValidationRunner with IConfigurationValidator interface for validating required settings at startup. Includes SecureStore and LDAP validators. Expand ConfigManager with pipeline editing UI, dialogs, and step editors. Update documentation with config validation guidance.
212 lines
9.0 KiB
C#
212 lines
9.0 KiB
C#
using Dapper;
|
|
using JdeScoping.DataAccess;
|
|
using JdeScoping.DataAccess.Interfaces;
|
|
using JdeScoping.DataSync.Dev.Configuration;
|
|
using JdeScoping.DataSync.Dev.Contracts;
|
|
using JdeScoping.DataSync.Dev.Options;
|
|
using JdeScoping.DataSync.Dev.Services;
|
|
using JdeScoping.DataSync.Etl.Pipeline;
|
|
using Microsoft.Data.SqlClient;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Microsoft.Extensions.Options;
|
|
using NSubstitute;
|
|
using Shouldly;
|
|
|
|
namespace JdeScoping.DataSync.Dev.Tests;
|
|
|
|
/// <summary>
|
|
/// Tests for DevEtlPipelineFactory.
|
|
/// </summary>
|
|
public class DevEtlPipelineFactoryTests
|
|
{
|
|
private readonly IDbConnectionFactory _connectionFactory;
|
|
private readonly ILogger<EtlPipeline> _logger;
|
|
private readonly string _cacheDirectory;
|
|
|
|
public DevEtlPipelineFactoryTests()
|
|
{
|
|
var config = new ConfigurationBuilder()
|
|
.SetBasePath(Directory.GetCurrentDirectory())
|
|
.AddJsonFile("appsettings.json", optional: true)
|
|
.AddEnvironmentVariables()
|
|
.Build();
|
|
|
|
_connectionFactory = new DbConnectionFactory(config, NullLogger<DbConnectionFactory>.Instance);
|
|
_logger = NullLogger<EtlPipeline>.Instance;
|
|
_cacheDirectory = config["DevEtl:CacheDirectory"]
|
|
?? Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "..", "CACHED_DB_FILES");
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_WithValidConfig_LoadsPipelines()
|
|
{
|
|
// Arrange
|
|
var pipelines = new Dictionary<string, DevPipelineConfig>
|
|
{
|
|
["TestTable"] = new(new DevSourceConfig("test.pb.zstd"), new DevDestinationConfig("TestTable"))
|
|
};
|
|
var config = new DevPipelinesRoot(null, pipelines);
|
|
|
|
// Act
|
|
var factory = new DevEtlPipelineFactory(_connectionFactory, config, _logger);
|
|
|
|
// Assert
|
|
factory.GetAvailableTables().ShouldContain("TestTable");
|
|
}
|
|
|
|
[Fact]
|
|
public void GetAvailableTables_Returns22Tables()
|
|
{
|
|
// Arrange
|
|
var factory = CreateFactoryFromConfig();
|
|
|
|
// Act
|
|
var tables = factory.GetAvailableTables().ToList();
|
|
|
|
// Assert
|
|
tables.Count.ShouldBe(22);
|
|
tables.ShouldContain("Branch");
|
|
tables.ShouldContain("WorkOrder_Curr");
|
|
tables.ShouldContain("LotUsage_Curr");
|
|
}
|
|
|
|
[Fact]
|
|
public void IsVeryLargeTable_ReturnsTrueForVeryLargeTables()
|
|
{
|
|
// Arrange
|
|
var factory = CreateFactoryFromConfig();
|
|
|
|
// Act & Assert
|
|
factory.IsVeryLargeTable("WorkOrderTime_Curr").ShouldBeTrue();
|
|
factory.IsVeryLargeTable("WorkOrderStep_Curr").ShouldBeTrue();
|
|
factory.IsVeryLargeTable("LotUsage_Curr").ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsVeryLargeTable_ReturnsFalseForSmallTables()
|
|
{
|
|
// Arrange
|
|
var factory = CreateFactoryFromConfig();
|
|
|
|
// Act & Assert
|
|
factory.IsVeryLargeTable("Branch").ShouldBeFalse();
|
|
factory.IsVeryLargeTable("Item").ShouldBeFalse();
|
|
factory.IsVeryLargeTable("JdeUser").ShouldBeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPipeline_WithValidTable_ReturnsPipeline()
|
|
{
|
|
// Arrange
|
|
var factory = CreateFactoryFromConfig();
|
|
|
|
// Skip if cache directory doesn't exist
|
|
if (!Directory.Exists(_cacheDirectory))
|
|
return;
|
|
|
|
var cacheFilePath = Path.Combine(_cacheDirectory, "branch.pb.zstd");
|
|
if (!File.Exists(cacheFilePath))
|
|
return;
|
|
|
|
// Act
|
|
var pipeline = factory.GetPipeline("Branch", _cacheDirectory);
|
|
|
|
// Assert
|
|
pipeline.ShouldNotBeNull();
|
|
pipeline.PipelineName.ShouldBe("Branch_Dev");
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPipeline_WithInvalidTable_ThrowsInvalidOperationException()
|
|
{
|
|
// Arrange
|
|
var factory = CreateFactoryFromConfig();
|
|
|
|
// Act & Assert
|
|
Should.Throw<InvalidOperationException>(() => factory.GetPipeline("NonExistentTable", _cacheDirectory));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPipeline_WithNullTableName_ThrowsArgumentException()
|
|
{
|
|
// Arrange
|
|
var factory = CreateFactoryFromConfig();
|
|
|
|
// Act & Assert
|
|
Should.Throw<ArgumentException>(() => factory.GetPipeline(null!, _cacheDirectory));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPipeline_WithEmptyCacheDirectory_ThrowsArgumentException()
|
|
{
|
|
// Arrange
|
|
var factory = CreateFactoryFromConfig();
|
|
|
|
// Act & Assert
|
|
Should.Throw<ArgumentException>(() => factory.GetPipeline("Branch", string.Empty));
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_WithNullConnectionFactory_ThrowsArgumentNullException()
|
|
{
|
|
// Arrange
|
|
var config = new DevPipelinesRoot(null, new Dictionary<string, DevPipelineConfig>());
|
|
|
|
// Act & Assert
|
|
Should.Throw<ArgumentNullException>(() => new DevEtlPipelineFactory(null!, config, _logger));
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_WithNullConfig_ThrowsArgumentNullException()
|
|
{
|
|
// Act & Assert
|
|
Should.Throw<ArgumentNullException>(() => new DevEtlPipelineFactory(_connectionFactory, (DevPipelinesRoot)null!, _logger));
|
|
}
|
|
|
|
private DevEtlPipelineFactory CreateFactoryFromConfig()
|
|
{
|
|
// Load actual config from JSON file
|
|
var settings = new DevPipelineSettings
|
|
{
|
|
SizeCategories = new SizeCategories
|
|
{
|
|
Small = ["Branch", "OrgHierarchy", "WorkCenter", "ProfitCenter"],
|
|
Medium = ["JdeUser", "FunctionCode", "Item", "RouteMaster"],
|
|
Large = ["Lot", "MisData_Curr", "MisData_Hist", "WorkOrder_Curr", "WorkOrder_Hist", "LotUsage_Hist", "WorkOrderComponent_Hist"],
|
|
VeryLarge = ["WorkOrderStep_Hist", "WorkOrderComponent_Curr", "WorkOrderRouting", "LotUsage_Curr", "WorkOrderStep_Curr", "WorkOrderTime_Hist", "WorkOrderTime_Curr"]
|
|
}
|
|
};
|
|
|
|
var pipelines = new Dictionary<string, DevPipelineConfig>
|
|
{
|
|
["Branch"] = new(new DevSourceConfig("branch.pb.zstd"), new DevDestinationConfig("Branch")),
|
|
["OrgHierarchy"] = new(new DevSourceConfig("orghierarchy.pb.zstd"), new DevDestinationConfig("OrgHierarchy")),
|
|
["WorkCenter"] = new(new DevSourceConfig("workcenter.pb.zstd"), new DevDestinationConfig("WorkCenter")),
|
|
["ProfitCenter"] = new(new DevSourceConfig("profitcenter.pb.zstd"), new DevDestinationConfig("ProfitCenter")),
|
|
["JdeUser"] = new(new DevSourceConfig("jdeuser.pb.zstd"), new DevDestinationConfig("JdeUser")),
|
|
["FunctionCode"] = new(new DevSourceConfig("functioncode.pb.zstd"), new DevDestinationConfig("FunctionCode")),
|
|
["Item"] = new(new DevSourceConfig("item.pb.zstd"), new DevDestinationConfig("Item")),
|
|
["RouteMaster"] = new(new DevSourceConfig("routemaster.pb.zstd"), new DevDestinationConfig("RouteMaster")),
|
|
["Lot"] = new(new DevSourceConfig("lot.pb.zstd"), new DevDestinationConfig("Lot")),
|
|
["MisData_Curr"] = new(new DevSourceConfig("misdata_curr.pb.zstd"), new DevDestinationConfig("MisData_Curr")),
|
|
["MisData_Hist"] = new(new DevSourceConfig("misdata_hist.pb.zstd"), new DevDestinationConfig("MisData_Hist")),
|
|
["WorkOrder_Curr"] = new(new DevSourceConfig("workorder_curr.pb.zstd"), new DevDestinationConfig("WorkOrder_Curr")),
|
|
["WorkOrder_Hist"] = new(new DevSourceConfig("workorder_hist.pb.zstd"), new DevDestinationConfig("WorkOrder_Hist")),
|
|
["LotUsage_Curr"] = new(new DevSourceConfig("lotusage_curr.pb.zstd"), new DevDestinationConfig("LotUsage_Curr")),
|
|
["LotUsage_Hist"] = new(new DevSourceConfig("lotusage_hist.pb.zstd"), new DevDestinationConfig("LotUsage_Hist")),
|
|
["WorkOrderComponent_Curr"] = new(new DevSourceConfig("workordercomponent_curr.pb.zstd"), new DevDestinationConfig("WorkOrderComponent_Curr")),
|
|
["WorkOrderComponent_Hist"] = new(new DevSourceConfig("workordercomponent_hist.pb.zstd"), new DevDestinationConfig("WorkOrderComponent_Hist")),
|
|
["WorkOrderStep_Curr"] = new(new DevSourceConfig("workorderstep_curr.pb.zstd"), new DevDestinationConfig("WorkOrderStep_Curr")),
|
|
["WorkOrderStep_Hist"] = new(new DevSourceConfig("workorderstep_hist.pb.zstd"), new DevDestinationConfig("WorkOrderStep_Hist")),
|
|
["WorkOrderTime_Curr"] = new(new DevSourceConfig("workordertime_curr.pb.zstd"), new DevDestinationConfig("WorkOrderTime_Curr")),
|
|
["WorkOrderTime_Hist"] = new(new DevSourceConfig("workordertime_hist.pb.zstd"), new DevDestinationConfig("WorkOrderTime_Hist")),
|
|
["WorkOrderRouting"] = new(new DevSourceConfig("workorderrouting.pb.zstd"), new DevDestinationConfig("WorkOrderRouting"))
|
|
};
|
|
|
|
var config = new DevPipelinesRoot(settings, pipelines);
|
|
return new DevEtlPipelineFactory(_connectionFactory, config, _logger);
|
|
}
|
|
}
|