Skip to content

Commit

Permalink
[RSN-31] - Create validators according to constraints (#32)
Browse files Browse the repository at this point in the history
Created validators according to constraints using fluent
validation.

---------

Co-authored-by: raczu <[email protected]>
  • Loading branch information
mkoper02 and raczu authored Jun 6, 2024
1 parent d814d53 commit d1ca7f0
Show file tree
Hide file tree
Showing 50 changed files with 2,707 additions and 67 deletions.
5 changes: 5 additions & 0 deletions Server/ReasnAPI/ReasnAPI.Tests/ReasnAPI.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Microsoft.Playwright.MSTest" Version="1.27.1" />
<PackageReference Include="Moq.EntityFrameworkCore" Version="8.0.1.2" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ReasnAPI\ReasnAPI.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="Microsoft.Playwright.MSTest" />
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
Expand Down
12 changes: 0 additions & 12 deletions Server/ReasnAPI/ReasnAPI.Tests/UnitTest1.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System.Net;
using Microsoft.AspNetCore.Http;
using Moq;
using ReasnAPI.Exceptions;
using ReasnAPI.Services.Exceptions;

namespace ReasnAPI.Tests.UnitTests.Exceptions;

[TestClass]
public class ServiceExceptionHandlerTests
{
private Mock<IProblemDetailsService> _mockProblemDetailsService = null!;
private ServiceExceptionHandler _handler = null!;

[TestInitialize]
public void Setup()
{
_mockProblemDetailsService = new Mock<IProblemDetailsService>();
_handler = new ServiceExceptionHandler(_mockProblemDetailsService.Object);
}

[TestMethod]
public async Task HandleException_WhenBadRequestException_ShouldReturnProblemDetails()
{
var httpContext = new DefaultHttpContext();
var exception = new BadRequestException("Bad request");

ProblemDetailsContext? problemDetailsContext = null;
_mockProblemDetailsService.Setup(x =>
x.TryWriteAsync(It.IsAny<ProblemDetailsContext>()))
.Callback<ProblemDetailsContext>(context => problemDetailsContext = context)
.ReturnsAsync(true);

await _handler.TryHandleAsync(httpContext, exception, CancellationToken.None);

Assert.AreEqual((int)HttpStatusCode.BadRequest, httpContext.Response.StatusCode);
Assert.IsNotNull(problemDetailsContext);
Assert.AreEqual("https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
problemDetailsContext.ProblemDetails.Type);
Assert.AreEqual("A bad request was made",
problemDetailsContext.ProblemDetails.Title);
Assert.AreEqual(exception, problemDetailsContext.Exception);
Assert.AreEqual(exception.Message, problemDetailsContext.ProblemDetails.Detail);
}

[TestMethod]
public async Task HandleException_WhenNotFoundException_ShouldReturnProblemDetails()
{
var httpContext = new DefaultHttpContext();
var exception = new NotFoundException("Resource not found");

ProblemDetailsContext? problemDetailsContext = null;
_mockProblemDetailsService.Setup(x =>
x.TryWriteAsync(It.IsAny<ProblemDetailsContext>()))
.Callback<ProblemDetailsContext>(context => problemDetailsContext = context)
.ReturnsAsync(true);

await _handler.TryHandleAsync(httpContext, exception, CancellationToken.None);

Assert.AreEqual((int)HttpStatusCode.NotFound, httpContext.Response.StatusCode);
Assert.IsNotNull(problemDetailsContext);
Assert.AreEqual("https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4",
problemDetailsContext.ProblemDetails.Type);
Assert.AreEqual("A resource was not found",
problemDetailsContext.ProblemDetails.Title);
Assert.AreEqual(exception, problemDetailsContext.Exception);
Assert.AreEqual(exception.Message, problemDetailsContext.ProblemDetails.Detail);
}

[TestMethod]
public async Task HandleException_WhenVerificationException_ShouldReturnProblemDetails()
{
var httpContext = new DefaultHttpContext();
var exception = new VerificationException("Verification error");

ProblemDetailsContext? problemDetailsContext = null;
_mockProblemDetailsService.Setup(x =>
x.TryWriteAsync(It.IsAny<ProblemDetailsContext>()))
.Callback<ProblemDetailsContext>(context => problemDetailsContext = context)
.ReturnsAsync(true);

await _handler.TryHandleAsync(httpContext, exception, CancellationToken.None);

Assert.AreEqual((int)HttpStatusCode.BadRequest, httpContext.Response.StatusCode);
Assert.IsNotNull(problemDetailsContext);
Assert.AreEqual("https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
problemDetailsContext.ProblemDetails.Type);
Assert.AreEqual("A verification error occurred",
problemDetailsContext.ProblemDetails.Title);
Assert.AreEqual(exception, problemDetailsContext.Exception);
Assert.AreEqual(exception.Message, problemDetailsContext.ProblemDetails.Detail);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Net;
using FluentValidation;
using FluentValidation.Results;
using Microsoft.AspNetCore.Http;
using Moq;
using ReasnAPI.Exceptions;

namespace ReasnAPI.Tests.UnitTests.Exceptions;

[TestClass]
public class ValidationExceptionHandlerTests
{
private Mock<IProblemDetailsService> _mockProblemDetailsService = null!;
private ValidationExceptionHandler _handler = null!;

[TestInitialize]
public void Setup()
{
_mockProblemDetailsService = new Mock<IProblemDetailsService>();
_handler = new ValidationExceptionHandler(_mockProblemDetailsService.Object);
}

[TestMethod]
public async Task HandleException_WhenValidationException_ShouldReturnProblemDetails()
{
var httpContext = new DefaultHttpContext();
var exception = new ValidationException(new List<ValidationFailure>
{
new ("Email", "'Email' must not be empty."),
new ("Password", "'Password' must not be empty.")
});

ProblemDetailsContext? problemDetailsContext = null;
_mockProblemDetailsService.Setup(x =>
x.TryWriteAsync(It.IsAny<ProblemDetailsContext>()))
.Callback<ProblemDetailsContext>(context => problemDetailsContext = context)
.ReturnsAsync(true);

await _handler.TryHandleAsync(httpContext, exception, CancellationToken.None);

Assert.AreEqual((int)HttpStatusCode.BadRequest, httpContext.Response.StatusCode);
Assert.IsNotNull(problemDetailsContext);
Assert.AreEqual("https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
problemDetailsContext.ProblemDetails.Type);
Assert.AreEqual("A validation error occurred",
problemDetailsContext.ProblemDetails.Title);
Assert.AreEqual(exception, problemDetailsContext.Exception);
Assert.AreEqual("One or more validation errors occurred",
problemDetailsContext.ProblemDetails.Detail);

Assert.IsNotNull(problemDetailsContext.ProblemDetails.Extensions);
Assert.IsTrue(problemDetailsContext.ProblemDetails.Extensions.ContainsKey("errors"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.WebUtilities;
using Moq;
using Moq.EntityFrameworkCore;
using ReasnAPI.Models.Authentication;
using ReasnAPI.Models.Database;
using ReasnAPI.Models.DTOs;
using ReasnAPI.Services.Authentication;
using ReasnAPI.Services.Exceptions;

namespace ReasnAPI.Tests.UnitTests.Services.Authentication;

[TestClass]
public class AuthServiceTests
{
private Mock<ReasnContext> _mockContext = null!;
private PasswordHasher<User> _hasher = null!;
private AuthService _service = null!;

[TestInitialize]
public void Setup()
{
_mockContext = new Mock<ReasnContext>();
_hasher = new PasswordHasher<User>();
_service = new AuthService(_mockContext.Object);

var user = new User
{
Email = "[email protected]",
Username = "jsnow",
Password = _hasher.HashPassword(null!, "password")
};

_mockContext.Setup(c => c.Users)
.ReturnsDbSet(new List<User> { user });
}

[TestMethod]
public void Login_WhenUserExistsAndPasswordIsCorrect_ShouldReturnUser()
{
var request = new LoginRequest
{
Email = "[email protected]",
Password = "password"
};

var result = _service.Login(request);

Assert.IsNotNull(result);
Assert.IsInstanceOfType(result, typeof(User));
}

[TestMethod]
public void Login_WhenUserDoesNotExist_ShouldThrowNotFoundException()
{
var request = new LoginRequest
{
Email = "[email protected]"
};

Assert.ThrowsException<NotFoundException>(() => _service.Login(request));
}

[TestMethod]
public void Login_WhenPasswordIsIncorrect_ShouldThrowVerificationException()
{
var request = new LoginRequest
{
Email = "[email protected]",
Password = "wrong-password"
};

Assert.ThrowsException<VerificationException>(() => _service.Login(request));
}

[TestMethod]
public void Register_WhenUserWithEmailAlreadyExists_ShouldThrowBadRequestException()
{
var request = new RegisterRequest
{
Email = "[email protected]"
};

Assert.ThrowsException<BadRequestException>(() => _service.Register(request));
}

[TestMethod]
public void Register_WhenUserWithUsernameAlreadyExists_ShouldThrowBadRequestException()
{
var request = new RegisterRequest
{
Email = "[email protected]",
Username = "jsnow"
};

Assert.ThrowsException<BadRequestException>(() => _service.Register(request));
}

[TestMethod]
public void Register_WhenUserDoesNotExist_ShouldReturnRegisteredUser()
{
var request = new RegisterRequest
{
Name = "Jon",
Surname = "Stark",
Email = "[email protected]",
Username = "jstark",
Password = "S3cureP@ssword!",
Phone = "+123 456789",
Address = new AddressDto
{
Street = "The Wall",
City = "Castle Black",
Country = "Westeros",
State = "The North"
},
Role = "User"
};

var result = _service.Register(request);

Assert.IsNotNull(result);
Assert.IsInstanceOfType(result, typeof(User));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography;
using Microsoft.Extensions.Configuration;
using Moq;
using ReasnAPI.Models.Database;
using ReasnAPI.Models.Enums;
using ReasnAPI.Services.Authentication;

namespace ReasnAPI.Tests.UnitTests.Services.Authentication;

[TestClass]
public class TokenServiceTests
{
private const int DurationInHours = 8;
private const string IssAudValue = "http://localhost:5272";
private TokenService _service = null!;
private Mock<IConfiguration> _mockConfiguration = null!;
private User _validUser = null!;

[TestInitialize]
public void Setup()
{
_mockConfiguration = new Mock<IConfiguration>();

var bytes = new byte[32];
RandomNumberGenerator.Fill(bytes);
_mockConfiguration.Setup(x =>
x["JwtSettings:Key"]).Returns(Convert.ToBase64String(bytes));

_mockConfiguration.Setup(x =>
x["JwtSettings:Issuer"]).Returns(IssAudValue);

var mockSection = new Mock<IConfigurationSection>();
var mockAudienceValue = new Mock<IConfigurationSection>();
mockAudienceValue.Setup(x => x.Value).Returns(IssAudValue);
mockSection.Setup(x =>
x.GetChildren()).Returns(new List<IConfigurationSection> { mockAudienceValue.Object });
_mockConfiguration.Setup(x =>
x.GetSection("JwtSettings:Audiences")).Returns(mockSection.Object);

var mockDurationValue = new Mock<IConfigurationSection>();
mockDurationValue.SetupGet(x => x.Value).Returns(DurationInHours.ToString());
_mockConfiguration.Setup(x =>
x.GetSection("JwtSettings:DurationInHours")).Returns(mockDurationValue.Object);

_service = new TokenService(_mockConfiguration.Object);

_validUser = new User
{
Id = 1,
Name = "Jon",
Surname = "Snow",
Email = "[email protected]",
Role = UserRole.User
};
}

[TestMethod]
public void GenerateToken_WhenValidUser_ShouldReturnTokenPayload()
{
var result = _service.GenerateToken(_validUser);

Assert.IsNotNull(result);
Assert.AreEqual("Bearer", result.TokenType);
Assert.IsNotNull(result.AccessToken);
Assert.AreEqual(DurationInHours * 60 * 60, result.ExpiresIn);
}

[TestMethod]
public void GenerateToken_WhenValidUser_ShouldReturnValidToken()
{
var result = _service.GenerateToken(_validUser);

var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.ReadToken(result.AccessToken) as JwtSecurityToken;

Assert.IsNotNull(token);
Assert.AreEqual(IssAudValue, token.Issuer);
Assert.AreEqual(IssAudValue, token.Audiences.First());
Assert.AreEqual(_validUser.Email, token.Subject);
Assert.AreEqual(DurationInHours * 60 * 60, (token.ValidTo - token.ValidFrom).TotalSeconds);
}
}
Loading

0 comments on commit d1ca7f0

Please sign in to comment.