using JdeScoping.Core.Interfaces; using JdeScoping.DataAccess.Exceptions; using Microsoft.Extensions.Logging; using NSubstitute; using Shouldly; using Xunit; namespace JdeScoping.DataAccess.Tests; /// /// Unit tests for DbConnectionFactory. /// Connection strings are retrieved from ISecureStoreService. /// public class DbConnectionFactoryTests { private readonly ISecureStoreService _secureStore; private readonly ILogger _logger; public DbConnectionFactoryTests() { _secureStore = Substitute.For(); _logger = Substitute.For>(); } #region Constructor Tests [Fact] public void Constructor_NullSecureStore_ThrowsArgumentNullException() { // Act & Assert Should.Throw(() => new DbConnectionFactory(null!, _logger)) .ParamName.ShouldBe("secureStore"); } [Fact] public void Constructor_NullLogger_ThrowsArgumentNullException() { // Act & Assert Should.Throw(() => new DbConnectionFactory(_secureStore, null!)) .ParamName.ShouldBe("logger"); } [Fact] public void Constructor_ValidParameters_CreatesInstance() { // Act var factory = new DbConnectionFactory(_secureStore, _logger); // Assert factory.ShouldNotBeNull(); } #endregion #region CreateLotFinderConnectionAsync Tests [Fact] public async Task CreateLotFinderConnectionAsync_MissingConnectionString_ThrowsConnectionException() { // Arrange _secureStore.Get("LotFinder").Returns((string?)null); var factory = new DbConnectionFactory(_secureStore, _logger); // Act & Assert var ex = await Should.ThrowAsync( async () => await factory.CreateLotFinderConnectionAsync()); ex.DataSource.ShouldBe("LotFinder"); ex.Message.ShouldContain("Connection string not found"); } [Fact] public async Task CreateLotFinderConnectionAsync_EmptyConnectionString_ThrowsConnectionException() { // Arrange _secureStore.Get("LotFinder").Returns(string.Empty); var factory = new DbConnectionFactory(_secureStore, _logger); // Act & Assert var ex = await Should.ThrowAsync( async () => await factory.CreateLotFinderConnectionAsync()); ex.DataSource.ShouldBe("LotFinder"); ex.Message.ShouldContain("Connection string not found"); } [Fact] public async Task CreateLotFinderConnectionAsync_InvalidConnectionString_ThrowsConnectionException() { // Arrange _secureStore.Get("LotFinder").Returns("Invalid connection string"); var factory = new DbConnectionFactory(_secureStore, _logger); // Act & Assert var ex = await Should.ThrowAsync( async () => await factory.CreateLotFinderConnectionAsync()); ex.DataSource.ShouldBe("LotFinder"); ex.Message.ShouldContain("Failed to open connection"); ex.InnerException.ShouldNotBeNull(); } [Fact] public async Task CreateLotFinderConnectionAsync_CancellationRequested_ThrowsOperationCanceledException() { // Arrange _secureStore.Get("LotFinder").Returns("Server=test;Database=test;"); var factory = new DbConnectionFactory(_secureStore, _logger); using var cts = new CancellationTokenSource(); cts.Cancel(); // Act & Assert await Should.ThrowAsync( async () => await factory.CreateLotFinderConnectionAsync(cts.Token)); } #endregion #region CreateJdeConnectionAsync Tests [Fact] public async Task CreateJdeConnectionAsync_MissingConnectionString_ThrowsConnectionException() { // Arrange _secureStore.Get("JDE").Returns((string?)null); var factory = new DbConnectionFactory(_secureStore, _logger); // Act & Assert var ex = await Should.ThrowAsync( async () => await factory.CreateJdeConnectionAsync()); ex.DataSource.ShouldBe("JDE"); ex.Message.ShouldContain("Connection string not found"); } [Fact] public async Task CreateJdeConnectionAsync_InvalidConnectionString_ThrowsConnectionException() { // Arrange _secureStore.Get("JDE").Returns("Invalid oracle connection"); var factory = new DbConnectionFactory(_secureStore, _logger); // Act & Assert var ex = await Should.ThrowAsync( async () => await factory.CreateJdeConnectionAsync()); ex.DataSource.ShouldBe("JDE"); ex.Message.ShouldContain("Failed to open connection"); } #endregion #region CreateJdeStageConnectionAsync Tests [Fact] public async Task CreateJdeStageConnectionAsync_MissingConnectionString_ThrowsConnectionException() { // Arrange _secureStore.Get("JDEStage").Returns((string?)null); var factory = new DbConnectionFactory(_secureStore, _logger); // Act & Assert var ex = await Should.ThrowAsync( async () => await factory.CreateJdeStageConnectionAsync()); ex.DataSource.ShouldBe("JDEStage"); ex.Message.ShouldContain("Connection string not found"); } #endregion #region CreateCmsConnectionAsync Tests [Fact] public async Task CreateCmsConnectionAsync_MissingConnectionString_ThrowsConnectionException() { // Arrange _secureStore.Get("CMS").Returns((string?)null); var factory = new DbConnectionFactory(_secureStore, _logger); // Act & Assert var ex = await Should.ThrowAsync( async () => await factory.CreateCmsConnectionAsync()); ex.DataSource.ShouldBe("CMS"); ex.Message.ShouldContain("Connection string not found"); } [Fact] public async Task CreateCmsConnectionAsync_InvalidConnectionString_ThrowsConnectionException() { // Arrange _secureStore.Get("CMS").Returns("Invalid oracle connection"); var factory = new DbConnectionFactory(_secureStore, _logger); // Act & Assert var ex = await Should.ThrowAsync( async () => await factory.CreateCmsConnectionAsync()); ex.DataSource.ShouldBe("CMS"); ex.Message.ShouldContain("Failed to open connection"); } #endregion #region Logging Tests [Fact] public async Task CreateLotFinderConnectionAsync_InvalidConnection_LogsError() { // Arrange _secureStore.Get("LotFinder").Returns("Invalid connection string"); var factory = new DbConnectionFactory(_secureStore, _logger); // Act try { await factory.CreateLotFinderConnectionAsync(); } catch (ConnectionException) { // Expected } // Assert - verify error logging was called (at least once - there may also be debug logs) _logger.Received().Log( LogLevel.Error, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any>()); } #endregion }