Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using AutoStack.Application.Common.Interfaces;
using AutoStack.Application.Common.Interfaces.Auth;
using AutoStack.Application.Features.Auth.Commands.Register;
using AutoStack.Application.Tests.Common;
using AutoStack.Domain.Entities;
using FluentAssertions;
using Microsoft.Extensions.Configuration;
using Moq;
using Xunit;

Expand All @@ -11,17 +13,26 @@ namespace AutoStack.Application.Tests.Features.Auth.Commands.Register;
public class RegisterCommandHandlerTests : CommandHandlerTestBase
{
private readonly Mock<IPasswordHasher> _mockPasswordHasher;
private readonly Mock<IEmailService> _mockEmailService;
private readonly Mock<IConfiguration> _mockConfiguration;
private readonly RegisterCommandHandler _handler;

public RegisterCommandHandlerTests()
{
_mockPasswordHasher = new Mock<IPasswordHasher>();
_mockEmailService = new Mock<IEmailService>();
_mockConfiguration = new Mock<IConfiguration>();

_mockConfiguration.Setup(c => c.GetSection("EmailVerification:ExpiryMinutes").Value)
.Returns("15");

_handler = new RegisterCommandHandler(
MockUserRepository.Object,
_mockPasswordHasher.Object,
MockUnitOfWork.Object,
MockAuditLog.Object
MockAuditLog.Object,
_mockEmailService.Object,
_mockConfiguration.Object
);
}

Expand Down Expand Up @@ -359,7 +370,7 @@ public async Task Handle_ShouldSaveChanges()

await _handler.Handle(command, CancellationToken.None);

MockUnitOfWork.Verify(u => u.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once);
MockUnitOfWork.Verify(u => u.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Exactly(2));
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
using AutoStack.Application.Common.Interfaces;
using AutoStack.Application.Features.Auth.Commands.ResendVerificationEmail;
using AutoStack.Application.Tests.Common;
using AutoStack.Domain.Entities;
using FluentAssertions;
using Microsoft.Extensions.Configuration;
using Moq;
using Xunit;

namespace AutoStack.Application.Tests.Features.Auth.Commands.ResendVerificationEmail;

public class ResendVerificationEmailCommandHandlerTests : CommandHandlerTestBase
{
private readonly Mock<IEmailService> _mockEmailService;
private readonly Mock<IConfiguration> _mockConfiguration;
private readonly ResendVerificationEmailCommandHandler _handler;

public ResendVerificationEmailCommandHandlerTests()
{
_mockEmailService = new Mock<IEmailService>();
_mockConfiguration = new Mock<IConfiguration>();

_mockConfiguration.Setup(c => c.GetSection("EmailVerification:ExpiryMinutes").Value)
.Returns("15");

_handler = new ResendVerificationEmailCommandHandler(
MockUserRepository.Object,
MockUnitOfWork.Object,
_mockEmailService.Object,
MockAuditLog.Object,
_mockConfiguration.Object
);
}

[Fact]
public async Task Handle_WithValidUser_ShouldGenerateAndSendCode()
{
var userId = Guid.NewGuid();
var user = User.CreateUser("test@example.com", "testuser");

MockUserRepository.Setup(r => r.GetByIdAsync(userId, It.IsAny<CancellationToken>()))
.ReturnsAsync(user);

MockUserRepository.Setup(r => r.UpdateAsync(It.IsAny<User>(), It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask);

MockUnitOfWork.Setup(u => u.SaveChangesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(1);

_mockEmailService.Setup(e => e.SendEmailAsync(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>()))
.Returns(Task.CompletedTask);

var command = new ResendVerificationEmailCommand(userId);
var result = await _handler.Handle(command, CancellationToken.None);

result.IsSuccess.Should().BeTrue();
user.EmailVerificationCode.Should().NotBeNullOrEmpty();
user.EmailVerificationCode.Should().HaveLength(6);
user.EmailVerificationCodeExpiry.Should().BeAfter(DateTime.UtcNow);
}

[Fact]
public async Task Handle_WithNonExistentUser_ShouldReturnFailure()
{
var userId = Guid.NewGuid();

MockUserRepository.Setup(r => r.GetByIdAsync(userId, It.IsAny<CancellationToken>()))
.ReturnsAsync((User?)null);

var command = new ResendVerificationEmailCommand(userId);
var result = await _handler.Handle(command, CancellationToken.None);

result.IsSuccess.Should().BeFalse();
result.Message.Should().Be("User not found");
}

[Fact]
public async Task Handle_WithAlreadyVerifiedEmail_ShouldReturnFailure()
{
var userId = Guid.NewGuid();
var user = User.CreateUser("test@example.com", "testuser");
user.SetEmailVerificationCode("123456", DateTime.UtcNow.AddMinutes(15));
user.VerifyEmail();

MockUserRepository.Setup(r => r.GetByIdAsync(userId, It.IsAny<CancellationToken>()))
.ReturnsAsync(user);

var command = new ResendVerificationEmailCommand(userId);
var result = await _handler.Handle(command, CancellationToken.None);

result.IsSuccess.Should().BeFalse();
result.Message.Should().Be("Email is already verified");
}

[Fact]
public async Task Handle_WithValidUser_ShouldSendEmail()
{
var userId = Guid.NewGuid();
var user = User.CreateUser("test@example.com", "testuser");

MockUserRepository.Setup(r => r.GetByIdAsync(userId, It.IsAny<CancellationToken>()))
.ReturnsAsync(user);

MockUserRepository.Setup(r => r.UpdateAsync(It.IsAny<User>(), It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask);

MockUnitOfWork.Setup(u => u.SaveChangesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(1);

_mockEmailService.Setup(e => e.SendEmailAsync(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>()))
.Returns(Task.CompletedTask);

var command = new ResendVerificationEmailCommand(userId);
await _handler.Handle(command, CancellationToken.None);

_mockEmailService.Verify(e => e.SendEmailAsync(
"test@example.com",
"Verify Your Email - AutoStack",
It.IsAny<string>()), Times.Once);
}

[Fact]
public async Task Handle_WithValidUser_ShouldSaveChanges()
{
var userId = Guid.NewGuid();
var user = User.CreateUser("test@example.com", "testuser");

MockUserRepository.Setup(r => r.GetByIdAsync(userId, It.IsAny<CancellationToken>()))
.ReturnsAsync(user);

MockUserRepository.Setup(r => r.UpdateAsync(It.IsAny<User>(), It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask);

MockUnitOfWork.Setup(u => u.SaveChangesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(1);

_mockEmailService.Setup(e => e.SendEmailAsync(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>()))
.Returns(Task.CompletedTask);

var command = new ResendVerificationEmailCommand(userId);
await _handler.Handle(command, CancellationToken.None);

MockUserRepository.Verify(r => r.UpdateAsync(user, It.IsAny<CancellationToken>()), Times.Once);
MockUnitOfWork.Verify(u => u.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once);
}

[Fact]
public async Task Handle_WithValidUser_ShouldUseConfiguredExpiry()
{
var userId = Guid.NewGuid();
var user = User.CreateUser("test@example.com", "testuser");

_mockConfiguration.Setup(c => c.GetSection("EmailVerification:ExpiryMinutes").Value)
.Returns("30");

var handler = new ResendVerificationEmailCommandHandler(
MockUserRepository.Object,
MockUnitOfWork.Object,
_mockEmailService.Object,
MockAuditLog.Object,
_mockConfiguration.Object
);

MockUserRepository.Setup(r => r.GetByIdAsync(userId, It.IsAny<CancellationToken>()))
.ReturnsAsync(user);

MockUserRepository.Setup(r => r.UpdateAsync(It.IsAny<User>(), It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask);

MockUnitOfWork.Setup(u => u.SaveChangesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(1);

_mockEmailService.Setup(e => e.SendEmailAsync(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>()))
.Returns(Task.CompletedTask);

var command = new ResendVerificationEmailCommand(userId);
await handler.Handle(command, CancellationToken.None);

user.EmailVerificationCodeExpiry.Should().BeCloseTo(DateTime.UtcNow.AddMinutes(30), TimeSpan.FromSeconds(5));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using AutoStack.Application.Features.Auth.Commands.ResendVerificationEmail;
using FluentAssertions;
using Xunit;

namespace AutoStack.Application.Tests.Features.Auth.Commands.ResendVerificationEmail;

public class ResendVerificationEmailCommandValidatorTests
{
private readonly ResendVerificationEmailCommandValidator _validator;

public ResendVerificationEmailCommandValidatorTests()
{
_validator = new ResendVerificationEmailCommandValidator();
}

[Fact]
public void Validate_WithValidCommand_ShouldNotHaveErrors()
{
var command = new ResendVerificationEmailCommand(Guid.NewGuid());

var result = _validator.Validate(command);

result.IsValid.Should().BeTrue();
}

[Fact]
public void Validate_WithEmptyUserId_ShouldHaveError()
{
var command = new ResendVerificationEmailCommand(Guid.Empty);

var result = _validator.Validate(command);

result.IsValid.Should().BeFalse();
result.Errors.Should().Contain(e => e.PropertyName == "UserId" && e.ErrorMessage == "User ID is required");
}
}
Loading
Loading