feat(datasync): add generic DbQuerySource for JDE/CMS/LotFinder

Extend DbQuerySource to support multiple connection types:
- Add connectionType parameter ("jde", "cms", "lotfinder")
- Use appropriate IDbConnectionFactory method for each type
- Support Dictionary<string, object> parameters
- Use DbConnection/DbCommand for cross-database compatibility
This commit is contained in:
Joseph Doherty
2026-01-06 13:30:00 -05:00
parent 1f7fd9f0f2
commit eb85ab6f34
2 changed files with 102 additions and 38 deletions
@@ -1,44 +1,84 @@
using JdeScoping.DataAccess.Interfaces;
using JdeScoping.DataSync.Etl.Sources;
using NSubstitute;
using Shouldly;
namespace JdeScoping.DataSync.Tests.Etl.Sources;
public class DbQuerySourceTests
{
[Fact]
public void Constructor_SetsSourceName()
[Theory]
[InlineData("jde")]
[InlineData("cms")]
[InlineData("lotfinder")]
public void Constructor_ValidConnectionType_Succeeds(string connectionType)
{
var factory = Substitute.For<IDbConnectionFactory>();
var source = new DbQuerySource(factory, "SELECT 1", "TestSource");
Assert.Equal("DbQuery:TestSource", source.SourceName);
var source = new DbQuerySource(factory, connectionType, "SELECT 1");
source.SourceName.ShouldBe($"DbQuery:{connectionType}");
}
[Theory]
[InlineData("JDE")]
[InlineData("CMS")]
[InlineData("LotFinder")]
[InlineData("LOTFINDER")]
public void Constructor_ConnectionType_IsCaseInsensitive(string connectionType)
{
var factory = Substitute.For<IDbConnectionFactory>();
var source = new DbQuerySource(factory, connectionType, "SELECT 1");
source.SourceName.ShouldBe($"DbQuery:{connectionType.ToLowerInvariant()}");
}
[Fact]
public void Constructor_NullName_UsesDefault()
public void Constructor_InvalidConnectionType_Throws()
{
var factory = Substitute.For<IDbConnectionFactory>();
var source = new DbQuerySource(factory, "SELECT 1");
Assert.Equal("DbQuery:Query", source.SourceName);
Should.Throw<ArgumentException>(() =>
new DbQuerySource(factory, "invalid", "SELECT 1"));
}
[Fact]
public void Constructor_NullConnectionType_Throws()
{
var factory = Substitute.For<IDbConnectionFactory>();
Should.Throw<ArgumentNullException>(() =>
new DbQuerySource(factory, null!, "SELECT 1"));
}
[Fact]
public void Constructor_NullQuery_Throws()
{
var factory = Substitute.For<IDbConnectionFactory>();
Should.Throw<ArgumentNullException>(() =>
new DbQuerySource(factory, "jde", null!));
}
[Fact]
public void Constructor_NullFactory_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() => new DbQuerySource(null!, "SELECT 1"));
Assert.Throws<ArgumentNullException>(() => new DbQuerySource(null!, "jde", "SELECT 1"));
}
[Fact]
public void Constructor_NullSql_ThrowsArgumentNullException()
public void Constructor_WithParameters_Succeeds()
{
var factory = Substitute.For<IDbConnectionFactory>();
Assert.Throws<ArgumentNullException>(() => new DbQuerySource(factory, null!));
var parameters = new Dictionary<string, object>
{
{ "MinDate", DateTime.Now },
{ "Status", 1 }
};
var source = new DbQuerySource(factory, "lotfinder", "SELECT * FROM T WHERE Date > @MinDate AND Status = @Status", parameters);
source.SourceName.ShouldBe("DbQuery:lotfinder");
}
[Fact]
public void Constructor_EmptySql_ThrowsArgumentException()
public void Constructor_WithCustomTimeout_Succeeds()
{
var factory = Substitute.For<IDbConnectionFactory>();
Assert.Throws<ArgumentException>(() => new DbQuerySource(factory, ""));
var source = new DbQuerySource(factory, "jde", "SELECT 1", commandTimeout: 7200);
source.SourceName.ShouldBe("DbQuery:jde");
}
}