Переглянути джерело

added auth tests

pull/156/head
meris.ahmatovic 3 роки тому
джерело
коміт
ce5d6b5edf

+ 0
- 1
Diligent.WebAPI.Business/Services/Interfaces/IUserService.cs Переглянути файл

@@ -10,7 +10,6 @@ namespace Diligent.WebAPI.Business.Services.Interfaces
Task CreateUser(CreateUserRequestDto model);
Task<bool?> ToggleEnable(User user);
Task RemoveUser(User user);
Task<bool> VerifyToken(User user, string token);
Task<ServiceResponseDTO<object>> SendRegistrationLink(InviteDTO invite);
Task<User> GetFirst();
}

+ 0
- 10
Diligent.WebAPI.Business/Services/UserService.cs Переглянути файл

@@ -126,15 +126,5 @@
Data = new { Message = "Link has been sent!" }
};
}

public async Task<bool> VerifyToken(User user, string token)
{
// this method is going to be updated
// curent new password value is static and only used for testing
// method is not complete and is currently only used to check if valid reset token is sent
var result = await _userManager.ResetPasswordAsync(user, token, "Nekasifra123!");
return result.Succeeded;
}

}
}

+ 0
- 22
Diligent.WebAPI.Host/Controllers/V1/UsersController.cs Переглянути файл

@@ -66,27 +66,5 @@ namespace Diligent.WebAPI.Host.Controllers.V1

return Ok(response.Data);
}

[Authorize]
[HttpPost("verify-invite")]
public async Task<IActionResult> VerifyInvite(string email, string token)
{
// controller endpoint currently used only for testing
// user should be enabled to log in after accepting invite and updating his account
var user = await _userService.GetByEmail(email);

var result = await _userService.VerifyToken(user, token);

return Ok(result);
}

[Authorize]
[HttpPost]
public async Task<IActionResult> CreateUser([FromBody] CreateUserRequestDto model)
{
await _userService.CreateUser(model);

return Ok();
}
}
}

+ 321
- 0
Diligent.WebAPI.Tests/Controllers/AuthControllerTest.cs Переглянути файл

@@ -0,0 +1,321 @@
using AutoMapper;
using Diligent.WebAPI.Business.MappingProfiles;
using Diligent.WebAPI.Contracts.DTOs;
using Diligent.WebAPI.Contracts.DTOs.Auth;
using Diligent.WebAPI.Contracts.DTOs.User;
using Diligent.WebAPI.Contracts.Models;
using Diligent.WebAPI.Data.Entities;

namespace Diligent.WebAPI.Tests.Controllers
{
public class AuthControllerTest
{
private IAuthenticationService _service = Substitute.For<IAuthenticationService>();

public AuthControllerTest()
{
}

[Fact]
public async Task ForgotPassword_ShouldReturn200OK_IfUserExistsAndMailIsSent()
{
_service.GetForgotPasswordUrlAsync("somemail@dilig.net").Returns(new ServiceResponseDTO<object>
{
Data = new { code = "token", email = "somemail@dilig.net" }
});

AuthenticationsController controller = new(_service);

var result = await controller.ForgotPassword("somemail@dilig.net");

(result as OkObjectResult).StatusCode.Should().Be(200);
}

[Fact]
public async Task ForgotPassword_ShouldReturn400BadRequest_IfUserNotFound()
{
_service.GetForgotPasswordUrlAsync("somemail@dilig.net").Returns(new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Email did not find."
});

AuthenticationsController controller = new(_service);

var result = await controller.ForgotPassword("somemail@dilig.net");

(result as BadRequestObjectResult).StatusCode.Should().Be(400);
}

[Fact]
public async Task ResetPassword_ShouldReturn200OK_IfUserExistsAndMailIsSent()
{
_service.PasswordResetAsync("somemail@dilig.net", "code", "NewPass123@").Returns(new ServiceResponseDTO<object>
{
Data = true
});

AuthenticationsController controller = new(_service);

var result = await controller.ResetPassword(new Contracts.Models.ResetPasswordModel
{
Email = "somemail@dilig.net",
Code = "code",
Password = "NewPass123@"
});

(result as OkObjectResult).StatusCode.Should().Be(200);
}

[Fact]
public async Task ResetPassword_ShouldReturn400BadRequest_IfUserNotFoundOrDbError()
{
_service.PasswordResetAsync("somemail@dilig.net", "code", "NewPass123@").Returns(new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Email did not find."
});

AuthenticationsController controller = new(_service);

var result = await controller.ResetPassword(new Contracts.Models.ResetPasswordModel
{
Email = "somemail@dilig.net",
Code = "code",
Password = "NewPass123@"
});

(result as BadRequestObjectResult).StatusCode.Should().Be(400);
}

