115 lines
4.0 KiB
C#
115 lines
4.0 KiB
C#
namespace WalletService.UnitTests.Application;
|
|
|
|
using FluentAssertions;
|
|
using Microsoft.Extensions.Logging;
|
|
using NSubstitute;
|
|
using WalletService.API.Application.Commands;
|
|
using WalletService.Domain.AggregatesModel.WalletAggregate;
|
|
using WalletService.Domain.Exceptions;
|
|
using WalletService.Domain.SeedWork;
|
|
using Xunit;
|
|
|
|
public class EscrowCommandHandlersTests
|
|
{
|
|
private readonly IWalletRepository _walletRepository;
|
|
private readonly ILogger<CreateHoldCommandHandler> _createLogger;
|
|
private readonly ILogger<ExecuteHoldCommandHandler> _executeLogger;
|
|
private readonly IUnitOfWork _unitOfWork;
|
|
|
|
public EscrowCommandHandlersTests()
|
|
{
|
|
_walletRepository = Substitute.For<IWalletRepository>();
|
|
_unitOfWork = Substitute.For<IUnitOfWork>();
|
|
_walletRepository.UnitOfWork.Returns(_unitOfWork);
|
|
_unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>()).Returns(true);
|
|
|
|
_createLogger = Substitute.For<ILogger<CreateHoldCommandHandler>>();
|
|
_executeLogger = Substitute.For<ILogger<ExecuteHoldCommandHandler>>();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateHold_ValidCommand_ShouldSucceed()
|
|
{
|
|
// Arrange
|
|
var walletId = Guid.NewGuid();
|
|
var userId = Guid.NewGuid();
|
|
var wallet = new Wallet(userId, CurrencyType.VND);
|
|
wallet.Deposit(1000m, CurrencyType.VND, "Init");
|
|
|
|
// Mock repository to return our wallet when searching by UserId (CreateHoldCommand uses UserId)
|
|
_walletRepository.GetByUserIdAsync(userId).Returns(wallet);
|
|
|
|
var handler = new CreateHoldCommandHandler(_walletRepository, _createLogger);
|
|
var command = new CreateHoldCommand(
|
|
userId,
|
|
100m,
|
|
"VND",
|
|
"CAMPAIGN",
|
|
Guid.NewGuid(),
|
|
"Test Hold");
|
|
|
|
// Act
|
|
var result = await handler.Handle(command, CancellationToken.None);
|
|
|
|
// Assert
|
|
result.Should().NotBeNull();
|
|
result.RemainingAmount.Should().Be(100m);
|
|
wallet.GetBalance(CurrencyType.VND).Should().Be(900m);
|
|
|
|
_walletRepository.Received(1).Update(wallet);
|
|
await _unitOfWork.Received(1).SaveEntitiesAsync(Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ExecuteHold_ValidCommand_ShouldSucceed()
|
|
{
|
|
// Arrange
|
|
var userId = Guid.NewGuid();
|
|
var wallet = new Wallet(userId, CurrencyType.VND);
|
|
wallet.Deposit(1000m, CurrencyType.VND, "Init");
|
|
var hold = wallet.Hold(100m, CurrencyType.VND, "REF", Guid.NewGuid(), "Desc");
|
|
|
|
// Mock repository to return wallet by ID (ExecuteHoldCommand uses WalletId)
|
|
_walletRepository.GetByIdAsync(wallet.Id).Returns(wallet);
|
|
|
|
var handler = new ExecuteHoldCommandHandler(_walletRepository, _executeLogger);
|
|
var command = new ExecuteHoldCommand(
|
|
wallet.Id,
|
|
hold.Id,
|
|
40m,
|
|
"EXEC_REF");
|
|
|
|
// Act
|
|
var result = await handler.Handle(command, CancellationToken.None);
|
|
|
|
// Assert
|
|
result.ExecutedAmount.Should().Be(40m);
|
|
result.RemainingAmount.Should().Be(60m);
|
|
|
|
await _unitOfWork.Received(1).SaveEntitiesAsync(Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ExecuteHold_InsufficientHoldAmount_ShouldThrow()
|
|
{
|
|
// Arrange
|
|
var userId = Guid.NewGuid();
|
|
var wallet = new Wallet(userId, CurrencyType.VND);
|
|
wallet.Deposit(1000m, CurrencyType.VND, "Init");
|
|
var hold = wallet.Hold(100m, CurrencyType.VND, "REF", Guid.NewGuid(), "Desc");
|
|
|
|
_walletRepository.GetByIdAsync(wallet.Id).Returns(wallet);
|
|
|
|
var handler = new ExecuteHoldCommandHandler(_walletRepository, _executeLogger);
|
|
var command = new ExecuteHoldCommand(
|
|
wallet.Id,
|
|
hold.Id,
|
|
150m, // Try to execute more than held
|
|
"EXEC_REF");
|
|
|
|
// Act & Assert
|
|
await Assert.ThrowsAsync<WalletDomainException>(() =>
|
|
handler.Handle(command, CancellationToken.None));
|
|
}
|
|
}
|