Files
Joseph Doherty bfc1c8064a refactor(securestore): store entire connection strings in SecureStore
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.
2026-01-23 14:44:04 -05:00

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