using Dapper; using JdeScoping.DataAccess; using JdeScoping.DataAccess.Interfaces; using JdeScoping.DataSync.Dev; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Abstractions; using NSubstitute; using Shouldly; namespace JdeScoping.DataSync.Dev.Tests; /// /// Integration tests for OrgHierarchy development ETL. /// Requires: Local SQL Server, CACHED_DB_FILES directory with orghierarchy.json.zstd /// public class OrgHierarchyDevEtlTests : IAsyncLifetime { private readonly string _connectionString; private readonly string _cacheDirectory; private readonly IDbConnectionFactory _connectionFactory; public OrgHierarchyDevEtlTests() { // Load configuration var config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true) .AddEnvironmentVariables() .Build(); _connectionString = config.GetConnectionString("LotFinderDB") ?? throw new InvalidOperationException("LotFinderDB connection string not configured."); _cacheDirectory = config["DevEtl:CacheDirectory"] ?? Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "..", "CACHED_DB_FILES"); _connectionFactory = new DbConnectionFactory(config, NullLogger.Instance); } public async Task InitializeAsync() { // Ensure OrgHierarchy table is empty before test await using var connection = new SqlConnection(_connectionString); await connection.OpenAsync(); await connection.ExecuteAsync("TRUNCATE TABLE dbo.OrgHierarchy"); } public Task DisposeAsync() => Task.CompletedTask; [Fact] public void Create_ReturnsValidPipeline() { // Arrange var cacheFilePath = Path.Combine(_cacheDirectory, OrgHierarchyDevEtl.CacheFileName); if (!File.Exists(cacheFilePath)) { // Skip test if cache file doesn't exist return; } // Act var pipeline = OrgHierarchyDevEtl.Create(_connectionFactory, cacheFilePath); // Assert pipeline.ShouldNotBeNull(); pipeline.PipelineName.ShouldBe("OrgHierarchy_Dev"); } [Fact] public async Task Execute_LoadsOrgHierarchyData() { // Arrange var cacheFilePath = Path.Combine(_cacheDirectory, OrgHierarchyDevEtl.CacheFileName); if (!File.Exists(cacheFilePath)) { // Skip test if cache file doesn't exist return; } var pipeline = OrgHierarchyDevEtl.Create(_connectionFactory, cacheFilePath); // Act var result = await pipeline.ExecuteAsync(); // Assert result.Success.ShouldBeTrue(result.Error?.Message ?? "Pipeline should succeed"); result.TotalRows.ShouldBeGreaterThan(0, "Should load at least one row"); // Verify data in database await using var connection = new SqlConnection(_connectionString); await connection.OpenAsync(); var count = await connection.ExecuteScalarAsync("SELECT COUNT(*) FROM dbo.OrgHierarchy"); count.ShouldBe((int)result.TotalRows, "Database row count should match pipeline result"); } [Fact] public async Task Registry_RunAsync_LoadsOrgHierarchy() { // Arrange if (!Directory.Exists(_cacheDirectory)) { // Skip test if cache directory doesn't exist return; } var registry = new DevEtlRegistry(_connectionFactory, _cacheDirectory); // Act var result = await registry.RunAsync("OrgHierarchy"); // Assert result.Success.ShouldBeTrue(result.Error?.Message ?? "Pipeline should succeed"); result.TotalRows.ShouldBeGreaterThan(0); } [Fact] public void Create_WithNullConnectionFactory_ThrowsArgumentNullException() { // Arrange var cacheFilePath = Path.Combine(_cacheDirectory, OrgHierarchyDevEtl.CacheFileName); // Act & Assert Should.Throw(() => OrgHierarchyDevEtl.Create(null!, cacheFilePath)); } [Fact] public void Create_WithEmptyCacheFilePath_ThrowsArgumentException() { // Arrange var mockFactory = Substitute.For(); // Act & Assert Should.Throw(() => OrgHierarchyDevEtl.Create(mockFactory, string.Empty)); } [Fact] public void Create_WithNonExistentCacheFile_ThrowsFileNotFoundException() { // Arrange var mockFactory = Substitute.For(); var nonExistentPath = "/nonexistent/path/orghierarchy.json.zstd"; // Act & Assert Should.Throw(() => OrgHierarchyDevEtl.Create(mockFactory, nonExistentPath)); } [Fact] public void DevEtlRegistry_GetAvailableTables_IncludesOrgHierarchy() { // Arrange if (!Directory.Exists(_cacheDirectory)) { return; } var registry = new DevEtlRegistry(_connectionFactory, _cacheDirectory); // Act var tables = registry.GetAvailableTables().ToList(); // Assert tables.ShouldContain("OrgHierarchy"); } }