Skip to content

Commit

Permalink
Refactor unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vanderlan committed Oct 31, 2023
1 parent ab24998 commit a3ecc1f
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 108 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
32 changes: 10 additions & 22 deletions src/Orion.Api/Controllers/V1/AuthController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Net;
using Asp.Versioning;
using MediatR;
using Microsoft.AspNetCore.Authorization;
Expand All @@ -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;

Expand All @@ -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<IActionResult> 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<IActionResult> RefreshToken([FromBody] LoginWithRefreshTokenRequest loginWithRefreshTokenRequest)
{
var loginResponse = await Mediator.Send(loginWithRefreshTokenRequest);

return await AuthorizeUser(loginResponse);
return AuthorizeUser(loginResponse);
}

private async Task<IActionResult> 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
});
}

Expand Down
5 changes: 2 additions & 3 deletions src/Orion.Api/Controllers/V1/UsersController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Net;
using Asp.Versioning;
using MediatR;
using Microsoft.AspNetCore.Mvc;
Expand All @@ -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;
Expand All @@ -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<User>))]
[SwaggerResponse((int)HttpStatusCode.OK,"A success response with a list of Users paginated", typeof(PagedList<UserGetPaginatedResponse>))]
[SwaggerResponse((int)HttpStatusCode.Unauthorized)]
public async Task<IActionResult> Get([FromQuery] UserGetPaginatedRequest filter)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,6 +18,20 @@ public async Task<LoginWithCredentialsResponse> 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
};
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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; }
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,6 +18,20 @@ public async Task<LoginWithRefreshTokenResponse> 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
};
}
}
Original file line number Diff line number Diff line change
@@ -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
};
}

}
23 changes: 13 additions & 10 deletions src/Orion.Domain.Core/Services/UserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public UserService(IUnitOfWork unitOfWork, IStringLocalizer<OrionResources> reso

public async Task<User> AddAsync(User user)
{
await ValidateUser(user);
await ValidateUser(user, validatePassword: true);

user.Password = user.Password.ToSha512();

Expand All @@ -39,14 +39,6 @@ public async Task<User> 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);
Expand All @@ -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;
Expand Down Expand Up @@ -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<PagedList<User>> ListPaginateAsync(UserFilter filter)
{
return await _unitOfWork.UserRepository.ListPaginateAsync(filter);
Expand Down
63 changes: 38 additions & 25 deletions tests/Orion.Test/Api/Controllers/AuthControllerTest.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -28,7 +28,7 @@ public class AuthControllerTest : BaseControllerTest
public AuthControllerTest()
{
_validRefreshToken = RefreshTokenFaker.Get(_validUser.Email);
SetupServiceMock();
SetupMediatorMock();
}

[Fact]
Expand Down Expand Up @@ -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<IMediator>();
var userServiceMock = new Mock<IUserService>();
var mediatorMock = new Mock<IMediator>();

userServiceMock.Setup(x => x.LoginAsync(_validUser.Email, _validUser.Password))
.ReturnsAsync(_validUser);

userServiceMock.Setup(x => x.AddRefreshTokenAsync(It.IsAny<RefreshToken>())).ReturnsAsync(RefreshTokenFaker.Get());
userServiceMock.Setup(x => x.SignInWithRehreshTokenAsync(_validRefreshToken.Refreshtoken, It.IsAny<string>())).ReturnsAsync(_validUser);
mediatorMock.Setup(x => x.Send(It.Is<LoginWithCredentialsRequest>(x => x.Password == _validUser.Password && x.Email == _validUser.Email),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new LoginWithCredentialsResponse
{
Email = _validUser.Email,
Name = _validUser.Name,
Profile = _validUser.Profile,
PublicId = _validUser.PublicId
});

mediatorMock.Setup(x => x.Send(It.Is<LoginWithRefreshTokenRequest>(x => x.RefreshToken == _validRefreshToken.Refreshtoken),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new LoginWithRefreshTokenResponse
{
Email = _validUser.Email,
Name = _validUser.Name,
Profile = _validUser.Profile,
PublicId = _validUser.PublicId
});

var inMemorySettings = new Dictionary<string, string> {
{"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);
}
}
Loading

0 comments on commit a3ecc1f

Please sign in to comment.