test(datasync): add integration tests for BranchDevEtl

Add integration tests for the Branch development ETL pipeline:
- BranchDevEtlTests with tests for pipeline creation and execution
- Tests verify pipeline creates correctly and loads data from cache files
- Added appsettings.json with local connection string and cache directory
- Added necessary packages (Configuration.Json, SqlClient, Dapper)

Tests require local SQL Server and CACHED_DB_FILES directory with
branch.json.zstd to pass; tests silently skip if resources unavailable.
This commit is contained in:
Joseph Doherty
2026-01-03 16:38:11 -05:00
parent badc6a43f3
commit e04e81b178
3 changed files with 193 additions and 0 deletions
@@ -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;
/// <summary>
/// Integration tests for Branch development ETL.
/// Requires: Local SQL Server, CACHED_DB_FILES directory with branch.json.zstd
/// </summary>
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<DbConnectionFactory>.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<int>("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<ArgumentNullException>(() => BranchDevEtl.Create(null!, cacheFilePath));
}
[Fact]
public void Create_WithEmptyCacheFilePath_ThrowsArgumentException()
{
// Arrange
var mockFactory = Substitute.For<IDbConnectionFactory>();
// Act & Assert
Should.Throw<ArgumentException>(() => BranchDevEtl.Create(mockFactory, string.Empty));
}
[Fact]
public void Create_WithNonExistentCacheFile_ThrowsFileNotFoundException()
{
// Arrange
var mockFactory = Substitute.For<IDbConnectionFactory>();
var nonExistentPath = "/nonexistent/path/branch.json.zstd";
// Act & Assert
Should.Throw<FileNotFoundException>(() => BranchDevEtl.Create(mockFactory, nonExistentPath));
}
[Fact]
public void DevEtlRegistry_WithNonExistentCacheDirectory_ThrowsDirectoryNotFoundException()
{
// Arrange
var mockFactory = Substitute.For<IDbConnectionFactory>();
var nonExistentPath = "/nonexistent/cache/directory";
// Act & Assert
Should.Throw<DirectoryNotFoundException>(() => 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");
}
}
@@ -25,6 +25,10 @@
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.1" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.3" />
<PackageReference Include="Dapper" Version="2.1.66" />
</ItemGroup>
<ItemGroup>
@@ -36,4 +40,8 @@
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>
@@ -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"
}
}