Files
pos-system/services/iam-service-net/tests/IamService.UnitTests/Domain/Verification/IdentityVerificationTests.cs

319 lines
9.6 KiB
C#

using Xunit;
using FluentAssertions;
using IamService.Domain.AggregatesModel.VerificationAggregate;
using IamService.Domain.Events;
namespace IamService.UnitTests.Domain.Verification;
/// <summary>
/// EN: Unit tests for IdentityVerification aggregate root.
/// VI: Unit tests cho IdentityVerification aggregate root.
/// </summary>
public class IdentityVerificationTests
{
private readonly Guid _validUserId = Guid.NewGuid();
#region Phone Verification Creation Tests
[Fact]
public void CreatePhoneVerification_ValidParameters_CreatesVerificationAndOtp()
{
// Arrange & Act
var (verification, otp) = IdentityVerification.CreatePhoneVerification(
_validUserId, "+84901234567");
// Assert
verification.Should().NotBeNull();
verification.Id.Should().NotBeEmpty();
verification.UserId.Should().Be(_validUserId);
verification.Type.Should().Be(VerificationType.Phone);
verification.Status.Should().Be(VerificationStatus.Pending);
verification.VerificationData.Should().Be("+84901234567");
verification.VerificationCodeHash.Should().NotBeNullOrEmpty();
verification.RequestedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
verification.ExpiresAt.Should().BeAfter(DateTime.UtcNow);
verification.AttemptCount.Should().Be(0);
otp.Should().HaveLength(6);
otp.Should().MatchRegex("^[0-9]{6}$");
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void CreatePhoneVerification_InvalidPhoneNumber_ThrowsArgumentException(string? phoneNumber)
{
// Arrange & Act
var act = () => IdentityVerification.CreatePhoneVerification(_validUserId, phoneNumber!);
// Assert
act.Should().Throw<ArgumentException>()
.WithMessage("*Phone number*empty*");
}
[Fact]
public void CreatePhoneVerification_EmptyUserId_ThrowsArgumentException()
{
// Arrange & Act
var act = () => IdentityVerification.CreatePhoneVerification(Guid.Empty, "+84901234567");
// Assert
act.Should().Throw<ArgumentException>()
.WithMessage("*User ID*empty*");
}
[Fact]
public void CreatePhoneVerification_RaisesVerificationRequestedEvent()
{
// Arrange & Act
var (verification, _) = IdentityVerification.CreatePhoneVerification(_validUserId, "+84901234567");
// Assert
verification.DomainEvents.Should().ContainSingle();
verification.DomainEvents.First().Should().BeOfType<VerificationRequestedEvent>();
}
#endregion
#region Document Verification Creation Tests
[Fact]
public void CreateDocumentVerification_ValidParameters_CreatesVerification()
{
// Arrange & Act
var verification = IdentityVerification.CreateDocumentVerification(
_validUserId,
"https://storage.example.com/documents/id-card.jpg",
"ID_CARD");
// Assert
verification.Should().NotBeNull();
verification.Id.Should().NotBeEmpty();
verification.UserId.Should().Be(_validUserId);
verification.Type.Should().Be(VerificationType.Document);
verification.Status.Should().Be(VerificationStatus.InProgress);
verification.VerificationData.Should().Be("https://storage.example.com/documents/id-card.jpg");
verification.Metadata.Should().Contain("ID_CARD");
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void CreateDocumentVerification_InvalidDocumentUrl_ThrowsArgumentException(string? documentUrl)
{
// Arrange & Act
var act = () => IdentityVerification.CreateDocumentVerification(_validUserId, documentUrl!);
// Assert
act.Should().Throw<ArgumentException>()
.WithMessage("*Document URL*empty*");
}
#endregion
#region VerifyCode Tests
[Fact]
public void VerifyCode_CorrectCode_ReturnsTrue()
{
// Arrange
var (verification, otp) = IdentityVerification.CreatePhoneVerification(_validUserId, "+84901234567");
// Act
var result = verification.VerifyCode(otp);
// Assert
result.Should().BeTrue();
verification.Status.Should().Be(VerificationStatus.Verified);
verification.VerifiedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
}
[Fact]
public void VerifyCode_IncorrectCode_ReturnsFalse()
{
// Arrange
var (verification, _) = IdentityVerification.CreatePhoneVerification(_validUserId, "+84901234567");
// Act
var result = verification.VerifyCode("000000");
// Assert
result.Should().BeFalse();
verification.Status.Should().Be(VerificationStatus.Pending);
verification.AttemptCount.Should().Be(1);
}
[Fact]
public void VerifyCode_ExceedsMaxAttempts_RejectsVerification()
{
// Arrange
var (verification, _) = IdentityVerification.CreatePhoneVerification(_validUserId, "+84901234567");
// Act - Try 6 times (max is 5)
for (int i = 0; i < 6; i++)
{
verification.VerifyCode("000000");
}
// Assert
verification.Status.Should().Be(VerificationStatus.Rejected);
verification.RejectionReason.Should().Contain("Maximum attempts");
}
[Fact]
public void VerifyCode_AlreadyVerified_ThrowsInvalidOperationException()
{
// Arrange
var (verification, otp) = IdentityVerification.CreatePhoneVerification(_validUserId, "+84901234567");
verification.VerifyCode(otp); // Verify first
// Act
var act = () => verification.VerifyCode("000000");
// Assert
act.Should().Throw<InvalidOperationException>()
.WithMessage("*final status*");
}
#endregion
#region MarkAsVerified Tests
[Fact]
public void MarkAsVerified_PendingStatus_ChangesToVerified()
{
// Arrange
var verification = IdentityVerification.CreateDocumentVerification(
_validUserId, "https://example.com/doc.jpg");
verification.ClearDomainEvents();
// Act
verification.MarkAsVerified();
// Assert
verification.Status.Should().Be(VerificationStatus.Verified);
verification.VerifiedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
}
[Fact]
public void MarkAsVerified_RaisesVerificationCompletedEvent()
{
// Arrange
var verification = IdentityVerification.CreateDocumentVerification(
_validUserId, "https://example.com/doc.jpg");
verification.ClearDomainEvents();
// Act
verification.MarkAsVerified();
// Assert
verification.DomainEvents.Should().ContainSingle();
verification.DomainEvents.First().Should().BeOfType<VerificationCompletedEvent>();
}
#endregion
#region MarkAsRejected Tests
[Fact]
public void MarkAsRejected_PendingStatus_ChangesToRejected()
{
// Arrange
var verification = IdentityVerification.CreateDocumentVerification(
_validUserId, "https://example.com/doc.jpg");
// Act
verification.MarkAsRejected("Document is blurry");
// Assert
verification.Status.Should().Be(VerificationStatus.Rejected);
verification.RejectionReason.Should().Be("Document is blurry");
}
[Fact]
public void MarkAsRejected_AlreadyFinal_ThrowsInvalidOperationException()
{
// Arrange
var (verification, otp) = IdentityVerification.CreatePhoneVerification(_validUserId, "+84901234567");
verification.VerifyCode(otp);
// Act
var act = () => verification.MarkAsRejected("Some reason");
// Assert
act.Should().Throw<InvalidOperationException>()
.WithMessage("*final status*");
}
#endregion
#region Cancel Tests
[Fact]
public void Cancel_PendingStatus_ChangesToCancelled()
{
// Arrange
var (verification, _) = IdentityVerification.CreatePhoneVerification(_validUserId, "+84901234567");
// Act
verification.Cancel();
// Assert
verification.Status.Should().Be(VerificationStatus.Cancelled);
}
[Fact]
public void Cancel_FinalStatus_ThrowsInvalidOperationException()
{
// Arrange
var (verification, otp) = IdentityVerification.CreatePhoneVerification(_validUserId, "+84901234567");
verification.VerifyCode(otp);
// Act
var act = () => verification.Cancel();
// Assert
act.Should().Throw<InvalidOperationException>()
.WithMessage("*final status*");
}
#endregion
#region Helper Properties Tests
[Fact]
public void IsExpired_BeforeExpiration_ReturnsFalse()
{
// Arrange
var (verification, _) = IdentityVerification.CreatePhoneVerification(_validUserId, "+84901234567");
// Assert
verification.IsExpired.Should().BeFalse();
}
[Fact]
public void CanRetry_UnderMaxAttempts_ReturnsTrue()
{
// Arrange
var (verification, _) = IdentityVerification.CreatePhoneVerification(_validUserId, "+84901234567");
verification.VerifyCode("000000"); // 1 failed attempt
// Assert
verification.CanRetry.Should().BeTrue();
}
[Fact]
public void CanRetry_AfterVerified_ReturnsFalse()
{
// Arrange
var (verification, otp) = IdentityVerification.CreatePhoneVerification(_validUserId, "+84901234567");
verification.VerifyCode(otp);
// Assert
verification.CanRetry.Should().BeFalse();
}
#endregion
}