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

200 lines
5.1 KiB
C#

using FluentAssertions;
using PromotionService.Domain.AggregatesModel.CampaignAggregate;
using PromotionService.Domain.Exceptions;
using Xunit;
namespace PromotionService.UnitTests.Domain;
/// <summary>
/// EN: Unit tests for Voucher entity.
/// VI: Unit tests cho Voucher entity.
/// </summary>
public class VoucherEntityTests
{
private const int DefaultValidityDays = 30;
#region Helper Methods
private static Voucher CreateAvailableVoucher() => new Voucher(
campaignId: Guid.NewGuid(),
code: "TEST-VOUCHER-001",
faceValue: 100_000m,
validityDays: DefaultValidityDays);
private static Voucher CreateClaimedVoucher()
{
var voucher = CreateAvailableVoucher();
voucher.Claim(Guid.NewGuid(), DefaultValidityDays);
return voucher;
}
#endregion
#region Constructor Tests
[Fact]
public void Constructor_ValidParameters_CreatesVoucherInAvailableStatus()
{
// Arrange & Act
var voucher = CreateAvailableVoucher();
// Assert
voucher.Id.Should().NotBeEmpty();
voucher.Code.Should().Be("TEST-VOUCHER-001");
voucher.FaceValue.Should().Be(100_000m);
voucher.RemainingValue.Should().Be(100_000m);
voucher.StatusId.Should().Be(VoucherStatus.Available.Id);
voucher.OwnerId.Should().BeNull();
}
#endregion
#region Claim Tests
[Fact]
public void Claim_AvailableVoucher_SetsOwnerAndClaimedStatus()
{
// Arrange
var voucher = CreateAvailableVoucher();
var userId = Guid.NewGuid();
// Act
voucher.Claim(userId, DefaultValidityDays);
// Assert
voucher.OwnerId.Should().Be(userId);
voucher.StatusId.Should().Be(VoucherStatus.Claimed.Id);
voucher.ClaimedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5));
voucher.ExpiresAt.Should().BeCloseTo(DateTime.UtcNow.AddDays(DefaultValidityDays), TimeSpan.FromSeconds(5));
}
[Fact]
public void Claim_AlreadyClaimedVoucher_ThrowsException()
{
// Arrange
var voucher = CreateClaimedVoucher();
// Act & Assert
var act = () => voucher.Claim(Guid.NewGuid(), DefaultValidityDays);
act.Should().Throw<VoucherAlreadyClaimedException>();
}
#endregion
#region Redeem Tests
[Fact]
public void Redeem_FullValue_SetsStatusToFullyRedeemed()
{
// Arrange
var voucher = CreateClaimedVoucher();
// Act
voucher.Redeem(100_000m); // Full face value
// Assert
voucher.RemainingValue.Should().Be(0);
voucher.StatusId.Should().Be(VoucherStatus.FullyRedeemed.Id);
voucher.RedeemedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5));
}
[Fact]
public void Redeem_PartialValue_SetsStatusToPartiallyRedeemed()
{
// Arrange
var voucher = CreateClaimedVoucher();
// Act
voucher.Redeem(50_000m); // Half
// Assert
voucher.RemainingValue.Should().Be(50_000m);
voucher.StatusId.Should().Be(VoucherStatus.PartiallyRedeemed.Id);
}
[Fact]
public void Redeem_MoreThanRemainingValue_CapsAtRemainingValue()
{
// Arrange
var voucher = CreateClaimedVoucher();
// Act - Request more than available
var actualRedeemed = voucher.Redeem(150_000m);
// Assert - Only redeems what's available
actualRedeemed.Should().Be(100_000m);
voucher.RemainingValue.Should().Be(0);
voucher.StatusId.Should().Be(VoucherStatus.FullyRedeemed.Id);
}
[Fact]
public void Redeem_AvailableVoucher_ThrowsException()
{
// Arrange
var voucher = CreateAvailableVoucher(); // Not claimed
// Act & Assert
var act = () => voucher.Redeem(50_000m);
act.Should().Throw<PromotionDomainException>()
.WithMessage("*not been claimed*");
}
#endregion
#region Expire Tests
[Fact]
public void Expire_ClaimedVoucher_SetsStatusToExpired()
{
// Arrange
var voucher = CreateClaimedVoucher();
// Act
voucher.Expire();
// Assert
voucher.StatusId.Should().Be(VoucherStatus.Expired.Id);
}
[Fact]
public void Expire_FullyRedeemedVoucher_DoesNotChangeStatus()
{
// Arrange
var voucher = CreateClaimedVoucher();
voucher.Redeem(100_000m);
// Act
voucher.Expire();
// Assert - Status remains FullyRedeemed
voucher.StatusId.Should().Be(VoucherStatus.FullyRedeemed.Id);
}
#endregion
#region Validation Tests
[Fact]
public void IsValidForRedemption_ClaimedNotExpired_ReturnsTrue()
{
// Arrange
var voucher = CreateClaimedVoucher();
// Act & Assert
voucher.IsValidForRedemption().Should().BeTrue();
}
[Fact]
public void IsValidForRedemption_FullyRedeemed_ReturnsFalse()
{
// Arrange
var voucher = CreateClaimedVoucher();
voucher.Redeem(100_000m);
// Act & Assert
voucher.IsValidForRedemption().Should().BeFalse();
}
#endregion
}