using JdeScoping.Core.Models.SearchResults; using JdeScoping.ExcelIO.Options; using JdeScoping.ExcelIO.Generators; using JdeScoping.ExcelIO.Mapping; using JdeScoping.ExcelIO.Mapping.Maps; using JdeScoping.ExcelIO.Tests.Fixtures; 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; public ExcelExportServiceTests() { var logger = Substitute.For>(); var 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(); registry.Register(new SearchResultMap()); registry.Register(new MisSearchResultMap()); registry.Register(new MisNonMatchSearchResultMap()); 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); using var workbook = ExcelTestHelpers.OpenWorkbook(result); workbook.NumberOfSheets.ShouldBeGreaterThanOrEqualTo(2); } [Fact] public async Task GenerateAsync_CreatesSearchCriteriaSheet() { var search = CreateMinimalSearchModel(); var result = await _service.GenerateAsync(search); using var workbook = ExcelTestHelpers.OpenWorkbook(result); workbook.GetSheet("Search Criteria").ShouldNotBeNull(); } [Fact] public async Task GenerateAsync_CreatesSearchResultsSheet() { var search = CreateMinimalSearchModel(); var result = await _service.GenerateAsync(search); using var workbook = ExcelTestHelpers.OpenWorkbook(result); workbook.GetSheet("Search Results").ShouldNotBeNull(); } [Fact] public async Task GenerateAsync_WithMisData_CreatesMisInfoSheet() { var search = CreateSearchModelWithMisData(); var result = await _service.GenerateAsync(search); using var workbook = ExcelTestHelpers.OpenWorkbook(result); workbook.NumberOfSheets.ShouldBe(4); workbook.GetSheet("MIS Info").ShouldNotBeNull(); } [Fact] public async Task GenerateAsync_WithMisData_CreatesInvestigationSheet() { var search = CreateSearchModelWithMisData(); var result = await _service.GenerateAsync(search); using var workbook = ExcelTestHelpers.OpenWorkbook(result); workbook.GetSheet("Investigation").ShouldNotBeNull(); } [Fact] public async Task GenerateAsync_WithoutMisData_DoesNotCreateMisSheets() { var search = CreateMinimalSearchModel(); search.ExtractMisData = false; var result = await _service.GenerateAsync(search); using var workbook = ExcelTestHelpers.OpenWorkbook(result); workbook.NumberOfSheets.ShouldBe(2); } [Fact] public async Task GenerateAsync_CancellationRequested_ThrowsOperationCanceled() { var search = CreateMinimalSearchModel(); var cts = new CancellationTokenSource(); cts.Cancel(); await Should.ThrowAsync( () => _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 workbook = ExcelTestHelpers.OpenWorkbook(result); var criteriaSheet = workbook.GetSheet("Search Criteria"); ExcelTestHelpers.GetCellText(criteriaSheet, 1, 2).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 workbook = ExcelTestHelpers.OpenWorkbook(result); var criteriaSheet = workbook.GetSheet("Search Criteria"); ExcelTestHelpers.GetCellText(criteriaSheet, 2, 2).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 workbook = ExcelTestHelpers.OpenWorkbook(result); var resultsSheet = workbook.GetSheet("Search Results"); ExcelTestHelpers.GetCellText(resultsSheet, 1, 1).ShouldBe("Work Order Number"); ExcelTestHelpers.GetCellNumber(resultsSheet, 2, 1).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" } ] }; } }