mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
311 lines
9.6 KiB
C#
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
|
|
);
|
|
}
|
|
}
|