using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using TrafagSalesExporter.Data; using TrafagSalesExporter.Models; using TrafagSalesExporter.Services; namespace TrafagSalesExporter.Tests; public sealed class ExportAuditCsvServiceTests : IDisposable { private readonly string _tempDirectory; public ExportAuditCsvServiceTests() { _tempDirectory = Path.Combine("C:\\TMP", $"trafag-audit-csv-tests-{Guid.NewGuid():N}"); Directory.CreateDirectory(_tempDirectory); } public void Dispose() { if (Directory.Exists(_tempDirectory)) Directory.Delete(_tempDirectory, recursive: true); } [Fact] public async Task WriteSiteAuditCsvAsync_Roundtrips_Transformed_SalesRecord() { var service = new ExportAuditCsvService(); var settings = new ExportSettings { AuditCsvEnabled = true, LocalAuditCsvFolder = _tempDirectory }; var site = new Site { TSC = "TRCH", Land = "Schweiz" }; var record = new SalesRecord { SourceSystem = "SAP", ExtractionDate = new DateTime(2026, 6, 11, 8, 30, 0, DateTimeKind.Utc), Tsc = "TRCH", SourceLineId = "line-1", DocumentEntry = 42, InvoiceNumber = "INV-1", PositionOnInvoice = 7, Material = "MAT;1", Name = "Artikel \"Audit\"", ProductDivisionCode = "0001", ProductDivisionText = "Pressure", ProductMappingAssigned = "TRUE", Quantity = 2.5m, SalesPriceValue = 1234.56m, SalesCurrency = "CHF", DocumentCurrency = "EUR", DocumentTotalForeignCurrency = 1300m, DocumentTotalLocalCurrency = 1234.56m, VatSumForeignCurrency = 0m, VatSumLocalCurrency = 0m, DocumentRate = 0.95m, CompanyCurrency = "CHF", PostingDate = new DateTime(2026, 6, 10), InvoiceDate = new DateTime(2026, 6, 11), Land = "Schweiz", DocumentType = "INV" }; var path = await service.WriteSiteAuditCsvAsync(site, settings, "SAP", _tempDirectory, [record]); Assert.True(File.Exists(path)); var records = await service.ReadLatestSiteAuditCsvRecordsAsync(settings); var roundtrip = Assert.Single(records); Assert.Equal("SAP", roundtrip.SourceSystem); Assert.Equal("TRCH", roundtrip.Tsc); Assert.Equal("line-1", roundtrip.SourceLineId); Assert.Equal("MAT;1", roundtrip.Material); Assert.Equal("Artikel \"Audit\"", roundtrip.Name); Assert.Equal(1234.56m, roundtrip.SalesPriceValue); Assert.Equal("CHF", roundtrip.SalesCurrency); Assert.Equal(new DateTime(2026, 6, 10), roundtrip.PostingDate); Assert.Equal(new DateTime(2026, 6, 11), roundtrip.InvoiceDate); } [Fact] public async Task CentralSalesDataProvider_Uses_AuditCsv_When_Configured() { var csvService = new ExportAuditCsvService(); await csvService.WriteSiteAuditCsvAsync( new Site { TSC = "TRUK", Land = "England" }, new ExportSettings { AuditCsvEnabled = true, LocalAuditCsvFolder = _tempDirectory }, "MANUAL_EXCEL", _tempDirectory, [ new SalesRecord { SourceSystem = "MANUAL_EXCEL", ExtractionDate = new DateTime(2026, 6, 11), Tsc = "TRUK", Land = "England", InvoiceNumber = "UK-1", SalesPriceValue = 10m, SalesCurrency = "GBP", InvoiceDate = new DateTime(2026, 1, 1) } ]); await using var connection = new SqliteConnection("DataSource=:memory:"); await connection.OpenAsync(); var options = new DbContextOptionsBuilder() .UseSqlite(connection) .Options; await using (var db = new AppDbContext(options)) { await db.Database.EnsureCreatedAsync(); db.ExportSettings.Add(new ExportSettings { UseAuditCsvAsCentralSource = true, LocalAuditCsvFolder = _tempDirectory }); db.Sites.Add(new Site { Id = 1, Schema = "DB", TSC = "TRDB", Land = "DB", SourceSystem = "DB", IsActive = true }); db.CentralSalesRecords.Add(new CentralSalesRecord { StoredAtUtc = DateTime.UtcNow, SiteId = 1, SourceSystem = "DB", ExtractionDate = new DateTime(2026, 6, 11), Tsc = "TRDB", InvoiceNumber = "DB-1", Land = "DB", DocumentType = "INV" }); await db.SaveChangesAsync(); } var dbFactory = new TestDbContextFactory(options); var centralService = new CentralSalesRecordService(dbFactory, new NullAppEventLogService()); var provider = new CentralSalesDataProvider(dbFactory, centralService, csvService); var records = await provider.GetRecordsAsync(); var record = Assert.Single(records); Assert.Equal("TRUK", record.Tsc); Assert.Equal("UK-1", record.InvoiceNumber); Assert.Equal(10m, record.SalesPriceValue); } private sealed class NullAppEventLogService : IAppEventLogService { public Task WriteAsync(string category, string message, string level = "Info", int? siteId = null, string? land = null, string? details = null) => Task.CompletedTask; public Task WriteDebugAsync(string category, string message, int? siteId = null, string? land = null, string? details = null) => Task.CompletedTask; } private sealed class TestDbContextFactory : IDbContextFactory { private readonly DbContextOptions _options; public TestDbContextFactory(DbContextOptions options) { _options = options; } public AppDbContext CreateDbContext() => new(_options); public Task CreateDbContextAsync(CancellationToken cancellationToken = default) => Task.FromResult(new AppDbContext(_options)); } }