From b3be36859480cfebcb81ddb77915cc9b23f52d84 Mon Sep 17 00:00:00 2001 From: Johannes Rest Date: Tue, 8 Sep 2026 10:13:04 +0200 Subject: [PATCH] Add regression tests for invoice validation and CSV parsing --- ImportAssist.slnx | 3 + .../ImportAssist.Core.Tests.csproj | 25 ++++++++ .../InvoiceCsvParserTests.cs | 55 ++++++++++++++++ .../InvoiceValidatorTests.cs | 63 +++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 tests/ImportAssist.Core.Tests/ImportAssist.Core.Tests.csproj create mode 100644 tests/ImportAssist.Core.Tests/InvoiceCsvParserTests.cs create mode 100644 tests/ImportAssist.Core.Tests/InvoiceValidatorTests.cs diff --git a/ImportAssist.slnx b/ImportAssist.slnx index f65393d..5f2fc31 100644 --- a/ImportAssist.slnx +++ b/ImportAssist.slnx @@ -6,6 +6,9 @@ + + + diff --git a/tests/ImportAssist.Core.Tests/ImportAssist.Core.Tests.csproj b/tests/ImportAssist.Core.Tests/ImportAssist.Core.Tests.csproj new file mode 100644 index 0000000..0531d54 --- /dev/null +++ b/tests/ImportAssist.Core.Tests/ImportAssist.Core.Tests.csproj @@ -0,0 +1,25 @@ + + + + net10.0 + enable + enable + false + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/ImportAssist.Core.Tests/InvoiceCsvParserTests.cs b/tests/ImportAssist.Core.Tests/InvoiceCsvParserTests.cs new file mode 100644 index 0000000..706dd03 --- /dev/null +++ b/tests/ImportAssist.Core.Tests/InvoiceCsvParserTests.cs @@ -0,0 +1,55 @@ +using ImportAssist.Core; +using Xunit; + +namespace ImportAssist.Core.Tests; + +public sealed class InvoiceCsvParserTests +{ + private const string Header = + "InvoiceId;CustomerId;Amount;Currency"; + + [Fact] + public void Parse_PreservesEmptyFieldsAndRawAmount() + { + var csv = $"{Header}\nINV-001;;abc;EUR"; + + var rows = InvoiceCsvParser.Parse(csv); + + var row = Assert.Single(rows); + + Assert.Equal( + new InvoiceRow("INV-001", "", "abc", "EUR"), + row); + } + + [Theory] + [InlineData("")] + [InlineData("InvoiceId;Amount")] + public void Parse_RejectsMissingOrIncorrectHeader(string csv) + { + var exception = Assert.Throws( + () => InvoiceCsvParser.Parse(csv)); + + Assert.Equal( + "Die Datei ist leer oder die Kopfzeile stimmt nicht.", + exception.Message); + } + + [Theory] + [InlineData("INV-002;C-200;89.00")] + [InlineData("INV-002;C-200;89.00;EUR;extra")] + [InlineData("INV-002;\"C-200\";89.00;EUR")] + public void Parse_RejectsMalformedRowWithCorrectLineNumber( + string malformedRow) + { + var csv = + $"{Header}\nINV-001;C-100;125.50;EUR\n{malformedRow}"; + + var exception = Assert.Throws( + () => InvoiceCsvParser.Parse(csv)); + + Assert.Equal( + "Zeile 3: Erwartet werden vier Spalten ohne Anführungszeichen.", + exception.Message); + } +} \ No newline at end of file diff --git a/tests/ImportAssist.Core.Tests/InvoiceValidatorTests.cs b/tests/ImportAssist.Core.Tests/InvoiceValidatorTests.cs new file mode 100644 index 0000000..796f7a9 --- /dev/null +++ b/tests/ImportAssist.Core.Tests/InvoiceValidatorTests.cs @@ -0,0 +1,63 @@ +using System.Globalization; +using ImportAssist.Core; +using Xunit; + +namespace ImportAssist.Core.Tests; + +public sealed class InvoiceValidatorTests +{ + [Theory] + [InlineData("de-DE")] + [InlineData("en-US")] + public void Validate_AcceptsDecimalPointRegardlessOfCurrentCulture( + string cultureName) + { + var originalCulture = CultureInfo.CurrentCulture; + + try + { + CultureInfo.CurrentCulture = new CultureInfo(cultureName); + var row = new InvoiceRow( + "INV-001", "C-100", "125.50", "EUR"); + + var errors = InvoiceValidator.Validate(row); + + Assert.Empty(errors); + } + finally + { + CultureInfo.CurrentCulture = originalCulture; + } + } + + [Theory] + [InlineData("abc", "Betrag ist keine gültige Zahl.")] + [InlineData("125,50", "Betrag ist keine gültige Zahl.")] + [InlineData("1,250.00", "Betrag ist keine gültige Zahl.")] + [InlineData("0", "Betrag muss größer als null sein.")] + [InlineData("-10.00", "Betrag muss größer als null sein.")] + public void Validate_RejectsInvalidAmount( + string amount, + string expectedError) + { + var row = new InvoiceRow( + "INV-001", "C-100", amount, "EUR"); + + var errors = InvoiceValidator.Validate(row); + + Assert.Equal(expectedError, Assert.Single(errors)); + } + + [Fact] + public void Validate_CollectsAllErrorsForOneInvoice() + { + var row = new InvoiceRow(" ", "\t", "0", "EUR"); + + var errors = InvoiceValidator.Validate(row); + + Assert.Equal(3, errors.Count); + Assert.Contains("Rechnungsnummer fehlt.", errors); + Assert.Contains("Kundennummer fehlt.", errors); + Assert.Contains("Betrag muss größer als null sein.", errors); + } +} \ No newline at end of file