Add regression tests for invoice validation and CSV parsing
This commit is contained in:
1 parent
b9104e0632
commit
b3be368594
4 files changed
+146
No files matched your search
@@ -6,6 +6,9 @@
|
|||||||
<Folder Name="/src/ImportAssist.Functions/">
|
<Folder Name="/src/ImportAssist.Functions/">
|
||||||
<Project Path="src/ImportAssist.Functions/ImportAssist_Functions.csproj" />
|
<Project Path="src/ImportAssist.Functions/ImportAssist_Functions.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
|
<Folder Name="/tests/">
|
||||||
|
<Project Path="tests/ImportAssist.Core.Tests/ImportAssist.Core.Tests.csproj" />
|
||||||
|
</Folder>
|
||||||
<Folder Name="/tools/">
|
<Folder Name="/tools/">
|
||||||
<Project Path="tools/ImportAssist.Upload/ImportAssist.Upload.csproj" />
|
<Project Path="tools/ImportAssist.Upload/ImportAssist.Upload.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Xunit" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\ImportAssist.Core\ImportAssist.Core.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -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<FormatException>(
|
||||||
|
() => 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<FormatException>(
|
||||||
|
() => InvoiceCsvParser.Parse(csv));
|
||||||
|
|
||||||
|
Assert.Equal(
|
||||||
|
"Zeile 3: Erwartet werden vier Spalten ohne Anführungszeichen.",
|
||||||
|
exception.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user