add test project
This commit is contained in:
@@ -0,0 +1,51 @@
|
|||||||
|
using CsharpFp2.Domain;
|
||||||
|
|
||||||
|
namespace CsharpFp2.Tests;
|
||||||
|
|
||||||
|
/// Tests covering domain-layer invariants on the Account aggregate directly,
|
||||||
|
/// independent of the application layer.
|
||||||
|
public class AccountTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Opening_an_account_with_a_negative_balance_throws()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||||
|
new Account(new AccountId(Guid.NewGuid()), new Money(-1m))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Withdrawing_a_zero_amount_throws()
|
||||||
|
{
|
||||||
|
var account = new Account(new AccountId(Guid.NewGuid()), new Money(100m));
|
||||||
|
|
||||||
|
Assert.Throws<InvalidOperationException>(() => account.Withdraw(new Money(0m)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Withdrawing_a_negative_amount_throws()
|
||||||
|
{
|
||||||
|
var account = new Account(new AccountId(Guid.NewGuid()), new Money(100m));
|
||||||
|
|
||||||
|
Assert.Throws<InvalidOperationException>(() => account.Withdraw(new Money(-10m)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Withdrawing_more_than_the_balance_throws()
|
||||||
|
{
|
||||||
|
var account = new Account(new AccountId(Guid.NewGuid()), new Money(100m));
|
||||||
|
|
||||||
|
Assert.Throws<InsufficientBalanceException>(() => account.Withdraw(new Money(101m)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Successive_withdrawals_are_each_applied_to_the_running_balance()
|
||||||
|
{
|
||||||
|
var account = new Account(new AccountId(Guid.NewGuid()), new Money(300m));
|
||||||
|
|
||||||
|
account.Withdraw(new Money(100m));
|
||||||
|
account.Withdraw(new Money(100m));
|
||||||
|
|
||||||
|
Assert.Equal(100m, account.Balance.Amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
using CsharpFp2.Applications;
|
||||||
|
using CsharpFp2.Contracts;
|
||||||
|
using CsharpFp2.Domain;
|
||||||
|
using CsharpFp2.Infrastructure;
|
||||||
|
|
||||||
|
namespace CsharpFp2.Tests;
|
||||||
|
|
||||||
|
/// Tests covering the application-layer use case surface:
|
||||||
|
/// what the WithdrawMoney feature does from the caller's perspective.
|
||||||
|
public class WithdrawMoneyHandlerTests
|
||||||
|
{
|
||||||
|
private static (
|
||||||
|
WithdrawMoneyHandler handler,
|
||||||
|
InMemoryAccountRepository repository
|
||||||
|
) BuildHandler()
|
||||||
|
{
|
||||||
|
var repository = new InMemoryAccountRepository();
|
||||||
|
var handler = new WithdrawMoneyHandler(repository);
|
||||||
|
return (handler, repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Withdrawing_from_an_account_reduces_its_balance_by_the_withdrawn_amount()
|
||||||
|
{
|
||||||
|
var (handler, repository) = BuildHandler();
|
||||||
|
var accountId = Guid.NewGuid();
|
||||||
|
repository.Save(new Account(new AccountId(accountId), new Money(200m)));
|
||||||
|
|
||||||
|
handler.Handle(new WithdrawMoneyCommand { AccountId = accountId, Amount = 75m });
|
||||||
|
|
||||||
|
var account = repository.GetById(new AccountId(accountId))!;
|
||||||
|
Assert.Equal(125m, account.Balance.Amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Withdrawing_the_entire_balance_leaves_the_account_at_zero()
|
||||||
|
{
|
||||||
|
var (handler, repository) = BuildHandler();
|
||||||
|
var accountId = Guid.NewGuid();
|
||||||
|
repository.Save(new Account(new AccountId(accountId), new Money(100m)));
|
||||||
|
|
||||||
|
handler.Handle(new WithdrawMoneyCommand { AccountId = accountId, Amount = 100m });
|
||||||
|
|
||||||
|
var account = repository.GetById(new AccountId(accountId))!;
|
||||||
|
Assert.Equal(0m, account.Balance.Amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Withdrawing_from_a_non_existent_account_throws()
|
||||||
|
{
|
||||||
|
var (handler, _) = BuildHandler();
|
||||||
|
var command = new WithdrawMoneyCommand { AccountId = Guid.NewGuid(), Amount = 50m };
|
||||||
|
|
||||||
|
Assert.Throws<InvalidOperationException>(() => handler.Handle(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Withdrawing_more_than_the_available_balance_throws()
|
||||||
|
{
|
||||||
|
var (handler, repository) = BuildHandler();
|
||||||
|
var accountId = Guid.NewGuid();
|
||||||
|
repository.Save(new Account(new AccountId(accountId), new Money(50m)));
|
||||||
|
|
||||||
|
Assert.Throws<InsufficientBalanceException>(() =>
|
||||||
|
handler.Handle(new WithdrawMoneyCommand { AccountId = accountId, Amount = 100m })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void After_a_failed_withdrawal_the_balance_is_unchanged()
|
||||||
|
{
|
||||||
|
var (handler, repository) = BuildHandler();
|
||||||
|
var accountId = Guid.NewGuid();
|
||||||
|
repository.Save(new Account(new AccountId(accountId), new Money(50m)));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
handler.Handle(new WithdrawMoneyCommand { AccountId = accountId, Amount = 999m });
|
||||||
|
}
|
||||||
|
catch (InsufficientBalanceException) { }
|
||||||
|
|
||||||
|
var account = repository.GetById(new AccountId(accountId))!;
|
||||||
|
Assert.Equal(50m, account.Balance.Amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<RootNamespace>CsharpFp2.Tests</RootNamespace>
|
||||||
|
<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="..\csharp-fp2\csharp-fp2.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
+15
@@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp-fp1", "csharp-fp1\cs
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp-fp2", "csharp-fp2\csharp-fp2.csproj", "{10A6FD3E-117A-4C9E-98E2-DAC31E7CE78A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp-fp2", "csharp-fp2\csharp-fp2.csproj", "{10A6FD3E-117A-4C9E-98E2-DAC31E7CE78A}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp-fp2.Tests", "csharp-fp2.Tests\csharp-fp2.Tests.csproj", "{4AFF063B-7125-4783-BE84-E1E9B4E2A462}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -101,6 +103,18 @@ Global
|
|||||||
{10A6FD3E-117A-4C9E-98E2-DAC31E7CE78A}.Release|x64.Build.0 = Release|Any CPU
|
{10A6FD3E-117A-4C9E-98E2-DAC31E7CE78A}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{10A6FD3E-117A-4C9E-98E2-DAC31E7CE78A}.Release|x86.ActiveCfg = Release|Any CPU
|
{10A6FD3E-117A-4C9E-98E2-DAC31E7CE78A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{10A6FD3E-117A-4C9E-98E2-DAC31E7CE78A}.Release|x86.Build.0 = Release|Any CPU
|
{10A6FD3E-117A-4C9E-98E2-DAC31E7CE78A}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -112,5 +126,6 @@ Global
|
|||||||
{7237398A-2E8B-4161-BA15-DB090395A1F2} = {CEC0EC27-0CEC-90C6-CABA-E58AB278E4DA}
|
{7237398A-2E8B-4161-BA15-DB090395A1F2} = {CEC0EC27-0CEC-90C6-CABA-E58AB278E4DA}
|
||||||
{C9D46510-994A-4C43-BA8F-33CA9BED79D0} = {89C53051-77F9-4C1C-ABE5-6D54AB398471}
|
{C9D46510-994A-4C43-BA8F-33CA9BED79D0} = {89C53051-77F9-4C1C-ABE5-6D54AB398471}
|
||||||
{10A6FD3E-117A-4C9E-98E2-DAC31E7CE78A} = {89C53051-77F9-4C1C-ABE5-6D54AB398471}
|
{10A6FD3E-117A-4C9E-98E2-DAC31E7CE78A} = {89C53051-77F9-4C1C-ABE5-6D54AB398471}
|
||||||
|
{4AFF063B-7125-4783-BE84-E1E9B4E2A462} = {89C53051-77F9-4C1C-ABE5-6D54AB398471}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
Reference in New Issue
Block a user