Files
the-biergarten-app/src/Core/Service/Service.Auth.Tests/RegisterService.test.cs

311 lines
9.6 KiB
C#

using Domain.Entities;
using Domain.Exceptions;
using FluentAssertions;
using Infrastructure.PasswordHashing;
using Infrastructure.Repository.Auth;
using Moq;
namespace Service.Auth.Tests;
public class RegisterServiceTest
{
private readonly Mock<IAuthRepository> _authRepoMock;
private readonly Mock<IPasswordInfrastructure> _passwordInfraMock;
private readonly Mock<ITokenService> _tokenServiceMock;
private readonly RegisterService _registerService;
public RegisterServiceTest()
{
_authRepoMock = new Mock<IAuthRepository>();
_passwordInfraMock = new Mock<IPasswordInfrastructure>();
_tokenServiceMock = new Mock<ITokenService>();
_registerService = new RegisterService(
_authRepoMock.Object,
_passwordInfraMock.Object,
_tokenServiceMock.Object
);
}
[Fact]
public async Task RegisterAsync_WithValidData_CreatesUserAndReturnsAuthServiceReturn()
{
// Arrange
var userAccount = new UserAccount
{
Username = "newuser",
FirstName = "John",
LastName = "Doe",
Email = "john.doe@example.com",
DateOfBirth = new DateTime(1990, 1, 1),
};
const string password = "SecurePassword123!";
const string hashedPassword = "hashed_password_value";
var expectedUserId = Guid.NewGuid();
// Mock: No existing user
_authRepoMock
.Setup(x => x.GetUserByUsernameAsync(userAccount.Username))
.ReturnsAsync((UserAccount?)null);
_authRepoMock
.Setup(x => x.GetUserByEmailAsync(userAccount.Email))
.ReturnsAsync((UserAccount?)null);
// Mock: Password hashing
_passwordInfraMock
.Setup(x => x.Hash(password))
.Returns(hashedPassword);
// Mock: User registration
_authRepoMock
.Setup(x =>
x.RegisterUserAsync(
userAccount.Username,
userAccount.FirstName,
userAccount.LastName,
userAccount.Email,
userAccount.DateOfBirth,
hashedPassword
)
)
.ReturnsAsync(
new UserAccount
{
UserAccountId = expectedUserId,
Username = userAccount.Username,
FirstName = userAccount.FirstName,
LastName = userAccount.LastName,
Email = userAccount.Email,
DateOfBirth = userAccount.DateOfBirth,
CreatedAt = DateTime.UtcNow,
}
);
// Mock: Token generation
_tokenServiceMock
.Setup(x => x.GenerateAccessToken(It.IsAny<UserAccount>()))
.Returns("access-token");
_tokenServiceMock
.Setup(x => x.GenerateRefreshToken(It.IsAny<UserAccount>()))
.Returns("refresh-token");
// Act
var result = await _registerService.RegisterAsync(
userAccount,
password
);
// Assert
result.Should().NotBeNull();
result.UserAccount.UserAccountId.Should().Be(expectedUserId);
result.UserAccount.Username.Should().Be(userAccount.Username);
result.UserAccount.Email.Should().Be(userAccount.Email);
result.AccessToken.Should().Be("access-token");
result.RefreshToken.Should().Be("refresh-token");
// Verify all mocks were called as expected
_authRepoMock.Verify(
x => x.GetUserByUsernameAsync(userAccount.Username),
Times.Once
);
_authRepoMock.Verify(
x => x.GetUserByEmailAsync(userAccount.Email),
Times.Once
);
_passwordInfraMock.Verify(x => x.Hash(password), Times.Once);
_authRepoMock.Verify(
x =>
x.RegisterUserAsync(
userAccount.Username,
userAccount.FirstName,
userAccount.LastName,
userAccount.Email,
userAccount.DateOfBirth,
hashedPassword
),
Times.Once
);
}
[Fact]
public async Task RegisterAsync_WithExistingUsername_ThrowsConflictException()
{
// Arrange
var userAccount = new UserAccount
{
Username = "existinguser",
FirstName = "Jane",
LastName = "Smith",
Email = "jane.smith@example.com",
DateOfBirth = new DateTime(1995, 5, 15),
};
var password = "Password123!";
var existingUser = new UserAccount
{
UserAccountId = Guid.NewGuid(),
Username = "existinguser",
FirstName = "Existing",
LastName = "User",
Email = "existing@example.com",
DateOfBirth = new DateTime(1990, 1, 1),
};
_authRepoMock
.Setup(x => x.GetUserByUsernameAsync(userAccount.Username))
.ReturnsAsync(existingUser);
_authRepoMock
.Setup(x => x.GetUserByEmailAsync(userAccount.Email))
.ReturnsAsync((UserAccount?)null);
// Act
var act = async () =>
await _registerService.RegisterAsync(userAccount, password);
// Assert
await act.Should()
.ThrowAsync<ConflictException>()
.WithMessage("Username or email already exists");
// Verify that registration was never called
_authRepoMock.Verify(
x =>
x.RegisterUserAsync(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<DateTime>(),
It.IsAny<string>()
),
Times.Never
);
}
[Fact]
public async Task RegisterAsync_WithExistingEmail_ThrowsConflictException()
{
// Arrange
var userAccount = new UserAccount
{
Username = "newuser",
FirstName = "Jane",
LastName = "Smith",
Email = "existing@example.com",
DateOfBirth = new DateTime(1995, 5, 15),
};
var password = "Password123!";
var existingUser = new UserAccount
{
UserAccountId = Guid.NewGuid(),
Username = "otheruser",
FirstName = "Existing",
LastName = "User",
Email = "existing@example.com",
DateOfBirth = new DateTime(1990, 1, 1),
};
_authRepoMock
.Setup(x => x.GetUserByUsernameAsync(userAccount.Username))
.ReturnsAsync((UserAccount?)null);
_authRepoMock
.Setup(x => x.GetUserByEmailAsync(userAccount.Email))
.ReturnsAsync(existingUser);
// Act
var act = async () =>
await _registerService.RegisterAsync(userAccount, password);
// Assert
await act.Should()
.ThrowAsync<ConflictException>()
.WithMessage("Username or email already exists");
_authRepoMock.Verify(
x =>
x.RegisterUserAsync(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<DateTime>(),
It.IsAny<string>()
),
Times.Never
);
}
[Fact]
public async Task RegisterAsync_PasswordIsHashed_BeforeStoringInDatabase()
{
// Arrange
var userAccount = new UserAccount
{
Username = "secureuser",
FirstName = "Secure",
LastName = "User",
Email = "secure@example.com",
DateOfBirth = new DateTime(1990, 1, 1),
};
var plainPassword = "PlainPassword123!";
var hashedPassword = "hashed_secure_password";
_authRepoMock
.Setup(x => x.GetUserByUsernameAsync(It.IsAny<string>()))
.ReturnsAsync((UserAccount?)null);
_authRepoMock
.Setup(x => x.GetUserByEmailAsync(It.IsAny<string>()))
.ReturnsAsync((UserAccount?)null);
_passwordInfraMock
.Setup(x => x.Hash(plainPassword))
.Returns(hashedPassword);
_authRepoMock
.Setup(x =>
x.RegisterUserAsync(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<DateTime>(),
hashedPassword
)
)
.ReturnsAsync(new UserAccount { UserAccountId = Guid.NewGuid() });
_tokenServiceMock
.Setup(x => x.GenerateAccessToken(It.IsAny<UserAccount>()))
.Returns("access-token");
_tokenServiceMock
.Setup(x => x.GenerateRefreshToken(It.IsAny<UserAccount>()))
.Returns("refresh-token");
// Act
await _registerService.RegisterAsync(userAccount, plainPassword);
// Assert
_passwordInfraMock.Verify(x => x.Hash(plainPassword), Times.Once);
_authRepoMock.Verify(
x =>
x.RegisterUserAsync(
userAccount.Username,
userAccount.FirstName,
userAccount.LastName,
userAccount.Email,
userAccount.DateOfBirth,
hashedPassword
), // Verify hashed password is used
Times.Once
);
}
}