diff --git a/NEW/tests/JdeScoping.DataSync.Tests/DevEtl/BranchDevEtlTests.cs b/NEW/tests/JdeScoping.DataSync.Tests/DevEtl/BranchDevEtlTests.cs new file mode 100644 index 0000000..1e49f40 --- /dev/null +++ b/NEW/tests/JdeScoping.DataSync.Tests/DevEtl/BranchDevEtlTests.cs @@ -0,0 +1,177 @@ +using Dapper; +using JdeScoping.DataAccess; +using JdeScoping.DataAccess.Interfaces; +using JdeScoping.DataSync.DevEtl; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging.Abstractions; +using NSubstitute; +using Shouldly; + +namespace JdeScoping.DataSync.Tests.DevEtl; + +/// +/// Integration tests for Branch development ETL. +/// Requires: Local SQL Server, CACHED_DB_FILES directory with branch.json.zstd +/// +public class BranchDevEtlTests : IAsyncLifetime +{ + private readonly string _connectionString; + private readonly string _cacheDirectory; + private readonly IDbConnectionFactory _connectionFactory; + + public BranchDevEtlTests() + { + // Load configuration + var config = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: true) + .AddEnvironmentVariables() + .Build(); + + _connectionString = config.GetConnectionString("LotFinder") + ?? throw new InvalidOperationException("LotFinder connection string not configured."); + + _cacheDirectory = config["DevEtl:CacheDirectory"] + ?? "/Users/dohertj2/Desktop/JdeScopingTool/CACHED_DB_FILES"; + + _connectionFactory = new DbConnectionFactory(config, NullLogger.Instance); + } + + public async Task InitializeAsync() + { + // Ensure Branch table is empty before test + await using var connection = new SqlConnection(_connectionString); + await connection.OpenAsync(); + await connection.ExecuteAsync("TRUNCATE TABLE dbo.Branch"); + } + + public Task DisposeAsync() => Task.CompletedTask; + + [Fact] + public void Create_ReturnsValidPipeline() + { + // Arrange + var cacheFilePath = Path.Combine(_cacheDirectory, BranchDevEtl.CacheFileName); + if (!File.Exists(cacheFilePath)) + { + // Skip test if cache file doesn't exist + return; + } + + // Act + var pipeline = BranchDevEtl.Create(_connectionFactory, cacheFilePath); + + // Assert + pipeline.ShouldNotBeNull(); + pipeline.PipelineName.ShouldBe("Branch_Dev"); + } + + [Fact] + public async Task Execute_LoadsBranchData() + { + // Arrange + var cacheFilePath = Path.Combine(_cacheDirectory, BranchDevEtl.CacheFileName); + if (!File.Exists(cacheFilePath)) + { + // Skip test if cache file doesn't exist + return; + } + + var pipeline = BranchDevEtl.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.Branch"); + + count.ShouldBe((int)result.TotalRows, "Database row count should match pipeline result"); + } + + [Fact] + public async Task Registry_RunAsync_LoadsBranch() + { + // 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("Branch"); + + // 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, BranchDevEtl.CacheFileName); + + // Act & Assert + Should.Throw(() => BranchDevEtl.Create(null!, cacheFilePath)); + } + + [Fact] + public void Create_WithEmptyCacheFilePath_ThrowsArgumentException() + { + // Arrange + var mockFactory = Substitute.For(); + + // Act & Assert + Should.Throw(() => BranchDevEtl.Create(mockFactory, string.Empty)); + } + + [Fact] + public void Create_WithNonExistentCacheFile_ThrowsFileNotFoundException() + { + // Arrange + var mockFactory = Substitute.For(); + var nonExistentPath = "/nonexistent/path/branch.json.zstd"; + + // Act & Assert + Should.Throw(() => BranchDevEtl.Create(mockFactory, nonExistentPath)); + } + + [Fact] + public void DevEtlRegistry_WithNonExistentCacheDirectory_ThrowsDirectoryNotFoundException() + { + // Arrange + var mockFactory = Substitute.For(); + var nonExistentPath = "/nonexistent/cache/directory"; + + // Act & Assert + Should.Throw(() => new DevEtlRegistry(mockFactory, nonExistentPath)); + } + + [Fact] + public void DevEtlRegistry_GetAvailableTables_IncludesBranch() + { + // Arrange + if (!Directory.Exists(_cacheDirectory)) + { + return; + } + + var registry = new DevEtlRegistry(_connectionFactory, _cacheDirectory); + + // Act + var tables = registry.GetAvailableTables().ToList(); + + // Assert + tables.ShouldContain("Branch"); + } +} diff --git a/NEW/tests/JdeScoping.DataSync.Tests/JdeScoping.DataSync.Tests.csproj b/NEW/tests/JdeScoping.DataSync.Tests/JdeScoping.DataSync.Tests.csproj index 352d954..7dfd001 100644 --- a/NEW/tests/JdeScoping.DataSync.Tests/JdeScoping.DataSync.Tests.csproj +++ b/NEW/tests/JdeScoping.DataSync.Tests/JdeScoping.DataSync.Tests.csproj @@ -25,6 +25,10 @@ + + + + @@ -36,4 +40,8 @@ + + + + diff --git a/NEW/tests/JdeScoping.DataSync.Tests/appsettings.json b/NEW/tests/JdeScoping.DataSync.Tests/appsettings.json new file mode 100644 index 0000000..865fbd9 --- /dev/null +++ b/NEW/tests/JdeScoping.DataSync.Tests/appsettings.json @@ -0,0 +1,8 @@ +{ + "ConnectionStrings": { + "LotFinder": "Server=localhost,1434;Database=ScopingTool;User Id=scopingapp;Password=Sc0ping@pp_Dev#2024;TrustServerCertificate=true" + }, + "DevEtl": { + "CacheDirectory": "/Users/dohertj2/Desktop/JdeScopingTool/CACHED_DB_FILES" + } +}