Files
jdescopingtool/NEW/tests/JdeScoping.DataSync.Dev.Tests/DevEtlPipelineFactoryTests.cs
T
Joseph Doherty e5fe2f06e9 feat: add startup config validation and document ConfigManager pipeline editor
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.
2026-01-21 17:47:15 -05:00

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);
}
}