diff --git a/HintKeep.Tests/HintKeep.Tests.csproj b/HintKeep.Tests/HintKeep.Tests.csproj index a1f0989..35370e8 100644 --- a/HintKeep.Tests/HintKeep.Tests.csproj +++ b/HintKeep.Tests/HintKeep.Tests.csproj @@ -10,7 +10,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/HintKeep.Tests/Integration/TestEmailServiceExtensions.cs b/HintKeep.Tests/Integration/TestEmailServiceExtensions.cs index 35b536a..6fa5e80 100644 --- a/HintKeep.Tests/Integration/TestEmailServiceExtensions.cs +++ b/HintKeep.Tests/Integration/TestEmailServiceExtensions.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using Moq; +using NSubstitute; namespace HintKeep.Tests.Integration { @@ -12,14 +12,13 @@ public static WebApplicationFactory WithEmailService(t where TEntryPoint : class => webApplicationFactory.WithEmailService(out var _); - public static WebApplicationFactory WithEmailService(this WebApplicationFactory webApplicationFactory, out Mock emailService) + public static WebApplicationFactory WithEmailService(this WebApplicationFactory webApplicationFactory, out IEmailService emailService) where TEntryPoint : class { - var emailServiceMock = new Mock(); - emailService = emailServiceMock; + var emailServiceSubstitute = emailService = Substitute.For(); return webApplicationFactory.WithWebHostBuilder( configuration => configuration.ConfigureTestServices( - services => services.AddSingleton(emailServiceMock.Object) + services => services.AddSingleton(emailServiceSubstitute) ) ); } diff --git a/HintKeep.Tests/Integration/TestSecurityServiceExtensions.cs b/HintKeep.Tests/Integration/TestSecurityServiceExtensions.cs index 7ec4c7a..d1eb547 100644 --- a/HintKeep.Tests/Integration/TestSecurityServiceExtensions.cs +++ b/HintKeep.Tests/Integration/TestSecurityServiceExtensions.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using Moq; +using NSubstitute; namespace HintKeep.Tests.Integration { @@ -12,14 +12,13 @@ public static WebApplicationFactory WithSecurityService webApplicationFactory.WithSecurityService(out var _); - public static WebApplicationFactory WithSecurityService(this WebApplicationFactory webApplicationFactory, out Mock securityService) + public static WebApplicationFactory WithSecurityService(this WebApplicationFactory webApplicationFactory, out ISecurityService securityService) where TEntryPoint : class { - var securityServiceMock = new Mock(); - securityService = securityServiceMock; + var securityServiceSubstitute = securityService = Substitute.For(); return webApplicationFactory.WithWebHostBuilder( configuration => configuration.ConfigureTestServices( - services => services.AddSingleton(securityServiceMock.Object) + services => services.AddSingleton(securityServiceSubstitute) ) ); } diff --git a/HintKeep.Tests/Integration/Users/PostTests.cs b/HintKeep.Tests/Integration/Users/PostTests.cs index 93a018e..6048a9e 100644 --- a/HintKeep.Tests/Integration/Users/PostTests.cs +++ b/HintKeep.Tests/Integration/Users/PostTests.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Net; using System.Net.Http.Json; using System.Threading.Tasks; @@ -6,7 +7,7 @@ using HintKeep.Storage; using HintKeep.Storage.Entities; using Microsoft.Azure.Cosmos.Table; -using Moq; +using NSubstitute; using Xunit; namespace HintKeep.Tests.Integration.Users @@ -63,16 +64,16 @@ public async Task Post_WhenUserDoesNotExist_ReturnsCreated() .WithEmailService(out var emailService) .CreateClient(); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); securityService - .Setup(securityService => securityService.GeneratePasswordSalt()) + .GeneratePasswordSalt() .Returns("#password-salt"); securityService - .Setup(securityService => securityService.ComputePasswordHash("#password-salt", "#Test-Password1")) + .ComputePasswordHash("#password-salt", "#Test-Password1") .Returns("#password-hash"); securityService - .Setup(securityService => securityService.GenerateConfirmationToken()) + .GenerateConfirmationToken() .Returns(new ConfirmationToken("#confirmation-token", TimeSpan.FromHours(1))); var response = await client.PostAsJsonAsync("/api/users", new { email = "#TEST@domain.com", hint = "#Test-Hint", password = "#Test-Password1" }); @@ -103,8 +104,10 @@ public async Task Post_WhenUserDoesNotExist_ReturnsCreated() Assert.True(DateTimeOffset.UtcNow.AddMinutes(55) < expiration); Assert.True(expiration < DateTimeOffset.UtcNow.AddMinutes(65)); - emailService.Verify(emailService => emailService.SendAsync("#TEST@domain.com", "Welcome to HintKeep!", It.Is(body => body.Contains("#confirmation-token"))), Times.Once); - emailService.VerifyNoOtherCalls(); + await emailService + .Received() + .SendAsync("#TEST@domain.com", "Welcome to HintKeep!", Arg.Is(body => body.Contains("#confirmation-token"))); + Assert.Single(emailService.ReceivedCalls()); } [Fact] @@ -116,10 +119,10 @@ public async Task Post_WhenUserEmailAlreadyExists_ReturnsConflict() .WithEmailService(out var emailService) .CreateClient(); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); securityService - .Setup(securityService => securityService.GenerateConfirmationToken()) + .GenerateConfirmationToken() .Returns(new ConfirmationToken("#confirmation-token", TimeSpan.FromHours(1))); entityTables.Users.Execute(TableOperation.Insert(new TableEntity { PartitionKey = "#email-hash".ToEncodedKeyProperty(), RowKey = "details" })); @@ -133,7 +136,7 @@ public async Task Post_WhenUserEmailAlreadyExists_ReturnsConflict() Assert.Equal("#email-hash".ToEncodedKeyProperty(), userEntity.PartitionKey); Assert.Equal("details", userEntity.RowKey); - emailService.VerifyNoOtherCalls(); + Assert.Empty(emailService.ReceivedCalls()); } } } \ No newline at end of file diff --git a/HintKeep.Tests/Integration/UsersConfirmations/PostTests.cs b/HintKeep.Tests/Integration/UsersConfirmations/PostTests.cs index 1f81dde..eb8ea9e 100644 --- a/HintKeep.Tests/Integration/UsersConfirmations/PostTests.cs +++ b/HintKeep.Tests/Integration/UsersConfirmations/PostTests.cs @@ -5,6 +5,7 @@ using HintKeep.Storage; using HintKeep.Storage.Entities; using Microsoft.Azure.Cosmos.Table; +using NSubstitute; using Xunit; namespace HintKeep.Tests.Integration.UsersConfirmations @@ -59,7 +60,7 @@ public async Task Post_WhenValidTokenExist_ReturnsCreated() } ); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); var response = await client.PostAsJsonAsync("/api/users/confirmations", new { token = "#test-token" }); @@ -109,7 +110,7 @@ public async Task Post_WhenExpiredTokenExist_ReturnsNotFound() Expiration = DateTimeOffset.UtcNow.AddDays(-1) })); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); var response = await client.PostAsJsonAsync("/api/users/confirmations", new { token = "#token" }); diff --git a/HintKeep.Tests/Integration/UsersHintsNotifications/PostTests.cs b/HintKeep.Tests/Integration/UsersHintsNotifications/PostTests.cs index 57d19b6..e4a96da 100644 --- a/HintKeep.Tests/Integration/UsersHintsNotifications/PostTests.cs +++ b/HintKeep.Tests/Integration/UsersHintsNotifications/PostTests.cs @@ -5,7 +5,7 @@ using HintKeep.Storage; using HintKeep.Storage.Entities; using Microsoft.Azure.Cosmos.Table; -using Moq; +using NSubstitute; using Xunit; namespace HintKeep.Tests.Integration.UsersHintsNotifications @@ -51,7 +51,7 @@ public async Task Post_WhenActiveUserExists_ReturnsCreated() } ); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); var response = await client.PostAsJsonAsync("/api/users/hints/notifications", new { email = "#TEST@domain.com" }); @@ -60,8 +60,10 @@ public async Task Post_WhenActiveUserExists_ReturnsCreated() Assert.Empty(await response.Content.ReadAsStringAsync()); Assert.Equal(new Uri("/api/users/sessions", UriKind.Relative), response.Headers.Location); - emailService.Verify(emailService => emailService.SendAsync("#TEST@domain.com", "HintKeep - Account Hint", It.IsRegex("#hint")), Times.Once); - emailService.VerifyNoOtherCalls(); + await emailService + .Received() + .SendAsync("#TEST@domain.com", "HintKeep - Account Hint", Arg.Is(body => body.Contains("#hint"))); + Assert.Single(emailService.ReceivedCalls()); } [Fact] @@ -72,7 +74,7 @@ public async Task Post_WhenUserDoesNotExist_ReturnsNotFound() .WithSecurityService(out var securityService) .CreateClient(); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); var response = await client.PostAsJsonAsync("/api/users/hints/notifications", new { email = "#TEST@domain.com" }); @@ -101,7 +103,7 @@ public async Task Post_WhenInactiveUserExists_ReturnsNotFound() } ); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); var response = await client.PostAsJsonAsync("/api/users/hints/notifications", new { email = "#TEST@domain.com" }); diff --git a/HintKeep.Tests/Integration/UsersPasswords/PostTests.cs b/HintKeep.Tests/Integration/UsersPasswords/PostTests.cs index edd61c1..a187fe4 100644 --- a/HintKeep.Tests/Integration/UsersPasswords/PostTests.cs +++ b/HintKeep.Tests/Integration/UsersPasswords/PostTests.cs @@ -2,11 +2,10 @@ using System.Net; using System.Net.Http.Json; using System.Threading.Tasks; -using HintKeep.Services; using HintKeep.Storage; using HintKeep.Storage.Entities; using Microsoft.Azure.Cosmos.Table; -using Moq; +using NSubstitute; using Xunit; namespace HintKeep.Tests.Integration.UsersPasswords @@ -45,7 +44,7 @@ public async Task Post_WhenTokenDoesNotExist_ReturnsNotFound() Expiration = DateTimeOffset.UtcNow.AddDays(-1) })); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); var response = await client.PostAsJsonAsync("/api/users/passwords", new { email = "#TEST@domain.com", token = "#token", password = "passWORD$123" }); @@ -69,7 +68,7 @@ public async Task Post_WhenUserDoesNotExist_ReturnsNotFound() Expiration = DateTimeOffset.UtcNow.AddDays(1) })); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); var response = await client.PostAsJsonAsync("/api/users/passwords", new { email = "#TEST@domain.com", token = "#token", password = "passWORD$123" }); @@ -108,13 +107,13 @@ public async Task Post_WhenValidTokenExist_ReturnsCreated() }) }); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); securityService - .Setup(securityService => securityService.GeneratePasswordSalt()) + .GeneratePasswordSalt() .Returns("#password-salt"); securityService - .Setup(securityService => securityService.ComputePasswordHash("#password-salt", "passWORD$123")) + .ComputePasswordHash("#password-salt", "passWORD$123") .Returns("#password-hash"); var response = await client.PostAsJsonAsync("/api/users/passwords", new { email = "#TEST@domain.com", token = "#token", password = "passWORD$123" }); @@ -134,8 +133,10 @@ public async Task Post_WhenValidTokenExist_ReturnsCreated() Assert.Equal("#password-hash", userEntity.Properties[nameof(UserEntity.PasswordHash)].StringValue); Assert.True(userEntity.Properties[nameof(UserEntity.IsActive)].BooleanValue); - emailService.Verify(emailService => emailService.SendAsync("#TEST@domain.com", "HintKeep - Password Reset", It.IsAny()), Times.Once); - emailService.VerifyNoOtherCalls(); + await emailService + .Received() + .SendAsync("#TEST@domain.com", "HintKeep - Password Reset", Arg.Any()); + Assert.Single(emailService.ReceivedCalls()); } [Fact] @@ -167,7 +168,7 @@ public async Task Post_WhenInactiveUserExists_ReturnsNotFound() }) }); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); var response = await client.PostAsJsonAsync("/api/users/passwords", new { email = "#TEST@domain.com", token = "#token", password = "passWORD$123" }); diff --git a/HintKeep.Tests/Integration/UsersPasswordsResets/PostTests.cs b/HintKeep.Tests/Integration/UsersPasswordsResets/PostTests.cs index d7e650a..e9a1d3c 100644 --- a/HintKeep.Tests/Integration/UsersPasswordsResets/PostTests.cs +++ b/HintKeep.Tests/Integration/UsersPasswordsResets/PostTests.cs @@ -6,7 +6,7 @@ using HintKeep.Storage; using HintKeep.Storage.Entities; using Microsoft.Azure.Cosmos.Table; -using Moq; +using NSubstitute; using Xunit; namespace HintKeep.Tests.Integration.UsersPasswordsResets @@ -51,10 +51,10 @@ public async Task Post_WhenActiveUserExists_ReturnsCreated() } ); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); securityService - .Setup(securityService => securityService.GenerateConfirmationToken()) + .GenerateConfirmationToken() .Returns(new ConfirmationToken("#confirmation-token", TimeSpan.FromHours(1))); var response = await client.PostAsJsonAsync("/api/users/passwords/resets", new { email = "#TEST@domain.com" }); @@ -73,8 +73,10 @@ public async Task Post_WhenActiveUserExists_ReturnsCreated() Assert.True(DateTimeOffset.UtcNow.AddMinutes(55) < expiration); Assert.True(expiration < DateTimeOffset.UtcNow.AddMinutes(65)); - emailService.Verify(emailService => emailService.SendAsync("#TEST@domain.com", "HintKeep - Password Reset", It.Is(body => body.Contains("#confirmation-token"))), Times.Once); - emailService.VerifyNoOtherCalls(); + await emailService + .Received() + .SendAsync("#TEST@domain.com", "HintKeep - Password Reset", Arg.Is(body => body.Contains("#confirmation-token"))); + Assert.Single(emailService.ReceivedCalls()); } [Fact] @@ -85,7 +87,7 @@ public async Task Post_WhenUserDoesNotExist_ReturnsNotFound() .WithSecurityService(out var securityService) .CreateClient(); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); var response = await client.PostAsJsonAsync("/api/users/passwords/resets", new { email = "#TEST@domain.com" }); @@ -114,7 +116,7 @@ public async Task Post_WhenInactiveUserExists_ReturnsNotFound() } ); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); var response = await client.PostAsJsonAsync("/api/users/passwords/resets", new { email = "#TEST@domain.com" }); diff --git a/HintKeep.Tests/Integration/UsersSessions/Post.cs b/HintKeep.Tests/Integration/UsersSessions/Post.cs index fae6bd8..eaf7a67 100644 --- a/HintKeep.Tests/Integration/UsersSessions/Post.cs +++ b/HintKeep.Tests/Integration/UsersSessions/Post.cs @@ -5,6 +5,7 @@ using HintKeep.Storage; using HintKeep.Storage.Entities; using Microsoft.Azure.Cosmos.Table; +using NSubstitute; using Xunit; namespace HintKeep.Tests.Integration.UsersSessions @@ -54,7 +55,7 @@ public async Task Post_WhenInactiveUserExists_ReturnsNotFound() IsActive = false })); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); var response = await client.PostAsJsonAsync("/api/users/sessions", new { email = "#TEST@domain.com", password = "#test-password" }); @@ -70,7 +71,7 @@ public async Task Post_WhenActiveUserExistButPasswordsDoNotMatch_ReturnsUnproces .WithSecurityService(out var securityService) .CreateClient(); securityService - .Setup(securityService => securityService.ComputePasswordHash("#password-salt", "#test-password")) + .ComputePasswordHash("#password-salt", "#test-password") .Returns("#password-hash-not-matching"); entityTables.Users.Execute(TableOperation.Insert(new UserEntity { @@ -81,10 +82,10 @@ public async Task Post_WhenActiveUserExistButPasswordsDoNotMatch_ReturnsUnproces IsActive = true })); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); securityService - .Setup(securityService => securityService.ComputePasswordHash("#password-salt", "#test-password")) + .ComputePasswordHash("#password-salt", "#test-password") .Returns("#password-hash-not-matching"); var response = await client.PostAsJsonAsync("/api/users/sessions", new { email = "#TEST@domain.com", password = "#test-password" }); @@ -111,10 +112,10 @@ public async Task Post_WhenActiveUserExsitsWithMatchingPassword_ReturnsCreated() IsActive = true })); securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); securityService - .Setup(securityService => securityService.ComputePasswordHash("#password-salt", "#test-password")) + .ComputePasswordHash("#password-salt", "#test-password") .Returns("#password-hash"); var response = await client.PostAsJsonAsync("/api/users/sessions", new { email = "#TEST@domain.com", password = "#test-password" }); diff --git a/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/CreateUserSessionCommandHandlerTests.cs b/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/CreateUserSessionCommandHandlerTests.cs index 4543d21..b7f582d 100644 --- a/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/CreateUserSessionCommandHandlerTests.cs +++ b/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/CreateUserSessionCommandHandlerTests.cs @@ -9,7 +9,7 @@ using HintKeep.Tests.Stubs; using MediatR; using Microsoft.Azure.Cosmos.Table; -using Moq; +using NSubstitute; using Xunit; namespace HintKeep.Tests.Unit.RequestsHandlers.Users.Commands @@ -17,16 +17,16 @@ namespace HintKeep.Tests.Unit.RequestsHandlers.Users.Commands public class CreateUserSessionCommandHandlerTests { private readonly IEntityTables _entityTables; - private readonly Mock _securityService; - private readonly Mock _sessionService; + private readonly ISecurityService _securityService; + private readonly ISessionService _sessionService; private readonly IRequestHandler _createUserSessionCommandHandler; public CreateUserSessionCommandHandlerTests() { _entityTables = new InMemoryEntityTables(); - _securityService = new Mock(); - _sessionService = new Mock(); - _createUserSessionCommandHandler = new CreateUserSessionCommandHandler(_entityTables, _securityService.Object, _sessionService.Object); + _securityService = Substitute.For(); + _sessionService = Substitute.For(); + _createUserSessionCommandHandler = new CreateUserSessionCommandHandler(_entityTables, _securityService, _sessionService); _entityTables.Users.Create(); } @@ -34,7 +34,7 @@ public CreateUserSessionCommandHandlerTests() public async Task Handle_WhenUserDoesNotExist_ThrowsException() { _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); await Assert.ThrowsAsync(() => _createUserSessionCommandHandler.Handle(new CreateUserSessionCommand("#TEST@domain.com", "#test-password"), default)); @@ -53,7 +53,7 @@ public async Task Handle_WhenInactiveUserExists_ThrowsException() } )); _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); await Assert.ThrowsAsync(() => _createUserSessionCommandHandler.Handle(new CreateUserSessionCommand("#TEST@domain.com", "#test-password"), default)); @@ -74,10 +74,10 @@ public async Task Handle_WhenPasswordDoesNotMatch_ThrowsException() } )); _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); _securityService - .Setup(securityService => securityService.ComputePasswordHash("#password-salt", "#test-password")) + .ComputePasswordHash("#password-salt", "#test-password") .Returns("#password-hash-not-matching"); var exception = await Assert.ThrowsAsync(() => _createUserSessionCommandHandler.Handle(new CreateUserSessionCommand("#TEST@domain.com", "#test-password"), default)); @@ -101,13 +101,13 @@ public async Task Handle_WhenPasswordMatches_ReturnsJsonWebTokenAndSetsLastLogin } )); _securityService - .Setup(securityService => securityService.ComputePasswordHash("#password-salt", "#test-password")) + .ComputePasswordHash("#password-salt", "#test-password") .Returns("#password-hash"); _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); _sessionService - .Setup(sessionService => sessionService.CreateJsonWebToken("#user-id", "#user-role")) + .CreateJsonWebToken("#user-id", "#user-role") .Returns("#json-web-token"); var jsonWebToken = await _createUserSessionCommandHandler.Handle(new CreateUserSessionCommand("#TEST@domain.com", "#test-password"), default); diff --git a/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/RegisterUserCommandHandlerTests.cs b/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/RegisterUserCommandHandlerTests.cs index b562a75..98018da 100644 --- a/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/RegisterUserCommandHandlerTests.cs +++ b/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/RegisterUserCommandHandlerTests.cs @@ -9,7 +9,7 @@ using HintKeep.Tests.Stubs; using MediatR; using Microsoft.Azure.Cosmos.Table; -using Moq; +using NSubstitute; using Xunit; namespace HintKeep.Tests.Unit.RequestsHandlers.Users.Commands @@ -17,33 +17,33 @@ namespace HintKeep.Tests.Unit.RequestsHandlers.Users.Commands public class RegisterUserCommandHandlerTests { private readonly IEntityTables _entityTables; - private readonly Mock _emailService; - private readonly Mock _securityService; + private readonly IEmailService _emailService; + private readonly ISecurityService _securityService; private readonly IRequestHandler _registerUserCommandHandler; public RegisterUserCommandHandlerTests() { _entityTables = new InMemoryEntityTables(); - _emailService = new Mock(); - _securityService = new Mock(); + _emailService = Substitute.For(); + _securityService = Substitute.For(); _entityTables.Users.Create(); - _registerUserCommandHandler = new RegisterUserCommandHandler(_entityTables, _emailService.Object, _securityService.Object); + _registerUserCommandHandler = new RegisterUserCommandHandler(_entityTables, _emailService, _securityService); } [Fact] public async Task Handle_WhenUserDoesNotExist_CreatesInactiveUserAndActivationToken() { _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); _securityService - .Setup(securityService => securityService.GeneratePasswordSalt()) + .GeneratePasswordSalt() .Returns("#password-salt"); _securityService - .Setup(securityService => securityService.ComputePasswordHash("#password-salt", "#test-password")) + .ComputePasswordHash("#password-salt", "#test-password") .Returns("#password-hash"); _securityService - .Setup(securityService => securityService.GenerateConfirmationToken()) + .GenerateConfirmationToken() .Returns(new ConfirmationToken("#activation-token", TimeSpan.FromMinutes(60))); await _registerUserCommandHandler.Handle( @@ -77,18 +77,20 @@ await _registerUserCommandHandler.Handle( Assert.True(DateTimeOffset.UtcNow.AddMinutes(55) < expiration); Assert.True(expiration < DateTimeOffset.UtcNow.AddMinutes(65)); - _emailService.Verify(emailService => emailService.SendAsync("#TEST@domain.com", "Welcome to HintKeep!", It.IsRegex("#activation-token")), Times.Once); - _emailService.VerifyNoOtherCalls(); + await _emailService + .Received() + .SendAsync("#TEST@domain.com", "Welcome to HintKeep!", Arg.Is(body => body.Contains("#activation-token"))); + Assert.Single(_emailService.ReceivedCalls()); } [Fact] public async Task Handle_WhenUserEmailAlreadyExists_ThrowsException() { _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); _securityService - .Setup(securityService => securityService.GenerateConfirmationToken()) + .GenerateConfirmationToken() .Returns(new ConfirmationToken("#token", TimeSpan.Zero)); _entityTables.Users.Execute(TableOperation.Insert(new TableEntity { PartitionKey = "#email-hash".ToEncodedKeyProperty(), RowKey = "details" })); @@ -101,7 +103,7 @@ await Assert.ThrowsAsync(() => _registerUserCommandHandler.Ha default )); - _emailService.VerifyNoOtherCalls(); + Assert.Empty(_emailService.ReceivedCalls()); } } } \ No newline at end of file diff --git a/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserPasswordResetCommandHandlerTests.cs b/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserPasswordResetCommandHandlerTests.cs index 485cdf0..b529e69 100644 --- a/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserPasswordResetCommandHandlerTests.cs +++ b/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserPasswordResetCommandHandlerTests.cs @@ -8,7 +8,7 @@ using HintKeep.Tests.Stubs; using MediatR; using Microsoft.Azure.Cosmos.Table; -using Moq; +using NSubstitute; using Xunit; namespace HintKeep.Tests.Unit.RequestsHandlers.Users.Commands @@ -16,16 +16,16 @@ namespace HintKeep.Tests.Unit.RequestsHandlers.Users.Commands public class UserPasswordResetCommandHandlerTests { private readonly InMemoryEntityTables _entityTables; - private readonly Mock _securityService; - private readonly Mock _emailService; + private readonly ISecurityService _securityService; + private readonly IEmailService _emailService; private readonly IRequestHandler _userPasswordResetCommandHandler; public UserPasswordResetCommandHandlerTests() { _entityTables = new InMemoryEntityTables(); - _securityService = new Mock(); - _emailService = new Mock(); - _userPasswordResetCommandHandler = new UserPasswordResetCommandHandler(_entityTables, _securityService.Object, _emailService.Object); + _securityService = Substitute.For(); + _emailService = Substitute.For(); + _userPasswordResetCommandHandler = new UserPasswordResetCommandHandler(_entityTables, _securityService, _emailService); _entityTables.Users.Create(); } @@ -33,7 +33,7 @@ public UserPasswordResetCommandHandlerTests() public async Task Handle_WhenTokenDoesNotExist_ThrowsException() { _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); await Assert.ThrowsAsync(() => _userPasswordResetCommandHandler.Handle( @@ -50,7 +50,7 @@ await Assert.ThrowsAsync(() => _userPasswordResetCommandHandl public async Task Handle_WhenExpiredTokenExist_ThrowsException() { _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); _entityTables.Users.Execute(TableOperation.Insert(new UserPasswordResetTokenEntity { @@ -76,7 +76,7 @@ await Assert.ThrowsAsync(() => _userPasswordResetCommandHandl public async Task Handle_WhenUserDoesNotExist_ThrowsException() { _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); _entityTables.Users.Execute(TableOperation.Insert(new UserPasswordResetTokenEntity { @@ -122,13 +122,13 @@ public async Task Handle_WhenValidTokenExist_ResetsPassword() }) }); _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); _securityService - .Setup(securityService => securityService.GeneratePasswordSalt()) + .GeneratePasswordSalt() .Returns("#password-salt"); _securityService - .Setup(securityService => securityService.ComputePasswordHash("#password-salt", "#password")) + .ComputePasswordHash("#password-salt", "#password") .Returns("#password-hash"); await _userPasswordResetCommandHandler.Handle( @@ -177,7 +177,7 @@ public async Task Handle_WhenInactiveUserExist_ThrowsException() }) }); _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); await Assert.ThrowsAsync(() => _userPasswordResetCommandHandler.Handle( diff --git a/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserRequestHintCommandHandlerTests.cs b/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserRequestHintCommandHandlerTests.cs index 6ff3cfc..9b3d8ad 100644 --- a/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserRequestHintCommandHandlerTests.cs +++ b/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserRequestHintCommandHandlerTests.cs @@ -8,7 +8,7 @@ using HintKeep.Tests.Stubs; using MediatR; using Microsoft.Azure.Cosmos.Table; -using Moq; +using NSubstitute; using Xunit; namespace HintKeep.Tests.Unit.RequestsHandlers.Users.Commands @@ -16,16 +16,16 @@ namespace HintKeep.Tests.Unit.RequestsHandlers.Users.Commands public class UserRequestHintCommandHandlerTests { private readonly IEntityTables _entityTables; - private readonly Mock _securityService; - private readonly Mock _emailService; + private readonly ISecurityService _securityService; + private readonly IEmailService _emailService; private readonly IRequestHandler _userRequestHintCommandHandler; public UserRequestHintCommandHandlerTests() { _entityTables = new InMemoryEntityTables(); - _securityService = new Mock(); - _emailService = new Mock(); - _userRequestHintCommandHandler = new UserRequestHintCommandHandler(_entityTables, _securityService.Object, _emailService.Object); + _securityService = Substitute.For(); + _emailService = Substitute.For(); + _userRequestHintCommandHandler = new UserRequestHintCommandHandler(_entityTables, _securityService, _emailService); _entityTables.Users.Create(); } @@ -33,7 +33,7 @@ public UserRequestHintCommandHandlerTests() public async Task Handle_WhenUserDoesNotExist_ThrowsException() { _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); await Assert.ThrowsAsync(() => _userRequestHintCommandHandler.Handle(new UserRequestHintCommand("#TEST@domain.com"), default)); @@ -50,7 +50,7 @@ public async Task Handle_WhenInactiveUserExist_ThrowsException() IsActive = false })); _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); await Assert.ThrowsAsync(() => _userRequestHintCommandHandler.Handle(new UserRequestHintCommand("#TEST@domain.com"), default)); @@ -68,13 +68,15 @@ public async Task Handle_WhenActiveUserExist_SendsEmailNotification() IsActive = true })); _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); await _userRequestHintCommandHandler.Handle(new UserRequestHintCommand("#TEST@domain.com"), default); - _emailService.Verify(emailService => emailService.SendAsync("#TEST@domain.com", "HintKeep - Account Hint", It.IsRegex("#hint")), Times.Once); - _emailService.VerifyNoOtherCalls(); + await _emailService + .Received() + .SendAsync("#TEST@domain.com", "HintKeep - Account Hint", Arg.Is(body => body.Contains("#hint"))); + Assert.Single(_emailService.ReceivedCalls()); } } } \ No newline at end of file diff --git a/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserRequestPasswordResetCommandHandlerTests.cs b/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserRequestPasswordResetCommandHandlerTests.cs index a6177af..9ea42df 100644 --- a/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserRequestPasswordResetCommandHandlerTests.cs +++ b/HintKeep.Tests/Unit/RequestsHandlers/Users/Commands/UserRequestPasswordResetCommandHandlerTests.cs @@ -9,7 +9,7 @@ using HintKeep.Tests.Stubs; using MediatR; using Microsoft.Azure.Cosmos.Table; -using Moq; +using NSubstitute; using Xunit; namespace HintKeep.Tests.Unit.RequestsHandlers.Users.Commands @@ -17,16 +17,16 @@ namespace HintKeep.Tests.Unit.RequestsHandlers.Users.Commands public class UserRequestPasswordResetCommandHandlerTests { private IEntityTables _entityTables; - private Mock _securityService; - private Mock _emailService; + private ISecurityService _securityService; + private IEmailService _emailService; private IRequestHandler _userRequestPasswordResetCommand; public UserRequestPasswordResetCommandHandlerTests() { _entityTables = new InMemoryEntityTables(); - _securityService = new Mock(); - _emailService = new Mock(); - _userRequestPasswordResetCommand = new UserRequestPasswordResetCommandHandler(_entityTables, _securityService.Object, _emailService.Object); + _securityService = Substitute.For(); + _emailService = Substitute.For(); + _userRequestPasswordResetCommand = new UserRequestPasswordResetCommandHandler(_entityTables, _securityService, _emailService); _entityTables.Users.Create(); } @@ -34,7 +34,7 @@ public UserRequestPasswordResetCommandHandlerTests() public async Task Handle_WhenUserDoesNotExist_ThrowsException() { _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); await Assert.ThrowsAsync(() => _userRequestPasswordResetCommand.Handle(new UserRequestPasswordResetCommand("#TEST@domain.com"), default)); @@ -51,7 +51,7 @@ public async Task Handle_WhenInactiveUserExists_ThrowsException() IsActive = false })); _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); await Assert.ThrowsAsync(() => _userRequestPasswordResetCommand.Handle(new UserRequestPasswordResetCommand("#TEST@domain.com"), default)); @@ -68,10 +68,10 @@ public async Task Handle_WhenActiveUserExists_ThrowsException() IsActive = true })); _securityService - .Setup(securityService => securityService.ComputeHash("#test@domain.com")) + .ComputeHash("#test@domain.com") .Returns("#email-hash"); _securityService - .Setup(securityService => securityService.GenerateConfirmationToken()) + .GenerateConfirmationToken() .Returns(new ConfirmationToken("#confirmation-token", TimeSpan.FromMinutes(60))); await _userRequestPasswordResetCommand.Handle(new UserRequestPasswordResetCommand("#TEST@domain.com"), default); @@ -86,8 +86,10 @@ public async Task Handle_WhenActiveUserExists_ThrowsException() Assert.True(DateTimeOffset.UtcNow.AddMinutes(55) < expiration); Assert.True(expiration < DateTimeOffset.UtcNow.AddMinutes(65)); - _emailService.Verify(emailService => emailService.SendAsync("#TEST@domain.com", "HintKeep - Password Reset", It.IsRegex("#confirmation-token")), Times.Once); - _emailService.VerifyNoOtherCalls(); + await _emailService + .Received() + .SendAsync("#TEST@domain.com", "HintKeep - Password Reset", Arg.Is(body => body.Contains("#confirmation-token"))); + Assert.Single(_emailService.ReceivedCalls()); } } } \ No newline at end of file