// Copyright (c) 2020 Tudor Stanciu using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using System; using System.Threading.Tasks; 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, IDisposable { private readonly IServiceScope _tuitioScope; private readonly IUserService _userService; public UserServiceTests(DependencyInjectionFixture fixture) { _tuitioScope = fixture.ServiceProvider.GetRequiredService().CreateScope(); _userService = _tuitioScope.ServiceProvider.GetRequiredService(); } public void Dispose() { _tuitioScope.Dispose(); } [Fact] public async Task Login_ShouldThrowArgumentExceptionIfUserNameIsNullOrEmptyString() { // Arrange var userName = ""; var password = ""; // Act // Assert await Assert.ThrowsAsync(nameof(userName), () => _userService.Login(userName, password)); await Assert.ThrowsAsync(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(nameof(password), () => _userService.Login(userName, password)); await Assert.ThrowsAsync(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(); 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(); 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()) { var newUserService = scope.ServiceProvider.GetRequiredService(); result = await newUserService.Logout(loginResult.Raw); } // 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); } } }