[Fact]
public async Task Authenticate_ShouldReturn400BadRequest_IfUserCredentialsInValid()
{
_service.Authenticate(Arg.Any<AuthenticateRequestDto>()).Returns(new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "Username is not valid"
});

AuthenticationsController controller = new(_service);

var result = await controller.Authenticate(new AuthenticateRequestDto
{
Username = "user",
Password = "NewPass123@"
});

(result as BadRequestObjectResult).StatusCode.Should().Be(400);
}

[Fact]
public async Task Authenticate_ShouldReturn200OK_IfUserCredentialsValid()
{
_service.Authenticate(Arg.Any<AuthenticateRequestDto>()).Returns(new ServiceResponseDTO<AuthenticateResponseDto>
{
Data = new AuthenticateResponseDto
{
Token = "token",
RefreshToken = "refreshToken",
Id = 1,
FirstName = "First",
LastName = "Last",
Username = "user"
}
});

AuthenticationsController controller = new(_service);

var result = await controller.Authenticate(new AuthenticateRequestDto
{
Username = "user",
Password = "NewPass123@"
});

(result as OkObjectResult).StatusCode.Should().Be(200);
}

[Fact]
public async Task RefreshToken_ShouldReturn400BadRequest_IfErrorOccured()
{
_service.RefreshTokenAsync(Arg.Any<RefreshTokenRequestDto>()).Returns(new RefreshTokenResultDto
{
Error = "Token is not associated with any user."
});

AuthenticationsController controller = new(_service);

var result = await controller.RefreshToken(new RefreshTokenRequestDto
{
RefreshToken = "refresh token",
Token = "token",
});

(result as BadRequestObjectResult).StatusCode.Should().Be(400);
}

[Fact]
public async Task RefreshToken_ShouldReturn200OK_IfValidTokens()
{
_service.RefreshTokenAsync(Arg.Any<RefreshTokenRequestDto>()).Returns(new RefreshTokenResultDto
{
Data = new AuthenticateResponseDto
{
Id = 1,
FirstName = "FirstName",
LastName = "LastName",
Username = "UserName",
Token = "token",
RefreshToken = "token"
}
});

AuthenticationsController controller = new(_service);

var result = await controller.RefreshToken(new RefreshTokenRequestDto
{
RefreshToken = "refresh token",
Token = "token",
});

(result as OkObjectResult).StatusCode.Should().Be(200);
}

[Fact]
public async Task Logout_ShouldReturn400BadRequest_IfErrorOccured()
{
_service.DeleteRefreshToken(Arg.Any<int>()).Returns(new ServiceResponseDTO<string>
{
IsError = true,
ErrorMessage = "There is no refresh token for user"
});

AuthenticationsController controller = new(_service);

var result = await controller.Logout(1);

(result as BadRequestObjectResult).StatusCode.Should().Be(400);
}

[Fact]
public async Task Logout_ShouldReturn200OK_IfValidTokenExists()
{
_service.DeleteRefreshToken(Arg.Any<int>()).Returns(new ServiceResponseDTO<string>
{
Data = "sdsd",
IsError = false,
});

AuthenticationsController controller = new(_service);

var result = await controller.Logout(1);
var res = result as StatusCodeResult;
Assert.NotNull(res);

res.StatusCode.Should().Be(200);
}

[Fact]
public async Task AuthenticateGoogle_ShouldReturn400BadRequest_IfUserCredentialsInValid()
{
_service.Authenticate(Arg.Any<GoogleApiModel>()).Returns(new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "Invalid Google Api Token"
});

AuthenticationsController controller = new(_service);

var result = await controller.GoogleLogin(new GoogleApiModel
{
Token = "Token",
User = new GoogleApiTokenInfo
{
email = "mail@dilig.net"
},
});


(result as BadRequestObjectResult).StatusCode.Should().Be(400);
}

