bfc1c8064a
Eliminates placeholder substitution (${KEY}) in favor of storing complete
connection strings as single encrypted values. SecureStore now auto-creates
entries for all connection strings defined in appsettings. ConfigManager
editor reads/writes values directly to SecureStore.
223 lines
9.6 KiB
C#
223 lines
9.6 KiB
C#
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;
|
|
|
|
/// <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();
|
|
|
|
// Create a mock SecureStore that returns connection strings from configuration
|
|
var secureStore = Substitute.For<ISecureStoreService>();
|
|
|
|
// 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<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);
|
|
}
|
|
}
|