feat: Initialize Strata.Excel.Core library with export, import, and test utilities

This commit introduces the `Strata.Excel.Core` project, a .NET library built on ClosedXML for robust Excel document generation and data import.

Key features include:
- **Export:** Flexible data export to Excel, supporting custom formatting, titles, subtitles, humanized headings, and batched processing for large datasets. Includes `ExcelContentResult` and `ZipFileContentResult` for ASP.NET Core integration.
- **Import:** Utilities to easily import data from Excel worksheets into C# objects.
- **Test Utilities:** Comprehensive helpers for comparing Excel workbooks in tests, handling resource extraction, and performing load tests.
- **Build Infrastructure:** Sets up a Dockerfile for building the library, including SonarQube and Dependency-Check scanning.
- **Project Structure:** Establishes `.gitignore`, `.dockerignore`, `nuget.config`, and a `README.md` with usage instructions and versioning guidelines.

This foundational commit provides a reusable and well-tested framework for Excel operations within Strata applications.
This commit is contained in:
Thom Lamb
2026-06-23 11:08:58 -05:00
parent 9a9b7f7437
commit 0c144f5c66
30 changed files with 3087 additions and 218 deletions
@@ -0,0 +1,80 @@
using ClosedXML.Excel;
using FluentAssertions;
using Newtonsoft.Json;
using NUnit.Framework;
using Strata.Excel.Core.Import;
using Strata.Excel.TestUtilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using static Strata.Excel.Core.Test.Unit.ExcelExportTests.TestExcelExport;
namespace Strata.Excel.Core.Test.Unit.ExcelImportTests
{
[TestFixture]
public class TestExcelImport
{
[OneTimeSetUp]
public void RunBeforeAnyTests()
{
Environment.CurrentDirectory = TestContext.CurrentContext.TestDirectory;
// or identically under the hoods
Directory.SetCurrentDirectory(TestContext.CurrentContext.TestDirectory);
}
private static readonly ResourceFileExtractor _extractor = new ResourceFileExtractor(".ExpectedResults.");
private static readonly ResourceFileExtractor _dataExtractor = new ResourceFileExtractor(".ExcelExportTests.");
[Test]
public void TestImportExcelWorksheet()
{
using (Stream stream = _extractor.ReadFileFromResourceToStream("TestCreateExcelWorkbook.xlsx"))
{
var wb = new XLWorkbook(stream);
wb.SaveAs(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "test.xlsx"));
var ws = wb.Worksheets.First();
var result = ws.GetDataFromExcel<TestData>();
var data = _dataExtractor.ReadFileFromResource("data.json");
var expected = JsonConvert.DeserializeObject<List<TestData>>(data);
result.Should().HaveCount(expected.Count)
.And.Contain(row => expected.Select(d => d.AccountCode).Contains(row.AccountCode))
.And.Contain(row => expected.Select(d => d.DatabaseName).Contains(row.DatabaseName))
.And.Contain(row => expected.Select(d => d.DatabaseFriendlyName).Contains(row.DatabaseFriendlyName))
.And.Contain(row => expected.Select(d => d.SphAccountRollupCategory).Contains(row.SphAccountRollupCategory))
.And.Contain(row => expected.Select(d => d.Description).Contains(row.Description));
}
}
[Test]
public void TestImportWhatsNeededExcelWorksheet()
{
using (Stream stream = _extractor.ReadFileFromResourceToStream("TestCreateExcelWorkbook.xlsx"))
{
var wb = new XLWorkbook(stream);
wb.SaveAs(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "test.xlsx"));
var ws = wb.Worksheets.First();
var result = ws.GetDataFromExcel<ImportWhatsNeeded>();
var data = _dataExtractor.ReadFileFromResource("data.json");
var expected = JsonConvert.DeserializeObject<List<ImportWhatsNeeded>>(data);
result.Should().HaveCount(expected.Count)
.And.Contain(row => expected.Select(d => d.AccountCode).Contains(row.AccountCode))
.And.Contain(row => expected.Select(d => d.DatabaseName).Contains(row.DatabaseName))
.And.Contain(row => expected.Select(d => d.DatabaseFriendlyName).Contains(row.DatabaseFriendlyName))
.And.Contain(row => expected.Select(d => d.SphAccountRollupCategory).Contains(row.SphAccountRollupCategory))
.And.Contain(row => expected.Select(d => d.Description).Contains(row.Description));
}
}
internal class ImportWhatsNeeded
{
public string DatabaseName { get; set; }
public string DatabaseFriendlyName { get; set; }
public string SphAccountRollupCategory { get; set; }
public string AccountCode { get; set; }
public string Description { get; set; }
}
}
}