[Fact]
public async Task AuthenticateGoogle_ShouldReturn200OK_IfUserCredentialsValid()
{
_service.Authenticate(Arg.Any<GoogleApiModel>()).Returns(new ServiceResponseDTO<AuthenticateResponseDto>
{
Data = new AuthenticateResponseDto
{
Token = "token",
RefreshToken = "refreshToken",
Id = 1,
FirstName = "First",
LastName = "Last",
Username = "user"
}
});

AuthenticationsController controller = new(_service);

var result = await controller.GoogleLogin(new GoogleApiModel
{
Token = "Token",
User = new GoogleApiTokenInfo
{
email = "mail@dilig.net"
},
});


(result as OkObjectResult).StatusCode.Should().Be(200);
}

[Fact]
public async Task Register_ShouldReturn400BadRequest_IfUserModelIsNotValid()
{
_service.Register(Arg.Any<RegisterDTO>()).Returns(new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "User not invited."
});

AuthenticationsController controller = new(_service);

var result = await controller.Register(new RegisterDTO
{
Confirm = "Password123@",
Password = "Password123@",
Token = "token",
LinkedIn = "link",
Email = "some.some@dilig.net",
Phone = "0628264606",
Position = "Senior Dev"
});


(result as BadRequestObjectResult).StatusCode.Should().Be(400);
}

[Fact]
public async Task Register_ShouldReturn200OK_IfUserModelIsValid()
{
_service.Register(Arg.Any<RegisterDTO>()).Returns(new ServiceResponseDTO<object> { Data = true });

AuthenticationsController controller = new(_service);

var result = await controller.Register(new RegisterDTO
{
Confirm = "Password123@",
Password = "Password123@",
Token = "token",
LinkedIn = "link",
Email = "some.some@dilig.net",
Phone = "0628264606",
Position = "Senior Dev"
});


(result as OkObjectResult).StatusCode.Should().Be(200);
}
}
}

+ 23
- 0
Diligent.WebAPI.Tests/Controllers/UsersControllerTest.cs Переглянути файл

@@ -131,5 +131,28 @@ namespace Diligent.WebAPI.Tests.Controllers

await Assert.ThrowsAsync<EntityNotFoundException>(() => usersController.DeleteUser(15));
}

[Fact]
public async Task ToggleEnable_ShouldReturn400_WhenUserDoesNotExist()
{
_userService.When(x => x.GetById(Arg.Any<int>()))
.Do(x => { throw new EntityNotFoundException(); });

UsersController usersController = new(_userService, _mapper);

await Assert.ThrowsAsync<EntityNotFoundException>(() => usersController.ToggleEnable(15));
}

[Fact]
public async Task ToggleEnable_ShouldReturnOk_WhenUserExists()
{
_userService.GetById(Arg.Any<int>()).Returns(_user);

UsersController usersController = new(_userService, _mapper);

var res = await usersController.DeleteUser(15);

(res as ObjectResult).StatusCode.Should().Be(200);
}
}
}

+ 15
- 0
Diligent.WebAPI.Tests/Helpers.cs Переглянути файл

@@ -20,5 +20,20 @@ namespace Diligent.WebAPI.Tests
}
return databaseContext;
}
public static async Task<DatabaseContext> GetDatabaseContextWithRelation(List<T> users, List<RefreshToken> tokens)
{
var options = new DbContextOptionsBuilder<DatabaseContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
var databaseContext = new DatabaseContext(options);
databaseContext.Database.EnsureCreated();
if (!await databaseContext.Set<T>().AnyAsync() || !await databaseContext.Set<RefreshToken>().AnyAsync())
{
await databaseContext.Set<T>().AddRangeAsync(users);
await databaseContext.Set<RefreshToken>().AddRangeAsync(tokens);
await databaseContext.SaveChangesAsync();
}
return databaseContext;
}
}
}

+ 787
- 0
Diligent.WebAPI.Tests/Services/AuthenticationServiceTests.cs Переглянути файл

@@ -0,0 +1,787 @@
using AutoMapper;
using Diligent.WebAPI.Business.MappingProfiles;
using Diligent.WebAPI.Business.Services;
using Diligent.WebAPI.Business.Settings;
using Diligent.WebAPI.Contracts.DTOs;
using Diligent.WebAPI.Contracts.DTOs.Auth;
using Diligent.WebAPI.Contracts.Models;
using Diligent.WebAPI.Data.Entities;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NSubstitute.ReturnsExtensions;
using System.Text;

