using JdeScoping.DataAccess.Options; using JdeScoping.DataAccess.Exceptions; using JdeScoping.DataAccess.Interfaces; using JdeScoping.DataAccess.Repositories; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NSubstitute; using NSubstitute.ExceptionExtensions; using Shouldly; using Xunit; namespace JdeScoping.DataAccess.Tests; /// /// Unit tests for JdeRepository. /// public class JdeRepositoryTests { private readonly IDbConnectionFactory _connectionFactory; private readonly ILogger _logger; private readonly IOptions _options; public JdeRepositoryTests() { _connectionFactory = Substitute.For(); _logger = Substitute.For>(); _options = Microsoft.Extensions.Options.Options.Create(new DataAccessOptions { DefaultTimeoutSeconds = 30, LotUsageTimeoutSeconds = 60, ProductionSchema = "PRODDTA", ArchiveSchema = "ARCDTAPD", StageSchema = "JDESTAGE" }); } #region Constructor Tests [Fact] public void Constructor_NullConnectionFactory_ThrowsArgumentNullException() { // Act & Assert Should.Throw( () => new JdeRepository(null!, _logger, _options)) .ParamName.ShouldBe("connectionFactory"); } [Fact] public void Constructor_NullLogger_ThrowsArgumentNullException() { // Act & Assert Should.Throw( () => new JdeRepository(_connectionFactory, null!, _options)) .ParamName.ShouldBe("logger"); } [Fact] public void Constructor_NullOptions_ThrowsArgumentNullException() { // Act & Assert Should.Throw( () => new JdeRepository(_connectionFactory, _logger, null!)) .ParamName.ShouldBe("options"); } [Fact] public void Constructor_ValidParameters_CreatesInstance() { // Act var repository = new JdeRepository(_connectionFactory, _logger, _options); // Assert repository.ShouldNotBeNull(); } #endregion #region Schema Replacement Tests - Work Orders [Fact] public async Task GetWorkOrdersAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert var ex = await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkOrdersAsync()) { } }); ex.DataSource.ShouldBe("JDE"); } [Fact] public async Task GetWorkOrdersArchiveAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert var ex = await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkOrdersArchiveAsync()) { } }); ex.DataSource.ShouldBe("JDE"); } #endregion #region Schema Replacement Tests - Work Order Steps [Fact] public async Task GetWorkOrderStepsAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert var ex = await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkOrderStepsAsync()) { } }); ex.DataSource.ShouldBe("JDE"); } [Fact] public async Task GetWorkOrderStepsArchiveAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkOrderStepsArchiveAsync()) { } }); } #endregion #region Schema Replacement Tests - Work Order Times [Fact] public async Task GetWorkOrderTimesAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkOrderTimesAsync()) { } }); } [Fact] public async Task GetWorkOrderTimesArchiveAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkOrderTimesArchiveAsync()) { } }); } #endregion #region Schema Replacement Tests - Work Order Routings [Fact] public async Task GetWorkOrderRoutingsAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkOrderRoutingsAsync()) { } }); } #endregion #region Schema Replacement Tests - Work Order Components [Fact] public async Task GetWorkOrderComponentsAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkOrderComponentsAsync()) { } }); } [Fact] public async Task GetWorkOrderComponentsArchiveAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkOrderComponentsArchiveAsync()) { } }); } #endregion #region Schema Replacement Tests - Lots [Fact] public async Task GetLotsAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetLotsAsync()) { } }); } #endregion #region Schema Replacement Tests - Lot Usages [Fact] public async Task GetLotUsagesAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetLotUsagesAsync()) { } }); } [Fact] public async Task GetLotUsagesArchiveAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetLotUsagesArchiveAsync()) { } }); } #endregion #region JDE Stage Connection Tests - Lot Locations [Fact] public async Task GetLotLocationsAsync_UsesJdeStageConnection() { // Arrange _connectionFactory.CreateJdeStageConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDEStage")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert - verify it uses Stage connection var ex = await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetLotLocationsAsync()) { } }); ex.DataSource.ShouldBe("JDEStage"); } #endregion #region Reference Data Tests [Fact] public async Task GetItemsAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetItemsAsync()) { } }); } [Fact] public async Task GetUsersAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetUsersAsync()) { } }); } [Fact] public async Task GetBranchesAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetBranchesAsync()) { } }); } [Fact] public async Task GetProfitCentersAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetProfitCentersAsync()) { } }); } [Fact] public async Task GetWorkCentersAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkCentersAsync()) { } }); } [Fact] public async Task GetStatusCodesAsync_UsesJdeStageConnection() { // Arrange _connectionFactory.CreateJdeStageConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDEStage")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert - verify it uses Stage connection var ex = await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetStatusCodesAsync()) { } }); ex.DataSource.ShouldBe("JDEStage"); } [Fact] public async Task GetFunctionCodesAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetFunctionCodesAsync()) { } }); } [Fact] public async Task GetOrgHierarchyAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetOrgHierarchyAsync()) { } }); } [Fact] public async Task GetRouteMastersAsync_ConnectionFails_ThrowsConnectionException() { // Arrange _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetRouteMastersAsync()) { } }); } #endregion #region Cancellation Tests [Fact] public async Task GetWorkOrdersAsync_CancellationRequested_ThrowsOperationCanceledException() { // Arrange using var cts = new CancellationTokenSource(); cts.Cancel(); _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new OperationCanceledException(cts.Token)); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkOrdersAsync(ct: cts.Token)) { } }); } [Fact] public async Task GetLotUsagesAsync_CancellationRequested_ThrowsOperationCanceledException() { // Arrange using var cts = new CancellationTokenSource(); cts.Cancel(); _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new OperationCanceledException(cts.Token)); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetLotUsagesAsync(ct: cts.Token)) { } }); } #endregion #region Incremental Sync Tests [Fact] public async Task GetWorkOrdersAsync_WithLastUpdateDT_UsesFilteredQuery() { // Arrange var lastUpdate = new DateTime(2024, 1, 15, 10, 30, 0); _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert - this just verifies the method accepts the parameter await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetWorkOrdersAsync(lastUpdate)) { } }); } [Fact] public async Task GetLotsAsync_WithLastUpdateDT_UsesFilteredQuery() { // Arrange var lastUpdate = new DateTime(2024, 1, 15, 10, 30, 0); _connectionFactory.CreateJdeConnectionAsync(Arg.Any()) .ThrowsAsync(new ConnectionException("Test connection error", "JDE")); var repository = new JdeRepository(_connectionFactory, _logger, _options); // Act & Assert await Should.ThrowAsync( async () => { await foreach (var _ in repository.GetLotsAsync(lastUpdate)) { } }); } #endregion #region Options Configuration Tests [Fact] public void Constructor_UsesConfiguredSchemas() { // Arrange var customOptions = Microsoft.Extensions.Options.Options.Create(new DataAccessOptions { ProductionSchema = "CUSTOM_PROD", ArchiveSchema = "CUSTOM_ARC", StageSchema = "CUSTOM_STG" }); // Act var repository = new JdeRepository(_connectionFactory, _logger, customOptions); // Assert repository.ShouldNotBeNull(); // The schema values are internal, verified through integration tests } [Fact] public void Constructor_UsesConfiguredTimeouts() { // Arrange var customOptions = Microsoft.Extensions.Options.Options.Create(new DataAccessOptions { DefaultTimeoutSeconds = 120, LotUsageTimeoutSeconds = 999999 }); // Act var repository = new JdeRepository(_connectionFactory, _logger, customOptions); // Assert repository.ShouldNotBeNull(); // The timeout values are internal, verified through integration tests } #endregion }