173 lines
6.0 KiB
C#
173 lines
6.0 KiB
C#
using FluentAssertions;
|
|
using Microsoft.Extensions.Logging;
|
|
using NSubstitute;
|
|
using PromotionService.API.Application.Commands;
|
|
using PromotionService.API.Application.Services;
|
|
using PromotionService.Domain.AggregatesModel.CampaignAggregate;
|
|
using PromotionService.Domain.Exceptions;
|
|
using PromotionService.Domain.SeedWork;
|
|
using Xunit;
|
|
|
|
namespace PromotionService.UnitTests.Application;
|
|
|
|
/// <summary>
|
|
/// EN: Unit tests for CreateCampaignCommandHandler.
|
|
/// VI: Unit tests cho CreateCampaignCommandHandler.
|
|
/// </summary>
|
|
public class CreateCampaignCommandHandlerTests
|
|
{
|
|
private readonly ICampaignRepository _campaignRepository;
|
|
private readonly IWalletServiceClient _walletService;
|
|
private readonly ILogger<CreateCampaignCommandHandler> _logger;
|
|
private readonly CreateCampaignCommandHandler _handler;
|
|
|
|
public CreateCampaignCommandHandlerTests()
|
|
{
|
|
_campaignRepository = Substitute.For<ICampaignRepository>();
|
|
_walletService = Substitute.For<IWalletServiceClient>();
|
|
_logger = Substitute.For<ILogger<CreateCampaignCommandHandler>>();
|
|
|
|
// Setup UnitOfWork
|
|
var unitOfWork = Substitute.For<IUnitOfWork>();
|
|
unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>()).Returns(true);
|
|
_campaignRepository.UnitOfWork.Returns(unitOfWork);
|
|
|
|
_handler = new CreateCampaignCommandHandler(
|
|
_campaignRepository,
|
|
_walletService,
|
|
_logger);
|
|
}
|
|
|
|
private static CreateCampaignCommand CreateValidCommand() => new CreateCampaignCommand(
|
|
MerchantId: Guid.NewGuid(),
|
|
MerchantWalletId: Guid.NewGuid(),
|
|
Name: "Test Campaign",
|
|
Description: "Test Description",
|
|
BackingAssetType: "Currency",
|
|
BackingAssetCode: "VND",
|
|
FaceValue: 100_000m,
|
|
AcquisitionType: "Free",
|
|
AcquisitionPrice: 0m,
|
|
TotalVouchers: 100,
|
|
StartDate: DateTime.UtcNow.AddDays(1),
|
|
EndDate: DateTime.UtcNow.AddDays(30),
|
|
VoucherValidityDays: 30,
|
|
MaxPerUser: 1);
|
|
|
|
[Fact]
|
|
public async Task Handle_ValidCommand_CreatesCampaignAndReturnsDto()
|
|
{
|
|
// Arrange
|
|
var command = CreateValidCommand();
|
|
var holdResult = new HoldResult(
|
|
HoldId: Guid.NewGuid(),
|
|
WalletId: command.MerchantWalletId,
|
|
Amount: command.FaceValue * command.TotalVouchers,
|
|
CurrencyType: command.BackingAssetCode,
|
|
ReferenceType: "CAMPAIGN",
|
|
ReferenceId: Guid.NewGuid(),
|
|
Status: "Active");
|
|
|
|
_walletService.CreateHoldAsync(
|
|
Arg.Any<Guid>(),
|
|
Arg.Any<decimal>(),
|
|
Arg.Any<string>(),
|
|
Arg.Any<string>(),
|
|
Arg.Any<Guid>(),
|
|
Arg.Any<string>(),
|
|
Arg.Any<CancellationToken>())
|
|
.Returns(holdResult);
|
|
|
|
_campaignRepository.Add(Arg.Any<Campaign>())
|
|
.Returns(callInfo => callInfo.Arg<Campaign>());
|
|
|
|
// Act
|
|
var result = await _handler.Handle(command, CancellationToken.None);
|
|
|
|
// Assert
|
|
result.Should().NotBeNull();
|
|
result.Name.Should().Be(command.Name);
|
|
result.TotalVouchers.Should().Be(command.TotalVouchers);
|
|
result.Status.Should().Be("Draft");
|
|
|
|
// Verify repository called
|
|
_campaignRepository.Received(1).Add(Arg.Is<Campaign>(c => c.Name == command.Name));
|
|
await _campaignRepository.UnitOfWork.Received(1).SaveEntitiesAsync(Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_ValidCommand_CallsWalletServiceToCreateEscrow()
|
|
{
|
|
// Arrange
|
|
var command = CreateValidCommand();
|
|
var expectedEscrowAmount = command.FaceValue * command.TotalVouchers;
|
|
|
|
_walletService.CreateHoldAsync(
|
|
Arg.Any<Guid>(),
|
|
Arg.Any<decimal>(),
|
|
Arg.Any<string>(),
|
|
Arg.Any<string>(),
|
|
Arg.Any<Guid>(),
|
|
Arg.Any<string>(),
|
|
Arg.Any<CancellationToken>())
|
|
.Returns(new HoldResult(
|
|
Guid.NewGuid(),
|
|
command.MerchantWalletId,
|
|
expectedEscrowAmount,
|
|
"VND",
|
|
"CAMPAIGN",
|
|
Guid.NewGuid(),
|
|
"Active"));
|
|
|
|
_campaignRepository.Add(Arg.Any<Campaign>())
|
|
.Returns(callInfo => callInfo.Arg<Campaign>());
|
|
|
|
// Act
|
|
await _handler.Handle(command, CancellationToken.None);
|
|
|
|
// Assert - Verify wallet service was called with correct escrow amount
|
|
await _walletService.Received(1).CreateHoldAsync(
|
|
command.MerchantWalletId,
|
|
expectedEscrowAmount,
|
|
command.BackingAssetCode,
|
|
"CAMPAIGN",
|
|
Arg.Any<Guid>(),
|
|
Arg.Is<string>(s => s.Contains(command.Name)),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Handle_InvalidAcquisitionType_ThrowsDomainException()
|
|
{
|
|
// Arrange
|
|
var command = new CreateCampaignCommand(
|
|
MerchantId: Guid.NewGuid(),
|
|
MerchantWalletId: Guid.NewGuid(),
|
|
Name: "Test Campaign",
|
|
Description: null,
|
|
BackingAssetType: "Currency",
|
|
BackingAssetCode: "VND",
|
|
FaceValue: 100_000m,
|
|
AcquisitionType: "InvalidType", // Invalid!
|
|
AcquisitionPrice: 0m,
|
|
TotalVouchers: 100,
|
|
StartDate: DateTime.UtcNow.AddDays(1),
|
|
EndDate: DateTime.UtcNow.AddDays(30));
|
|
|
|
// Act & Assert
|
|
var act = async () => await _handler.Handle(command, CancellationToken.None);
|
|
await act.Should().ThrowAsync<PromotionDomainException>()
|
|
.WithMessage("*Invalid acquisition type*");
|
|
|
|
// Verify wallet service was NOT called
|
|
await _walletService.DidNotReceive().CreateHoldAsync(
|
|
Arg.Any<Guid>(),
|
|
Arg.Any<decimal>(),
|
|
Arg.Any<string>(),
|
|
Arg.Any<string>(),
|
|
Arg.Any<Guid>(),
|
|
Arg.Any<string>(),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
}
|