namespace Diligent.WebAPI.Tests.Services
{
public class AuthenticationServiceTests
{
private readonly List<User> _users;
private readonly List<RefreshToken> _tokens;
private readonly IUserStore<User> _mockStore;
private readonly UserManager<User> _mockUserManager;
private readonly IEmailer _emailer = Substitute.For<IEmailer>();
private readonly IMapper _mapper;
private readonly ILogger<AuthenticationService> _logger = Substitute.For<ILogger<AuthenticationService>>();
private readonly IHttpClientService _httpClient = Substitute.For<IHttpClientService>();

public AuthenticationServiceTests()
{
_mockStore = Substitute.For<IUserStore<User>>();
_mockUserManager = Substitute.For<UserManager<User>>(_mockStore, null, null, null, null, null, null, null, null);
_tokens = new List<RefreshToken>
{
new RefreshToken
{
Id = 1,
CreationDate = DateTime.UtcNow,
ExpiryDate = DateTime.UtcNow.AddDays(5),
UserId = 1,
Invalidated = false,
JwtId = "string",
Used = false,
Token = "refresh"
},
new RefreshToken
{
Id = 2,
CreationDate = DateTime.UtcNow,
ExpiryDate = DateTime.UtcNow.AddDays(5),
UserId = 2,
Invalidated = false,
JwtId = "string",
Used = false,
Token = "refresh"
},
new RefreshToken
{
Id = 3,
CreationDate = DateTime.UtcNow,
ExpiryDate = DateTime.UtcNow.AddDays(5),
UserId = 3,
Invalidated = false,
JwtId = "string",
Used = false,
Token = "refresh"
}
};

_users = new List<User>
{
new User
{
Id = 1,
PasswordHash = "AQAAAAEAACcQAAAAEJnWVhD/qftzqJq5XOUD0BxEBEwhd7vS46HeDD+9cwEsqO9ev9xEORJVjmFMASUGJg==",
FirstName = "User",
LastName = "One",
UserName = "user1",
NormalizedUserName = "USER1",
Email = "user1@dilig.net",
NormalizedEmail = "USER1@DILIG.NET",
EmailConfirmed = false,
IsEnabled = true,
AccessFailedCount = 0,
SecurityStamp = "2D3XPK2P5MAKO377AWFU3T4ZFFYTSOJX",
ConcurrencyStamp = "2D3XPK2P5MAKO377AWFU3T4ZFFYTSOJX",
},
new User
{
Id = 2,
PasswordHash = "AQAAAAEAACcQAAAAEJnWVhD/qftzqJq5XOUD0BxEBEwhd7vS46HeDD+9cwEsqO9ev9xEORJVjmFMASUGJg==",
FirstName = "User",
LastName = "Two",
UserName = "user2",
NormalizedUserName = "USER2",
Email = "user2@dilig.net",
NormalizedEmail = "USER2@DILIG.NET",
EmailConfirmed = false,
IsEnabled = true,
AccessFailedCount = 0,
SecurityStamp = "2D3XPK2P5MAKO377AWFU3T4ZFFYTSOJX",
ConcurrencyStamp = "2D3XPK2P5MAKO377AWFU3T4ZFFYTSOJX",
},
new User
{
Id = 3,
PasswordHash = "AQAAAAEAACcQAAAAEJnWVhD/qftzqJq5XOUD0BxEBEwhd7vS46HeDD+9cwEsqO9ev9xEORJVjmFMASUGJg==",
FirstName = "User",
LastName = "Three",
UserName = "user3",
NormalizedUserName = "USER3",
Email = "user3@dilig.net",
NormalizedEmail = "USER3@DILIG.NET",
EmailConfirmed = false,
IsEnabled = false,
AccessFailedCount = 0,
SecurityStamp = "2D3XPK2P5MAKO377AWFU3T4ZFFYTSOJX",
ConcurrencyStamp = "2D3XPK2P5MAKO377AWFU3T4ZFFYTSOJX",
},
new User
{
Id = 4,
PasswordHash = "AQAAAAEAACcQAAAAEJnWVhD/qftzqJq5XOUD0BxEBEwhd7vS46HeDD+9cwEsqO9ev9xEORJVjmFMASUGJg==",
FirstName = "User",
LastName = "Four",
UserName = "user4",
NormalizedUserName = "USER4",
Email = "user4@dilig.net",
NormalizedEmail = "USER4@DILIG.NET",
EmailConfirmed = false,
IsEnabled = true,
AccessFailedCount = 0,
SecurityStamp = "2D3XPK2P5MAKO377AWFU3T4ZFFYTSOJX",
ConcurrencyStamp = "2D3XPK2P5MAKO377AWFU3T4ZFFYTSOJX",
}
};

// configure mapper
var configuration = new MapperConfiguration(cfg => cfg.AddProfiles(
new List<Profile>
{
new UserMappingProfile()
}));

_mapper = new Mapper(configuration);
}

[Fact]
public async Task Authenticate_ShouldReturnError_IfInvalidUsername()
{
_mockUserManager.FindByNameAsync(Arg.Any<string>()).ReturnsNull();
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.Authenticate(new Contracts.DTOs.Auth.AuthenticateRequestDto
{
Username = "Any",
Password = "Any"
});

result.Should().BeEquivalentTo(new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "Username is not valid"
});
}

[Fact]
public async Task Authenticate_ShouldCall_CheckPasswordAsync()
{
_mockUserManager.FindByNameAsync(Arg.Any<string>()).Returns(_users[1]);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

await service.Authenticate(new Contracts.DTOs.Auth.AuthenticateRequestDto
{
Username = _users[1].Email,
Password = "Any"
});

await _mockUserManager.Received(1).CheckPasswordAsync(_users[1], "Any");
}

[Fact]
public async Task Authenticate_ShouldReturnError_IfUserIsDisabled()
{
_mockUserManager.FindByNameAsync(Arg.Any<string>()).Returns(_users[2]);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.Authenticate(new Contracts.DTOs.Auth.AuthenticateRequestDto
{
Username = _users[2].Email,
Password = "Any"
});

result.Should().BeEquivalentTo(new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = $"User with email {_users[2].Email} has no permission to log in."
});
}

