Harden owner and suggestion invariants for concurrent writes
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using GameList.Data;
|
||||
using GameList.Domain;
|
||||
using GameList.Infrastructure;
|
||||
using GameList.Tests.Support;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -247,4 +249,32 @@ public class AuthTests
|
||||
resp.EnsureSuccessStatusCode();
|
||||
Assert.True(resp.Headers.TryGetValues("Set-Cookie", out var cookies) && cookies.Any(c => c.Contains("player")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Owner_uniqueness_is_enforced_by_database_constraint()
|
||||
{
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var ownerClient = factory.CreateClientWithCookies();
|
||||
await ownerClient.RegisterAsync("owner1", admin: true);
|
||||
|
||||
var thrown = await Assert.ThrowsAsync<DbUpdateException>(() => factory.WithDbContextAsync(async db =>
|
||||
{
|
||||
var (hash, salt) = PasswordHasher.HashPassword("Pass123!");
|
||||
db.Players.Add(new Player
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Username = "owner2",
|
||||
NormalizedUsername = "owner2",
|
||||
PasswordHash = hash,
|
||||
PasswordSalt = salt,
|
||||
DisplayName = "Owner2",
|
||||
IsOwner = true,
|
||||
IsAdmin = true
|
||||
});
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}));
|
||||
|
||||
Assert.Contains("Players.IsOwner", thrown.InnerException?.Message ?? thrown.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using GameList.Domain;
|
||||
using GameList.Tests.Support;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -626,4 +627,41 @@ public class SuggestionTests
|
||||
Assert.False(db.Votes.Any(v => v.SuggestionId == id));
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Suggestion_limit_is_enforced_by_database_trigger_without_joker()
|
||||
{
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("dbcap");
|
||||
|
||||
var playerId = await factory.WithDbContextAsync(async db => await db.Players.Select(p => p.Id).SingleAsync());
|
||||
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
{
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
db.Suggestions.Add(new Suggestion
|
||||
{
|
||||
PlayerId = playerId,
|
||||
Name = $"Seed {i}"
|
||||
});
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
});
|
||||
|
||||
var thrown = await Assert.ThrowsAsync<DbUpdateException>(() => factory.WithDbContextAsync(async db =>
|
||||
{
|
||||
db.Suggestions.Add(new Suggestion
|
||||
{
|
||||
PlayerId = playerId,
|
||||
Name = "Blocked by trigger"
|
||||
});
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}));
|
||||
|
||||
Assert.Contains("suggestion_limit_exceeded", thrown.InnerException?.Message ?? thrown.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user