177 lines
5.0 KiB
C#
177 lines
5.0 KiB
C#
using ColaFlow.Modules.Identity.Domain.Entities;
|
|
using FluentAssertions;
|
|
using Xunit;
|
|
|
|
namespace ColaFlow.Modules.Identity.Domain.Tests.Entities;
|
|
|
|
public sealed class EmailRateLimitTests
|
|
{
|
|
private readonly Guid _tenantId = Guid.NewGuid();
|
|
private const string TestEmail = "user@example.com";
|
|
private const string OperationType = "verification";
|
|
|
|
[Fact]
|
|
public void Create_WithValidData_ShouldSucceed()
|
|
{
|
|
// Arrange & Act
|
|
var rateLimit = EmailRateLimit.Create(TestEmail, _tenantId, OperationType);
|
|
|
|
// Assert
|
|
rateLimit.Should().NotBeNull();
|
|
rateLimit.Id.Should().NotBe(Guid.Empty);
|
|
rateLimit.Email.Should().Be(TestEmail.ToLower());
|
|
rateLimit.TenantId.Should().Be(_tenantId);
|
|
rateLimit.OperationType.Should().Be(OperationType);
|
|
rateLimit.AttemptsCount.Should().Be(1);
|
|
rateLimit.LastSentAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
|
|
}
|
|
|
|
[Fact]
|
|
public void Create_ShouldNormalizeEmail()
|
|
{
|
|
// Arrange
|
|
const string mixedCaseEmail = "User@EXAMPLE.COM";
|
|
|
|
// Act
|
|
var rateLimit = EmailRateLimit.Create(mixedCaseEmail, _tenantId, OperationType);
|
|
|
|
// Assert
|
|
rateLimit.Email.Should().Be("user@example.com");
|
|
}
|
|
|
|
[Fact]
|
|
public void Create_WithEmptyEmail_ShouldThrowException()
|
|
{
|
|
// Arrange & Act
|
|
var act = () => EmailRateLimit.Create(string.Empty, _tenantId, OperationType);
|
|
|
|
// Assert
|
|
act.Should().Throw<ArgumentException>()
|
|
.WithMessage("*Email cannot be empty*");
|
|
}
|
|
|
|
[Fact]
|
|
public void Create_WithEmptyOperationType_ShouldThrowException()
|
|
{
|
|
// Arrange & Act
|
|
var act = () => EmailRateLimit.Create(TestEmail, _tenantId, string.Empty);
|
|
|
|
// Assert
|
|
act.Should().Throw<ArgumentException>()
|
|
.WithMessage("*Operation type cannot be empty*");
|
|
}
|
|
|
|
[Fact]
|
|
public void RecordAttempt_ShouldIncrementCount()
|
|
{
|
|
// Arrange
|
|
var rateLimit = EmailRateLimit.Create(TestEmail, _tenantId, OperationType);
|
|
var initialCount = rateLimit.AttemptsCount;
|
|
var initialLastSentAt = rateLimit.LastSentAt;
|
|
|
|
// Wait a bit to ensure time difference
|
|
System.Threading.Thread.Sleep(10);
|
|
|
|
// Act
|
|
rateLimit.RecordAttempt();
|
|
|
|
// Assert
|
|
rateLimit.AttemptsCount.Should().Be(initialCount + 1);
|
|
rateLimit.LastSentAt.Should().BeAfter(initialLastSentAt);
|
|
}
|
|
|
|
[Fact]
|
|
public void RecordAttempt_MultipleCallsMultiple_ShouldIncrementCorrectly()
|
|
{
|
|
// Arrange
|
|
var rateLimit = EmailRateLimit.Create(TestEmail, _tenantId, OperationType);
|
|
|
|
// Act
|
|
rateLimit.RecordAttempt();
|
|
rateLimit.RecordAttempt();
|
|
rateLimit.RecordAttempt();
|
|
|
|
// Assert
|
|
rateLimit.AttemptsCount.Should().Be(4); // 1 initial + 3 increments
|
|
}
|
|
|
|
[Fact]
|
|
public void ResetAttempts_ShouldResetCountToOne()
|
|
{
|
|
// Arrange
|
|
var rateLimit = EmailRateLimit.Create(TestEmail, _tenantId, OperationType);
|
|
rateLimit.RecordAttempt();
|
|
rateLimit.RecordAttempt();
|
|
rateLimit.RecordAttempt();
|
|
|
|
// Act
|
|
rateLimit.ResetAttempts();
|
|
|
|
// Assert
|
|
rateLimit.AttemptsCount.Should().Be(1);
|
|
}
|
|
|
|
[Fact]
|
|
public void ResetAttempts_ShouldUpdateLastSentAt()
|
|
{
|
|
// Arrange
|
|
var rateLimit = EmailRateLimit.Create(TestEmail, _tenantId, OperationType);
|
|
var initialLastSentAt = rateLimit.LastSentAt;
|
|
|
|
// Wait a bit to ensure time difference
|
|
System.Threading.Thread.Sleep(10);
|
|
|
|
// Act
|
|
rateLimit.ResetAttempts();
|
|
|
|
// Assert
|
|
rateLimit.LastSentAt.Should().BeAfter(initialLastSentAt);
|
|
rateLimit.LastSentAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
|
|
}
|
|
|
|
[Fact]
|
|
public void IsWindowExpired_WithinWindow_ShouldReturnFalse()
|
|
{
|
|
// Arrange
|
|
var rateLimit = EmailRateLimit.Create(TestEmail, _tenantId, OperationType);
|
|
var window = TimeSpan.FromMinutes(5);
|
|
|
|
// Act
|
|
var isExpired = rateLimit.IsWindowExpired(window);
|
|
|
|
// Assert
|
|
isExpired.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsWindowExpired_OutsideWindow_ShouldReturnTrue()
|
|
{
|
|
// Arrange
|
|
var rateLimit = EmailRateLimit.Create(TestEmail, _tenantId, OperationType);
|
|
var window = TimeSpan.FromMilliseconds(1);
|
|
|
|
// Wait for window to expire
|
|
System.Threading.Thread.Sleep(10);
|
|
|
|
// Act
|
|
var isExpired = rateLimit.IsWindowExpired(window);
|
|
|
|
// Assert
|
|
isExpired.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsWindowExpired_WithZeroWindow_ShouldReturnTrue()
|
|
{
|
|
// Arrange
|
|
var rateLimit = EmailRateLimit.Create(TestEmail, _tenantId, OperationType);
|
|
var window = TimeSpan.Zero;
|
|
|
|
// Act
|
|
var isExpired = rateLimit.IsWindowExpired(window);
|
|
|
|
// Assert
|
|
isExpired.Should().BeTrue();
|
|
}
|
|
}
|