Files
ColaFlow/colaflow-api/tests/Modules/Identity/ColaFlow.Modules.Identity.Domain.Tests/Entities/PasswordResetTokenTests.cs
Yaojia Wang 172d0de1fe
Some checks failed
Code Coverage / Generate Coverage Report (push) Has been cancelled
Tests / Run Tests (9.0.x) (push) Has been cancelled
Tests / Docker Build Test (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Add test
2025-11-04 00:20:42 +01:00

215 lines
6.2 KiB
C#

using ColaFlow.Modules.Identity.Domain.Aggregates.Users;
using ColaFlow.Modules.Identity.Domain.Entities;
using FluentAssertions;
using Xunit;
namespace ColaFlow.Modules.Identity.Domain.Tests.Entities;
public sealed class PasswordResetTokenTests
{
private readonly UserId _userId = UserId.CreateUnique();
private const string TestTokenHash = "hashed_token_value";
private const string TestIpAddress = "192.168.1.1";
private const string TestUserAgent = "Mozilla/5.0";
[Fact]
public void Create_WithValidData_ShouldSucceed()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(1);
// Act
var token = PasswordResetToken.Create(
_userId,
TestTokenHash,
expiresAt,
TestIpAddress,
TestUserAgent);
// Assert
token.Should().NotBeNull();
token.Id.Should().NotBe(Guid.Empty);
token.UserId.Should().Be(_userId);
token.TokenHash.Should().Be(TestTokenHash);
token.ExpiresAt.Should().Be(expiresAt);
token.IpAddress.Should().Be(TestIpAddress);
token.UserAgent.Should().Be(TestUserAgent);
token.UsedAt.Should().BeNull();
token.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
token.IsExpired.Should().BeFalse();
token.IsUsed.Should().BeFalse();
token.IsValid.Should().BeTrue();
}
[Fact]
public void Create_WithoutOptionalParameters_ShouldSucceed()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(1);
// Act
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
// Assert
token.Should().NotBeNull();
token.IpAddress.Should().BeNull();
token.UserAgent.Should().BeNull();
}
[Fact]
public void IsExpired_WithFutureExpiration_ShouldReturnFalse()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(1);
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
// Act & Assert
token.IsExpired.Should().BeFalse();
}
[Fact]
public void IsExpired_WithPastExpiration_ShouldReturnTrue()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(-1);
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
// Act & Assert
token.IsExpired.Should().BeTrue();
}
[Fact]
public void IsUsed_WhenNotUsed_ShouldReturnFalse()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(1);
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
// Act & Assert
token.IsUsed.Should().BeFalse();
}
[Fact]
public void IsUsed_WhenUsed_ShouldReturnTrue()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(1);
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
token.MarkAsUsed();
// Act & Assert
token.IsUsed.Should().BeTrue();
}
[Fact]
public void IsValid_WithValidToken_ShouldReturnTrue()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(1);
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
// Act & Assert
token.IsValid.Should().BeTrue();
}
[Fact]
public void IsValid_WithExpiredToken_ShouldReturnFalse()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(-1);
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
// Act & Assert
token.IsValid.Should().BeFalse();
}
[Fact]
public void IsValid_WithUsedToken_ShouldReturnFalse()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(1);
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
token.MarkAsUsed();
// Act & Assert
token.IsValid.Should().BeFalse();
}
[Fact]
public void MarkAsUsed_WithValidToken_ShouldSetUsedAt()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(1);
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
// Act
token.MarkAsUsed();
// Assert
token.UsedAt.Should().NotBeNull();
token.UsedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
token.IsUsed.Should().BeTrue();
token.IsValid.Should().BeFalse();
}
[Fact]
public void MarkAsUsed_WithExpiredToken_ShouldThrowException()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(-1);
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
// Act
var act = () => token.MarkAsUsed();
// Assert
act.Should().Throw<InvalidOperationException>()
.WithMessage("*Token is not valid for password reset*");
}
[Fact]
public void MarkAsUsed_WithAlreadyUsedToken_ShouldThrowException()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(1);
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
token.MarkAsUsed();
// Act
var act = () => token.MarkAsUsed();
// Assert
act.Should().Throw<InvalidOperationException>()
.WithMessage("*Token is not valid for password reset*");
}
[Fact]
public void Create_WithShortExpiration_ShouldBeSecure()
{
// Arrange - Tokens should expire quickly for security (e.g., 1 hour)
var expiresAt = DateTime.UtcNow.AddHours(1);
// Act
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
// Assert
token.ExpiresAt.Should().BeBefore(DateTime.UtcNow.AddHours(2));
}
[Fact]
public void MarkAsUsed_PreventTokenReuse_ShouldEnforceSingleUse()
{
// Arrange
var expiresAt = DateTime.UtcNow.AddHours(1);
var token = PasswordResetToken.Create(_userId, TestTokenHash, expiresAt);
// Act - Use token once
token.MarkAsUsed();
// Assert - Subsequent use should fail
var act = () => token.MarkAsUsed();
act.Should().Throw<InvalidOperationException>()
.WithMessage("*Token is not valid for password reset*");
}
}