[Fact]
public async Task Authenticate_ShouldReturnError_IfInvalidPassword()
{
_mockUserManager.FindByNameAsync(Arg.Any<string>()).Returns(_users[1]);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.Authenticate(new Contracts.DTOs.Auth.AuthenticateRequestDto
{
Username = _users[1].Email,
Password = "Any"
});

result.Should().BeEquivalentTo(new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = $"Password is not correct"
});
}

[Fact]
public async Task Authenticate_ShouldReturnError_IfUserIsLockedOut()
{
_mockUserManager.FindByNameAsync(Arg.Any<string>()).Returns(_users[3]);
_mockUserManager.CheckPasswordAsync(Arg.Any<User>(), Arg.Any<string>()).Returns(true);
_mockUserManager.IsLockedOutAsync(Arg.Any<User>()).Returns(true);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings {
Secret = "S1231251WAS124AS"
});

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.Authenticate(new Contracts.DTOs.Auth.AuthenticateRequestDto
{
Username = _users[3].Email,
Password = "Nekasifra123!"
});

result.Should().BeEquivalentTo(new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "The account is locked out"
});
}

[Fact]
public async Task Authenticate_ShouldGenerateToken_IfCredsAreValid()
{
_mockUserManager.FindByNameAsync(Arg.Any<string>()).Returns(_users[1]);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.Authenticate(new Contracts.DTOs.Auth.AuthenticateRequestDto
{
Username = _users[1].Email,
Password = "Nekasifra123!"
});

Assert.IsType<ServiceResponseDTO<AuthenticateResponseDto>>(result);
}

[Fact]
public async Task AuthenticateGoogle_ShouldReturnError_IfInvalidApiToken()
{
_httpClient.IsTokenValid(Arg.Any<string>()).Returns(false);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.Authenticate(new GoogleApiModel
{
Token = "t",
User = new GoogleApiTokenInfo
{
email = "something@dilig.net"
}
});

result.Should().BeEquivalentTo(new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "Invalid Google Api Token"
});
}

[Fact]
public async Task AuthenticateGoogle_ShouldReturnError_IfUserDoesntExist()
{
_httpClient.IsTokenValid(Arg.Any<string>()).Returns(true);
_mockUserManager.FindByNameAsync(Arg.Any<string>()).ReturnsNull();
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.Authenticate(new GoogleApiModel
{
Token = "t",
User = new GoogleApiTokenInfo
{
email = _users[1].Email,
}
});

result.Should().BeEquivalentTo(new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = $"User with email {_users[1].Email} does not exist in database"
});
}

[Fact]
public async Task AuthenticateGoogle_ShouldReturnError_IfUserIsDisabled()
{
_httpClient.IsTokenValid(Arg.Any<string>()).Returns(true);
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).Returns(_users[2]);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.Authenticate(new GoogleApiModel
{
Token = "token",
User = new GoogleApiTokenInfo
{
email = _users[2].Email,
}
});

result.Should().BeEquivalentTo(new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = $"User with email {_users[2].Email} has no permission to log in."
});
}

