using e_suite.API.Common.models; using e_suite.Database.Core.Models; using e_suite.Database.Core.Tables.UserManager; using e_suite.Nuget.PasswordHasher; using eSuite.Core.MailService; using Microsoft.AspNetCore.Identity; using Moq; using NUnit.Framework; using UserManager.UnitTests.Helpers; namespace UserManager.UnitTests.UserManager; [TestFixture] public class LoginUnitTests : UserManagerTestBase { [SetUp] public override async Task Setup() { await base.Setup(); } [Test] public async Task Login_UserNotFound_ReturnsFailed() { //Arrange var login = new Login { Email = "fred@bloggs.com" //User does not exist in fake database. }; //Act var result = await UserManager.Login(login); //Assert Assert.That(result.Result, Is.EqualTo(LoginResult.Failed)); } [Test] public async Task Login_VerifyHashedPasswordFails_ReturnsNull() { //Arrange var existingUser = new User { Id = 15, Email = "testuser@sun-strategy.com", Password = "testing", EmailConfirmed = true }; await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default); var login = new Login { Email = existingUser.Email, Password = "testing" }; CustomPasswordHasherMock.Setup(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password)).Returns(PasswordVerificationResult.Failed); //Act var result = await UserManager.Login(login); //Assert CustomPasswordHasherMock.Verify(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password), Times.Once); Assert.That(result.Result, Is.EqualTo(LoginResult.Failed)); } [Test] public async Task Login_VerifyHashedPasswordMatches_ReturnsSuccess() { //Arrange var existingUser = new User { Id = 15, Email = "testuser@sun-strategy.com", Password = "testing", EmailConfirmed = true }; await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default); var login = new Login { Email = existingUser.Email, Password = "testing" }; CustomPasswordHasherMock.Setup(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password)).Returns(PasswordVerificationResult.Success); //Act var result = await UserManager.Login(login); //Assert CustomPasswordHasherMock.Verify(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password), Times.Once); CustomPasswordHasherMock.Verify(x => x.HashPassword(It.IsAny(), login.Password), Times.Never); Assert.That(result.Result, Is.EqualTo(LoginResult.Success)); Assert.That(result.Token, Is.Not.Empty); } [Test] public async Task Login_VerifyHashedPasswordRequestsRehash_ReturnsSuccessAndRehashesPassword() { //Arrange var existingUser = new User { Id = 14, Email = "testuser@sun-strategy.com", Password = "testing", EmailConfirmed = true }; await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default); var login = new Login { Email = existingUser.Email, Password = "testing" }; var newHashedPassword = "123456"; CustomPasswordHasherMock.Setup(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password)).Returns(PasswordVerificationResult.SuccessRehashNeeded); CustomPasswordHasherMock.Setup(x => x.HashPassword(It.IsAny(), login.Password)).Returns(newHashedPassword); //Act var result = await UserManager.Login(login); //Assert CustomPasswordHasherMock.Verify(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password), Times.Once); CustomPasswordHasherMock.Verify(x => x.HashPassword(It.IsAny(), login.Password), Times.Once); Assert.That(result.Result, Is.EqualTo(LoginResult.Success)); Assert.That(result.Token, Is.Not.Empty); } [Test] public async Task Login_EmailNotConfirmed_ReturnsError() { //Arrange var existingUser = new User { Id = 16, Email = "testuser2@sun-strategy.com", Password = "testing", EmailConfirmed = false }; await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default); var login = new Login { Email = existingUser.Email, Password = "testing" }; CustomPasswordHasherMock.Setup(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password)).Returns(PasswordVerificationResult.Success); //Act var result = await UserManager.Login(login); //Assert Assert.That(result.Result, Is.EqualTo(LoginResult.EmailNotConfirmed)); } [Test] public async Task Login_RequestTfaDisable_ReturnsTwoFactorAuthenticationRemovalRequested() { //Arrange var existingUser = new User { Id = 16, Email = "testuser3@sun-strategy.com", Password = "testing", EmailConfirmed = true, UsingTwoFactorAuthentication = true, FirstName = "Test3", LastName = "User" }; await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default); var login = new Login { Email = existingUser.Email, Password = "testing", RequestTfaRemoval = true }; CustomPasswordHasherMock.Setup(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password)).Returns(PasswordVerificationResult.Success); MailRequest actualMailRequest = null!; MailServiceMock.Setup(x => x.RequestEMailAsync(It.IsAny(), It.IsAny())) .Callback((mailRequest, cancellationToken) => { actualMailRequest = mailRequest; }); //Act var result = await UserManager.Login(login); //Assert that the new row was added to the database correctly. var emailUserAction = UserManagerRepository.EmailUserActions.SingleOrDefault(x => x.User.Email == login.Email); Assert.That(emailUserAction, Is.Not.Null); Assert.That(emailUserAction?.EmailActionType, Is.EqualTo(EmailUserActionType.DisableAuthenticator)); Assert.That(emailUserAction?.User.Email, Is.EqualTo(login.Email)); Assert.That(emailUserAction?.Token, Is.Not.Empty); //Assert that the e-mail request was sent. MailServiceMock.Verify(x => x.RequestEMailAsync(It.IsAny(), It.IsAny()), Times.Once); Assert.That(actualMailRequest, Is.Not.Null); Assert.That(actualMailRequest.EmailType, Is.EqualTo(MailType.DisableAuthenticator)); Assert.That(actualMailRequest.To.Count, Is.EqualTo(1)); Assert.That(actualMailRequest.To[0].DisplayName, Is.EqualTo(existingUser.DisplayName)); Assert.That(actualMailRequest.To[0].Email, Is.EqualTo(login.Email)); Assert.That(actualMailRequest.Parameters.Count, Is.EqualTo(1)); Assert.That(actualMailRequest.Parameters["url"], Is.Not.Empty); Assert.That(result.Result, Is.EqualTo(LoginResult.TwoFactorAuthenticationRemovalRequested)); } [Test] public async Task Login_TfaEnabledButCodeNotProvided_ReturnsTwoFactorAuthenticationCodeRequired() { //Arrange var existingUser = new User { Id = 16, Email = "testuser3@sun-strategy.com", Password = "testing", EmailConfirmed = true, UsingTwoFactorAuthentication = true, FirstName = "Test3", LastName = "User" }; await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default); var login = new Login { Email = existingUser.Email, Password = "testing", RequestTfaRemoval = false }; CustomPasswordHasherMock.Setup(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password)).Returns(PasswordVerificationResult.Success); //Act var result = await UserManager.Login(login); //Assert Assert.That(result.Result, Is.EqualTo(LoginResult.TwoFactorAuthenticationCodeRequired)); } [Test] public async Task Login_TfaEnabledButCodeIncorrect_ReturnsTwoFactorAuthenticationCodeIncorrect() { //Arrange var existingUser = new User { Id = 16, Email = "testuser3@sun-strategy.com", Password = "testing", EmailConfirmed = true, UsingTwoFactorAuthentication = true, FirstName = "Test3", LastName = "User" }; await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default); var login = new Login { Email = existingUser.Email, Password = "testing", RequestTfaRemoval = false, SecurityCode = "123456" }; CustomPasswordHasherMock.Setup(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password)).Returns(PasswordVerificationResult.Success); //Act var result = await UserManager.Login(login); //Assert TwoFactorAuthenticatorMock.Verify(x => x.ValidateTwoFactorPIN(It.IsAny(), login.SecurityCode, false), Times.Once); Assert.That(result.Result, Is.EqualTo(LoginResult.TwoFactorAuthenticationCodeIncorrect)); } [Test] public async Task Login_TfaEnabledAndCodeCorrect_ReturnsSuccess() { //Arrange var existingUser = new User { Id = 16, Email = "testuser3@sun-strategy.com", Password = "testing", EmailConfirmed = true, UsingTwoFactorAuthentication = true, FirstName = "Test3", LastName = "User" }; await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default); var login = new Login { Email = "testuser3@sun-strategy.com", //User exists in fake database. But email is not confirmed. Password = "testing", RequestTfaRemoval = false, SecurityCode = "123456" }; CustomPasswordHasherMock.Setup(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password)).Returns(PasswordVerificationResult.Success); TwoFactorAuthenticatorMock.Setup(x => x.ValidateTwoFactorPIN(It.IsAny(), login.SecurityCode, It.IsAny())).Returns(true); //Act var result = await UserManager.Login(login); //Assert Assert.That(result.Result, Is.EqualTo(LoginResult.Success)); Assert.That(result.Token, Is.Not.Empty); } [Test] public async Task Login_AccountIsNotActive_ReturnsFailed() { //Arrange var existingUser = new User { Id = 15, Email = "testuser@sun-strategy.com", Password = "testing", EmailConfirmed = true, Active = false }; await UserManagerRepository.AddUser(AuditUserDetails, existingUser, default); var login = new Login { Email = existingUser.Email, Password = "testing" }; CustomPasswordHasherMock.Setup(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), login.Password)).Returns(PasswordVerificationResult.Success); //Act var result = await UserManager.Login(login); //Assert Assert.That(result.Result, Is.EqualTo(LoginResult.Failed)); Assert.That(result.Token, Is.Empty); } }