Files
Ho Ngoc Hai 76d75c753b Migrate
2026-05-23 18:37:02 +07:00

129 lines
4.4 KiB
C#

using FluentAssertions;
using Microsoft.Extensions.Logging;
using NSubstitute;
using PromotionService.API.Application.Commands;
using PromotionService.Domain.AggregatesModel.CampaignAggregate;
using PromotionService.Domain.Exceptions;
using PromotionService.Domain.SeedWork;
using Xunit;
namespace PromotionService.UnitTests.Application;
/// <summary>
/// EN: Unit tests for ClaimVoucherCommandHandler.
/// VI: Unit tests cho ClaimVoucherCommandHandler.
/// </summary>
public class ClaimVoucherCommandHandlerTests
{
private readonly ICampaignRepository _campaignRepository;
private readonly ILogger<ClaimVoucherCommandHandler> _logger;
private readonly ClaimVoucherCommandHandler _handler;
public ClaimVoucherCommandHandlerTests()
{
_campaignRepository = Substitute.For<ICampaignRepository>();
_logger = Substitute.For<ILogger<ClaimVoucherCommandHandler>>();
var unitOfWork = Substitute.For<IUnitOfWork>();
unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>()).Returns(true);
_campaignRepository.UnitOfWork.Returns(unitOfWork);
_handler = new ClaimVoucherCommandHandler(_campaignRepository, _logger);
}
private static Campaign CreateActiveCampaign()
{
var campaign = new Campaign(
merchantId: Guid.NewGuid(),
name: "Free Voucher Campaign",
description: null,
backingAssetType: AssetType.Currency,
backingAssetCode: "VND",
faceValue: 50_000m,
acquisitionType: AcquisitionType.Free,
acquisitionPrice: 0m,
totalVouchers: 100,
startDate: DateTime.UtcNow.AddDays(-1),
endDate: DateTime.UtcNow.AddDays(30),
voucherValidityDays: 30,
maxPerUser: 1);
campaign.SetEscrowHold(Guid.NewGuid(), Guid.NewGuid());
campaign.GenerateVouchers(100);
campaign.Activate();
return campaign;
}
[Fact]
public async Task Handle_FreeCampaign_ClaimsVoucherSuccessfully()
{
// Arrange
var campaign = CreateActiveCampaign();
var userId = Guid.NewGuid();
var command = new ClaimVoucherCommand(campaign.Id, userId);
_campaignRepository.GetByIdAsync(campaign.Id)
.Returns(campaign);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
result.Should().NotBeNull();
result.OwnerId.Should().Be(userId);
result.Status.Should().Be("Claimed");
result.RemainingValue.Should().Be(50_000m);
_campaignRepository.Received(1).Update(campaign);
await _campaignRepository.UnitOfWork.Received(1).SaveEntitiesAsync(Arg.Any<CancellationToken>());
}
[Fact]
public async Task Handle_NonFreeCampaign_ThrowsDomainException()
{
// Arrange
var campaign = new Campaign(
merchantId: Guid.NewGuid(),
name: "Paid Voucher Campaign",
description: null,
backingAssetType: AssetType.Currency,
backingAssetCode: "VND",
faceValue: 100_000m,
acquisitionType: AcquisitionType.Purchase, // Not free!
acquisitionPrice: 50_000m,
totalVouchers: 100,
startDate: DateTime.UtcNow.AddDays(-1),
endDate: DateTime.UtcNow.AddDays(30),
voucherValidityDays: 30,
maxPerUser: 1);
campaign.SetEscrowHold(Guid.NewGuid(), Guid.NewGuid());
campaign.GenerateVouchers(100);
campaign.Activate();
_campaignRepository.GetByIdAsync(campaign.Id).Returns(campaign);
var command = new ClaimVoucherCommand(campaign.Id, Guid.NewGuid());
// Act & Assert
var act = async () => await _handler.Handle(command, CancellationToken.None);
await act.Should().ThrowAsync<PromotionDomainException>()
.WithMessage("*requires payment*");
}
[Fact]
public async Task Handle_CampaignNotFound_ThrowsDomainException()
{
// Arrange
var nonExistentId = Guid.NewGuid();
_campaignRepository.GetByIdAsync(nonExistentId).Returns((Campaign?)null);
var command = new ClaimVoucherCommand(nonExistentId, Guid.NewGuid());
// Act & Assert
var act = async () => await _handler.Handle(command, CancellationToken.None);
await act.Should().ThrowAsync<PromotionDomainException>()
.WithMessage("*not found*");
}
}