using Dapper; using JdeScoping.Core.Interfaces; 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; /// /// Tests for DevEtlPipelineFactory. /// public class DevEtlPipelineFactoryTests { private readonly IDbConnectionFactory _connectionFactory; private readonly ILogger _logger; private readonly string _cacheDirectory; public DevEtlPipelineFactoryTests() { var config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true) .AddEnvironmentVariables() .Build(); // Create a mock SecureStore that returns connection strings from configuration var secureStore = Substitute.For(); // Setup the mock to return connection strings from config secureStore.Get("LotFinder").Returns(config.GetConnectionString("LotFinder")); secureStore.Get("JDE").Returns(config.GetConnectionString("JDE")); secureStore.Get("JDEStage").Returns(config.GetConnectionString("JDEStage")); secureStore.Get("CMS").Returns(config.GetConnectionString("CMS")); secureStore.Get("GIW").Returns(config.GetConnectionString("GIW")); _connectionFactory = new DbConnectionFactory(secureStore, NullLogger.Instance); _logger = NullLogger.Instance; _cacheDirectory = config["DevEtl:CacheDirectory"] ?? Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "..", "CACHED_DB_FILES"); } [Fact] public void Constructor_WithValidConfig_LoadsPipelines() { // Arrange var pipelines = new Dictionary { ["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(() => factory.GetPipeline("NonExistentTable", _cacheDirectory)); } [Fact] public void GetPipeline_WithNullTableName_ThrowsArgumentException() { // Arrange var factory = CreateFactoryFromConfig(); // Act & Assert Should.Throw(() => factory.GetPipeline(null!, _cacheDirectory)); } [Fact] public void GetPipeline_WithEmptyCacheDirectory_ThrowsArgumentException() { // Arrange var factory = CreateFactoryFromConfig(); // Act & Assert Should.Throw(() => factory.GetPipeline("Branch", string.Empty)); } [Fact] public void Constructor_WithNullConnectionFactory_ThrowsArgumentNullException() { // Arrange var config = new DevPipelinesRoot(null, new Dictionary()); // Act & Assert Should.Throw(() => new DevEtlPipelineFactory(null!, config, _logger)); } [Fact] public void Constructor_WithNullConfig_ThrowsArgumentNullException() { // Act & Assert Should.Throw(() => 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 { ["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); } }