Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically Set CreatedAt and UpdatedAt #244

Merged
merged 13 commits into from
Sep 22, 2024
47 changes: 33 additions & 14 deletions LeaderboardBackend.Test/Categories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -14,16 +19,23 @@ namespace LeaderboardBackend.Test;
internal class Categories
{
private static TestApiClient _apiClient = null!;
private static TestApiFactory _factory = null!;
private static WebApplicationFactory<Program> _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<IClock, FakeClock>(_ => _clock);
});
});
_apiClient = new TestApiClient(_factory.CreateClient());

_factory.ResetDatabase();
PostgresDatabaseFixture.ResetDatabaseToTemplate();
_jwt = (await _apiClient.LoginAdminUser()).Token;
}

Expand All @@ -44,6 +56,9 @@ await _apiClient.Awaiting(
[Test]
public static async Task CreateCategory_GetCategory_OK()
{
Instant now = Instant.FromUnixTimeSeconds(1);
_clock.Reset(now);

LeaderboardViewModel createdLeaderboard = await _apiClient.Post<LeaderboardViewModel>(
"/leaderboards/create",
new()
Expand All @@ -58,27 +73,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<CategoryViewModel>(
"/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);
zysim marked this conversation as resolved.
Show resolved Hide resolved

CategoryViewModel retrievedCategory = await _apiClient.Get<CategoryViewModel>(
$"/api/category/{createdCategory?.Id}", new() { }
);

Assert.AreEqual(createdCategory, retrievedCategory);
retrievedCategory.Should().BeEquivalentTo(request, opts => opts.Excluding(c => c.LeaderboardId));
zysim marked this conversation as resolved.
Show resolved Hide resolved
}
}
49 changes: 29 additions & 20 deletions LeaderboardBackend.Test/Features/Users/ConfirmAccountTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IClock, FakeClock>(_ => _clock);
});
}).Services.CreateScope();
zysim marked this conversation as resolved.
Show resolved Hide resolved

_client = _factory.WithWebHostBuilder(builder =>
{
Expand All @@ -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<ApplicationContext>();
AccountConfirmation confirmation = new()
{
CreatedAt = Instant.FromUnixTimeSeconds(0),
ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)),
ExpiresAt = now.Plus(Duration.FromHours(1)),
User = new()
{
Email = "[email protected]",
Expand All @@ -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();
Expand All @@ -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<ApplicationContext>();

AccountConfirmation confirmation = new()
{
CreatedAt = Instant.FromUnixTimeSeconds(0),
ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)),
ExpiresAt = now.Plus(Duration.FromHours(1)),
User = new()
{
Email = "[email protected]",
Expand All @@ -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();
Expand All @@ -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<ApplicationContext>();

AccountConfirmation confirmation = new()
{
CreatedAt = Instant.FromUnixTimeSeconds(0),
ExpiresAt = Instant.FromUnixTimeSeconds(0).Plus(Duration.FromHours(1)),
ExpiresAt = now.Plus(Duration.FromHours(1)),
User = new()
{
Email = "[email protected]",
Expand All @@ -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();
Expand All @@ -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<ApplicationContext>();

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 = "[email protected]",
Expand All @@ -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();
Expand All @@ -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 = "[email protected]",
Expand All @@ -180,11 +188,12 @@ public async Task ConfirmAccount_Success()
ApplicationContext context = _scope.ServiceProvider.GetRequiredService<ApplicationContext>();
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);
}
}
16 changes: 12 additions & 4 deletions LeaderboardBackend.Test/Features/Users/RegistrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,33 @@ public class RegistrationTests : IntegrationTestsBase
public async Task Register_ValidRequest_CreatesAndReturnsUser()
{
Mock<IEmailSender> emailSenderMock = new();
Instant now = Instant.FromUnixTimeSeconds(1);

using HttpClient client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddScoped(_ => emailSenderMock.Object);
services.AddSingleton<IClock, FakeClock>(_ => new(Instant.FromUnixTimeSeconds(1)));
services.AddSingleton<IClock, FakeClock>(_ => 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<UserViewModel>(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<string>(),
Expand All @@ -66,14 +71,17 @@ public async Task Register_ValidRequest_CreatesAndReturnsUser()
using IServiceScope scope = _factory.Services.CreateScope();
using ApplicationContext dbContext = scope.ServiceProvider.GetRequiredService<ApplicationContext>();
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);
Expand Down
Loading