diff --git a/LeaderboardBackend.Test/Categories.cs b/LeaderboardBackend.Test/Categories.cs index 14c0054f..e8a6ec04 100644 --- a/LeaderboardBackend.Test/Categories.cs +++ b/LeaderboardBackend.Test/Categories.cs @@ -6,6 +6,11 @@ using LeaderboardBackend.Models.ViewModels; using LeaderboardBackend.Test.TestApi; using LeaderboardBackend.Test.TestApi.Extensions; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using NodaTime.Testing; using NUnit.Framework; namespace LeaderboardBackend.Test; @@ -14,16 +19,23 @@ namespace LeaderboardBackend.Test; internal class Categories { private static TestApiClient _apiClient = null!; - private static TestApiFactory _factory = null!; + private static WebApplicationFactory _factory = null!; + private static readonly FakeClock _clock = new(new Instant()); private static string? _jwt; [OneTimeSetUp] public async Task OneTimeSetUp() { - _factory = new TestApiFactory(); - _apiClient = _factory.CreateTestApiClient(); + _factory = new TestApiFactory().WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(services => + { + services.AddSingleton(_ => _clock); + }); + }); + _apiClient = new TestApiClient(_factory.CreateClient()); - _factory.ResetDatabase(); + PostgresDatabaseFixture.ResetDatabaseToTemplate(); _jwt = (await _apiClient.LoginAdminUser()).Token; } @@ -50,6 +62,9 @@ await _apiClient.Get( [Test] public static async Task CreateCategory_GetCategory_OK() { + Instant now = Instant.FromUnixTimeSeconds(1); + _clock.Reset(now); + LeaderboardViewModel createdLeaderboard = await _apiClient.Post( "/leaderboards/create", new() @@ -64,27 +79,31 @@ public static async Task CreateCategory_GetCategory_OK() } ); + CreateCategoryRequest request = new() + { + Name = "1 Player", + Slug = "1_player", + LeaderboardId = createdLeaderboard.Id, + Info = null, + SortDirection = SortDirection.Ascending, + Type = RunType.Time + }; + CategoryViewModel createdCategory = await _apiClient.Post( "/categories/create", new() { - Body = new CreateCategoryRequest() - { - Name = "1 Player", - Slug = "1_player", - LeaderboardId = createdLeaderboard.Id, - Info = null, - SortDirection = SortDirection.Ascending, - Type = RunType.Time - }, + Body = request, Jwt = _jwt } ); + createdCategory.CreatedAt.Should().Be(now); + CategoryViewModel retrievedCategory = await _apiClient.Get( $"/api/category/{createdCategory?.Id}", new() { } ); - Assert.AreEqual(createdCategory, retrievedCategory); + retrievedCategory.Should().BeEquivalentTo(request, opts => opts.Excluding(c => c.LeaderboardId)); } } diff --git a/LeaderboardBackend.Test/Features/Users/ConfirmAccountTests.cs b/LeaderboardBackend.Test/Features/Users/ConfirmAccountTests.cs index 7a5d3b22..4fa0ff29 100644 --- a/LeaderboardBackend.Test/Features/Users/ConfirmAccountTests.cs +++ b/LeaderboardBackend.Test/Features/Users/ConfirmAccountTests.cs @@ -23,7 +23,13 @@ public class ConfirmAccountTests : IntegrationTestsBase [SetUp] public void Init() { - _scope = _factory.Services.CreateScope(); + _scope = _factory.WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(services => + { + services.AddSingleton(_ => _clock); + }); + }).Services.CreateScope(); _client = _factory.WithWebHostBuilder(builder => { @@ -44,12 +50,12 @@ public void TearDown() [Test] public async Task ConfirmAccount_BadConfirmationId() { - _clock.Reset(Instant.FromUnixTimeSeconds(1)); + Instant now = Instant.FromUnixTimeSeconds(1); + _clock.Reset(now); ApplicationContext context = _scope.ServiceProvider.GetRequiredService(); AccountConfirmation confirmation = new() { - CreatedAt = Instant.FromUnixTimeSeconds(0), - ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)), + ExpiresAt = now.Plus(Duration.FromHours(1)), User = new() { Email = "test@email.com", @@ -60,6 +66,7 @@ public async Task ConfirmAccount_BadConfirmationId() context.AccountConfirmations.Add(confirmation); await context.SaveChangesAsync(); + confirmation.CreatedAt.Should().Be(now); HttpResponseMessage res = await _client.PutAsync(Routes.ConfirmAccount(Guid.NewGuid()), null); res.StatusCode.Should().Be(HttpStatusCode.NotFound); context.ChangeTracker.Clear(); @@ -78,13 +85,12 @@ public async Task ConfirmAccount_MalformedConfirmationId() [Test] public async Task ConfirmAccount_BadRole() { - _clock.Reset(Instant.FromUnixTimeSeconds(1)); + Instant now = Instant.FromUnixTimeSeconds(1); + _clock.Reset(now); ApplicationContext context = _scope.ServiceProvider.GetRequiredService(); - AccountConfirmation confirmation = new() { - CreatedAt = Instant.FromUnixTimeSeconds(0), - ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)), + ExpiresAt = now.Plus(Duration.FromHours(1)), User = new() { Email = "test@email.com", @@ -96,7 +102,7 @@ public async Task ConfirmAccount_BadRole() context.AccountConfirmations.Add(confirmation); await context.SaveChangesAsync(); - + confirmation.CreatedAt.Should().Be(now); HttpResponseMessage res = await _client.PutAsync(Routes.ConfirmAccount(confirmation.Id), null); res.StatusCode.Should().Be(HttpStatusCode.Conflict); context.ChangeTracker.Clear(); @@ -107,13 +113,13 @@ public async Task ConfirmAccount_BadRole() [Test] public async Task ConfirmAccount_Expired() { - _clock.Reset(Instant.FromUnixTimeSeconds(1) + Duration.FromHours(1)); + Instant now = Instant.FromUnixTimeSeconds(1); + _clock.Reset(now); ApplicationContext context = _scope.ServiceProvider.GetRequiredService(); AccountConfirmation confirmation = new() { - CreatedAt = Instant.FromUnixTimeSeconds(0), - ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)), + ExpiresAt = now.Plus(Duration.FromHours(1)), User = new() { Email = "test@email.com", @@ -124,6 +130,7 @@ public async Task ConfirmAccount_Expired() context.AccountConfirmations.Add(confirmation); await context.SaveChangesAsync(); + _clock.Reset(now + Duration.FromHours(2)); HttpResponseMessage res = await _client.PutAsync(Routes.ConfirmAccount(confirmation.Id), null); res.StatusCode.Should().Be(HttpStatusCode.NotFound); context.ChangeTracker.Clear(); @@ -135,14 +142,14 @@ public async Task ConfirmAccount_Expired() [Test] public async Task ConfirmAccount_AlreadyUsed() { - _clock.Reset(Instant.FromUnixTimeSeconds(1)); + Instant now = Instant.FromUnixTimeSeconds(1); + _clock.Reset(now); ApplicationContext context = _scope.ServiceProvider.GetRequiredService(); AccountConfirmation confirmation = new() { - CreatedAt = Instant.FromUnixTimeSeconds(0), - ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)), - UsedAt = Instant.FromUnixTimeSeconds(5), + ExpiresAt = now.Plus(Duration.FromHours(1)), + UsedAt = now.Plus(Duration.FromSeconds(5)), User = new() { Email = "test@email.com", @@ -153,6 +160,7 @@ public async Task ConfirmAccount_AlreadyUsed() context.AccountConfirmations.Add(confirmation); await context.SaveChangesAsync(); + _clock.AdvanceMinutes(1); HttpResponseMessage res = await _client.PutAsync(Routes.ConfirmAccount(confirmation.Id), null); res.StatusCode.Should().Be(HttpStatusCode.NotFound); context.ChangeTracker.Clear(); @@ -163,12 +171,12 @@ public async Task ConfirmAccount_AlreadyUsed() [Test] public async Task ConfirmAccount_Success() { - _clock.Reset(Instant.FromUnixTimeSeconds(1)); + Instant now = Instant.FromUnixTimeSeconds(1); + _clock.Reset(now); AccountConfirmation confirmation = new() { - CreatedAt = Instant.FromUnixTimeSeconds(0), - ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)), + ExpiresAt = now.Plus(Duration.FromHours(1)), User = new() { Email = "test@email.com", @@ -180,11 +188,12 @@ public async Task ConfirmAccount_Success() ApplicationContext context = _scope.ServiceProvider.GetRequiredService(); context.AccountConfirmations.Add(confirmation); await context.SaveChangesAsync(); + _clock.AdvanceMinutes(5); HttpResponseMessage res = await _client.PutAsync(Routes.ConfirmAccount(confirmation.Id), null); res.Should().HaveStatusCode(HttpStatusCode.OK); context.ChangeTracker.Clear(); AccountConfirmation? conf = await context.AccountConfirmations.Include(c => c.User).SingleOrDefaultAsync(c => c.Id == confirmation.Id); - conf!.UsedAt.Should().Be(Instant.FromUnixTimeSeconds(1)); + conf!.UsedAt.Should().Be(now.Plus(Duration.FromMinutes(5))); conf!.User.Role.Should().Be(UserRole.Confirmed); } } diff --git a/LeaderboardBackend.Test/Features/Users/RegistrationTests.cs b/LeaderboardBackend.Test/Features/Users/RegistrationTests.cs index 291db890..c094c794 100644 --- a/LeaderboardBackend.Test/Features/Users/RegistrationTests.cs +++ b/LeaderboardBackend.Test/Features/Users/RegistrationTests.cs @@ -32,28 +32,33 @@ public class RegistrationTests : IntegrationTestsBase public async Task Register_ValidRequest_CreatesAndReturnsUser() { Mock emailSenderMock = new(); + Instant now = Instant.FromUnixTimeSeconds(1); + using HttpClient client = _factory.WithWebHostBuilder(builder => { builder.ConfigureTestServices(services => { services.AddScoped(_ => emailSenderMock.Object); - services.AddSingleton(_ => new(Instant.FromUnixTimeSeconds(1))); + services.AddSingleton(_ => new(now)); }); }) .CreateClient(); + RegisterRequest request = _registerReqFaker.Generate(); HttpResponseMessage res = await client.PostAsJsonAsync(Routes.REGISTER, request); res.Should().HaveStatusCode(HttpStatusCode.Created); UserViewModel? content = await res.Content.ReadFromJsonAsync(TestInitCommonFields.JsonSerializerOptions); - content.Should().NotBeNull().And.BeEquivalentTo(new UserViewModel + + content.Should().NotBeNull().And.Be(new UserViewModel { Id = content!.Id, Username = request.Username, Role = UserRole.Registered, - CreatedAt = Instant.FromUnixTimeSeconds(1) + CreatedAt = now }); + emailSenderMock.Verify(x => x.EnqueueEmailAsync( It.IsAny(), @@ -66,14 +71,17 @@ public async Task Register_ValidRequest_CreatesAndReturnsUser() using IServiceScope scope = _factory.Services.CreateScope(); using ApplicationContext dbContext = scope.ServiceProvider.GetRequiredService(); User? createdUser = dbContext.Users.FirstOrDefault(u => u.Id == content.Id); + createdUser.Should().NotBeNull().And.BeEquivalentTo(new User { Id = content!.Id, Password = createdUser!.Password, Username = request.Username, Email = request.Email, - Role = UserRole.Registered + Role = UserRole.Registered, + CreatedAt = now }); + AccountConfirmation confirmation = dbContext.AccountConfirmations.First(c => c.UserId == createdUser.Id); confirmation.Should().NotBeNull(); confirmation.CreatedAt.ToUnixTimeSeconds().Should().Be(1); diff --git a/LeaderboardBackend.Test/Features/Users/ResetPasswordTests.cs b/LeaderboardBackend.Test/Features/Users/ResetPasswordTests.cs index 79646479..14f94b95 100644 --- a/LeaderboardBackend.Test/Features/Users/ResetPasswordTests.cs +++ b/LeaderboardBackend.Test/Features/Users/ResetPasswordTests.cs @@ -40,7 +40,11 @@ public void OneTimeSetUp() [SetUp] public void Init() { - _scope = _factory.Services.CreateScope(); + _scope = _factory.WithWebHostBuilder( + builder => builder.ConfigureTestServices( + services => services.AddSingleton(_ => _clock) + ) + ).Services.CreateScope(); } [TearDown] @@ -75,8 +79,7 @@ public async Task ResetPassword_Expired() AccountRecovery recovery = new() { - CreatedAt = Instant.FromUnixTimeSeconds(0), - ExpiresAt = Instant.FromUnixTimeSeconds(0) + Duration.FromHours(1), + ExpiresAt = _clock.GetCurrentInstant() + Duration.FromHours(1), User = new() { Email = $"pwdresettestuser{userNumber}@email.com", @@ -89,6 +92,8 @@ public async Task ResetPassword_Expired() context.AccountRecoveries.Add(recovery); await context.SaveChangesAsync(); + _clock.AdvanceHours(2); + HttpResponseMessage res = await _client.PostAsJsonAsync(Routes.RecoverAccount(recovery.Id), new ChangePasswordRequest { Password = "AValidP4ssword" @@ -117,20 +122,23 @@ public async Task ResetPassword_NotMostRecent() AccountRecovery recovery1 = new() { - CreatedAt = Instant.FromUnixTimeSeconds(20), - ExpiresAt = Instant.FromUnixTimeSeconds(20) + Duration.FromHours(1), + ExpiresAt = _clock.GetCurrentInstant() + Duration.FromHours(1), User = user }; + context.AccountRecoveries.Add(recovery1); + await context.SaveChangesAsync(); + _clock.AdvanceMinutes(1); + AccountRecovery recovery2 = new() { - CreatedAt = Instant.FromUnixTimeSeconds(30), - ExpiresAt = Instant.FromUnixTimeSeconds(30) + Duration.FromHours(1), + ExpiresAt = _clock.GetCurrentInstant() + Duration.FromHours(1), User = user }; - context.AccountRecoveries.AddRange(recovery1, recovery2); + context.AccountRecoveries.Add(recovery2); await context.SaveChangesAsync(); + _clock.AdvanceMinutes(1); HttpResponseMessage res = await _client.PostAsJsonAsync(Routes.RecoverAccount(recovery1.Id), new ChangePasswordRequest { @@ -152,9 +160,8 @@ public async Task ResetPassword_AlreadyUsed() AccountRecovery recovery = new() { - CreatedAt = Instant.FromUnixTimeSeconds(20), - ExpiresAt = Instant.FromUnixTimeSeconds(20) + Duration.FromHours(1), - UsedAt = Instant.FromUnixTimeSeconds(30), + ExpiresAt = _clock.GetCurrentInstant() + Duration.FromHours(1), + UsedAt = _clock.GetCurrentInstant() + Duration.FromMinutes(1), User = new() { Email = $"pwdresettestuser{userNumber}@email.com", @@ -166,6 +173,7 @@ public async Task ResetPassword_AlreadyUsed() context.AccountRecoveries.Add(recovery); await context.SaveChangesAsync(); + _clock.AdvanceMinutes(2); HttpResponseMessage res = await _client.PostAsJsonAsync(Routes.RecoverAccount(recovery.Id), new ChangePasswordRequest { @@ -175,7 +183,7 @@ public async Task ResetPassword_AlreadyUsed() res.Should().HaveStatusCode(System.Net.HttpStatusCode.NotFound); context.ChangeTracker.Clear(); recovery = await context.AccountRecoveries.Include(ar => ar.User).SingleAsync(ar => ar.Id == recovery.Id); - recovery.UsedAt.Should().Be(Instant.FromUnixTimeSeconds(30)); + recovery.UsedAt.Should().Be(_clock.GetCurrentInstant() - Duration.FromMinutes(1)); BCryptNet.EnhancedVerify("P4ssword", recovery.User.Password).Should().BeTrue(); } @@ -187,8 +195,7 @@ public async Task ResetPassword_Banned() AccountRecovery recovery = new() { - CreatedAt = Instant.FromUnixTimeSeconds(20), - ExpiresAt = Instant.FromUnixTimeSeconds(20) + Duration.FromHours(1), + ExpiresAt = _clock.GetCurrentInstant() + Duration.FromHours(1), User = new() { Email = $"pwdresettestuser{userNumber}@email.com", @@ -226,8 +233,7 @@ public async Task ResetPassword_BadPassword(string pwd) AccountRecovery recovery = new() { - CreatedAt = Instant.FromUnixTimeSeconds(20), - ExpiresAt = Instant.FromUnixTimeSeconds(20) + Duration.FromHours(1), + ExpiresAt = _clock.GetCurrentInstant() + Duration.FromHours(1), User = new() { Email = $"pwdresettestuser{userNumber}@email.com", @@ -268,8 +274,7 @@ public async Task ResetPassword_SamePassword() AccountRecovery recovery = new() { - CreatedAt = Instant.FromUnixTimeSeconds(20), - ExpiresAt = Instant.FromUnixTimeSeconds(20) + Duration.FromHours(1), + ExpiresAt = _clock.GetCurrentInstant() + Duration.FromHours(1), User = new() { Email = $"pwdresettestuser{userNumber}@email.com", @@ -303,8 +308,7 @@ public async Task ResetPassword_Success(UserRole role) AccountRecovery recovery = new() { - CreatedAt = Instant.FromUnixTimeSeconds(20), - ExpiresAt = Instant.FromUnixTimeSeconds(20) + Duration.FromHours(1), + ExpiresAt = _clock.GetCurrentInstant() + Duration.FromHours(1), User = new() { Email = $"pwdresettestuser{userNumber}@email.com", @@ -316,6 +320,7 @@ public async Task ResetPassword_Success(UserRole role) context.AccountRecoveries.Add(recovery); await context.SaveChangesAsync(); + _clock.AdvanceMinutes(1); HttpResponseMessage res = await _client.PostAsJsonAsync(Routes.RecoverAccount(recovery.Id), new ChangePasswordRequest { @@ -325,7 +330,7 @@ public async Task ResetPassword_Success(UserRole role) res.Should().HaveStatusCode(System.Net.HttpStatusCode.OK); context.ChangeTracker.Clear(); recovery = await context.AccountRecoveries.Include(ar => ar.User).SingleAsync(ar => ar.Id == recovery.Id); - recovery.UsedAt.Should().Be(Instant.FromUnixTimeSeconds(10) + Duration.FromHours(1)); + recovery.UsedAt.Should().Be(_clock.GetCurrentInstant()); BCryptNet.EnhancedVerify("AValidP4ssword", recovery.User.Password).Should().BeTrue(); } } diff --git a/LeaderboardBackend.Test/Features/Users/TestRecoveryTests.cs b/LeaderboardBackend.Test/Features/Users/TestRecoveryTests.cs index 8902b98d..e2c174dc 100644 --- a/LeaderboardBackend.Test/Features/Users/TestRecoveryTests.cs +++ b/LeaderboardBackend.Test/Features/Users/TestRecoveryTests.cs @@ -31,7 +31,11 @@ public void OneTimeSetUp() public void Init() { _factory.ResetDatabase(); - _scope = _factory.Services.CreateScope(); + _scope = _factory.WithWebHostBuilder(builder => + builder.ConfigureTestServices(services => + services.AddSingleton(_ => _clock) + ) + ).Services.CreateScope(); } [TearDown] @@ -48,13 +52,11 @@ public async Task TestRecovery_BadRecoveryId(string id) [Test] public async Task TestRecovery_Expired() { - _clock.Reset(Instant.FromUnixTimeSeconds(1) + Duration.FromHours(2)); ApplicationContext context = _scope.ServiceProvider.GetRequiredService(); AccountRecovery recovery = new() { - CreatedAt = Instant.FromUnixTimeSeconds(0), - ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)), + ExpiresAt = _clock.GetCurrentInstant().Plus(Duration.FromHours(1)), User = new() { Email = "test@email.com", @@ -66,6 +68,7 @@ public async Task TestRecovery_Expired() context.AccountRecoveries.Add(recovery); await context.SaveChangesAsync(); + _clock.AdvanceHours(2); HttpResponseMessage res = await _client.GetAsync(Routes.RecoverAccount(recovery.Id)); res.StatusCode.Should().Be(HttpStatusCode.NotFound); } @@ -73,7 +76,6 @@ public async Task TestRecovery_Expired() [Test] public async Task TestRecovery_Old() { - _clock.Reset(Instant.FromUnixTimeSeconds(10)); ApplicationContext context = _scope.ServiceProvider.GetRequiredService(); User user = new() @@ -87,20 +89,23 @@ public async Task TestRecovery_Old() AccountRecovery recovery1 = new() { - CreatedAt = Instant.FromUnixTimeSeconds(0), - ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)), + ExpiresAt = _clock.GetCurrentInstant().Plus(Duration.FromHours(1)), User = user }; + context.AccountRecoveries.Add(recovery1); + await context.SaveChangesAsync(); + _clock.AdvanceMinutes(1); + AccountRecovery recovery2 = new() { - CreatedAt = Instant.FromUnixTimeSeconds(5), - ExpiresAt = Instant.FromUnixTimeSeconds(5).Plus(Duration.FromHours(1)), + ExpiresAt = _clock.GetCurrentInstant().Plus(Duration.FromHours(1)), User = user }; - await context.AccountRecoveries.AddRangeAsync(recovery1, recovery2); + context.AccountRecoveries.Add(recovery2); await context.SaveChangesAsync(); + _clock.AdvanceMinutes(1); HttpResponseMessage res = await _client.GetAsync(Routes.RecoverAccount(recovery1.Id)); res.Should().HaveStatusCode(HttpStatusCode.NotFound); } @@ -108,14 +113,12 @@ public async Task TestRecovery_Old() [Test] public async Task TestRecovery_Used() { - _clock.Reset(Instant.FromUnixTimeSeconds(10)); ApplicationContext context = _scope.ServiceProvider.GetRequiredService(); AccountRecovery recovery = new() { - CreatedAt = Instant.FromUnixTimeSeconds(0), - ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)), - UsedAt = Instant.FromUnixTimeSeconds(5), + ExpiresAt = _clock.GetCurrentInstant().Plus(Duration.FromHours(1)), + UsedAt = _clock.GetCurrentInstant().Plus(Duration.FromMinutes(1)), User = new() { Email = "test@email.com", @@ -127,6 +130,7 @@ public async Task TestRecovery_Used() context.AccountRecoveries.Add(recovery); await context.SaveChangesAsync(); + _clock.AdvanceMinutes(2); HttpResponseMessage res = await _client.GetAsync(Routes.RecoverAccount(recovery.Id)); res.Should().HaveStatusCode(HttpStatusCode.NotFound); } @@ -137,13 +141,11 @@ public async Task TestRecovery_Used() [TestCase(UserRole.Registered, HttpStatusCode.OK)] public async Task TestRecovery_Roles(UserRole role, HttpStatusCode expected) { - _clock.Reset(Instant.FromUnixTimeSeconds(1)); ApplicationContext context = _scope.ServiceProvider.GetRequiredService(); AccountRecovery recovery = new() { - CreatedAt = Instant.FromUnixTimeSeconds(0), - ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)), + ExpiresAt = _clock.GetCurrentInstant().Plus(Duration.FromHours(1)), User = new() { Email = "test@email.com", @@ -155,6 +157,7 @@ public async Task TestRecovery_Roles(UserRole role, HttpStatusCode expected) context.AccountRecoveries.Add(recovery); await context.SaveChangesAsync(); + recovery.CreatedAt.Should().Be(_clock.GetCurrentInstant()); HttpResponseMessage res = await _client.GetAsync(Routes.RecoverAccount(recovery.Id)); res.Should().HaveStatusCode(expected); } diff --git a/LeaderboardBackend.Test/Leaderboards.cs b/LeaderboardBackend.Test/Leaderboards.cs index 79e75936..c5bba5c4 100644 --- a/LeaderboardBackend.Test/Leaderboards.cs +++ b/LeaderboardBackend.Test/Leaderboards.cs @@ -2,11 +2,17 @@ using System.Collections.Generic; using System.Linq; using System.Net; +using System.Threading; using System.Threading.Tasks; using LeaderboardBackend.Models.Requests; using LeaderboardBackend.Models.ViewModels; using LeaderboardBackend.Test.TestApi; using LeaderboardBackend.Test.TestApi.Extensions; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using NodaTime.Testing; using NUnit.Framework; namespace LeaderboardBackend.Test; @@ -15,7 +21,8 @@ namespace LeaderboardBackend.Test; internal class Leaderboards { private static TestApiClient _apiClient = null!; - private static TestApiFactory _factory = null!; + private static WebApplicationFactory _factory = null!; + private static readonly FakeClock _clock = new(new Instant()); private static string? _jwt; private readonly Faker _createBoardReqFaker = @@ -27,10 +34,17 @@ internal class Leaderboards [OneTimeSetUp] public async Task OneTimeSetUp() { - _factory = new TestApiFactory(); - _apiClient = _factory.CreateTestApiClient(); + _factory = new TestApiFactory().WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(services => + { + services.AddSingleton(_ => _clock); + }); + }); + + _apiClient = new TestApiClient(_factory.CreateClient()); - _factory.ResetDatabase(); + PostgresDatabaseFixture.ResetDatabaseToTemplate(); _jwt = (await _apiClient.LoginAdminUser()).Token; } @@ -57,26 +71,29 @@ await _apiClient.Get( [Test] public async Task CreateLeaderboard_GetLeaderboard_OK() { - CreateLeaderboardRequest req = _createBoardReqFaker.Generate(); + CreateLeaderboardRequest req = new() + { + Name = "Super Mario 64", + Slug = "super-mario-64", + Info = "The iQue is not allowed." + }; + + Instant now = Instant.FromUnixTimeSeconds(1); + _clock.Reset(now); + LeaderboardViewModel createdLeaderboard = await _apiClient.Post( "/leaderboards/create", new() { Body = req, Jwt = _jwt } ); + createdLeaderboard.CreatedAt.Should().Be(now); + LeaderboardViewModel retrievedLeaderboard = await _apiClient.Get( $"/api/leaderboard/{createdLeaderboard?.Id}", new() ); - createdLeaderboard.Should().NotBeNull(); - (string, string) expectedCreatedBoard = ValueTuple.Create(req.Name, req.Slug); - (string, string) actualCreatedBoard = ValueTuple.Create( - createdLeaderboard!.Name, - createdLeaderboard.Slug - ); - expectedCreatedBoard.Should().BeEquivalentTo(actualCreatedBoard); - - createdLeaderboard.Should().BeEquivalentTo(retrievedLeaderboard); + retrievedLeaderboard.Should().BeEquivalentTo(req); } [Test] @@ -85,8 +102,8 @@ public async Task CreateLeaderboards_GetLeaderboards() IEnumerable> boardCreationTasks = _createBoardReqFaker .GenerateBetween(3, 10) .Select( - req => - _apiClient.Post( + async req => + await _apiClient.Post( "/leaderboards/create", new() { Body = req, Jwt = _jwt } ) @@ -127,7 +144,7 @@ await _apiClient.Post( new() ); - leaderboard.Should().BeEquivalentTo(createdLeaderboard); + leaderboard.Should().BeEquivalentTo(createReqBody); } [Test]