2023-03-13 18:36:59 +02:00
|
|
|
|
// Copyright (c) 2020 Tudor Stanciu
|
|
|
|
|
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
2023-03-13 23:27:13 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Threading.Tasks;
|
2023-03-13 18:36:59 +02:00
|
|
|
|
using Tuitio.Application.Services.Abstractions;
|
|
|
|
|
using Tuitio.Application.Tests.Fixtures;
|
|
|
|
|
using Tuitio.Domain.Data.DbContexts;
|
|
|
|
|
using Tuitio.Domain.Models.Account;
|
|
|
|
|
using Xunit;
|
|
|
|
|
|
|
|
|
|
namespace Tuitio.Application.Tests
|
|
|
|
|
{
|
|
|
|
|
public class UserServiceTests : IClassFixture<DependencyInjectionFixture>, IDisposable
|
|
|
|
|
{
|
|
|
|
|
private readonly IServiceScope _tuitioScope;
|
|
|
|
|
private readonly IUserService _userService;
|
|
|
|
|
|
|
|
|
|
public UserServiceTests(DependencyInjectionFixture fixture)
|
|
|
|
|
{
|
|
|
|
|
_tuitioScope = fixture.ServiceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();
|
|
|
|
|
_userService = _tuitioScope.ServiceProvider.GetRequiredService<IUserService>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
_tuitioScope.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task Login_ShouldThrowArgumentExceptionIfUserNameIsNullOrEmptyString()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var userName = "";
|
|
|
|
|
var password = "";
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
await Assert.ThrowsAsync<ArgumentException>(nameof(userName), () => _userService.Login(userName, password));
|
|
|
|
|
await Assert.ThrowsAsync<ArgumentException>(nameof(userName), () => _userService.Login(null, password));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task Login_ShouldThrowArgumentExceptionIfPasswordIsNullOrEmptyString()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var userName = "tuitio.test.user";
|
|
|
|
|
var password = "";
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
await Assert.ThrowsAsync<ArgumentException>(nameof(password), () => _userService.Login(userName, password));
|
|
|
|
|
await Assert.ThrowsAsync<ArgumentException>(nameof(password), () => _userService.Login(userName, null));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task Login_ShouldReturnAValidToken()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var userName = "tuitio.user";
|
|
|
|
|
var password = "pass123";
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
var result = await _userService.Login(userName, password);
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
Assert.NotNull(result);
|
|
|
|
|
Assert.NotNull(result.Token);
|
|
|
|
|
Assert.NotEmpty(result.Raw);
|
|
|
|
|
Assert.Equal(userName, result.Token.UserName);
|
|
|
|
|
Assert.True(result.Token.TokenId != Guid.Empty, "Token id cannot be an empty guid.");
|
|
|
|
|
Assert.NotEmpty(result.Token.LockStamp);
|
|
|
|
|
Assert.True(result.Token.ExpiresIn > 0, "Token expiration must be a positive number.");
|
|
|
|
|
Assert.True((DateTime.UtcNow - result.Token.CreatedAt).TotalMinutes <= 1, "Token creation date must be within the last minute.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task Login_ShouldSetCorrectLastLoginDate()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var userName = "tuitio.user";
|
|
|
|
|
var password = "pass123";
|
|
|
|
|
var dbContext = _tuitioScope.ServiceProvider.GetRequiredService<TuitioDbContext>();
|
|
|
|
|
await _userService.Login(userName, password);
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
var userFromDb = await dbContext.Users.FirstOrDefaultAsync(z => z.UserName == userName);
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
Assert.NotNull(userFromDb);
|
|
|
|
|
Assert.True(userFromDb.LastLoginDate.HasValue, "Last login date cannot be null after user login");
|
|
|
|
|
Assert.True((DateTime.UtcNow - userFromDb.LastLoginDate.Value).TotalMinutes <= 1, "Last login date must be within the last minute.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task Login_ShouldSaveTokenInDatabase()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var userName = "tuitio.user";
|
|
|
|
|
var password = "pass123";
|
|
|
|
|
var dbContext = _tuitioScope.ServiceProvider.GetRequiredService<TuitioDbContext>();
|
|
|
|
|
var loginResult = await _userService.Login(userName, password);
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
var userTokenFromDb = await dbContext.UserTokens.FirstOrDefaultAsync(z => z.TokenId == loginResult.Token.TokenId);
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
Assert.NotNull(userTokenFromDb);
|
|
|
|
|
Assert.True(loginResult.Token.TokenId != Guid.Empty, "Token id cannot be an empty guid.");
|
|
|
|
|
Assert.True((DateTime.UtcNow - userTokenFromDb.ValidFrom).TotalMinutes <= 1, "Token valid from date must be within the last minute.");
|
|
|
|
|
Assert.True(userTokenFromDb.ValidUntil > DateTime.UtcNow, "Token valid until date must be greater than the current date.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task Login_ShouldReturnNullResultForWrongCredentials()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var userName = "tuitio.user";
|
|
|
|
|
var password = "wrong_password";
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
var result = await _userService.Login(userName, password);
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
Assert.Null(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task Authorize_ShouldReturnAValidToken()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var userName = "tuitio.user";
|
|
|
|
|
var password = "pass123";
|
|
|
|
|
var loginResult = await _userService.Login(userName, password);
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
var result = _userService.Authorize(loginResult.Raw);
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
Assert.NotNull(result);
|
|
|
|
|
Assert.Equal(userName, result.UserName);
|
|
|
|
|
Assert.NotNull(result.SecurityStamp);
|
|
|
|
|
Assert.NotNull(result.LockStamp);
|
|
|
|
|
Assert.True(result.TokenId != Guid.Empty, "Token id cannot be an empty guid.");
|
|
|
|
|
Assert.True(result.ExpiresIn > 0, "Token expiration must be a positive number.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void Authorize_ShouldReturnNull()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var unauthorizedToken = "unauthorized-token";
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
var result = _userService.Authorize(unauthorizedToken);
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
Assert.Null(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task Logout_ShouldSuccessfullyLogoutTheUser()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var userName = "tuitio.user";
|
|
|
|
|
var password = "pass123";
|
|
|
|
|
var loginResult = await _userService.Login(userName, password);
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
LogoutResult result;
|
|
|
|
|
using (var scope = _tuitioScope.ServiceProvider.CreateScope())
|
|
|
|
|
{
|
2023-03-13 23:24:32 +02:00
|
|
|
|
var newUserService = scope.ServiceProvider.GetRequiredService<IUserService>();
|
|
|
|
|
result = await newUserService.Logout(loginResult.Raw);
|
2023-03-13 18:36:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
Assert.NotNull(result);
|
|
|
|
|
Assert.Equal(userName, result.UserName);
|
|
|
|
|
Assert.True((DateTime.UtcNow - result.LogoutDate).TotalMinutes <= 1, "Logout date must be within the last minute.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task Logout_ShouldReturnNullResultForUnauthenticatedToken()
|
|
|
|
|
{
|
|
|
|
|
// Arrange
|
|
|
|
|
var unauthenticatedToken = "unauthenticated-token";
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
var result = await _userService.Logout(unauthenticatedToken);
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
Assert.Null(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|