refactor: relocate options classes to dedicated Options folders

Move configuration options from Core/DataAccess/DataSync/ExcelIO to
dedicated Options folders within each project for better organization.
Update all references and tests accordingly.
This commit is contained in:
Joseph Doherty
2026-01-03 08:55:08 -05:00
parent 3cb73eb09f
commit ec4c8fab87
52 changed files with 4628 additions and 202 deletions
@@ -1,4 +1,4 @@
using JdeScoping.DataAccess.Configuration;
using JdeScoping.DataAccess.Options;
using JdeScoping.DataAccess.Exceptions;
using JdeScoping.DataAccess.Interfaces;
using JdeScoping.DataAccess.Repositories;
@@ -24,7 +24,7 @@ public class CmsRepositoryTests
{
_connectionFactory = Substitute.For<IDbConnectionFactory>();
_logger = Substitute.For<ILogger<CmsRepository>>();
_options = Options.Create(new DataAccessOptions
_options = Microsoft.Extensions.Options.Options.Create(new DataAccessOptions
{
DefaultTimeoutSeconds = 30,
MisDataTimeoutSeconds = 60000
@@ -197,7 +197,7 @@ public class CmsRepositoryTests
public void Constructor_UsesMisDataTimeout()
{
// Arrange
var customOptions = Options.Create(new DataAccessOptions
var customOptions = Microsoft.Extensions.Options.Options.Create(new DataAccessOptions
{
MisDataTimeoutSeconds = 999999
});
@@ -214,7 +214,7 @@ public class CmsRepositoryTests
public void Constructor_DefaultMisDataTimeout_Is60000Seconds()
{
// Arrange
var defaultOptions = Options.Create(new DataAccessOptions());
var defaultOptions = Microsoft.Extensions.Options.Options.Create(new DataAccessOptions());
// Act
var repository = new CmsRepository(_connectionFactory, _logger, defaultOptions);
@@ -1,4 +1,4 @@
using JdeScoping.DataAccess.Configuration;
using JdeScoping.DataAccess.Options;
using JdeScoping.DataAccess.Exceptions;
using JdeScoping.DataAccess.Interfaces;
using JdeScoping.DataAccess.Repositories;
@@ -24,7 +24,7 @@ public class JdeRepositoryTests
{
_connectionFactory = Substitute.For<IDbConnectionFactory>();
_logger = Substitute.For<ILogger<JdeRepository>>();
_options = Options.Create(new DataAccessOptions
_options = Microsoft.Extensions.Options.Options.Create(new DataAccessOptions
{
DefaultTimeoutSeconds = 30,
LotUsageTimeoutSeconds = 60,
@@ -637,7 +637,7 @@ public class JdeRepositoryTests
public void Constructor_UsesConfiguredSchemas()
{
// Arrange
var customOptions = Options.Create(new DataAccessOptions
var customOptions = Microsoft.Extensions.Options.Options.Create(new DataAccessOptions
{
ProductionSchema = "CUSTOM_PROD",
ArchiveSchema = "CUSTOM_ARC",
@@ -656,7 +656,7 @@ public class JdeRepositoryTests
public void Constructor_UsesConfiguredTimeouts()
{
// Arrange
var customOptions = Options.Create(new DataAccessOptions
var customOptions = Microsoft.Extensions.Options.Options.Create(new DataAccessOptions
{
DefaultTimeoutSeconds = 120,
LotUsageTimeoutSeconds = 999999
@@ -3,7 +3,7 @@ using JdeScoping.Core.Models.Enums;
using JdeScoping.Core.Models.Inventory;
using JdeScoping.Core.Models.Search;
using JdeScoping.Core.ViewModels;
using JdeScoping.DataAccess.Configuration;
using JdeScoping.DataAccess.Options;
using JdeScoping.DataAccess.Exceptions;
using JdeScoping.DataAccess.Interfaces;
using JdeScoping.DataAccess.Repositories;
@@ -29,7 +29,7 @@ public class LotFinderRepositoryTests
{
_connectionFactory = Substitute.For<IDbConnectionFactory>();
_logger = Substitute.For<ILogger<LotFinderRepository>>();
_options = Options.Create(new DataAccessOptions
_options = Microsoft.Extensions.Options.Options.Create(new DataAccessOptions
{
DefaultTimeoutSeconds = 30,
RebuildIndexTimeoutSeconds = 60
@@ -4,7 +4,7 @@ using System.Runtime.CompilerServices;
using JdeScoping.Core.Interfaces;
using JdeScoping.Core.Models.Enums;
using JdeScoping.DataAccess.Interfaces;
using JdeScoping.DataSync.Configuration;
using JdeScoping.DataSync.Options;
using JdeScoping.DataSync.Contracts;
using JdeScoping.DataSync.IntegrationTests.Infrastructure;
using JdeScoping.DataSync.Models;
@@ -316,7 +316,7 @@ public class TableSyncOperationTests : IAsyncLifetime
return new MassInsertResult(records.Count, TimeSpan.Zero, true);
});
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
BatchSize = 1000,
BulkCopyBatchSize = 100
@@ -1,5 +1,5 @@
using System.Diagnostics.Metrics;
using JdeScoping.DataSync.Configuration;
using JdeScoping.DataSync.Options;
using JdeScoping.DataSync.Contracts;
using JdeScoping.DataSync.Telemetry;
using Microsoft.Extensions.DependencyInjection;
@@ -22,7 +22,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_WhenDisabled_ExitsImmediately()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = false
});
@@ -56,7 +56,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_WhenEnabled_StartsAndCanBeStopped()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromMilliseconds(100)
@@ -106,7 +106,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_GracefulShutdown_CompletesCleanly()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromSeconds(10) // Long interval
@@ -152,7 +152,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_AtStartup_CallsCloseOpenUpdateEntries()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromMilliseconds(50)
@@ -199,7 +199,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_WhenCloseOpenEntriesFindsEntries_LogsAndContinues()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromMilliseconds(50)
@@ -248,7 +248,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_WhenCloseOpenEntriesThrows_ContinuesStarting()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromMilliseconds(50)
@@ -301,7 +301,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_CallsOrchestratorForParallelExecution()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromMilliseconds(50),
@@ -349,7 +349,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_WhenOrchestratorThrows_ContinuesNextCycle()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromMilliseconds(50)
@@ -404,7 +404,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_WhenCancelled_StopsGracefully()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromSeconds(10)
@@ -462,7 +462,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_PassesCancellationTokenToOrchestrator()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromMilliseconds(50)
@@ -510,7 +510,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_WhenCancelledDuringDelay_ExitsCleanly()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromMinutes(5) // Long delay
@@ -562,7 +562,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_UsesNewScopePerCycle()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromMilliseconds(50)
@@ -610,7 +610,7 @@ public class DataSyncServiceTests
public async Task ExecuteAsync_WhenSyncFails_ContinuesRunning()
{
// Arrange
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
Enabled = true,
CheckInterval = TimeSpan.FromMilliseconds(50)
@@ -1,7 +1,7 @@
using JdeScoping.Core.Models;
using JdeScoping.Core.Models.Enums;
using JdeScoping.Core.Models.Infrastructure;
using JdeScoping.DataSync.Configuration;
using JdeScoping.DataSync.Options;
using JdeScoping.DataSync.Contracts;
using JdeScoping.DataSync.Services;
using Microsoft.Extensions.Logging.Abstractions;
@@ -23,7 +23,7 @@ public class ScheduleCheckerTests
public ScheduleCheckerTests()
{
_repository = Substitute.For<IDataUpdateRepository>();
_options = Options.Create(new DataSyncOptions
_options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
LookbackMultiplier = 3,
DataSources = []
@@ -1,6 +1,6 @@
using System.Diagnostics.Metrics;
using JdeScoping.Core.Models.Enums;
using JdeScoping.DataSync.Configuration;
using JdeScoping.DataSync.Options;
using JdeScoping.DataSync.Contracts;
using JdeScoping.DataSync.Models;
using JdeScoping.DataSync.Services;
@@ -27,7 +27,7 @@ public class SyncOrchestratorTests
public SyncOrchestratorTests()
{
_scheduleChecker = Substitute.For<IScheduleChecker>();
_options = Options.Create(new DataSyncOptions
_options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
MaxDegreeOfParallelism = 4
});
@@ -176,7 +176,7 @@ public class SyncOrchestratorTests
public async Task ExecutePendingSyncsAsync_RespectsMaxDegreeOfParallelism()
{
// Arrange: Create 10 tasks but limit parallelism to 2
var options = Options.Create(new DataSyncOptions
var options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
MaxDegreeOfParallelism = 2
});
@@ -5,7 +5,7 @@ using JdeScoping.Core.Interfaces;
using JdeScoping.Core.Models;
using JdeScoping.Core.Models.Enums;
using JdeScoping.DataAccess.Interfaces;
using JdeScoping.DataSync.Configuration;
using JdeScoping.DataSync.Options;
using JdeScoping.DataSync.Contracts;
using JdeScoping.DataSync.Models;
using JdeScoping.DataSync.Services;
@@ -40,7 +40,7 @@ public class TableSyncOperationTests
_bulkMergeHelper = Substitute.For<IBulkMergeHelper>();
_configRegistry = Substitute.For<IMergeConfigurationRegistry>();
_options = Options.Create(new DataSyncOptions
_options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
BatchSize = 1000,
BulkCopyBatchSize = 100
@@ -1,5 +1,5 @@
using ClosedXML.Excel;
using JdeScoping.ExcelIO.Configuration;
using JdeScoping.ExcelIO.Options;
using JdeScoping.ExcelIO.Generators;
using JdeScoping.ExcelIO.Helpers;
using JdeScoping.ExcelIO.Models.Reporting;
@@ -16,7 +16,7 @@ public class CriteriaSheetGeneratorTests
public CriteriaSheetGeneratorTests()
{
_options = Options.Create(new ExcelExportOptions
_options = Microsoft.Extensions.Options.Options.Create(new ExcelExportOptions
{
CriteriaSheetPassword = "TestPassword"
});
@@ -1,5 +1,5 @@
using ClosedXML.Excel;
using JdeScoping.ExcelIO.Configuration;
using JdeScoping.ExcelIO.Options;
using JdeScoping.ExcelIO.Generators;
using JdeScoping.ExcelIO.Helpers;
using JdeScoping.ExcelIO.Models.Reporting;
@@ -23,7 +23,7 @@ public class ExcelExportIntegrationTests
public ExcelExportIntegrationTests()
{
_logger = Substitute.For<ILogger<ExcelExportService>>();
_options = Options.Create(new ExcelExportOptions
_options = Microsoft.Extensions.Options.Options.Create(new ExcelExportOptions
{
CriteriaSheetPassword = "TestCriteriaPass",
DataSheetPassword = "TestDataPass"
@@ -1,5 +1,5 @@
using ClosedXML.Excel;
using JdeScoping.ExcelIO.Configuration;
using JdeScoping.ExcelIO.Options;
using JdeScoping.ExcelIO.Generators;
using JdeScoping.ExcelIO.Helpers;
using JdeScoping.ExcelIO.Models.Reporting;
@@ -20,7 +20,7 @@ public class ExcelExportServiceTests
public ExcelExportServiceTests()
{
_logger = Substitute.For<ILogger<ExcelExportService>>();
_options = Options.Create(new ExcelExportOptions
_options = Microsoft.Extensions.Options.Options.Create(new ExcelExportOptions
{
CriteriaSheetPassword = "TestCriteriaPass",
DataSheetPassword = "TestDataPass"
@@ -1,5 +1,5 @@
using ClosedXML.Excel;
using JdeScoping.ExcelIO.Configuration;
using JdeScoping.ExcelIO.Options;
using JdeScoping.ExcelIO.Formatting;
using JdeScoping.ExcelIO.Generators;
using JdeScoping.ExcelIO.Helpers;
@@ -24,7 +24,7 @@ public class LegacyComparisonTests
public LegacyComparisonTests()
{
var logger = Substitute.For<ILogger<ExcelExportService>>();
var options = Options.Create(new ExcelExportOptions
var options = Microsoft.Extensions.Options.Options.Create(new ExcelExportOptions
{
CriteriaSheetPassword = "JDE_SCOPING_TOOL_PASS",
DataSheetPassword = "JDESCOPINGTOOL"
@@ -1,4 +1,4 @@
using JdeScoping.Core.Options;
using JdeScoping.Infrastructure.Options;
using JdeScoping.Infrastructure.Auth;
using JdeScoping.Infrastructure.Tests.Helpers;
using Microsoft.Extensions.Logging;
@@ -52,19 +52,16 @@ public class LdapIntegrationTests
{
// Arrange
var testUser = MockLdapServer.ValidGroupMemberUser;
var ldapOptions = Options.Create(new LdapOptions
var ldapOptions = Microsoft.Extensions.Options.Options.Create(new LdapOptions
{
ServerUrls = MockLdapServer.FakeServerUrls,
GroupDn = MockLdapServer.TestGroupDn,
SearchBase = MockLdapServer.TestSearchBase,
ConnectionTimeoutSeconds = 1 // Fast timeout for test
});
var authOptions = Options.Create(new AuthOptions
{
ConnectionTimeoutSeconds = 1, // Fast timeout for test
AdminBypassUsers = []
});
var service = new LdapAuthService(ldapOptions, authOptions, _logger);
var service = new LdapAuthService(ldapOptions, _logger);
// Act
var result = await service.AuthenticateAsync(testUser.Username, testUser.Password);
@@ -105,19 +102,16 @@ public class LdapIntegrationTests
{
// Arrange
var testUser = MockLdapServer.ValidNotInGroupUser;
var ldapOptions = Options.Create(new LdapOptions
var ldapOptions = Microsoft.Extensions.Options.Options.Create(new LdapOptions
{
ServerUrls = MockLdapServer.FakeServerUrls,
GroupDn = MockLdapServer.TestGroupDn,
SearchBase = MockLdapServer.TestSearchBase,
ConnectionTimeoutSeconds = 1
});
var authOptions = Options.Create(new AuthOptions
{
ConnectionTimeoutSeconds = 1,
AdminBypassUsers = []
});
var service = new LdapAuthService(ldapOptions, authOptions, _logger);
var service = new LdapAuthService(ldapOptions, _logger);
// Act
var result = await service.AuthenticateAsync(testUser.Username, testUser.Password);
@@ -152,19 +146,16 @@ public class LdapIntegrationTests
{
// Arrange
var testUser = MockLdapServer.InvalidCredentialsUser;
var ldapOptions = Options.Create(new LdapOptions
var ldapOptions = Microsoft.Extensions.Options.Options.Create(new LdapOptions
{
ServerUrls = MockLdapServer.FakeServerUrls,
GroupDn = MockLdapServer.TestGroupDn,
SearchBase = MockLdapServer.TestSearchBase,
ConnectionTimeoutSeconds = 1
});
var authOptions = Options.Create(new AuthOptions
{
ConnectionTimeoutSeconds = 1,
AdminBypassUsers = []
});
var service = new LdapAuthService(ldapOptions, authOptions, _logger);
var service = new LdapAuthService(ldapOptions, _logger);
// Act
var result = await service.AuthenticateAsync(testUser.Username, testUser.Password);
@@ -205,19 +196,16 @@ public class LdapIntegrationTests
public async Task AuthenticateAsync_AllServersFail_ReturnsConnectionError()
{
// Arrange
var ldapOptions = Options.Create(new LdapOptions
var ldapOptions = Microsoft.Extensions.Options.Options.Create(new LdapOptions
{
ServerUrls = MockLdapServer.FakeServerUrls, // 3 fake servers that will all fail
GroupDn = MockLdapServer.TestGroupDn,
SearchBase = MockLdapServer.TestSearchBase,
ConnectionTimeoutSeconds = 1 // Fast timeout for test
});
var authOptions = Options.Create(new AuthOptions
{
ConnectionTimeoutSeconds = 1, // Fast timeout for test
AdminBypassUsers = []
});
var service = new LdapAuthService(ldapOptions, authOptions, _logger);
var service = new LdapAuthService(ldapOptions, _logger);
// Act
var result = await service.AuthenticateAsync("anyuser", "anypassword");
@@ -1,5 +1,5 @@
using JdeScoping.Core.Options;
using JdeScoping.Infrastructure.Auth;
using JdeScoping.Infrastructure.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NSubstitute;
@@ -10,20 +10,16 @@ namespace JdeScoping.Infrastructure.Tests.Unit;
public class LdapAuthServiceTests
{
private readonly IOptions<LdapOptions> _ldapOptions;
private readonly IOptions<AuthOptions> _authOptions;
private readonly ILogger<LdapAuthService> _logger;
public LdapAuthServiceTests()
{
_ldapOptions = Options.Create(new LdapOptions
_ldapOptions = Microsoft.Extensions.Options.Options.Create(new LdapOptions
{
ServerUrls = ["ldap.test.com"],
GroupDn = "CN=TestGroup,DC=test,DC=com",
SearchBase = "DC=test,DC=com",
ConnectionTimeoutSeconds = 5
});
_authOptions = Options.Create(new AuthOptions
{
ConnectionTimeoutSeconds = 5,
AdminBypassUsers = []
});
_logger = Substitute.For<ILogger<LdapAuthService>>();
@@ -33,7 +29,7 @@ public class LdapAuthServiceTests
public async Task AuthenticateAsync_EmptyUsername_ReturnsFailure()
{
// Arrange
var service = new LdapAuthService(_ldapOptions, _authOptions, _logger);
var service = new LdapAuthService(_ldapOptions, _logger);
// Act
var result = await service.AuthenticateAsync("", "password");
@@ -47,7 +43,7 @@ public class LdapAuthServiceTests
public async Task AuthenticateAsync_EmptyPassword_ReturnsFailure()
{
// Arrange
var service = new LdapAuthService(_ldapOptions, _authOptions, _logger);
var service = new LdapAuthService(_ldapOptions, _logger);
// Act
var result = await service.AuthenticateAsync("user", "");
@@ -61,13 +57,14 @@ public class LdapAuthServiceTests
public async Task AuthenticateAsync_NoServersConfigured_ReturnsConnectionError()
{
// Arrange
var emptyServerOptions = Options.Create(new LdapOptions
var emptyServerOptions = Microsoft.Extensions.Options.Options.Create(new LdapOptions
{
ServerUrls = [],
GroupDn = "CN=TestGroup,DC=test,DC=com",
SearchBase = "DC=test,DC=com"
SearchBase = "DC=test,DC=com",
AdminBypassUsers = []
});
var service = new LdapAuthService(emptyServerOptions, _authOptions, _logger);
var service = new LdapAuthService(emptyServerOptions, _logger);
// Act
var result = await service.AuthenticateAsync("user", "password");
@@ -81,7 +78,7 @@ public class LdapAuthServiceTests
public void GetUserInfoAsync_ThrowsNotSupportedException()
{
// Arrange
var service = new LdapAuthService(_ldapOptions, _authOptions, _logger);
var service = new LdapAuthService(_ldapOptions, _logger);
// Act & Assert
Should.Throw<NotSupportedException>(() => service.GetUserInfoAsync("user").GetAwaiter().GetResult());
@@ -93,11 +90,15 @@ public class LdapAuthServiceTests
// Arrange
// Note: We can't fully test admin bypass without a real LDAP server since bind still happens.
// This test verifies the configuration is recognized by checking that bypass users are configured.
var authOptionsWithBypass = Options.Create(new AuthOptions
var ldapOptionsWithBypass = Microsoft.Extensions.Options.Options.Create(new LdapOptions
{
ServerUrls = ["ldap.test.com"],
GroupDn = "CN=TestGroup,DC=test,DC=com",
SearchBase = "DC=test,DC=com",
ConnectionTimeoutSeconds = 5,
AdminBypassUsers = ["bypassuser", "adminuser"]
});
var service = new LdapAuthService(_ldapOptions, authOptionsWithBypass, _logger);
var service = new LdapAuthService(ldapOptionsWithBypass, _logger);
// Act - attempt to authenticate the bypass user (will fail LDAP connection, but config is exercised)
var result = await service.AuthenticateAsync("bypassuser", "anypassword");
@@ -113,14 +114,15 @@ public class LdapAuthServiceTests
public async Task AuthenticateAsync_MultipleServersConfigured_TriesEachUntilAllFail()
{
// Arrange
var multiServerOptions = Options.Create(new LdapOptions
var multiServerOptions = Microsoft.Extensions.Options.Options.Create(new LdapOptions
{
ServerUrls = ["ldap1.test.com", "ldap2.test.com", "ldap3.test.com"],
GroupDn = "CN=TestGroup,DC=test,DC=com",
SearchBase = "DC=test,DC=com",
ConnectionTimeoutSeconds = 1 // Fast timeout for test
ConnectionTimeoutSeconds = 1, // Fast timeout for test
AdminBypassUsers = []
});
var service = new LdapAuthService(multiServerOptions, _authOptions, _logger);
var service = new LdapAuthService(multiServerOptions, _logger);
// Act
var result = await service.AuthenticateAsync("testuser", "testpassword");