Added more unit tests.
This commit is contained in:
+131
-15
@@ -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<InsufficientFundsException>();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConfiguredTaskAwaitable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fe929cb9673c947ad9269facdac90006adb3400_003F14_003Fd939fe0a_003FConfiguredTaskAwaitable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConstructorBindingFactory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fda476e261fcf49b394c1074b25b4cdce2b2960_003Fd4_003Fb1aa95c1_003FConstructorBindingFactory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConstructorBindingFactory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fda476e261fcf49b394c1074b25b4cdce2b2960_003Fb3_003F33475782_003FConstructorBindingFactory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADbContext_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fda476e261fcf49b394c1074b25b4cdce2b2960_003F37_003F1b1010ed_003FDbContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALateBoundTestFramework_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fcffa676d9bfc6c7a1c9d0d25ead15b3b0c9e12e3187bf397eaa52260e5fd91_003FLateBoundTestFramework_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMessageContext_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fb263759e5fefb34bcd589e63aeba791cef6f591ecbc93d1a7e62befa9e8bc6_003FMessageContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
|
||||
Reference in New Issue
Block a user