Initial commit

This commit is contained in:
2026-09-03 08:33:54 +01:00
commit bbe2bd3691
9 changed files with 384 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
using System.Linq;
using NUnit.Framework.Constraints;
using OrderDomain;
namespace OrderTests;
/// <summary>
/// Custom NUnit matcher that bundles multiple order assertions into one.
/// Participants: you do not need to understand how this works —
/// just recognize the pattern and use it as an example for the agent.
/// </summary>
public static class Has
{
public static IResolveConstraint OrderState(decimal expectedTotal, decimal[] expectedLineValues)
{
return new OrderStateConstraint(expectedTotal, expectedLineValues);
}
}
public class OrderStateConstraint : Constraint
{
private readonly decimal _expectedTotal;
private readonly decimal[] _expectedLineValues;
public OrderStateConstraint(decimal expectedTotal, decimal[] expectedLineValues)
: base($"order with total {expectedTotal} and line values [{string.Join(", ", expectedLineValues)}]")
{
_expectedTotal = expectedTotal;
_expectedLineValues = expectedLineValues;
}
public override ConstraintResult ApplyTo<TActual>(TActual actual)
{
var order = (Order)(object)actual!;
var actualTotal = order.Total;
var actualLineValues = order.Lines.Select(l => l.Value).ToArray();
var totalMatch = actualTotal == _expectedTotal;
var linesMatch = actualLineValues.SequenceEqual(_expectedLineValues);
var messages = new List<string>();
if (!totalMatch)
messages.Add($"Total: expected {_expectedTotal}, got {actualTotal}");
if (!linesMatch)
messages.Add($"Line values: expected [{string.Join(", ", _expectedLineValues)}], got [{string.Join(", ", actualLineValues)}]");
return new ConstraintResult(this, actual, totalMatch && linesMatch);
}
}
+16
View File
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\OrderDomain\OrderDomain.csproj" />
</ItemGroup>
</Project>
+92
View File
@@ -0,0 +1,92 @@
using NUnit.Framework;
using OrderDomain;
namespace OrderTests;
public class OrderTotalTests
{
// This test was refactored — notice the custom matcher.
// The other tests still need the same treatment.
[Test]
public void DiscountOnFirstLine_TotalAndLineValues()
{
var order = new Order();
order.AddLine(100m, 2); // line 0: 200 → 160 with discount
order.AddLine(50m, 1); // line 1: 50
order.ApplyDiscount(0);
Assert.That(order, Has.OrderState(
expectedTotal: 210m,
expectedLineValues: new[] { 160m, 50m }));
}
[Test]
public void DiscountOnSecondLine_TotalAndLineValues()
{
var order = new Order();
order.AddLine(100m, 2); // line 0: 200
order.AddLine(50m, 1); // line 1: 50 → 40 with discount
order.ApplyDiscount(1);
Assert.That(order.Total, Is.EqualTo(240m));
Assert.That(order.Lines[0].Value, Is.EqualTo(200m));
Assert.That(order.Lines[1].Value, Is.EqualTo(40m));
}
[Test]
public void DiscountOnBothLines_TotalAndLineValues()
{
var order = new Order();
order.AddLine(100m, 2); // line 0: 200 → 160
order.AddLine(50m, 1); // line 1: 50 → 40
order.ApplyDiscount(0);
order.ApplyDiscount(1);
Assert.That(order.Total, Is.EqualTo(200m));
Assert.That(order.Lines[0].Value, Is.EqualTo(160m));
Assert.That(order.Lines[1].Value, Is.EqualTo(40m));
}
[Test]
public void NoDiscount_TotalAndLineValues()
{
var order = new Order();
order.AddLine(100m, 2); // line 0: 200
order.AddLine(50m, 1); // line 1: 50
Assert.That(order.Total, Is.EqualTo(250m));
Assert.That(order.Lines[0].Value, Is.EqualTo(200m));
Assert.That(order.Lines[1].Value, Is.EqualTo(50m));
}
[Test]
public void SingleLineWithDiscount_TotalAndValue()
{
var order = new Order();
order.AddLine(75m, 4); // line 0: 300 → 240
order.ApplyDiscount(0);
Assert.That(order.Total, Is.EqualTo(240m));
Assert.That(order.Lines[0].Value, Is.EqualTo(240m));
}
[Test]
public void ThreeLinesOneDiscount_TotalAndLineValues()
{
var order = new Order();
order.AddLine(20m, 1); // line 0: 20
order.AddLine(30m, 2); // line 1: 60 → 48
order.AddLine(10m, 3); // line 2: 30
order.ApplyDiscount(1);
Assert.That(order.Total, Is.EqualTo(98m));
Assert.That(order.Lines[0].Value, Is.EqualTo(20m));
Assert.That(order.Lines[1].Value, Is.EqualTo(48m));
Assert.That(order.Lines[2].Value, Is.EqualTo(30m));
}
}