From a3ecc1f9fb8bfe1a8a165fe8073969e59e1b5e72 Mon Sep 17 00:00:00 2001 From: 83585-Vanderlan Silva Date: Tue, 31 Oct 2023 11:15:31 -0300 Subject: [PATCH] Refactor unit tests --- README.md | 2 +- .../Controllers/V1/AuthController.cs | 32 +++------- .../Controllers/V1/UsersController.cs | 5 +- .../LoginWithCredentialsRequestHandler.cs | 18 +++++- .../LoginWithCredentialsResponse.cs | 13 +--- .../LoginWithRefreshTokenRequestHandler.cs | 18 +++++- .../LoginWithRefreshTokenResponse.cs | 12 +--- src/Orion.Domain.Core/Services/UserService.cs | 23 ++++--- .../Api/Controllers/AuthControllerTest.cs | 63 +++++++++++-------- .../Api/Controllers/UsersControllerTest.cs | 38 ++++++----- .../Domain/Services/UserServiceTest.cs | 2 - 11 files changed, 118 insertions(+), 108 deletions(-) diff --git a/README.md b/README.md index dd68be5..ffb5576 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ The main objective is to start projects with a clean and simple architecture, wi # in the src/ folder - dotnet ef migrations add MigrationName -p Orion.Infra.Data -s Orion.Api + dotnet ef migrations add MigrationName -p Orion.Infra.Data -s Orion.Api dotnet ef database update -p Orion.Infra.Data -s Orion.Api --verbose Author: https://github.com/vanderlan \ No newline at end of file diff --git a/src/Orion.Api/Controllers/V1/AuthController.cs b/src/Orion.Api/Controllers/V1/AuthController.cs index db0af9a..96b495e 100644 --- a/src/Orion.Api/Controllers/V1/AuthController.cs +++ b/src/Orion.Api/Controllers/V1/AuthController.cs @@ -1,4 +1,3 @@ -using System.Net; using Asp.Versioning; using MediatR; using Microsoft.AspNetCore.Authorization; @@ -8,11 +7,9 @@ using Orion.Api.Models; using Orion.Application.Core.Commands.LoginWithCredentials; using Orion.Application.Core.Commands.LoginWithRefreshToken; -using Orion.Domain.Core.Entities; using Orion.Domain.Core.Exceptions; -using Orion.Domain.Core.Extensions; -using Orion.Domain.Core.Services.Interfaces; using Swashbuckle.AspNetCore.Annotations; +using System.Net; namespace Orion.Api.Controllers.V1; @@ -22,55 +19,46 @@ namespace Orion.Api.Controllers.V1; public class AuthController : ApiController { private readonly IConfiguration _configuration; - private readonly IUserService _userService; - public AuthController(IUserService userService, IMediator mediator, IConfiguration configuration) : base(mediator) + public AuthController(IMediator mediator, IConfiguration configuration) : base(mediator) { _configuration = configuration; - _userService = userService; } [HttpPost("Login")] [SwaggerResponse((int)HttpStatusCode.OK,"A success reponse with a Token, Refresh Token and expiration date" ,typeof(LoginWithCredentialsResponse))] - [SwaggerResponse((int)HttpStatusCode.BadRequest,"A error response with the error description" ,typeof(ExceptionResponse))] - [SwaggerResponse((int)HttpStatusCode.Unauthorized,"When the credetials are invalid")] + [SwaggerResponse((int)HttpStatusCode.BadRequest,"A error response with the error description", typeof(ExceptionResponse))] + [SwaggerResponse((int)HttpStatusCode.Unauthorized)] public async Task Login([FromBody] LoginWithCredentialsRequest loginWithCredentialsRequest) { var loginResponse = await Mediator.Send(loginWithCredentialsRequest); - return await AuthorizeUser(loginResponse); + return AuthorizeUser(loginResponse); } [HttpPost("RefreshToken")] [SwaggerResponse((int)HttpStatusCode.OK,"A success reponse with a Token, Refresh Token and expiration date" ,typeof(LoginWithRefreshTokenResponse))] - [SwaggerResponse((int)HttpStatusCode.BadRequest,"A error response with the error description" ,typeof(ExceptionResponse))] - [SwaggerResponse((int)HttpStatusCode.Unauthorized,"When the credetials are invalid")] + [SwaggerResponse((int)HttpStatusCode.BadRequest,"A error response with the error description", typeof(ExceptionResponse))] + [SwaggerResponse((int)HttpStatusCode.Unauthorized)] public async Task RefreshToken([FromBody] LoginWithRefreshTokenRequest loginWithRefreshTokenRequest) { var loginResponse = await Mediator.Send(loginWithRefreshTokenRequest); - return await AuthorizeUser(loginResponse); + return AuthorizeUser(loginResponse); } - private async Task AuthorizeUser(LoginWithCredentialsResponse loginWithCredentialsResponse) + private IActionResult AuthorizeUser(LoginWithCredentialsResponse loginWithCredentialsResponse) { if (loginWithCredentialsResponse != null) { var (token, validTo) = AuthenticationConfiguration.CreateToken(loginWithCredentialsResponse, _configuration); - var refreshToken = await _userService.AddRefreshTokenAsync( - new RefreshToken - { - Email = loginWithCredentialsResponse.Email, - Refreshtoken = Guid.NewGuid().ToString().ToSha512() - }); - return Ok( new UserApiTokenModel { Token = token, Expiration = validTo, - RefreshToken = refreshToken.Refreshtoken + RefreshToken = loginWithCredentialsResponse.RefreshToken }); } diff --git a/src/Orion.Api/Controllers/V1/UsersController.cs b/src/Orion.Api/Controllers/V1/UsersController.cs index 5b3857f..819e5e5 100644 --- a/src/Orion.Api/Controllers/V1/UsersController.cs +++ b/src/Orion.Api/Controllers/V1/UsersController.cs @@ -1,4 +1,3 @@ -using System.Net; using Asp.Versioning; using MediatR; using Microsoft.AspNetCore.Mvc; @@ -9,10 +8,10 @@ using Orion.Application.Core.Commands.UserUpdate; using Orion.Application.Core.Queries.UserGetById; using Orion.Application.Core.Queries.UserGetPaginated; -using Orion.Domain.Core.Entities; using Orion.Domain.Core.Exceptions; using Orion.Domain.Core.ValueObjects.Pagination; using Swashbuckle.AspNetCore.Annotations; +using System.Net; using static Orion.Domain.Core.Authentication.AuthorizationConfiguration; namespace Orion.Api.Controllers.V1; @@ -28,7 +27,7 @@ public UsersController(IMediator mediator) : base(mediator) } [HttpGet] - [SwaggerResponse((int)HttpStatusCode.OK,"A success response with a list of Users paginated", typeof(PagedList))] + [SwaggerResponse((int)HttpStatusCode.OK,"A success response with a list of Users paginated", typeof(PagedList))] [SwaggerResponse((int)HttpStatusCode.Unauthorized)] public async Task Get([FromQuery] UserGetPaginatedRequest filter) { diff --git a/src/Orion.Application.Core/Commands/LoginWithCredentials/LoginWithCredentialsRequestHandler.cs b/src/Orion.Application.Core/Commands/LoginWithCredentials/LoginWithCredentialsRequestHandler.cs index 725c011..cfc3c67 100644 --- a/src/Orion.Application.Core/Commands/LoginWithCredentials/LoginWithCredentialsRequestHandler.cs +++ b/src/Orion.Application.Core/Commands/LoginWithCredentials/LoginWithCredentialsRequestHandler.cs @@ -1,4 +1,6 @@ using MediatR; +using Orion.Domain.Core.Entities; +using Orion.Domain.Core.Extensions; using Orion.Domain.Core.Services.Interfaces; namespace Orion.Application.Core.Commands.LoginWithCredentials; @@ -16,6 +18,20 @@ public async Task Handle(LoginWithCredentialsReque { var user = await _userService.LoginAsync(request.Email, request.Password); - return (LoginWithCredentialsResponse)user; + var refreshToken = await _userService.AddRefreshTokenAsync( + new RefreshToken + { + Email = user.Email, + Refreshtoken = Guid.NewGuid().ToString().ToSha512() + }); + + return new LoginWithCredentialsResponse + { + Email = user.Email, + Name = user.Name, + PublicId = user.PublicId, + Profile = user.Profile, + RefreshToken = refreshToken.Refreshtoken + }; } } \ No newline at end of file diff --git a/src/Orion.Application.Core/Commands/LoginWithCredentials/LoginWithCredentialsResponse.cs b/src/Orion.Application.Core/Commands/LoginWithCredentials/LoginWithCredentialsResponse.cs index 9e51486..a4b14e8 100644 --- a/src/Orion.Application.Core/Commands/LoginWithCredentials/LoginWithCredentialsResponse.cs +++ b/src/Orion.Application.Core/Commands/LoginWithCredentials/LoginWithCredentialsResponse.cs @@ -1,5 +1,4 @@ using Orion.Domain.Core.Authentication; -using Orion.Domain.Core.Entities; using Orion.Domain.Core.Entities.Enuns; namespace Orion.Application.Core.Commands.LoginWithCredentials; @@ -11,15 +10,5 @@ public class LoginWithCredentialsResponse public string Email { get; set; } public UserProfile Profile { get; set; } public string ProfileDescription => Profile == UserProfile.Admin ? AuthorizationConfiguration.Roles.Admin : AuthorizationConfiguration.Roles.Customer; - - public static explicit operator LoginWithCredentialsResponse(User user) - { - return new LoginWithCredentialsResponse - { - Email = user.Email, - Name = user.Name, - PublicId = user.PublicId, - Profile = user.Profile - }; - } + public string RefreshToken { get; set; } } \ No newline at end of file diff --git a/src/Orion.Application.Core/Commands/LoginWithRefreshToken/LoginWithRefreshTokenRequestHandler.cs b/src/Orion.Application.Core/Commands/LoginWithRefreshToken/LoginWithRefreshTokenRequestHandler.cs index 1b9959f..cadd065 100644 --- a/src/Orion.Application.Core/Commands/LoginWithRefreshToken/LoginWithRefreshTokenRequestHandler.cs +++ b/src/Orion.Application.Core/Commands/LoginWithRefreshToken/LoginWithRefreshTokenRequestHandler.cs @@ -1,4 +1,6 @@ using MediatR; +using Orion.Domain.Core.Entities; +using Orion.Domain.Core.Extensions; using Orion.Domain.Core.Services.Interfaces; namespace Orion.Application.Core.Commands.LoginWithRefreshToken; @@ -16,6 +18,20 @@ public async Task Handle(LoginWithRefreshTokenReq { var user = await _userService.SignInWithRehreshTokenAsync(request.RefreshToken, request.Token); - return (LoginWithRefreshTokenResponse)user; + var refreshToken = await _userService.AddRefreshTokenAsync( + new RefreshToken + { + Email = user.Email, + Refreshtoken = Guid.NewGuid().ToString().ToSha512() + }); + + return new LoginWithRefreshTokenResponse + { + Email = user.Email, + Name = user.Name, + PublicId = user.PublicId, + Profile = user.Profile, + RefreshToken = refreshToken.Refreshtoken + }; } } \ No newline at end of file diff --git a/src/Orion.Application.Core/Commands/LoginWithRefreshToken/LoginWithRefreshTokenResponse.cs b/src/Orion.Application.Core/Commands/LoginWithRefreshToken/LoginWithRefreshTokenResponse.cs index 7d71546..91928dc 100644 --- a/src/Orion.Application.Core/Commands/LoginWithRefreshToken/LoginWithRefreshTokenResponse.cs +++ b/src/Orion.Application.Core/Commands/LoginWithRefreshToken/LoginWithRefreshTokenResponse.cs @@ -1,18 +1,8 @@ using Orion.Application.Core.Commands.LoginWithCredentials; -using Orion.Domain.Core.Entities; namespace Orion.Application.Core.Commands.LoginWithRefreshToken; public class LoginWithRefreshTokenResponse : LoginWithCredentialsResponse { - public static explicit operator LoginWithRefreshTokenResponse(User user) - { - return new LoginWithRefreshTokenResponse - { - Email = user.Email, - Name = user.Name, - PublicId = user.PublicId, - Profile = user.Profile - }; - } + } \ No newline at end of file diff --git a/src/Orion.Domain.Core/Services/UserService.cs b/src/Orion.Domain.Core/Services/UserService.cs index 27e0e23..351143c 100644 --- a/src/Orion.Domain.Core/Services/UserService.cs +++ b/src/Orion.Domain.Core/Services/UserService.cs @@ -29,7 +29,7 @@ public UserService(IUnitOfWork unitOfWork, IStringLocalizer reso public async Task AddAsync(User user) { - await ValidateUser(user); + await ValidateUser(user, validatePassword: true); user.Password = user.Password.ToSha512(); @@ -39,14 +39,6 @@ public async Task AddAsync(User user) return added; } - private async Task ValidateUser(User user) - { - var userFound = await _unitOfWork.UserRepository.FindByEmailAsync(user.Email); - - if (userFound != null && userFound.PublicId != user.PublicId) - throw new ConflictException(_messages[UserMessages.EmailExists], _messages[ExceptionsTitles.ValidationError]); - } - public async Task DeleteAsync(string publicId) { await _unitOfWork.UserRepository.DeleteAsync(publicId); @@ -69,7 +61,7 @@ public async Task UpdateAsync(User user) { var entitySaved = await FindByIdAsync(user.PublicId); - await ValidateUser(user); + await ValidateUser(user, validatePassword: false); entitySaved.Email = user.Email; entitySaved.Name = user.Name; @@ -138,6 +130,17 @@ private string GetClaimFromJwtToken(string jtwToken, string claimName) } } + private async Task ValidateUser(User user, bool validatePassword) + { + if (string.IsNullOrEmpty(user.Password) && validatePassword) + throw new BusinessException(_messages[UserMessages.EmptyPasword]); + + var userFound = await _unitOfWork.UserRepository.FindByEmailAsync(user.Email); + + if (userFound != null && userFound.PublicId != user.PublicId) + throw new ConflictException(_messages[UserMessages.EmailExists], _messages[ExceptionsTitles.ValidationError]); + } + public async Task> ListPaginateAsync(UserFilter filter) { return await _unitOfWork.UserRepository.ListPaginateAsync(filter); diff --git a/tests/Orion.Test/Api/Controllers/AuthControllerTest.cs b/tests/Orion.Test/Api/Controllers/AuthControllerTest.cs index 83e695d..e8cc445 100644 --- a/tests/Orion.Test/Api/Controllers/AuthControllerTest.cs +++ b/tests/Orion.Test/Api/Controllers/AuthControllerTest.cs @@ -1,20 +1,20 @@ +using MediatR; using Microsoft.AspNetCore.Mvc; -using Moq; -using System; -using System.Threading.Tasks; -using Orion.Api.Models; -using Orion.Domain.Core.Services.Interfaces; -using Xunit; using Microsoft.Extensions.Configuration; -using System.Collections.Generic; -using MediatR; -using Orion.Test.Api.Controllers.BaseController; -using Orion.Domain.Core.Entities; -using Orion.Api.Controllers.V1; +using Moq; using Orion.Api.Configuration; +using Orion.Api.Controllers.V1; +using Orion.Api.Models; using Orion.Application.Core.Commands.LoginWithCredentials; using Orion.Application.Core.Commands.LoginWithRefreshToken; +using Orion.Domain.Core.Entities; +using Orion.Test.Api.Controllers.BaseController; using Orion.Test.Faker; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; namespace Orion.Test.Api.Controllers; @@ -28,7 +28,7 @@ public class AuthControllerTest : BaseControllerTest public AuthControllerTest() { _validRefreshToken = RefreshTokenFaker.Get(_validUser.Email); - SetupServiceMock(); + SetupMediatorMock(); } [Fact] @@ -109,28 +109,41 @@ public async Task RefreshToken_WithInvalidRefreshToken_ReturnsUnauthorized() Assert.Equal(401, contentResult.StatusCode); } - private void SetupServiceMock() + private void SetupMediatorMock() { - var mediatotMock = new Mock(); - var userServiceMock = new Mock(); + var mediatorMock = new Mock(); - userServiceMock.Setup(x => x.LoginAsync(_validUser.Email, _validUser.Password)) - .ReturnsAsync(_validUser); - - userServiceMock.Setup(x => x.AddRefreshTokenAsync(It.IsAny())).ReturnsAsync(RefreshTokenFaker.Get()); - userServiceMock.Setup(x => x.SignInWithRehreshTokenAsync(_validRefreshToken.Refreshtoken, It.IsAny())).ReturnsAsync(_validUser); + mediatorMock.Setup(x => x.Send(It.Is(x => x.Password == _validUser.Password && x.Email == _validUser.Email), + It.IsAny())) + .ReturnsAsync(new LoginWithCredentialsResponse + { + Email = _validUser.Email, + Name = _validUser.Name, + Profile = _validUser.Profile, + PublicId = _validUser.PublicId + }); + + mediatorMock.Setup(x => x.Send(It.Is(x => x.RefreshToken == _validRefreshToken.Refreshtoken), + It.IsAny())) + .ReturnsAsync(new LoginWithRefreshTokenResponse + { + Email = _validUser.Email, + Name = _validUser.Name, + Profile = _validUser.Profile, + PublicId = _validUser.PublicId + }); var inMemorySettings = new Dictionary { - {"JwtOptions:SymmetricSecurityKey", "5cCI6IkpXVCJ9.eyJlbWFpbCI6InZhbmRlcmxhbi5nc0BnbWFpbC5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJhZG1p"}, - {"JwtOptions:Issuer", "http://www.myapplication.com"}, - {"JwtOptions:Audience", "http://www.myapplication.com"}, - {"JwtOptions:TokenExpirationMinutes", "15"} + {"JwtOptions:SymmetricSecurityKey", "5cCI6IkpXVCJ9.eyJlbWFpbCI6InZhbmRlcmxhbi5nc0BnbWFpbC5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJhZG1p"}, + {"JwtOptions:Issuer", "http://www.myapplication.com"}, + {"JwtOptions:Audience", "http://www.myapplication.com"}, + {"JwtOptions:TokenExpirationMinutes", "15"} }; _configuration = new ConfigurationBuilder() .AddInMemoryCollection(inMemorySettings) .Build(); - _authController = new AuthController(userServiceMock.Object,mediatotMock.Object, _configuration); + _authController = new AuthController(mediatorMock.Object, _configuration); } } diff --git a/tests/Orion.Test/Api/Controllers/UsersControllerTest.cs b/tests/Orion.Test/Api/Controllers/UsersControllerTest.cs index 80f4265..bdeb326 100644 --- a/tests/Orion.Test/Api/Controllers/UsersControllerTest.cs +++ b/tests/Orion.Test/Api/Controllers/UsersControllerTest.cs @@ -1,18 +1,17 @@ +using MediatR; using Microsoft.AspNetCore.Mvc; using Moq; -using System.Collections.Generic; -using System.Threading.Tasks; -using MediatR; -using Orion.Domain.Core.Services.Interfaces; -using Xunit; -using Orion.Test.Api.Controllers.BaseController; -using Orion.Domain.Core.Entities; using Orion.Api.Controllers.V1; using Orion.Application.Core.Queries.UserGetById; using Orion.Application.Core.Queries.UserGetPaginated; -using Orion.Domain.Core.Filters; +using Orion.Domain.Core.Entities; using Orion.Domain.Core.ValueObjects.Pagination; +using Orion.Test.Api.Controllers.BaseController; using Orion.Test.Faker; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; namespace Orion.Test.Api.Controllers; @@ -23,7 +22,7 @@ public class UsersControllerTestTest : BaseControllerTest public UsersControllerTestTest() { - SetupServiceMock(); + SetupMediatorMock(); } [Fact] @@ -57,7 +56,6 @@ public async Task PostUser_WithValidData_CreateAUser() Assert.Equal(201, contentResult.StatusCode); } - [Fact] public async Task PutUser_WithValidData_UpdateUser() { @@ -91,17 +89,18 @@ public async Task GetUsers_WithValidFilter_ReturnsAListOfUsers() var result = await _usersController.Get(new UserGetPaginatedRequest()); var contentResult = (OkObjectResult)result; - var userPagedList = (PagedList)contentResult.Value; + var userPagedList = (PagedList)contentResult.Value; //assert Assert.Equal(4, userPagedList.Count); Assert.Equal(200, contentResult.StatusCode); } - private void SetupServiceMock() + private void SetupMediatorMock() { var mediatorMock = new Mock(); - var userList = new List(4) + + var userList = new List(4) { _validUser, UserFaker.Get(), @@ -109,14 +108,13 @@ private void SetupServiceMock() UserFaker.Get() }; - var userListPaginated = new PagedList(userList, 4); + var userListPaginated = new PagedList(userList, 4); + + mediatorMock.Setup(x => x.Send(It.IsAny(),It.IsAny())) + .ReturnsAsync(userListPaginated); - // mediatorMock.Setup(x => x.FindByIdAsync(_validUser.PublicId)).ReturnsAsync(_validUser); - // mediatorMock.Setup(x => x.AddAsync(It.IsAny())).ReturnsAsync(UserFaker.Get()); - // mediatorMock.Setup(x => x.UpdateAsync(It.IsAny())).Verifiable(); - // mediatorMock.Setup(x => x.DeleteAsync(_validUser.PublicId)).Verifiable(); - // mediatorMock.Setup(x => x.ListPaginateAsync(It.IsAny())). - // ReturnsAsync(userListPaginated); + mediatorMock.Setup(x => x.Send(It.IsAny(),It.IsAny())) + .ReturnsAsync((UserGetByIdResponse) _validUser); _usersController = new UsersController(mediatorMock.Object); } diff --git a/tests/Orion.Test/Domain/Services/UserServiceTest.cs b/tests/Orion.Test/Domain/Services/UserServiceTest.cs index d8a62e8..dcddb08 100644 --- a/tests/Orion.Test/Domain/Services/UserServiceTest.cs +++ b/tests/Orion.Test/Domain/Services/UserServiceTest.cs @@ -145,7 +145,6 @@ public async Task UpdateAsync_WithValidData_UpdateUserAsSuccess() userSaved.Name = "Jane"; userSaved.Email = "newemail@gmail.com"; - userSaved.Password = "123"; await userService.UpdateAsync(userSaved); @@ -154,7 +153,6 @@ public async Task UpdateAsync_WithValidData_UpdateUserAsSuccess() //assert Assert.Equal(userSaved.Email, userEdited.Email); - Assert.Equal(userSaved.Password.ToSha512(), userEdited.Password); Assert.Equal(userSaved.Name, userEdited.Name); await userService.DeleteAsync(userSaved.PublicId);