[Fact]
public async Task AuthenticateGoogle_ShouldGenerateToken()
{
_httpClient.IsTokenValid(Arg.Any<string>()).Returns(true);
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).Returns(_users[1]);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings
{
Secret = "S1231251WAS124AS"
});

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.Authenticate(new GoogleApiModel
{
Token = "token",
User = new GoogleApiTokenInfo
{
email = _users[1].Email
}
});

Assert.IsType<ServiceResponseDTO<AuthenticateResponseDto>>(result);
}

[Fact]
public async Task Register_ShouldReturnError_IfUserNotFound()
{
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).ReturnsNull();
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.Register(new Contracts.DTOs.User.RegisterDTO
{
Token = "token",
LinkedIn = "Link",
Confirm = "Password123!",
Email = _users[1].Email,
Password = "Password123!",
Phone = "123",
Position = "Senior Dev"
});

result.Should().BeEquivalentTo(new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "User not invited."
});
}

[Fact]
public async Task Register_ShouldCreateUser_IfUserExists()
{
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).Returns(_users[1]);

var databaseContext = await Helpers<User>.GetDatabaseContext(_users);
var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

_mockUserManager.ResetPasswordAsync(Arg.Any<User>(), Arg.Any<string>(), Arg.Any<string>()).Returns(IdentityResult.Success);

var result = await service.Register(new Contracts.DTOs.User.RegisterDTO
{
Token = "token",
LinkedIn = "Link",
Confirm = "Password123!",
Email = _users[1].Email,
Password = "Password123!",
Phone = "123",
Position = "Senior Dev"
});

result.Should().BeEquivalentTo(new ServiceResponseDTO<object>
{
Data = true,
});
}

[Fact]
public async Task Register_ShouldThrowError_IfUnsuccessfulReset()
{
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).Returns(_users[1]);

var databaseContext = await Helpers<User>.GetDatabaseContext(_users);
var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

_mockUserManager.ResetPasswordAsync(Arg.Any<User>(), Arg.Any<string>(), Arg.Any<string>())
.Returns(IdentityResult.Failed(
new IdentityError[] {
new IdentityError {
Description= "Failed"
}
}
));

var result = await service.Register(new Contracts.DTOs.User.RegisterDTO
{
Token = "token",
LinkedIn = "Link",
Confirm = "Password123!",
Email = _users[1].Email,
Password = "Password123!",
Phone = "123",
Position = "Senior Dev"
});

result.Should().BeEquivalentTo(new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Failed"
});
}

[Fact]
public async Task PasswordResetAsync_ShouldReturnError_IfUserNotFound()
{
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).ReturnsNull();
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.PasswordResetAsync(
email: _users[1].Email,
code: "code",
password: "Password123!"
);

result.Should().BeEquivalentTo(new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Email did not find."
});
}

[Fact]
public async Task PasswordResetAsync_ShouldResetToken_IfUserFound()
{
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).Returns(_users[1]);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

_mockUserManager.ResetPasswordAsync(Arg.Any<User>(), Arg.Any<string>(), Arg.Any<string>())
.Returns(IdentityResult.Success);

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.PasswordResetAsync(
email: _users[1].Email,
code: "code",
password: "Password123!"
);

result.Should().BeEquivalentTo(new ServiceResponseDTO<object>
{ Data = true }
);
}

[Fact]
public async Task PasswordResetAsync_ShouldReturnError_IfUnsuccessfulReset()
{
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).Returns(_users[1]);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

_mockUserManager.ResetPasswordAsync(Arg.Any<User>(), Arg.Any<string>(), Arg.Any<string>())
.Returns(IdentityResult.Failed(
new IdentityError[] {
new IdentityError {
Description= "Failed"
}
}
));

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.PasswordResetAsync(
email: _users[1].Email,
code: "code",
password: "Password123!"
);

result.Should().BeEquivalentTo(new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Failed"
});
}

[Fact]
public async Task GetForgotPasswordUrlAsync_ShouldReturnError_IfUserNotFound()
{
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).ReturnsNull();
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.GetForgotPasswordUrlAsync(_users[1].Email);

result.Should().BeEquivalentTo(new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Email did not find."
});
}

[Fact]
public async Task GetForgotPasswordUrlAsync_ShouldReturnSuccess_IfUserIsFound()
{
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).Returns(_users[1]);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

await service.GetForgotPasswordUrlAsync(_users[1].Email);

