Merge pull request 'dotnet unit tests' (#26) from feature/tests into develop
Reviewed-on: Natlinux/DotNetAngular#26
This commit was merged in pull request #26.
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
using Application.Common.Results;
|
||||
|
||||
namespace Application.UnitTest.Common.Results;
|
||||
|
||||
public class ResultTests
|
||||
{
|
||||
[Fact]
|
||||
public void Success_ShouldReturnResultWithIsSuccessTrue()
|
||||
{
|
||||
var result = Result.Success();
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.False(result.IsFailure);
|
||||
Assert.Equal(ErrorTypeConstant.None, result.Error.Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Success_WithValue_ShouldReturnResultWithValue()
|
||||
{
|
||||
var result = Result.Success("test-value");
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.Equal("test-value", result.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Failure_ShouldReturnResultWithIsFailureTrue()
|
||||
{
|
||||
var error = new Error("TestError", "Something went wrong");
|
||||
var result = Result.Failure(error);
|
||||
Assert.True(result.IsFailure);
|
||||
Assert.False(result.IsSuccess);
|
||||
Assert.Equal("TestError", result.Error.Code);
|
||||
Assert.Equal("Something went wrong", result.Error.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Failure_WithValueType_ShouldReturnTypedResult()
|
||||
{
|
||||
var error = new Error("NotFound", "Item not found");
|
||||
var result = Result.Failure<string>(error);
|
||||
Assert.True(result.IsFailure);
|
||||
Assert.Throws<InvalidOperationException>(() => result.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ShouldThrow_WhenSuccessWithError()
|
||||
{
|
||||
var error = new Error("SomeError", "message");
|
||||
Assert.Throws<InvalidOperationException>(() => new ResultTestCase(true, error));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ShouldThrow_WhenFailureWithNoError()
|
||||
{
|
||||
var noneError = new Error("None", string.Empty);
|
||||
Assert.Throws<InvalidOperationException>(() => new ResultTestCase(false, noneError));
|
||||
}
|
||||
}
|
||||
|
||||
public class ResultTestCase : Result
|
||||
{
|
||||
public ResultTestCase(bool isSuccess, Error error) : base(isSuccess, error)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using Application.DTOs;
|
||||
|
||||
namespace Application.UnitTest.DTOs;
|
||||
|
||||
public class PagedResultTests
|
||||
{
|
||||
[Fact]
|
||||
public void TotalPages_ShouldCalculateCorrectly_WhenExactDivision()
|
||||
{
|
||||
var paged = new PagedResult<string>
|
||||
{
|
||||
Items = ["a", "b", "c", "d", "e"],
|
||||
TotalCount = 5,
|
||||
PageNumber = 1,
|
||||
PageSize = 5
|
||||
};
|
||||
Assert.Equal(1, paged.TotalPages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TotalPages_ShouldRoundUp_WhenNotExactDivision()
|
||||
{
|
||||
var paged = new PagedResult<string>
|
||||
{
|
||||
Items = ["a", "b", "c"],
|
||||
TotalCount = 10,
|
||||
PageNumber = 1,
|
||||
PageSize = 3
|
||||
};
|
||||
Assert.Equal(4, paged.TotalPages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TotalPages_ShouldBeZero_WhenTotalCountIsZero()
|
||||
{
|
||||
var paged = new PagedResult<string>
|
||||
{
|
||||
Items = [],
|
||||
TotalCount = 0,
|
||||
PageNumber = 1,
|
||||
PageSize = 10
|
||||
};
|
||||
Assert.Equal(0, paged.TotalPages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasPreviousPage_ShouldBeFalse_WhenOnFirstPage()
|
||||
{
|
||||
var paged = new PagedResult<string>
|
||||
{
|
||||
Items = [],
|
||||
TotalCount = 20,
|
||||
PageNumber = 1,
|
||||
PageSize = 10
|
||||
};
|
||||
Assert.False(paged.HasPreviousPage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasPreviousPage_ShouldBeTrue_WhenPastFirstPage()
|
||||
{
|
||||
var paged = new PagedResult<string>
|
||||
{
|
||||
Items = [],
|
||||
TotalCount = 20,
|
||||
PageNumber = 2,
|
||||
PageSize = 10
|
||||
};
|
||||
Assert.True(paged.HasPreviousPage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasNext_ShouldBeFalse_WhenOnLastPage()
|
||||
{
|
||||
var paged = new PagedResult<string>
|
||||
{
|
||||
Items = [],
|
||||
TotalCount = 20,
|
||||
PageNumber = 2,
|
||||
PageSize = 10
|
||||
};
|
||||
Assert.False(paged.HasNext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasNext_ShouldBeTrue_WhenMorePagesExist()
|
||||
{
|
||||
var paged = new PagedResult<string>
|
||||
{
|
||||
Items = [],
|
||||
TotalCount = 20,
|
||||
PageNumber = 1,
|
||||
PageSize = 10
|
||||
};
|
||||
Assert.True(paged.HasNext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasNext_ShouldBeFalse_WhenOnlyOnePage()
|
||||
{
|
||||
var paged = new PagedResult<string>
|
||||
{
|
||||
Items = [],
|
||||
TotalCount = 3,
|
||||
PageNumber = 1,
|
||||
PageSize = 10
|
||||
};
|
||||
Assert.False(paged.HasNext);
|
||||
}
|
||||
}
|
||||
@@ -134,6 +134,28 @@ public class AuthenticationServiceTests
|
||||
Assert.Equal(AuthError.UserNotFound, result.Error);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoginAsync_ShouldReturnFailure_WhenPasswordIsWrong()
|
||||
{
|
||||
var password = "ValidP@ss1!";
|
||||
var user = new User
|
||||
{
|
||||
Id = 1,
|
||||
Username = "testuser",
|
||||
Email = "test@example.com",
|
||||
Password = new PasswordHasher<User>().HashPassword(
|
||||
new User { Username = "testuser", Email = "test@example.com", Password = password },
|
||||
password)
|
||||
};
|
||||
var request = new LoginRequest(user.Email, "WrongPassword1!");
|
||||
_userRepository.GetUserByEmailAsync(request.Email).Returns(user);
|
||||
|
||||
var result = await _sut.LoginAsync(request);
|
||||
|
||||
Assert.True(result.IsFailure);
|
||||
Assert.Equal(AuthError.InvalidPassword, result.Error);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RefreshTokensAsync_ShouldReturnSuccess_WhenRefreshTokenIsValid()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using Application.Models;
|
||||
using Application.Services;
|
||||
using MailKit.Net.Smtp;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MimeKit;
|
||||
using NSubstitute;
|
||||
|
||||
namespace Application.UnitTest.Services;
|
||||
|
||||
public class EmailServiceTests
|
||||
{
|
||||
private readonly IConfiguration _configuration = Substitute.For<IConfiguration>();
|
||||
private readonly ISmtpClient _smtpClient = Substitute.For<ISmtpClient>();
|
||||
private readonly EmailService _sut;
|
||||
|
||||
public EmailServiceTests()
|
||||
{
|
||||
_configuration["EmailSettings:From"].Returns("sender@gmail.com");
|
||||
_configuration["EmailSettings:SmtpServer"].Returns("smtp.gmail.com");
|
||||
_configuration["EmailSettings:Password"].Returns("app-password");
|
||||
|
||||
_sut = new EmailService(_configuration, () => _smtpClient);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SendEmailAsync_ShouldConnectAndAuthenticate()
|
||||
{
|
||||
var request = new EmailRequest("recipient@example.com", "Test Subject", "<p>Hello</p>");
|
||||
|
||||
_sut.SendEmailAsync(request);
|
||||
|
||||
_smtpClient.Received(1).Connect("smtp.gmail.com", 465, true);
|
||||
_smtpClient.Received(1).Authenticate("sender@gmail.com", "app-password");
|
||||
_smtpClient.Received(1).Send(Arg.Any<MimeMessage>());
|
||||
_smtpClient.Received(1).Disconnect(true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SendEmailAsync_ShouldSendMessageWithCorrectDetails()
|
||||
{
|
||||
var request = new EmailRequest("recipient@example.com", "Welcome!", "<h1>Hi</h1>");
|
||||
|
||||
_sut.SendEmailAsync(request);
|
||||
|
||||
_smtpClient.Received(1).Send(Arg.Is<MimeMessage>(msg =>
|
||||
msg.To.Mailboxes.Any(m => m.Address == "recipient@example.com") &&
|
||||
msg.Subject == "Welcome!"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SendEmailAsync_ShouldDisposeClient_AfterSending()
|
||||
{
|
||||
var request = new EmailRequest("recipient@example.com", "Subject", "Body");
|
||||
|
||||
_sut.SendEmailAsync(request);
|
||||
|
||||
_smtpClient.Received(1).Disconnect(true);
|
||||
_smtpClient.Received(1).Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using Application.Services;
|
||||
using Domain.Entities;
|
||||
using Domain.Interface;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using NSubstitute;
|
||||
|
||||
namespace Application.UnitTest.Services;
|
||||
|
||||
public class JwtServiceTests
|
||||
{
|
||||
private readonly IConfiguration _configuration = Substitute.For<IConfiguration>();
|
||||
private readonly IUserRepository _userRepository = Substitute.For<IUserRepository>();
|
||||
private readonly IUnitOfWork _unitOfWork = Substitute.For<IUnitOfWork>();
|
||||
private readonly JwtService _sut;
|
||||
|
||||
public JwtServiceTests()
|
||||
{
|
||||
var section = Substitute.For<IConfigurationSection>();
|
||||
section.Value.Returns("veryveryveryveryveryveryverysecretkey");
|
||||
_configuration.GetSection("Jwt:Key").Returns(section);
|
||||
|
||||
_configuration["Jwt:Key"].Returns("veryveryveryveryveryveryverysecretkey");
|
||||
_configuration["Jwt:Issuer"].Returns("https://localhost:7091");
|
||||
_configuration["Jwt:Audience"].Returns("http://localhost:5184");
|
||||
|
||||
_sut = new JwtService(_configuration, _userRepository, _unitOfWork);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GenerateTokenAsync_ShouldReturnToken_WhenUserIsValid()
|
||||
{
|
||||
var user = new User
|
||||
{
|
||||
Id = 1,
|
||||
Username = "testuser",
|
||||
Email = "test@example.com",
|
||||
Password = "hash"
|
||||
};
|
||||
_userRepository.GetUserRolesByEmailAsync(user.Email)
|
||||
.Returns(["User"]);
|
||||
|
||||
var token = await _sut.GenerateTokenAsync(user);
|
||||
|
||||
Assert.NotNull(token);
|
||||
Assert.NotEmpty(token);
|
||||
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwtToken = handler.ReadJwtToken(token);
|
||||
|
||||
Assert.Equal("https://localhost:7091", jwtToken.Issuer);
|
||||
Assert.Contains(jwtToken.Claims, c => c.Type == "email" && c.Value == user.Email);
|
||||
Assert.Contains(jwtToken.Claims, c => c.Type == "UserId" && c.Value == "1");
|
||||
Assert.Contains(jwtToken.Claims, c => c.Type == "username" && c.Value == user.Username);
|
||||
Assert.Contains(jwtToken.Claims, c => c.Type == "role" && c.Value == "User");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GenerateTokenAsync_ShouldIncludeAllRoles()
|
||||
{
|
||||
var user = new User
|
||||
{
|
||||
Id = 2,
|
||||
Username = "adminuser",
|
||||
Email = "admin@example.com",
|
||||
Password = "hash"
|
||||
};
|
||||
_userRepository.GetUserRolesByEmailAsync(user.Email)
|
||||
.Returns(["Admin", "User"]);
|
||||
|
||||
var token = await _sut.GenerateTokenAsync(user);
|
||||
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwtToken = handler.ReadJwtToken(token);
|
||||
var roleClaims = jwtToken.Claims.Where(c => c.Type == "role").Select(c => c.Value).ToList();
|
||||
|
||||
Assert.Contains("Admin", roleClaims);
|
||||
Assert.Contains("User", roleClaims);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GenerateAndSaveRefreshTokenAsync_ShouldUpdateUserAndCommit()
|
||||
{
|
||||
var user = new User
|
||||
{
|
||||
Id = 1,
|
||||
Username = "testuser",
|
||||
Email = "test@example.com",
|
||||
Password = "hash"
|
||||
};
|
||||
|
||||
var refreshToken = await _sut.GenerateAndSaveRefreshTokenAsync(user);
|
||||
|
||||
Assert.NotNull(refreshToken);
|
||||
Assert.NotEmpty(refreshToken);
|
||||
Assert.Equal(refreshToken, user.RefreshToken);
|
||||
Assert.NotNull(user.RefreshTokenExpiryTime);
|
||||
Assert.True(user.RefreshTokenExpiryTime > DateTime.UtcNow);
|
||||
_userRepository.Received(1).Update(user);
|
||||
await _unitOfWork.Received(1).CommitAsync();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateRefreshTokenAsync_ShouldReturnNull_WhenUserNotFound()
|
||||
{
|
||||
_userRepository.GetUserByIdAsync(99).Returns((User?)null);
|
||||
|
||||
var result = await _sut.ValidateRefreshTokenAsync(99, "some-token");
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateRefreshTokenAsync_ShouldReturnNull_WhenTokenMismatch()
|
||||
{
|
||||
var user = new User
|
||||
{
|
||||
Id = 1,
|
||||
Username = "testuser",
|
||||
Email = "test@example.com",
|
||||
Password = "hash",
|
||||
RefreshToken = "stored-token",
|
||||
RefreshTokenExpiryTime = DateTime.UtcNow.AddDays(1)
|
||||
};
|
||||
_userRepository.GetUserByIdAsync(1).Returns(user);
|
||||
|
||||
var result = await _sut.ValidateRefreshTokenAsync(1, "wrong-token");
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateRefreshTokenAsync_ShouldReturnNull_WhenTokenExpired()
|
||||
{
|
||||
var user = new User
|
||||
{
|
||||
Id = 1,
|
||||
Username = "testuser",
|
||||
Email = "test@example.com",
|
||||
Password = "hash",
|
||||
RefreshToken = "expired-token",
|
||||
RefreshTokenExpiryTime = DateTime.UtcNow.AddDays(-1)
|
||||
};
|
||||
_userRepository.GetUserByIdAsync(1).Returns(user);
|
||||
|
||||
var result = await _sut.ValidateRefreshTokenAsync(1, "expired-token");
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateRefreshTokenAsync_ShouldReturnUser_WhenTokenValid()
|
||||
{
|
||||
var user = new User
|
||||
{
|
||||
Id = 1,
|
||||
Username = "testuser",
|
||||
Email = "test@example.com",
|
||||
Password = "hash",
|
||||
RefreshToken = "valid-token",
|
||||
RefreshTokenExpiryTime = DateTime.UtcNow.AddDays(1)
|
||||
};
|
||||
_userRepository.GetUserByIdAsync(1).Returns(user);
|
||||
|
||||
var result = await _sut.ValidateRefreshTokenAsync(1, "valid-token");
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(1, result.Id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user