Migrate ExcelIO from ClosedXML to NPOI
This commit is contained in:
@@ -1,249 +1,228 @@
|
||||
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" }
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
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<ILogger<ExcelExportService>>();
|
||||
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<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 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" }
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user