await _emailer.ReceivedWithAnyArgs(1)
.SendEmailAndWriteToDbAsync("mail", "reset", "url", true);
}

[Fact]
public async Task GetForgotPasswordUrlAsync_ShouldCallEmailer_IfUserIsFound()
{
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).Returns(_users[1]);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var authSettings = Options.Create(new AuthorizationSettings { });

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.GetForgotPasswordUrlAsync(_users[1].Email);

result.Should().BeOfType(typeof(ServiceResponseDTO<object>));
}

[Fact]
public async Task UpdateRefreshToken_ShouldUpdate()
{
var databaseContext = await Helpers<User>.GetDatabaseContextWithRelation(_users, _tokens);
var authSettings = Options.Create(new AuthorizationSettings { });
var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var first = await databaseContext.RefreshTokens.FindAsync(1);

await service.UpdateRefreshToken(first);
databaseContext.Entry(first).Should().NotBeNull();
}

[Fact]
public async Task DeleteRefreshToken_ShouldReturnError_IfTokenDoesNotExist()
{
var databaseContext = await Helpers<User>.GetDatabaseContextWithRelation(_users, _tokens);

var authSettings = Options.Create(new AuthorizationSettings { });
var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.DeleteRefreshToken(1000); // not existing token

result.Should().BeEquivalentTo(new ServiceResponseDTO<string>
{
IsError = true,
ErrorMessage = "There is no refresh token for user"
});
}

[Fact]
public async Task DeleteRefreshToken_ShouldSucceed_IfTokenExists()
{
var databaseContext = await Helpers<User>.GetDatabaseContextWithRelation(_users, _tokens);

var authSettings = Options.Create(new AuthorizationSettings { });
var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.DeleteRefreshToken(1); // not existing token

result.Should().BeEquivalentTo(new ServiceResponseDTO<string>
{
Data = null
});
}

[Fact]
public async Task GetRefreshTokenByUserId_ShouldReturnTokenOrNull()
{
var databaseContext = await Helpers<User>.GetDatabaseContextWithRelation(_users, _tokens);

var authSettings = Options.Create(new AuthorizationSettings { });
var frontEndSettings = Options.Create(new FrontEndSettings
{
BaseUrl = "some url"
});

var service = new AuthenticationService(authSettings, frontEndSettings, _mockUserManager, databaseContext, _emailer, _logger, _httpClient, _mapper);

var result = await service.GetRefreshTokenByUserId(1);

result.Should().BeEquivalentTo(_tokens[0]);
}
}
}

+ 136
- 2
Diligent.WebAPI.Tests/Services/UserServiceTests.cs Переглянути файл

@@ -1,4 +1,5 @@
using AutoMapper;
using Diligent.WebAPI.Business.Helper;
using Diligent.WebAPI.Business.MappingProfiles;
using Diligent.WebAPI.Business.Services;
using Diligent.WebAPI.Business.Settings;
@@ -12,6 +13,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NSubstitute;
using NSubstitute.ReturnsExtensions;

namespace Diligent.WebAPI.Tests.Services
{
@@ -64,6 +66,7 @@ namespace Diligent.WebAPI.Tests.Services
_mapper = new Mapper(configuration);
}


[Fact]
public async Task GetById_ShouldReturnUser()
{
@@ -78,6 +81,19 @@ namespace Diligent.WebAPI.Tests.Services
result.Should().Be(_user);
}

[Fact]
public async Task GetById_ShouldThrowExceptionIfUserNotFound()
{
_mockUserManager.FindByIdAsync(Arg.Any<string>()).ReturnsNull();
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontSettings = Substitute.For<IOptions<FrontEndSettings>>();
var mailer = Substitute.For<IEmailer>();

var service = new UserService(frontSettings, _mockUserManager, _mapper, databaseContext, mailer, _logger);
await Assert.ThrowsAsync<EntityNotFoundException>(() => service.GetById(15));
}

[Fact]
public async Task GetByEmail_ShouldReturnUser()
{
@@ -93,17 +109,66 @@ namespace Diligent.WebAPI.Tests.Services
}

[Fact]
public async Task RemoveUser_ShouldReturnError()
public async Task GetByEmail_ShouldThrowExceptionIfUserNotFound()
{
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).ReturnsNull();
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontSettings = Substitute.For<IOptions<FrontEndSettings>>();
var mailer = Substitute.For<IEmailer>();

