Add test
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
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 EmailVerificationTokenTests
|
||||
{
|
||||
private readonly UserId _userId = UserId.CreateUnique();
|
||||
private const string TestTokenHash = "hashed_token_value";
|
||||
|
||||
[Fact]
|
||||
public void Create_WithValidData_ShouldSucceed()
|
||||
{
|
||||
// Arrange
|
||||
var expiresAt = DateTime.UtcNow.AddHours(24);
|
||||
|
||||
// Act
|
||||
var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
|
||||
|
||||
// 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.VerifiedAt.Should().BeNull();
|
||||
token.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
|
||||
token.IsExpired.Should().BeFalse();
|
||||
token.IsVerified.Should().BeFalse();
|
||||
token.IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsExpired_WithFutureExpiration_ShouldReturnFalse()
|
||||
{
|
||||
// Arrange
|
||||
var expiresAt = DateTime.UtcNow.AddHours(24);
|
||||
var token = EmailVerificationToken.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 = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
|
||||
|
||||
// Act & Assert
|
||||
token.IsExpired.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsVerified_WhenNotVerified_ShouldReturnFalse()
|
||||
{
|
||||
// Arrange
|
||||
var expiresAt = DateTime.UtcNow.AddHours(24);
|
||||
var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
|
||||
|
||||
// Act & Assert
|
||||
token.IsVerified.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsVerified_WhenVerified_ShouldReturnTrue()
|
||||
{
|
||||
// Arrange
|
||||
var expiresAt = DateTime.UtcNow.AddHours(24);
|
||||
var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
|
||||
token.MarkAsVerified();
|
||||
|
||||
// Act & Assert
|
||||
token.IsVerified.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValid_WithValidToken_ShouldReturnTrue()
|
||||
{
|
||||
// Arrange
|
||||
var expiresAt = DateTime.UtcNow.AddHours(24);
|
||||
var token = EmailVerificationToken.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 = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
|
||||
|
||||
// Act & Assert
|
||||
token.IsValid.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValid_WithVerifiedToken_ShouldReturnFalse()
|
||||
{
|
||||
// Arrange
|
||||
var expiresAt = DateTime.UtcNow.AddHours(24);
|
||||
var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
|
||||
token.MarkAsVerified();
|
||||
|
||||
// Act & Assert
|
||||
token.IsValid.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MarkAsVerified_WithValidToken_ShouldSetVerifiedAt()
|
||||
{
|
||||
// Arrange
|
||||
var expiresAt = DateTime.UtcNow.AddHours(24);
|
||||
var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
|
||||
|
||||
// Act
|
||||
token.MarkAsVerified();
|
||||
|
||||
// Assert
|
||||
token.VerifiedAt.Should().NotBeNull();
|
||||
token.VerifiedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
|
||||
token.IsVerified.Should().BeTrue();
|
||||
token.IsValid.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MarkAsVerified_WithExpiredToken_ShouldThrowException()
|
||||
{
|
||||
// Arrange
|
||||
var expiresAt = DateTime.UtcNow.AddHours(-1);
|
||||
var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
|
||||
|
||||
// Act
|
||||
var act = () => token.MarkAsVerified();
|
||||
|
||||
// Assert
|
||||
act.Should().Throw<InvalidOperationException>()
|
||||
.WithMessage("*Token is not valid for verification*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MarkAsVerified_WithAlreadyVerifiedToken_ShouldThrowException()
|
||||
{
|
||||
// Arrange
|
||||
var expiresAt = DateTime.UtcNow.AddHours(24);
|
||||
var token = EmailVerificationToken.Create(_userId, TestTokenHash, expiresAt);
|
||||
token.MarkAsVerified();
|
||||
|
||||
// Act
|
||||
var act = () => token.MarkAsVerified();
|
||||
|
||||
// Assert
|
||||
act.Should().Throw<InvalidOperationException>()
|
||||
.WithMessage("*Token is not valid for verification*");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
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*");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants;
|
||||
using ColaFlow.Modules.Identity.Domain.Aggregates.Users;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace ColaFlow.Modules.Identity.Domain.Tests.Entities;
|
||||
|
||||
public sealed class UserTenantRoleTests
|
||||
{
|
||||
private readonly UserId _userId = UserId.CreateUnique();
|
||||
private readonly TenantId _tenantId = TenantId.CreateUnique();
|
||||
private readonly Guid _assignedByUserId = Guid.NewGuid();
|
||||
|
||||
[Fact]
|
||||
public void Create_WithValidData_ShouldSucceed()
|
||||
{
|
||||
// Arrange & Act
|
||||
var userTenantRole = UserTenantRole.Create(
|
||||
_userId,
|
||||
_tenantId,
|
||||
TenantRole.TenantMember,
|
||||
_assignedByUserId);
|
||||
|
||||
// Assert
|
||||
userTenantRole.Should().NotBeNull();
|
||||
userTenantRole.Id.Should().NotBe(Guid.Empty);
|
||||
userTenantRole.UserId.Should().Be(_userId);
|
||||
userTenantRole.TenantId.Should().Be(_tenantId);
|
||||
userTenantRole.Role.Should().Be(TenantRole.TenantMember);
|
||||
userTenantRole.AssignedByUserId.Should().Be(_assignedByUserId);
|
||||
userTenantRole.AssignedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_WithoutAssignedByUserId_ShouldSucceed()
|
||||
{
|
||||
// Arrange & Act
|
||||
var userTenantRole = UserTenantRole.Create(
|
||||
_userId,
|
||||
_tenantId,
|
||||
TenantRole.TenantAdmin);
|
||||
|
||||
// Assert
|
||||
userTenantRole.Should().NotBeNull();
|
||||
userTenantRole.AssignedByUserId.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateRole_WithValidRole_ShouldUpdate()
|
||||
{
|
||||
// Arrange
|
||||
var userTenantRole = UserTenantRole.Create(
|
||||
_userId,
|
||||
_tenantId,
|
||||
TenantRole.TenantMember);
|
||||
var originalAssignedAt = userTenantRole.AssignedAt;
|
||||
var updatedByUserId = Guid.NewGuid();
|
||||
|
||||
// Act
|
||||
userTenantRole.UpdateRole(TenantRole.TenantAdmin, updatedByUserId);
|
||||
|
||||
// Assert
|
||||
userTenantRole.Role.Should().Be(TenantRole.TenantAdmin);
|
||||
userTenantRole.AssignedByUserId.Should().Be(updatedByUserId);
|
||||
userTenantRole.AssignedAt.Should().Be(originalAssignedAt); // AssignedAt should NOT change
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateRole_WithSameRole_ShouldBeIdempotent()
|
||||
{
|
||||
// Arrange
|
||||
var userTenantRole = UserTenantRole.Create(
|
||||
_userId,
|
||||
_tenantId,
|
||||
TenantRole.TenantMember,
|
||||
_assignedByUserId);
|
||||
var originalAssignedByUserId = userTenantRole.AssignedByUserId;
|
||||
var originalRole = userTenantRole.Role;
|
||||
|
||||
// Act
|
||||
userTenantRole.UpdateRole(TenantRole.TenantMember, Guid.NewGuid());
|
||||
|
||||
// Assert - Should not update if role is the same
|
||||
userTenantRole.Role.Should().Be(originalRole);
|
||||
userTenantRole.AssignedByUserId.Should().Be(originalAssignedByUserId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasPermission_WithTenantOwner_ShouldReturnTrueForAllPermissions()
|
||||
{
|
||||
// Arrange
|
||||
var userTenantRole = UserTenantRole.Create(
|
||||
_userId,
|
||||
_tenantId,
|
||||
TenantRole.TenantOwner);
|
||||
|
||||
// Act & Assert
|
||||
userTenantRole.HasPermission("read_projects").Should().BeTrue();
|
||||
userTenantRole.HasPermission("write_projects").Should().BeTrue();
|
||||
userTenantRole.HasPermission("delete_projects").Should().BeTrue();
|
||||
userTenantRole.HasPermission("any_permission").Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasPermission_WithAIAgent_ShouldReturnTrueForReadAndPreview()
|
||||
{
|
||||
// Arrange
|
||||
var userTenantRole = UserTenantRole.Create(
|
||||
_userId,
|
||||
_tenantId,
|
||||
TenantRole.AIAgent);
|
||||
|
||||
// Act & Assert
|
||||
userTenantRole.HasPermission("read_projects").Should().BeTrue();
|
||||
userTenantRole.HasPermission("read_users").Should().BeTrue();
|
||||
userTenantRole.HasPermission("write_preview_changes").Should().BeTrue();
|
||||
userTenantRole.HasPermission("write_direct_changes").Should().BeFalse();
|
||||
userTenantRole.HasPermission("delete_projects").Should().BeFalse();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user