Files
jdescopingtool/NEW/tests/JdeScoping.ExcelIO.Tests/ExcelExportServiceTests.cs
T
Joseph Doherty 604bfe919c refactor: address code review findings across all projects
Apply comprehensive fixes from code reviews including:
- Extract shared utilities (SqlFormatHelper, CellValueConverter, DbDestinationBase)
- Add interface abstractions (IAuthenticationService, IDatabaseMigrator, IMisQueryBuilder)
- Implement SecureStore for encrypted secrets storage
- Fix error handling with proper HTTP status codes and logging
- Optimize double enumeration in DevEtlRegistry
- Add DataSync.Dev README for developer onboarding
- Extract filter panel base classes to reduce duplication
- Update code review docs to mark all issues as fixed
2026-01-19 11:05:36 -05:00

250 lines
8.2 KiB
C#

using ClosedXML.Excel;
using JdeScoping.Core.Models.SearchResults;
using JdeScoping.ExcelIO.Options;
using JdeScoping.ExcelIO.Generators;
using JdeScoping.ExcelIO.Mapping;
using JdeScoping.ExcelIO.Mapping.Maps;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NSubstitute;
using Shouldly;
using Xunit;
namespace JdeScoping.ExcelIO.Tests;
public class ExcelExportServiceTests
{
private readonly ExcelExportService _service;
private readonly ILogger<ExcelExportService> _logger;
private readonly IOptions<ExcelExportOptions> _options;
public ExcelExportServiceTests()
{
_logger = Substitute.For<ILogger<ExcelExportService>>();
_options = Microsoft.Extensions.Options.Options.Create(new ExcelExportOptions
{
CriteriaSheetPassword = "TestCriteriaPass",
DataSheetPassword = "TestDataPass"
});
var registry = CreateTestRegistry();
var tableWriter = new FluentTableWriter(registry);
var criteriaGenerator = new CriteriaSheetGenerator(_options, tableWriter);
_service = new ExcelExportService(_logger, _options, criteriaGenerator, tableWriter, registry);
}
private static ExcelMapRegistry CreateTestRegistry()
{
var registry = new ExcelMapRegistry();
// Search result maps
registry.Register(new SearchResultMap());
registry.Register(new MisSearchResultMap());
registry.Register(new MisNonMatchSearchResultMap());
// Filter entry maps
registry.Register(new TimespanFilterMap());
registry.Register(new WorkOrderFilterEntryMap());
registry.Register(new ItemNumberFilterEntryMap());
registry.Register(new ProfitCenterFilterEntryMap());
registry.Register(new WorkCenterFilterEntryMap());
registry.Register(new OperatorFilterEntryMap());
registry.Register(new ComponentLotFilterEntryMap());
registry.Register(new ItemOperationMisFilterEntryMap());
return registry;
}
[Fact]
public async Task GenerateAsync_ReturnsValidExcelBytes()
{
var search = CreateMinimalSearchModel();
var result = await _service.GenerateAsync(search);
result.ShouldNotBeNull();
result.Length.ShouldBeGreaterThan(0);
// Verify it's a valid Excel file
using var stream = new MemoryStream(result);
using var workbook = new XLWorkbook(stream);
workbook.Worksheets.Count.ShouldBeGreaterThanOrEqualTo(2);
}
[Fact]
public async Task GenerateAsync_CreatesSearchCriteriaSheet()
{
var search = CreateMinimalSearchModel();
var result = await _service.GenerateAsync(search);
using var stream = new MemoryStream(result);
using var workbook = new XLWorkbook(stream);
workbook.Worksheets.TryGetWorksheet("Search Criteria", out var criteriaSheet).ShouldBeTrue();
criteriaSheet.ShouldNotBeNull();
}
[Fact]
public async Task GenerateAsync_CreatesSearchResultsSheet()
{
var search = CreateMinimalSearchModel();
var result = await _service.GenerateAsync(search);
using var stream = new MemoryStream(result);
using var workbook = new XLWorkbook(stream);
workbook.Worksheets.TryGetWorksheet("Search Results", out var resultsSheet).ShouldBeTrue();
resultsSheet.ShouldNotBeNull();
}
[Fact]
public async Task GenerateAsync_WithMisData_CreatesMisInfoSheet()
{
var search = CreateSearchModelWithMisData();
var result = await _service.GenerateAsync(search);
using var stream = new MemoryStream(result);
using var workbook = new XLWorkbook(stream);
workbook.Worksheets.Count.ShouldBe(4); // Criteria, Results, MIS Info, Investigation
workbook.Worksheets.TryGetWorksheet("MIS Info", out var misSheet).ShouldBeTrue();
misSheet.ShouldNotBeNull();
}
[Fact]
public async Task GenerateAsync_WithMisData_CreatesInvestigationSheet()
{
var search = CreateSearchModelWithMisData();
var result = await _service.GenerateAsync(search);
using var stream = new MemoryStream(result);
using var workbook = new XLWorkbook(stream);
workbook.Worksheets.TryGetWorksheet("Investigation", out var investigationSheet).ShouldBeTrue();
investigationSheet.ShouldNotBeNull();
}
[Fact]
public async Task GenerateAsync_WithoutMisData_DoesNotCreateMisSheets()
{
var search = CreateMinimalSearchModel();
search.ExtractMisData = false;
var result = await _service.GenerateAsync(search);
using var stream = new MemoryStream(result);
using var workbook = new XLWorkbook(stream);
workbook.Worksheets.Count.ShouldBe(2); // Only Criteria and Results
}
[Fact]
public async Task GenerateAsync_CancellationRequested_ThrowsOperationCanceled()
{
var search = CreateMinimalSearchModel();
var cts = new CancellationTokenSource();
cts.Cancel();
await Should.ThrowAsync<OperationCanceledException>(
() => _service.GenerateAsync(search, cts.Token));
}
[Fact]
public async Task GenerateAsync_CriteriaSheet_ContainsSearchName()
{
var search = CreateMinimalSearchModel();
search.Name = "Test Search Name";
var result = await _service.GenerateAsync(search);
using var stream = new MemoryStream(result);
using var workbook = new XLWorkbook(stream);
var criteriaSheet = workbook.Worksheet("Search Criteria");
criteriaSheet.Cell(1, 2).Value.GetText().ShouldBe("Test Search Name");
}
[Fact]
public async Task GenerateAsync_CriteriaSheet_ContainsUserName()
{
var search = CreateMinimalSearchModel();
search.UserName = "testuser";
var result = await _service.GenerateAsync(search);
using var stream = new MemoryStream(result);
using var workbook = new XLWorkbook(stream);
var criteriaSheet = workbook.Worksheet("Search Criteria");
criteriaSheet.Cell(2, 2).Value.GetText().ShouldBe("testuser");
}
[Fact]
public async Task GenerateAsync_ResultsSheet_ContainsResultData()
{
var search = CreateMinimalSearchModel();
search.Results.Add(new SearchResult
{
WorkOrderNumber = 12345,
ItemNumber = "ITEM-001",
LotNumber = "LOT-001",
Flagged = true
});
var result = await _service.GenerateAsync(search);
using var stream = new MemoryStream(result);
using var workbook = new XLWorkbook(stream);
var resultsSheet = workbook.Worksheet("Search Results");
// Check header row
resultsSheet.Cell(1, 1).Value.GetText().ShouldBe("Work Order Number");
// Check data row
resultsSheet.Cell(2, 1).Value.GetNumber().ShouldBe(12345);
}
private static SearchModel CreateMinimalSearchModel()
{
return new SearchModel
{
Id = 1,
Name = "Test Search",
UserName = "testuser",
SubmitDt = DateTime.Now.AddHours(-1),
StartDt = DateTime.Now.AddMinutes(-30),
EndDt = DateTime.Now,
ExtractMisData = false,
Results = []
};
}
private static SearchModel CreateSearchModelWithMisData()
{
return new SearchModel
{
Id = 1,
Name = "Test Search with MIS",
UserName = "testuser",
SubmitDt = DateTime.Now.AddHours(-1),
StartDt = DateTime.Now.AddMinutes(-30),
EndDt = DateTime.Now,
ExtractMisData = true,
Results = [
new SearchResult { WorkOrderNumber = 12345, Flagged = true }
],
MisResults = [
new MisSearchResult { ItemNumber = "ITEM-001", MisNumber = "MIS-001" }
],
MisNonMatchResults = [
new MisNonMatchSearchResult { WorkOrderNumber = 12345, ItemNumber = "ITEM-001" }
]
};
}
}