var service = new UserService(frontSettings, _mockUserManager, _mapper, databaseContext, mailer, _logger);
await Assert.ThrowsAsync<EntityNotFoundException>(() => service.GetByEmail("someMail"));
}
// ?
[Fact]
public async Task GetFirst_ShouldThrowExceptionIfUsersNotFound()
{
_mockUserManager.Users.FirstOrDefaultAsync().ReturnsNull();
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontSettings = Substitute.For<IOptions<FrontEndSettings>>();
var mailer = Substitute.For<IEmailer>();

var service = new UserService(frontSettings, _mockUserManager, _mapper, databaseContext, mailer, _logger);
await Assert.ThrowsAsync<EntityNotFoundException>(() => service.GetFirst());
}

// ?
[Fact]
public async Task GetFirst_ShouldReturnUser()
{
//var userStoreStub = NSubstitute.Substitute.For<IUserStore<ApplicationUser>>();
//var mockM = Substitute.For<UserManager<>>();
_mockUserManager.Users.FirstOrDefault().Returns(_user);
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontSettings = Substitute.For<IOptions<FrontEndSettings>>();
var mailer = Substitute.For<IEmailer>();

var service = new UserService(frontSettings, _mockUserManager, _mapper, databaseContext, mailer, _logger);
var result = await service.GetFirst();
result.Should().Be(_user);
}
[Fact]
public async Task RemoveUser_ShouldDeleteUser()
{
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

_mockUserManager.FindByIdAsync(Arg.Any<string>()).Returns(n => _user);

var frontSettings = Substitute.For<IOptions<FrontEndSettings>>();
var mailer = Substitute.For<IEmailer>();

var service = new UserService(frontSettings, _mockUserManager, _mapper, databaseContext, mailer, _logger);
await service.RemoveUser(_user);

await Assert.ThrowsAsync<EntityNotFoundException>(async () => await service.GetById(2));
_mockUserManager.FindByIdAsync(Arg.Any<string>()).ReturnsNull();

await Assert.ThrowsAsync<EntityNotFoundException>(()=>service.GetById(1));
}

[Fact]
@@ -136,5 +201,74 @@ namespace Diligent.WebAPI.Tests.Services

Assert.Equal(true, result.IsError);
}

[Fact]
public async Task Invite_ShouldCallCreateAsyncIfUserDoesNotExist()
{
_mockUserManager.FindByEmailAsync(Arg.Any<string>()).ReturnsNull();
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);
var frontSettings = Options.Create<FrontEndSettings>(new FrontEndSettings
{
BaseUrl = "some url"
});
var mailer = Substitute.For<IEmailer>();
var service = new UserService(frontSettings, _mockUserManager, _mapper, databaseContext, mailer, _logger);

var result = await service.SendRegistrationLink(new Contracts.DTOs.User.InviteDTO
{
Email = "string@dilig.net",
FirstName = "string",
LastName = "string",
});

var newUser = new User
{
UserName = "string",
Email = "string",
FirstName = "string",
LastName = "string",
IsEnabled = false
}
;
await _mockUserManager.ReceivedWithAnyArgs(1).CreateAsync(newUser, "Something123@");
await mailer.ReceivedWithAnyArgs(1).SendEmailAndWriteToDbAsync("string@dilig.net", "Welcome", HTMLHelper.RenderRegisterPage($"localhost/register?token=token&email=string@dilig.net"), isHtml: true);

result.Should().BeEquivalentTo(
new ServiceResponseDTO<object>
{
Data = new { Message = "Link has been sent!" }
}
);
}

[Fact]
public async Task CreateUser_ShouldCallCreateAsync()
{
var databaseContext = await Helpers<User>.GetDatabaseContext(_users);

var frontSettings = Substitute.For<IOptions<FrontEndSettings>>();
var mailer = Substitute.For<IEmailer>();

// mock mapper to count calls
var mockMapper = Substitute.For<IMapper>();
var service = new UserService(frontSettings, _mockUserManager, mockMapper, databaseContext, mailer, _logger);

var model = new Contracts.DTOs.Auth.CreateUserRequestDto
{
Email = "string",
Password = "SomePass123@",
UserName = "string",
FirstName = "string",
LastName = "string",
};

await service.CreateUser(model);

// real mapping to use user as argument
var user = _mapper.Map<User>(model);

mockMapper.ReceivedWithAnyArgs(1).Map<User>(model);
await _mockUserManager.ReceivedWithAnyArgs(1).CreateAsync(user, "SomePass123");
}
}
}

Завантаження…
Відмінити
Зберегти