Add explicit write transactions and deterministic ordering tests
This commit is contained in:
@@ -13,6 +13,8 @@ internal sealed class AdminWorkflowService(AppDbContext db)
|
||||
state.ResultsOpen = request.ResultsOpen;
|
||||
state.UpdatedAt = DateTimeOffset.UtcNow;
|
||||
|
||||
await using var tx = await db.Database.BeginTransactionAsync();
|
||||
|
||||
if (request.ResultsOpen)
|
||||
{
|
||||
await db.Players.ExecuteUpdateAsync(p => p.SetProperty(x => x.CurrentPhase, Phase.Results));
|
||||
@@ -23,6 +25,7 @@ internal sealed class AdminWorkflowService(AppDbContext db)
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
await tx.CommitAsync();
|
||||
var currentState = await db.AppState.AsNoTracking().FirstAsync();
|
||||
return Results.Ok(new
|
||||
{
|
||||
@@ -207,6 +210,8 @@ internal sealed class AdminWorkflowService(AppDbContext db)
|
||||
|
||||
public async Task<IResult> ResetAsync()
|
||||
{
|
||||
await using var tx = await db.Database.BeginTransactionAsync();
|
||||
|
||||
await db.Votes.ExecuteDeleteAsync();
|
||||
await db.Suggestions.ExecuteDeleteAsync();
|
||||
|
||||
@@ -215,6 +220,7 @@ internal sealed class AdminWorkflowService(AppDbContext db)
|
||||
state.ResultsOpen = false;
|
||||
state.UpdatedAt = DateTimeOffset.UtcNow;
|
||||
await db.SaveChangesAsync();
|
||||
await tx.CommitAsync();
|
||||
|
||||
return Results.Ok(new
|
||||
{
|
||||
|
||||
@@ -67,6 +67,8 @@ internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFact
|
||||
MaxPlayers = request.MaxPlayers
|
||||
};
|
||||
|
||||
await using var tx = await db.Database.BeginTransactionAsync();
|
||||
|
||||
db.Suggestions.Add(suggestion);
|
||||
|
||||
if (usingJoker)
|
||||
@@ -76,6 +78,7 @@ internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFact
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
await tx.CommitAsync();
|
||||
|
||||
return Results.Created($"/api/suggestions/{suggestion.Id}", new { suggestion.Id });
|
||||
}
|
||||
@@ -95,6 +98,8 @@ internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFact
|
||||
if (suggestion == null)
|
||||
return Results.NotFound(new { error = "Suggestion not found." });
|
||||
|
||||
await using var tx = await db.Database.BeginTransactionAsync();
|
||||
|
||||
await db.Suggestions
|
||||
.Where(s => s.ParentSuggestionId == suggestion.Id)
|
||||
.ExecuteUpdateAsync(s => s.SetProperty(x => x.ParentSuggestionId, (int?)null));
|
||||
@@ -103,6 +108,7 @@ internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFact
|
||||
|
||||
db.Suggestions.Remove(suggestion);
|
||||
await db.SaveChangesAsync();
|
||||
await tx.CommitAsync();
|
||||
return Results.NoContent();
|
||||
}
|
||||
|
||||
|
||||
@@ -244,11 +244,11 @@ public class AdminTests
|
||||
{
|
||||
var p = await db.Players.FirstAsync(x => !x.IsAdmin);
|
||||
p.VotesFinal = true;
|
||||
var state = await db.AppState.FirstAsync();
|
||||
state.UpdatedAt = DateTimeOffset.UnixEpoch;
|
||||
await db.SaveChangesAsync();
|
||||
});
|
||||
|
||||
var beforeState = await factory.WithDbContextAsync(async db => await db.AppState.AsNoTracking().FirstAsync());
|
||||
await Task.Delay(5);
|
||||
var close = await admin.PostAsJsonAsync("/api/admin/results", new { resultsOpen = false });
|
||||
close.EnsureSuccessStatusCode();
|
||||
|
||||
@@ -259,7 +259,7 @@ public class AdminTests
|
||||
Assert.False(p.VotesFinal);
|
||||
var state = await db.AppState.AsNoTracking().FirstAsync();
|
||||
Assert.False(state.ResultsOpen);
|
||||
Assert.True(state.UpdatedAt > beforeState.UpdatedAt);
|
||||
Assert.True(state.UpdatedAt > DateTimeOffset.UnixEpoch);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -365,28 +365,15 @@ public class SuggestionTests
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("mine");
|
||||
|
||||
await client.PostAsJsonAsync("/api/suggestions", new
|
||||
var secondId = await client.CreateSuggestionAsync("Second");
|
||||
var thirdId = await client.CreateSuggestionAsync("Third");
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
{
|
||||
Name = "Second",
|
||||
Genre = (string?)null,
|
||||
Description = (string?)null,
|
||||
ScreenshotUrl = (string?)null,
|
||||
YoutubeUrl = (string?)null,
|
||||
GameUrl = (string?)null,
|
||||
MinPlayers = (int?)null,
|
||||
MaxPlayers = (int?)null
|
||||
});
|
||||
await Task.Delay(10);
|
||||
await client.PostAsJsonAsync("/api/suggestions", new
|
||||
{
|
||||
Name = "Third",
|
||||
Genre = (string?)null,
|
||||
Description = (string?)null,
|
||||
ScreenshotUrl = (string?)null,
|
||||
YoutubeUrl = (string?)null,
|
||||
GameUrl = (string?)null,
|
||||
MinPlayers = (int?)null,
|
||||
MaxPlayers = (int?)null
|
||||
var second = await db.Suggestions.FindAsync(secondId);
|
||||
var third = await db.Suggestions.FindAsync(thirdId);
|
||||
second!.CreatedAt = DateTimeOffset.UtcNow.AddMinutes(-1);
|
||||
third!.CreatedAt = DateTimeOffset.UtcNow;
|
||||
await db.SaveChangesAsync();
|
||||
});
|
||||
|
||||
var mine = await client.GetFromJsonAsync<List<JsonElement>>("/api/suggestions/mine");
|
||||
@@ -572,11 +559,13 @@ public class SuggestionTests
|
||||
await client.RegisterAsync("owner");
|
||||
|
||||
var id1 = await client.CreateSuggestionAsync("Alpha");
|
||||
await Task.Delay(10);
|
||||
var id2 = await client.CreateSuggestionAsync("Beta");
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
{
|
||||
var alpha = await db.Suggestions.FindAsync(id1);
|
||||
var beta = await db.Suggestions.FindAsync(id2);
|
||||
alpha!.CreatedAt = DateTimeOffset.UtcNow.AddMinutes(-1);
|
||||
beta!.CreatedAt = DateTimeOffset.UtcNow;
|
||||
beta!.ParentSuggestionId = id1;
|
||||
await db.SaveChangesAsync();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user