Files
pos-system/microservices/services/promotion-service-net/tests/PromotionService.UnitTests/Application/CreateCampaignCommandHandlerTests.cs
Ho Ngoc Hai 76d75c753b Migrate
2026-05-23 18:37:02 +07:00

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>());
}
}