From b37ca9222d444863cf605d7320f7ec691dba28b9 Mon Sep 17 00:00:00 2001 From: Brian Johnson Date: Thu, 30 Jul 2026 13:36:05 +0200 Subject: [PATCH] Added more unit tests. --- .../TransferFundsHandlerTests.cs | 146 ++++++++++++++++-- .../Handlers/ApplyFeeHandler.cs | 2 +- .../a-frame-architecture.sln.DotSettings.user | 1 + 3 files changed, 133 insertions(+), 16 deletions(-) diff --git a/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/TransferFundsHandlerTests.cs b/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/TransferFundsHandlerTests.cs index 89f4e80..a2a6ba9 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/TransferFundsHandlerTests.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/TransferFundsHandlerTests.cs @@ -2,6 +2,7 @@ using FinancialApi.Application.Commands; using FinancialApi.Application.Handlers; using FinancialApi.Application.Models; using FinancialApi.Domain.Entities; +using FinancialApi.Domain.Exceptions; using FluentAssertions; using FluentAssertions.Execution; using Microsoft.Extensions.Time.Testing; @@ -10,29 +11,144 @@ namespace FinancialApi.Application.UnitTests; public class TransferFundsHandlerTests { - public class ApplyFeeHandlerTests + public class LiabilityAccount { - [Fact] - public void GivenValidAccounts_BasicFee_ShouldValidateEverything() + [Theory] + [InlineData(100.0, 20.0, 80.0)] + [InlineData(199.78, 10.0, 189.78)] + [InlineData(200000, 0.09, 199999.91)] + [InlineData(1000, 1000, 0)] + public void GivenStartingBalanceGreaterThanTransfer_ApplyFee_ShouldBeExpectedBalance( + decimal startingBalance, + decimal feeAmount, + decimal expectedBalance + ) { // Arrange - var fakeTimeProvider = new FakeTimeProvider(); - var command = new TransferFundsCommand(1, 2, 100); - var sourceAccount = new Account(1, 100, AccountType.Liability); - var destAccount = new Account(1, 100, AccountType.Liability); - var transferContext = new TransferContext(sourceAccount, destAccount, fakeTimeProvider.GetUtcNow()); + var timeProvider = new FakeTimeProvider(); + var command = new TransferFundsCommand(1, 2, feeAmount); + var sourceAccount = new Account(1, startingBalance, AccountType.Liability); + var destAccount = new Account(2, 0, AccountType.Liability); + var transferContext = new TransferContext(sourceAccount, destAccount, timeProvider.GetUtcNow()); // Act - var (sourceWrite, destWrite, journalWrite, @event) = TransferFundsHandler.Handle(command, transferContext); + var (customerWrite, revenueWrite, journalWrite, @event) = + TransferFundsHandler.Handle(command, transferContext); // Assert using var _ = new AssertionScope(); - sourceWrite.Entity.Balance.Should() - .Be(0); - destWrite.Entity.Balance.Should() - .Be(200); - journalWrite.Entity.Lines.Should() - .HaveCountGreaterThanOrEqualTo(2); + customerWrite.Entity.Balance.Should() + .Be(expectedBalance); + } + + [Theory] + [InlineData(100.0, 110.0)] + [InlineData(199.78, 200.0)] + [InlineData(200000, 200000.01)] + [InlineData(1000, 1999999)] + public void GivenStartingBalanceLessThanTransfer_ApplyFee_ShouldFail(decimal startingBalance, decimal feeAmount) + { + // Arrange + var timeProvider = new FakeTimeProvider(); + var command = new TransferFundsCommand(1, 2, feeAmount); + var sourceAccount = new Account(1, startingBalance, AccountType.Liability); + var destAccount = new Account(2, 0, AccountType.Liability); + var transferContext = new TransferContext(sourceAccount, destAccount, timeProvider.GetUtcNow()); + + // Act + var act = () => TransferFundsHandler.Handle(command, transferContext); + + // Assert + using var _ = new AssertionScope(); + act.Should() + .Throw(); + } + } + + public class CreditAccount + { + [Theory] + [InlineData(100.0, 20.0, 80.0)] + [InlineData(199.78, 10.0, 189.78)] + [InlineData(200000, 0.09, 199999.91)] + [InlineData(1000, 1000, 0)] + public void GivenStartingBalanceGreaterThanFee_ApplyFee_ShouldBeExpectedBalance( + decimal startingBalance, + decimal feeAmount, + decimal expectedBalance + ) + { + // Arrange + var timeProvider = new FakeTimeProvider(); + var command = new TransferFundsCommand(1, 2, feeAmount); + var sourceAccount = new Account(1, startingBalance, AccountType.Credit); + var destAccount = new Account(2, 0, AccountType.Credit); + var transferContext = new TransferContext(sourceAccount, destAccount, timeProvider.GetUtcNow()); + + // Act + var (customerWrite, revenueWrite, journalWrite, @event) = + TransferFundsHandler.Handle(command, transferContext); + + // Assert + using var _ = new AssertionScope(); + customerWrite.Entity.Balance.Should() + .Be(expectedBalance); + } + + [Theory] + [InlineData(100.0, 110.0, -10.0)] + [InlineData(199.78, 200.0, -0.22)] + [InlineData(200000, 200000.01, -0.01)] + [InlineData(1000, 1999999, -1998999)] + public void GivenStartingBalanceLessThanFee_ApplyFee_ShouldSucceed( + decimal startingBalance, + decimal feeAmount, + decimal expectedBalance + ) + { + // Arrange + var timeProvider = new FakeTimeProvider(); + var command = new TransferFundsCommand(1, 2, feeAmount); + var sourceAccount = new Account(1, startingBalance, AccountType.Credit); + var destAccount = new Account(2, 0, AccountType.Credit); + var transferContext = new TransferContext(sourceAccount, destAccount, timeProvider.GetUtcNow()); + + // Act + var act = () => TransferFundsHandler.Handle(command, transferContext); + + // Assert + using var _ = new AssertionScope(); + act.Should() + .NotThrow(); + } + + + [Theory] + [InlineData(100.0, 110.0, -10.0)] + [InlineData(199.78, 200.0, -0.22)] + [InlineData(200000, 200000.01, -0.01)] + [InlineData(1000, 1999999, -1998999)] + public void GivenStartingBalanceLessThanFee_ApplyFee_ShouldBeExpectedBalance( + decimal startingBalance, + decimal feeAmount, + decimal expectedBalance + ) + { + // Arrange + var timeProvider = new FakeTimeProvider(); + var command = new TransferFundsCommand(1, 2, feeAmount); + var sourceAccount = new Account(1, startingBalance, AccountType.Credit); + var destAccount = new Account(2, 0, AccountType.Credit); + var transferContext = new TransferContext(sourceAccount, destAccount, timeProvider.GetUtcNow()); + + // Act + var (customerWrite, revenueWrite, journalWrite, @event) = + TransferFundsHandler.Handle(command, transferContext); + + // Assert + using var _ = new AssertionScope(); + customerWrite.Entity.Balance.Should() + .Be(expectedBalance); } } } \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ApplyFeeHandler.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ApplyFeeHandler.cs index a8ba3ff..c5f160e 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ApplyFeeHandler.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ApplyFeeHandler.cs @@ -16,7 +16,7 @@ public static class ApplyFeeHandler var dest = await query.FindByIdAsync(99999); if (source == null || dest == null) { - throw new InvalidOperationException($"Cannot process transfer. Account(s) not found."); + throw new InvalidOperationException("Cannot process transfer. Account(s) not found."); } return new FeeContext(source, dest, timeProvider.GetUtcNow()); diff --git a/wolverine/a-frame-architecture/a-frame-architecture.sln.DotSettings.user b/wolverine/a-frame-architecture/a-frame-architecture.sln.DotSettings.user index 576504b..5bac67b 100644 --- a/wolverine/a-frame-architecture/a-frame-architecture.sln.DotSettings.user +++ b/wolverine/a-frame-architecture/a-frame-architecture.sln.DotSettings.user @@ -1,6 +1,7 @@  ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded