ec4c8fab87
Move configuration options from Core/DataAccess/DataSync/ExcelIO to dedicated Options folders within each project for better organization. Update all references and tests accordingly.
639 lines
23 KiB
C#
639 lines
23 KiB
C#
using JdeScoping.Core.Models;
|
|
using JdeScoping.Core.Models.Enums;
|
|
using JdeScoping.Core.Models.Inventory;
|
|
using JdeScoping.Core.Models.Search;
|
|
using JdeScoping.Core.ViewModels;
|
|
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;
|
|
|
|
/// <summary>
|
|
/// Unit tests for LotFinderRepository.
|
|
/// </summary>
|
|
public class LotFinderRepositoryTests
|
|
{
|
|
private readonly IDbConnectionFactory _connectionFactory;
|
|
private readonly ILogger<LotFinderRepository> _logger;
|
|
private readonly IOptions<DataAccessOptions> _options;
|
|
|
|
public LotFinderRepositoryTests()
|
|
{
|
|
_connectionFactory = Substitute.For<IDbConnectionFactory>();
|
|
_logger = Substitute.For<ILogger<LotFinderRepository>>();
|
|
_options = Microsoft.Extensions.Options.Options.Create(new DataAccessOptions
|
|
{
|
|
DefaultTimeoutSeconds = 30,
|
|
RebuildIndexTimeoutSeconds = 60
|
|
});
|
|
}
|
|
|
|
#region Constructor Tests
|
|
|
|
[Fact]
|
|
public void Constructor_NullConnectionFactory_ThrowsArgumentNullException()
|
|
{
|
|
// Act & Assert
|
|
Should.Throw<ArgumentNullException>(
|
|
() => new LotFinderRepository(null!, _logger, _options))
|
|
.ParamName.ShouldBe("connectionFactory");
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_NullLogger_ThrowsArgumentNullException()
|
|
{
|
|
// Act & Assert
|
|
Should.Throw<ArgumentNullException>(
|
|
() => new LotFinderRepository(_connectionFactory, null!, _options))
|
|
.ParamName.ShouldBe("logger");
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_NullOptions_ThrowsArgumentNullException()
|
|
{
|
|
// Act & Assert
|
|
Should.Throw<ArgumentNullException>(
|
|
() => new LotFinderRepository(_connectionFactory, _logger, null!))
|
|
.ParamName.ShouldBe("options");
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_ValidParameters_CreatesInstance()
|
|
{
|
|
// Act
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Assert
|
|
repository.ShouldNotBeNull();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RebuildIndicesAsync - Table Name Validation Tests
|
|
|
|
[Theory]
|
|
[InlineData("Branch")]
|
|
[InlineData("DataUpdate")]
|
|
[InlineData("FunctionCode")]
|
|
[InlineData("Item")]
|
|
[InlineData("JdeUser")]
|
|
[InlineData("Lot")]
|
|
[InlineData("LotLocation")]
|
|
[InlineData("LotUsage_Curr")]
|
|
[InlineData("LotUsage_Hist")]
|
|
[InlineData("MisData")]
|
|
[InlineData("OrgHierarchy")]
|
|
[InlineData("ProfitCenter")]
|
|
[InlineData("RouteMaster")]
|
|
[InlineData("Search")]
|
|
[InlineData("StatusCode")]
|
|
[InlineData("WorkCenter")]
|
|
[InlineData("WorkOrder_Curr")]
|
|
[InlineData("WorkOrder_Hist")]
|
|
[InlineData("WorkOrderComponent_Curr")]
|
|
[InlineData("WorkOrderComponent_Hist")]
|
|
[InlineData("WorkOrderRouting")]
|
|
[InlineData("WorkOrderStep_Curr")]
|
|
[InlineData("WorkOrderStep_Hist")]
|
|
[InlineData("WorkOrderTime_Curr")]
|
|
[InlineData("WorkOrderTime_Hist")]
|
|
public async Task RebuildIndicesAsync_ValidTableName_DoesNotThrowArgumentException(string tableName)
|
|
{
|
|
// Arrange - expect connection exception since we have no real connection
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert - should throw QueryException (wrapped ConnectionException), not ArgumentException
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.RebuildIndicesAsync(tableName));
|
|
|
|
ex.QueryName.ShouldBe("SQL_REBUILD_INDICES");
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("InvalidTable")]
|
|
[InlineData("DropTable")]
|
|
[InlineData("Users")]
|
|
[InlineData("sys.tables")]
|
|
[InlineData("'; DROP TABLE Users; --")]
|
|
[InlineData("WorkOrder")]
|
|
[InlineData("branch")] // Case-insensitive should still work
|
|
public async Task RebuildIndicesAsync_InvalidTableName_ThrowsArgumentException(string tableName)
|
|
{
|
|
// Arrange
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
// Note: "branch" is case-insensitive match for "Branch", so it should NOT throw
|
|
if (tableName.Equals("branch", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Case-insensitive match - will try to connect and throw QueryException
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection", "LotFinderDB"));
|
|
|
|
await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.RebuildIndicesAsync(tableName));
|
|
}
|
|
else
|
|
{
|
|
var ex = await Should.ThrowAsync<ArgumentException>(
|
|
async () => await repository.RebuildIndicesAsync(tableName));
|
|
|
|
ex.ParamName.ShouldBe("tableName");
|
|
ex.Message.ShouldContain($"Invalid table name: {tableName}");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TruncateTableAsync - Table Name Validation Tests
|
|
|
|
[Theory]
|
|
[InlineData("Branch")]
|
|
[InlineData("Item")]
|
|
[InlineData("WorkOrder_Curr")]
|
|
public async Task TruncateTableAsync_ValidTableName_DoesNotThrowArgumentException(string tableName)
|
|
{
|
|
// Arrange - expect connection exception since we have no real connection
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert - should throw QueryException (wrapped ConnectionException), not ArgumentException
|
|
await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.TruncateTableAsync(tableName));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("InvalidTable")]
|
|
[InlineData("'; DELETE FROM Users; --")]
|
|
public async Task TruncateTableAsync_InvalidTableName_ThrowsArgumentException(string tableName)
|
|
{
|
|
// Arrange
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<ArgumentException>(
|
|
async () => await repository.TruncateTableAsync(tableName));
|
|
|
|
ex.ParamName.ShouldBe("tableName");
|
|
ex.Message.ShouldContain($"Invalid table name: {tableName}");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BulkInsertAsync - Table Name Validation Tests
|
|
|
|
[Theory]
|
|
[InlineData("Branch")]
|
|
[InlineData("Item")]
|
|
public async Task BulkInsertAsync_ValidTableName_DoesNotThrowArgumentException(string tableName)
|
|
{
|
|
// Arrange - expect connection exception since we have no real connection
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
var records = new List<Item>();
|
|
|
|
// Act & Assert - should throw QueryException (wrapped ConnectionException), not ArgumentException
|
|
await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.BulkInsertAsync(tableName, records));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("InvalidTable")]
|
|
[InlineData("'; TRUNCATE TABLE Users; --")]
|
|
public async Task BulkInsertAsync_InvalidTableName_ThrowsArgumentException(string tableName)
|
|
{
|
|
// Arrange
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
var records = new List<Item>();
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<ArgumentException>(
|
|
async () => await repository.BulkInsertAsync(tableName, records));
|
|
|
|
ex.ParamName.ShouldBe("tableName");
|
|
ex.Message.ShouldContain($"Invalid table name: {tableName}");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Connection Exception Handling Tests
|
|
|
|
[Fact]
|
|
public async Task GetUserSearchesAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.GetUserSearchesAsync("testuser"));
|
|
|
|
ex.QueryName.ShouldBe("SQL_GET_USER_SEARCHES");
|
|
ex.Repository.ShouldBe("LotFinderRepository");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetQueuedSearchesAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.GetQueuedSearchesAsync());
|
|
|
|
ex.QueryName.ShouldBe("SQL_GET_QUEUED_SEARCHES");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetSearchAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.GetSearchAsync(1));
|
|
|
|
ex.QueryName.ShouldBe("SQL_GET_SEARCH");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetSearchResultsAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.GetSearchResultsAsync(1));
|
|
|
|
ex.QueryName.ShouldBe("SQL_GET_SEARCH_RESULTS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SubmitSearchAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
var search = new Search { UserName = "testuser", Name = "Test Search" };
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.SubmitSearchAsync(search));
|
|
|
|
ex.QueryName.ShouldBe("SubmitSearch");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateSearchStatusAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.UpdateSearchStatusAsync(1, SearchStatus.Running));
|
|
|
|
ex.QueryName.ShouldBe("SQL_UPDATE_SEARCH_STATUS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateSearchResultsAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.UpdateSearchResultsAsync(1, [1, 2, 3]));
|
|
|
|
ex.QueryName.ShouldBe("SQL_UPDATE_SEARCH_RESULTS");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Reference Data Lookup Exception Handling Tests
|
|
|
|
[Fact]
|
|
public async Task SearchItemsAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.SearchItemsAsync("test"));
|
|
|
|
ex.QueryName.ShouldBe("SQL_SEARCH_ITEMS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LookupItemsAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.LookupItemsAsync(["ITEM001"]));
|
|
|
|
ex.QueryName.ShouldBe("SQL_LOOKUP_ITEMS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LookupWorkordersAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.LookupWorkordersAsync([12345]));
|
|
|
|
ex.QueryName.ShouldBe("SQL_LOOKUP_WORKORDERS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SearchWorkCentersAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.SearchWorkCentersAsync("test"));
|
|
|
|
ex.QueryName.ShouldBe("SQL_SEARCH_WORK_CENTERS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LookupWorkCentersAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.LookupWorkCentersAsync(["WC01"]));
|
|
|
|
ex.QueryName.ShouldBe("SQL_LOOKUP_WORK_CENTERS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SearchProfitCentersAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.SearchProfitCentersAsync("test"));
|
|
|
|
ex.QueryName.ShouldBe("SQL_SEARCH_PROFIT_CENTERS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LookupProfitCentersAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.LookupProfitCentersAsync(["PC01"]));
|
|
|
|
ex.QueryName.ShouldBe("SQL_LOOKUP_PROFIT_CENTERS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SearchUsersAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.SearchUsersAsync("test"));
|
|
|
|
ex.QueryName.ShouldBe("SQL_SEARCH_USERS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LookupUsersAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.LookupUsersAsync(["USER01"]));
|
|
|
|
ex.QueryName.ShouldBe("SQL_LOOKUP_USERS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LookupLotsAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
var lots = new List<LotViewModel> { new LotViewModel { LotNumber = "LOT001", ItemNumber = "ITEM001" } };
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.LookupLotsAsync(lots));
|
|
|
|
ex.QueryName.ShouldBe("SQL_LOOKUP_LOTS");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Data Sync Exception Handling Tests
|
|
|
|
[Fact]
|
|
public async Task GetLastDataUpdatesAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.GetLastDataUpdatesAsync());
|
|
|
|
ex.QueryName.ShouldBe("SQL_GET_LAST_DATA_UPDATES");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetTableSpecAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.GetTableSpecAsync("Item"));
|
|
|
|
ex.QueryName.ShouldBe("SQL_GET_TABLE_COLUMNS");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostProcessMisDataAsync_ConnectionFails_ThrowsQueryException()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
var ex = await Should.ThrowAsync<QueryException>(
|
|
async () => await repository.PostProcessMisDataAsync());
|
|
|
|
ex.QueryName.ShouldBe("SQL_POSTPROCESS_MISDATA");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Cancellation Tests
|
|
|
|
[Fact]
|
|
public async Task GetUserSearchesAsync_CancellationRequested_ThrowsOperationCanceledException()
|
|
{
|
|
// Arrange
|
|
using var cts = new CancellationTokenSource();
|
|
cts.Cancel();
|
|
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new OperationCanceledException(cts.Token));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
await Should.ThrowAsync<OperationCanceledException>(
|
|
async () => await repository.GetUserSearchesAsync("testuser", cts.Token));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetQueuedSearchesAsync_CancellationRequested_ThrowsOperationCanceledException()
|
|
{
|
|
// Arrange
|
|
using var cts = new CancellationTokenSource();
|
|
cts.Cancel();
|
|
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new OperationCanceledException(cts.Token));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act & Assert
|
|
await Should.ThrowAsync<OperationCanceledException>(
|
|
async () => await repository.GetQueuedSearchesAsync(cts.Token));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Logging Tests
|
|
|
|
[Fact]
|
|
public async Task GetUserSearchesAsync_ConnectionFails_LogsError()
|
|
{
|
|
// Arrange
|
|
_connectionFactory.CreateLotFinderConnectionAsync(Arg.Any<CancellationToken>())
|
|
.ThrowsAsync(new ConnectionException("Test connection error", "LotFinderDB"));
|
|
|
|
var repository = new LotFinderRepository(_connectionFactory, _logger, _options);
|
|
|
|
// Act
|
|
try
|
|
{
|
|
await repository.GetUserSearchesAsync("testuser");
|
|
}
|
|
catch (QueryException)
|
|
{
|
|
// Expected
|
|
}
|
|
|
|
// Assert - verify logging was called
|
|
_logger.ReceivedWithAnyArgs(1).Log(
|
|
LogLevel.Error,
|
|
Arg.Any<EventId>(),
|
|
Arg.Any<object>(),
|
|
Arg.Any<Exception>(),
|
|
Arg.Any<Func<object, Exception?, string>>());
|
|
}
|
|
|
|
#endregion
|
|
}
|