C# formatting
This commit is contained in:
@@ -12,7 +12,7 @@ public class AdminTests
|
||||
[Fact]
|
||||
public async Task Admin_vote_status_marks_ready_when_all_finalized()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
await admin.PostAsJsonAsync("/api/me/phase/next", new { }); // move to Vote
|
||||
@@ -25,9 +25,17 @@ public class AdminTests
|
||||
|
||||
var s1 = await p1.CreateSuggestionAsync("A");
|
||||
await p1.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await p1.PostAsJsonAsync("/api/votes", new { SuggestionId = s1, Score = 5 });
|
||||
await p1.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = s1,
|
||||
Score = 5
|
||||
});
|
||||
await p1.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
|
||||
await p2.PostAsJsonAsync("/api/votes", new { SuggestionId = s1, Score = 7 });
|
||||
await p2.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = s1,
|
||||
Score = 7
|
||||
});
|
||||
await p2.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
|
||||
await admin.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
|
||||
|
||||
@@ -40,7 +48,7 @@ public class AdminTests
|
||||
[Fact]
|
||||
public async Task Grant_joker_only_in_vote_phase()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
|
||||
@@ -54,7 +62,7 @@ public class AdminTests
|
||||
[Fact]
|
||||
public async Task Delete_player_cascades_suggestions_and_votes()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
|
||||
@@ -63,23 +71,37 @@ public class AdminTests
|
||||
var suggestionId = await player.CreateSuggestionAsync("DeleteGame");
|
||||
|
||||
await player.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await player.PostAsJsonAsync("/api/votes", new { SuggestionId = suggestionId, Score = 8 });
|
||||
await player.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = suggestionId,
|
||||
Score = 8
|
||||
});
|
||||
|
||||
var resp = await admin.DeleteAsync($"/api/admin/players/{await player.GetProfileIdAsync()}");
|
||||
resp.EnsureSuccessStatusCode();
|
||||
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
await factory.WithDbContextAsync(db =>
|
||||
{
|
||||
Assert.Single(db.Players); // admin remains
|
||||
Assert.Empty(db.Suggestions);
|
||||
Assert.Empty(db.Votes);
|
||||
try
|
||||
{
|
||||
Assert.Single(db.Players); // admin remains
|
||||
|
||||
Assert.Empty(db.Suggestions);
|
||||
|
||||
Assert.Empty(db.Votes);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return Task.FromException(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Link_suggestions_errors_on_same_id_and_already_linked()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
var player = factory.CreateClientWithCookies();
|
||||
@@ -91,20 +113,32 @@ public class AdminTests
|
||||
await player.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await admin.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
|
||||
var same = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new { SourceSuggestionId = a, TargetSuggestionId = a });
|
||||
var same = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new
|
||||
{
|
||||
SourceSuggestionId = a,
|
||||
TargetSuggestionId = a
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, same.StatusCode);
|
||||
|
||||
var first = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new { SourceSuggestionId = a, TargetSuggestionId = b });
|
||||
var first = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new
|
||||
{
|
||||
SourceSuggestionId = a,
|
||||
TargetSuggestionId = b
|
||||
});
|
||||
first.EnsureSuccessStatusCode();
|
||||
|
||||
var already = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new { SourceSuggestionId = a, TargetSuggestionId = b });
|
||||
var already = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new
|
||||
{
|
||||
SourceSuggestionId = a,
|
||||
TargetSuggestionId = b
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, already.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Unlink_suggestions_clears_group_votes()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
var player = factory.CreateClientWithCookies();
|
||||
@@ -114,24 +148,41 @@ public class AdminTests
|
||||
var b = await player.CreateSuggestionAsync("Game B");
|
||||
await player.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await admin.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await admin.PostAsJsonAsync("/api/admin/link-suggestions", new { SourceSuggestionId = a, TargetSuggestionId = b });
|
||||
await admin.PostAsJsonAsync("/api/admin/link-suggestions", new
|
||||
{
|
||||
SourceSuggestionId = a,
|
||||
TargetSuggestionId = b
|
||||
});
|
||||
|
||||
await player.PostAsJsonAsync("/api/votes", new { SuggestionId = a, Score = 6 });
|
||||
await player.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = a,
|
||||
Score = 6
|
||||
});
|
||||
|
||||
var resp = await admin.PostAsJsonAsync("/api/admin/unlink-suggestions", new { suggestionId = a });
|
||||
resp.EnsureSuccessStatusCode();
|
||||
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
await factory.WithDbContextAsync(db =>
|
||||
{
|
||||
Assert.Empty(db.Votes);
|
||||
Assert.All(db.Suggestions, s => Assert.Null(s.ParentSuggestionId));
|
||||
try
|
||||
{
|
||||
Assert.Empty(db.Votes);
|
||||
|
||||
Assert.All(db.Suggestions, s => Assert.Null(s.ParentSuggestionId));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return Task.FromException(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Reset_and_factory_reset_clear_state()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
var player = factory.CreateClientWithCookies();
|
||||
@@ -141,27 +192,46 @@ public class AdminTests
|
||||
var reset = await admin.PostAsJsonAsync("/api/admin/reset", new { });
|
||||
reset.EnsureSuccessStatusCode();
|
||||
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
await factory.WithDbContextAsync(db =>
|
||||
{
|
||||
Assert.Empty(db.Suggestions);
|
||||
Assert.Empty(db.Votes);
|
||||
Assert.All(db.Players, p => Assert.Equal(Phase.Suggest, p.CurrentPhase));
|
||||
try
|
||||
{
|
||||
Assert.Empty(db.Suggestions);
|
||||
|
||||
Assert.Empty(db.Votes);
|
||||
|
||||
Assert.All(db.Players, p => Assert.Equal(Phase.Suggest, p.CurrentPhase));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return Task.FromException(exception);
|
||||
}
|
||||
});
|
||||
|
||||
var factoryReset = await admin.PostAsJsonAsync("/api/admin/factory-reset", new { });
|
||||
factoryReset.EnsureSuccessStatusCode();
|
||||
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
await factory.WithDbContextAsync(db =>
|
||||
{
|
||||
Assert.Empty(db.Players);
|
||||
Assert.Single(db.AppState);
|
||||
try
|
||||
{
|
||||
Assert.Empty(db.Players);
|
||||
|
||||
Assert.Single(db.AppState);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return Task.FromException(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Admin_results_closing_moves_back_to_vote_and_clears_finalize()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
var player = factory.CreateClientWithCookies();
|
||||
@@ -196,7 +266,7 @@ public class AdminTests
|
||||
[Fact]
|
||||
public async Task Vote_status_lists_waiting_players()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
await admin.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
@@ -208,7 +278,11 @@ public class AdminTests
|
||||
var s = await p1.CreateSuggestionAsync("Game");
|
||||
await p1.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await p2.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await p1.PostAsJsonAsync("/api/votes", new { SuggestionId = s, Score = 5 });
|
||||
await p1.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = s,
|
||||
Score = 5
|
||||
});
|
||||
await p1.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
|
||||
|
||||
var status = await admin.GetFromJsonAsync<JsonElement>("/api/admin/vote-status");
|
||||
@@ -220,7 +294,7 @@ public class AdminTests
|
||||
[Fact]
|
||||
public async Task Grant_joker_in_vote_sets_flag_and_unfinalizes()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
|
||||
@@ -243,7 +317,7 @@ public class AdminTests
|
||||
[Fact]
|
||||
public async Task Link_requires_vote_phase_and_reparents_votes_reset()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
var player = factory.CreateClientWithCookies();
|
||||
@@ -252,16 +326,28 @@ public class AdminTests
|
||||
var a = await player.CreateSuggestionAsync("A");
|
||||
var b = await player.CreateSuggestionAsync("B");
|
||||
|
||||
var beforeVotePhase = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new { SourceSuggestionId = a, TargetSuggestionId = b });
|
||||
var beforeVotePhase = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new
|
||||
{
|
||||
SourceSuggestionId = a,
|
||||
TargetSuggestionId = b
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, beforeVotePhase.StatusCode);
|
||||
|
||||
await admin.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await player.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
|
||||
await player.PostAsJsonAsync("/api/votes", new { SuggestionId = a, Score = 3 });
|
||||
await player.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = a,
|
||||
Score = 3
|
||||
});
|
||||
await player.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
|
||||
|
||||
var link = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new { SourceSuggestionId = a, TargetSuggestionId = b });
|
||||
var link = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new
|
||||
{
|
||||
SourceSuggestionId = a,
|
||||
TargetSuggestionId = b
|
||||
});
|
||||
link.EnsureSuccessStatusCode();
|
||||
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
@@ -276,7 +362,7 @@ public class AdminTests
|
||||
[Fact]
|
||||
public async Task Unlink_not_found_returns_empty_payload()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
await admin.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
@@ -290,7 +376,7 @@ public class AdminTests
|
||||
[Fact]
|
||||
public async Task Reset_clears_flags_and_factory_reset_seeds_defaults()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
var p = factory.CreateClientWithCookies();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using GameList.Data;
|
||||
using GameList.Infrastructure;
|
||||
using GameList.Tests.Support;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -13,7 +12,7 @@ public class AuthTests
|
||||
[Fact]
|
||||
public async Task Register_trims_limits_and_sets_cookie_and_normalized_username()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
|
||||
var response = await client.PostAsJsonAsync("/api/auth/register", new
|
||||
@@ -25,8 +24,7 @@ public class AuthTests
|
||||
});
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
Assert.True(response.Headers.TryGetValues("Set-Cookie", out var cookies) &&
|
||||
cookies.Any(c => c.Contains(PlayerIdentityExtensions.PlayerCookieName)));
|
||||
Assert.True(response.Headers.TryGetValues("Set-Cookie", out var cookies) && cookies.Any(c => c.Contains(PlayerIdentityExtensions.PlayerCookieName)));
|
||||
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
{
|
||||
@@ -41,7 +39,7 @@ public class AuthTests
|
||||
[Fact]
|
||||
public async Task Register_rejects_overlength_username_or_display_name()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
|
||||
var tooLongUser = new string('u', 25);
|
||||
@@ -67,7 +65,7 @@ public class AuthTests
|
||||
[Fact]
|
||||
public async Task Login_sets_last_login_and_fills_missing_display_name()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("loginfill");
|
||||
|
||||
@@ -93,7 +91,7 @@ public class AuthTests
|
||||
[Fact]
|
||||
public async Task Register_with_admin_key_sets_admin_flag()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
|
||||
var response = await client.RegisterAsync("adminuser", admin: true);
|
||||
@@ -106,7 +104,7 @@ public class AuthTests
|
||||
[Fact]
|
||||
public async Task Register_duplicate_username_returns_conflict()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
|
||||
var first = await client.RegisterAsync("duplicate");
|
||||
@@ -120,7 +118,7 @@ public class AuthTests
|
||||
[Fact]
|
||||
public async Task Login_with_wrong_password_returns_unauthorized()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
|
||||
await client.RegisterAsync("player1");
|
||||
@@ -133,20 +131,31 @@ public class AuthTests
|
||||
[Fact]
|
||||
public async Task Register_validates_required_fields()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
|
||||
var missing = await client.PostAsJsonAsync("/api/auth/register", new { Username = "", Password = "", DisplayName = "" });
|
||||
var missing = await client.PostAsJsonAsync("/api/auth/register", new
|
||||
{
|
||||
Username = "",
|
||||
Password = "",
|
||||
DisplayName = ""
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, missing.StatusCode);
|
||||
|
||||
var badKey = await client.PostAsJsonAsync("/api/auth/register", new { Username = "u", Password = "p", DisplayName = "d", AdminKey = "wrong" });
|
||||
var badKey = await client.PostAsJsonAsync("/api/auth/register", new
|
||||
{
|
||||
Username = "u",
|
||||
Password = "p",
|
||||
DisplayName = "d",
|
||||
AdminKey = "wrong"
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, badKey.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Non_admin_cannot_access_admin_routes()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var player = factory.CreateClientWithCookies();
|
||||
await player.RegisterAsync("regular");
|
||||
|
||||
@@ -157,7 +166,7 @@ public class AuthTests
|
||||
[Fact]
|
||||
public async Task Admin_can_access_admin_routes()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("adminuser", admin: true);
|
||||
|
||||
@@ -168,7 +177,7 @@ public class AuthTests
|
||||
[Fact]
|
||||
public async Task Logout_clears_cookie()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("logoutme");
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Security.Claims;
|
||||
using GameList.Data;
|
||||
using GameList.Domain;
|
||||
using GameList.Infrastructure;
|
||||
using GameList.Tests.Support;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace GameList.Tests;
|
||||
|
||||
@@ -16,7 +13,7 @@ public class FiltersTests
|
||||
[Fact]
|
||||
public async Task Admin_only_filter_blocks_non_admin()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("user");
|
||||
|
||||
@@ -29,7 +26,7 @@ public class FiltersTests
|
||||
[Fact]
|
||||
public async Task Phase_requirement_allows_admin_override_when_enabled()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var ctx = await BuildContextAsync(factory, isAdmin: true, phase: Phase.Suggest);
|
||||
var filter = new PhaseRequirementFilter(Phase.Vote, allowAdminOverride: true);
|
||||
var called = false;
|
||||
@@ -46,7 +43,7 @@ public class FiltersTests
|
||||
[Fact]
|
||||
public async Task Phase_or_joker_filter_blocks_without_joker_in_vote()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var ctx = await BuildContextAsync(factory, isAdmin: false, phase: Phase.Vote, hasJoker: false);
|
||||
var filter = new PhaseOrJokerFilter();
|
||||
var result = await filter.InvokeAsync(ctx, _ => ValueTask.FromResult<object?>(Results.Ok()));
|
||||
@@ -62,8 +59,8 @@ public class FiltersTests
|
||||
Id = Guid.NewGuid(),
|
||||
Username = $"user-{Guid.NewGuid():N}",
|
||||
NormalizedUsername = $"user-{Guid.NewGuid():N}",
|
||||
PasswordHash = new byte[] { 1 },
|
||||
PasswordSalt = new byte[] { 1 },
|
||||
PasswordHash = [1],
|
||||
PasswordSalt = [1],
|
||||
IsAdmin = isAdmin,
|
||||
CurrentPhase = phase,
|
||||
HasJoker = hasJoker,
|
||||
@@ -75,28 +72,20 @@ public class FiltersTests
|
||||
var ctx = new DefaultHttpContext
|
||||
{
|
||||
RequestServices = scope.ServiceProvider,
|
||||
User = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
User = new ClaimsPrincipal(new ClaimsIdentity([
|
||||
new Claim(ClaimTypes.NameIdentifier, player.Id.ToString()),
|
||||
new Claim(ClaimTypes.Name, player.Username),
|
||||
new Claim(PlayerIdentityExtensions.AdminClaim, isAdmin ? "true" : "false")
|
||||
}, "cookie"))
|
||||
], "cookie"))
|
||||
};
|
||||
|
||||
return new TestInvocationContext(ctx);
|
||||
}
|
||||
|
||||
private class TestInvocationContext : EndpointFilterInvocationContext
|
||||
private class TestInvocationContext(DefaultHttpContext context) : EndpointFilterInvocationContext
|
||||
{
|
||||
private readonly DefaultHttpContext _context;
|
||||
|
||||
public TestInvocationContext(DefaultHttpContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public override HttpContext HttpContext => _context;
|
||||
public override object?[] Arguments => Array.Empty<object?>();
|
||||
public override HttpContext HttpContext => context;
|
||||
public override object?[] Arguments => [];
|
||||
public override T GetArgument<T>(int index) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -104,11 +93,9 @@ public class FiltersTests
|
||||
{
|
||||
var http = new DefaultHttpContext
|
||||
{
|
||||
RequestServices = new ServiceCollection()
|
||||
.AddLogging()
|
||||
.BuildServiceProvider()
|
||||
RequestServices = new ServiceCollection().AddLogging().BuildServiceProvider(),
|
||||
Response = { Body = new MemoryStream() }
|
||||
};
|
||||
http.Response.Body = new MemoryStream();
|
||||
await ((IResult)result!).ExecuteAsync(http);
|
||||
Assert.Equal(statusCode, http.Response.StatusCode);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using GameList.Infrastructure;
|
||||
@@ -7,10 +6,7 @@ using GameList.Endpoints;
|
||||
using GameList.Tests.Support;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using System.Text.Json;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
@@ -36,9 +32,8 @@ public class HelperTests
|
||||
File.WriteAllText(index, "<meta name=\"app-base\" content=\"\">");
|
||||
|
||||
var env = new FakeEnv { WebRootPath = webRoot };
|
||||
var method = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
|
||||
.First(m => m.Name.Contains("UpdateIndexMetaBase"));
|
||||
method.Invoke(null, new object?[] { env, "/pick" });
|
||||
var method = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public).First(m => m.Name.Contains("UpdateIndexMetaBase"));
|
||||
method.Invoke(null, [env, "/pick"]);
|
||||
|
||||
var text = File.ReadAllText(index);
|
||||
Assert.Contains("content=\"/pick\"", text);
|
||||
@@ -53,9 +48,8 @@ public class HelperTests
|
||||
File.WriteAllText(index, "<html></html>");
|
||||
|
||||
var env = new FakeEnv { WebRootPath = webRoot };
|
||||
var method = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
|
||||
.First(m => m.Name.Contains("UpdateIndexMetaBase"));
|
||||
method.Invoke(null, new object?[] { env, "/pick" });
|
||||
var method = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public).First(m => m.Name.Contains("UpdateIndexMetaBase"));
|
||||
method.Invoke(null, [env, "/pick"]);
|
||||
|
||||
Assert.Equal("<html></html>", File.ReadAllText(index));
|
||||
}
|
||||
@@ -76,14 +70,15 @@ public class HelperTests
|
||||
if (req.Method == HttpMethod.Head)
|
||||
{
|
||||
var resp = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
resp.Content = new ByteArrayContent(Array.Empty<byte>());
|
||||
resp.Content = new ByteArrayContent([]);
|
||||
resp.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
||||
resp.Content.Headers.ContentLength = 100;
|
||||
return resp;
|
||||
}
|
||||
|
||||
return new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new ByteArrayContent(Array.Empty<byte>())
|
||||
Content = new ByteArrayContent([])
|
||||
{
|
||||
Headers =
|
||||
{
|
||||
@@ -125,7 +120,7 @@ public class HelperTests
|
||||
handler.SetResponder(_ =>
|
||||
{
|
||||
var resp = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
resp.Content = new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes("not image"));
|
||||
resp.Content = new ByteArrayContent("not image"u8.ToArray());
|
||||
resp.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
|
||||
resp.Content.Headers.ContentLength = 9;
|
||||
return resp;
|
||||
@@ -138,7 +133,7 @@ public class HelperTests
|
||||
[Fact]
|
||||
public void Link_root_helpers_handle_groups()
|
||||
{
|
||||
var roots = EndpointHelpers.BuildLinkRoots(new[] { (1, (int?)null), (2, 1), (3, (int?)null) });
|
||||
var roots = EndpointHelpers.BuildLinkRoots([(1, null), (2, 1), (3, null)]);
|
||||
Assert.Equal(1, roots[1]);
|
||||
Assert.Equal(1, roots[2]);
|
||||
Assert.Equal(3, roots[3]);
|
||||
@@ -162,9 +157,9 @@ public class HelperTests
|
||||
{
|
||||
var parentMap = new Dictionary<int, int?>
|
||||
{
|
||||
{1, 2},
|
||||
{2, 3},
|
||||
{3, 1}
|
||||
{ 1, 2 },
|
||||
{ 2, 3 },
|
||||
{ 3, 1 }
|
||||
};
|
||||
|
||||
var root = EndpointHelpers.FindRootId(1, parentMap);
|
||||
@@ -174,16 +169,13 @@ public class HelperTests
|
||||
[Fact]
|
||||
public async Task Global_exception_handler_returns_json_error()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory().WithWebHostBuilder(builder =>
|
||||
await using var factory = new TestWebApplicationFactory().WithWebHostBuilder(builder =>
|
||||
{
|
||||
builder.Configure(app =>
|
||||
{
|
||||
app.UseGlobalExceptionLogging();
|
||||
app.UseRouting();
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapGet("/boom", _ => throw new InvalidOperationException("boom"));
|
||||
});
|
||||
app.UseEndpoints(endpoints => { endpoints.MapGet("/boom", _ => throw new InvalidOperationException("boom")); });
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System.Security.Claims;
|
||||
using GameList.Domain;
|
||||
using GameList.Infrastructure;
|
||||
using GameList.Tests.Support;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace GameList.Tests;
|
||||
|
||||
@@ -14,16 +12,16 @@ public class IdentityTests
|
||||
[Fact]
|
||||
public async Task Sign_in_sets_claims_and_cookie()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
var ctx = BuildAuthContext(factory.Services);
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var ctx = BuildAuthContext();
|
||||
|
||||
var player = new Player
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Username = "claimuser",
|
||||
NormalizedUsername = "claimuser",
|
||||
PasswordHash = new byte[] { 1 },
|
||||
PasswordSalt = new byte[] { 1 },
|
||||
PasswordHash = [1],
|
||||
PasswordSalt = [1],
|
||||
DisplayName = "Claim",
|
||||
IsAdmin = true
|
||||
};
|
||||
@@ -31,15 +29,14 @@ public class IdentityTests
|
||||
await PlayerIdentityExtensions.SignInPlayerAsync(ctx, player);
|
||||
|
||||
var cookies = ctx.Response.Headers["Set-Cookie"];
|
||||
Assert.NotNull(cookies);
|
||||
Assert.Contains(cookies!, v => v.Contains(PlayerIdentityExtensions.PlayerCookieName));
|
||||
Assert.Contains(cookies, v => v != null && v.Contains(PlayerIdentityExtensions.PlayerCookieName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Sign_out_clears_principal()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
var ctx = BuildAuthContext(factory.Services);
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var ctx = BuildAuthContext();
|
||||
var player = new Player();
|
||||
await PlayerIdentityExtensions.SignInPlayerAsync(ctx, player);
|
||||
|
||||
@@ -48,15 +45,11 @@ public class IdentityTests
|
||||
Assert.False(ctx.User.Identity?.IsAuthenticated ?? false);
|
||||
}
|
||||
|
||||
private static DefaultHttpContext BuildAuthContext(IServiceProvider services)
|
||||
private static DefaultHttpContext BuildAuthContext()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
serviceCollection.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddCookie(options =>
|
||||
{
|
||||
options.Cookie.Name = PlayerIdentityExtensions.PlayerCookieName;
|
||||
});
|
||||
serviceCollection.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => { options.Cookie.Name = PlayerIdentityExtensions.PlayerCookieName; });
|
||||
serviceCollection.AddLogging();
|
||||
var provider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ public class MiddlewareTests
|
||||
[Fact]
|
||||
public async Task Deleted_player_cookie_is_signed_out()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("ghost");
|
||||
|
||||
@@ -29,7 +29,7 @@ public class MiddlewareTests
|
||||
[Fact]
|
||||
public async Task Existing_player_passes_through_middleware()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("live");
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ public class ResultsTests
|
||||
[Fact]
|
||||
public async Task Results_available_after_admin_unlocks()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
|
||||
@@ -18,7 +18,11 @@ public class ResultsTests
|
||||
var suggestionId = await player.CreateSuggestionAsync("ResultGame");
|
||||
|
||||
await player.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await player.PostAsJsonAsync("/api/votes", new { SuggestionId = suggestionId, Score = 8 });
|
||||
await player.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = suggestionId,
|
||||
Score = 8
|
||||
});
|
||||
await player.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
|
||||
|
||||
await admin.PostAsJsonAsync("/api/admin/results", new { resultsOpen = true });
|
||||
@@ -36,7 +40,7 @@ public class ResultsTests
|
||||
[Fact]
|
||||
public async Task Results_locked_returns_error()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("user");
|
||||
await client.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
@@ -47,7 +51,7 @@ public class ResultsTests
|
||||
[Fact]
|
||||
public async Task Results_require_results_phase_and_auth()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
var player = factory.CreateClientWithCookies();
|
||||
@@ -65,17 +69,21 @@ public class ResultsTests
|
||||
[Fact]
|
||||
public async Task Results_payload_contains_fields_and_ordering()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
|
||||
var player = factory.CreateClientWithCookies();
|
||||
await player.RegisterAsync("player");
|
||||
var s1 = await player.CreateSuggestionAsync("High");
|
||||
var s2 = await player.CreateSuggestionAsync("NoVotes");
|
||||
_ = await player.CreateSuggestionAsync("NoVotes");
|
||||
|
||||
await player.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await player.PostAsJsonAsync("/api/votes", new { SuggestionId = s1, Score = 9 });
|
||||
await player.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = s1,
|
||||
Score = 9
|
||||
});
|
||||
await player.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
|
||||
|
||||
await admin.PostAsJsonAsync("/api/admin/results", new { resultsOpen = true });
|
||||
@@ -83,7 +91,7 @@ public class ResultsTests
|
||||
|
||||
var results = await player.GetFromJsonAsync<List<JsonElement>>("/api/results");
|
||||
Assert.NotNull(results);
|
||||
Assert.Equal(2, results!.Count);
|
||||
Assert.Equal(2, results.Count);
|
||||
Assert.Equal("High", results[0].GetProperty("name").GetString());
|
||||
Assert.Equal(9, (int)results[0].GetProperty("average").GetDouble());
|
||||
Assert.Equal(1, results[0].GetProperty("count").GetInt32());
|
||||
|
||||
@@ -14,7 +14,7 @@ public class StateTests
|
||||
[Fact]
|
||||
public async Task State_endpoint_returns_expected_payload_for_authenticated_user()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("payload");
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
@@ -27,7 +27,7 @@ public class StateTests
|
||||
|
||||
var state = await client.GetFromJsonAsync<JsonElement>("/api/state");
|
||||
|
||||
Assert.Equal(Phase.Suggest.ToString(), state.GetProperty("currentPhase").GetString());
|
||||
Assert.Equal(nameof(Phase.Suggest), state.GetProperty("currentPhase").GetString());
|
||||
Assert.False(state.GetProperty("votesFinal").GetBoolean());
|
||||
Assert.True(state.GetProperty("hasJoker").GetBoolean());
|
||||
Assert.True(state.GetProperty("players").GetInt32() >= 1);
|
||||
@@ -38,7 +38,7 @@ public class StateTests
|
||||
[Fact]
|
||||
public async Task GetPhase_upgrades_reveal_and_resets_when_results_close()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
Guid playerId = Guid.Empty;
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
{
|
||||
@@ -47,8 +47,8 @@ public class StateTests
|
||||
Id = Guid.NewGuid(),
|
||||
Username = "legacy",
|
||||
NormalizedUsername = "legacy",
|
||||
PasswordHash = new byte[] { 1 },
|
||||
PasswordSalt = new byte[] { 1 },
|
||||
PasswordHash = [1],
|
||||
PasswordSalt = [1],
|
||||
DisplayName = "Legacy",
|
||||
CurrentPhase = Phase.Reveal,
|
||||
VotesFinal = true
|
||||
@@ -63,7 +63,7 @@ public class StateTests
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
var phase = await GameList.Endpoints.EndpointHelpers.GetPhase(db, playerId);
|
||||
var phase = await Endpoints.EndpointHelpers.GetPhase(db, playerId);
|
||||
Assert.Equal(Phase.Results, phase);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class StateTests
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
var phase = await GameList.Endpoints.EndpointHelpers.GetPhase(db, playerId);
|
||||
var phase = await Endpoints.EndpointHelpers.GetPhase(db, playerId);
|
||||
var player = await db.Players.FindAsync(playerId);
|
||||
Assert.Equal(Phase.Vote, phase);
|
||||
Assert.False(player!.VotesFinal);
|
||||
@@ -87,7 +87,7 @@ public class StateTests
|
||||
[Fact]
|
||||
public async Task Phase_next_advances_and_clears_votesfinal()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("advance");
|
||||
|
||||
@@ -111,13 +111,13 @@ public class StateTests
|
||||
toResults.EnsureSuccessStatusCode();
|
||||
var me = await client.GetFromJsonAsync<JsonElement>("/api/me");
|
||||
Assert.False(me.GetProperty("votesFinal").GetBoolean());
|
||||
Assert.Equal(Phase.Results.ToString(), me.GetProperty("currentPhase").GetString());
|
||||
Assert.Equal(nameof(Phase.Results), me.GetProperty("currentPhase").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Phase_prev_moves_back_and_clears_votesfinal()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
|
||||
@@ -132,14 +132,14 @@ public class StateTests
|
||||
backToSuggest.EnsureSuccessStatusCode();
|
||||
|
||||
var me = await admin.GetFromJsonAsync<JsonElement>("/api/me");
|
||||
Assert.Equal(Phase.Suggest.ToString(), me.GetProperty("currentPhase").GetString());
|
||||
Assert.Equal(nameof(Phase.Suggest), me.GetProperty("currentPhase").GetString());
|
||||
Assert.False(me.GetProperty("votesFinal").GetBoolean());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Name_endpoint_rejects_over_16_chars()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("namelimit");
|
||||
|
||||
@@ -150,7 +150,7 @@ public class StateTests
|
||||
[Fact]
|
||||
public async Task Cannot_advance_to_results_when_locked()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("player");
|
||||
|
||||
@@ -165,7 +165,7 @@ public class StateTests
|
||||
[Fact]
|
||||
public async Task Admin_opening_results_moves_players_to_results_phase()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
|
||||
@@ -177,14 +177,14 @@ public class StateTests
|
||||
|
||||
var state = await player.GetFromJsonAsync<JsonElement>("/api/state");
|
||||
|
||||
Assert.Equal(Phase.Results.ToString(), state.GetProperty("currentPhase").GetString());
|
||||
Assert.Equal(nameof(Phase.Results), state.GetProperty("currentPhase").GetString());
|
||||
Assert.True(state.GetProperty("resultsOpen").GetBoolean());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Name_endpoint_trims_and_rejects_blank()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("nametest");
|
||||
|
||||
@@ -200,7 +200,7 @@ public class StateTests
|
||||
[Fact]
|
||||
public async Task Phase_prev_admin_only()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var player = factory.CreateClientWithCookies();
|
||||
await player.RegisterAsync("phase");
|
||||
|
||||
@@ -213,13 +213,13 @@ public class StateTests
|
||||
var back = await admin.PostAsJsonAsync("/api/me/phase/prev", new { });
|
||||
back.EnsureSuccessStatusCode();
|
||||
var me = await admin.GetFromJsonAsync<JsonElement>("/api/me");
|
||||
Assert.Equal(Phase.Suggest.ToString(), me.GetProperty("currentPhase").GetString());
|
||||
Assert.Equal(nameof(Phase.Suggest), me.GetProperty("currentPhase").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task State_endpoint_requires_auth_and_counts()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var anon = factory.CreateClient();
|
||||
var unauthorized = await anon.GetAsync("/api/state");
|
||||
Assert.NotEqual(HttpStatusCode.OK, unauthorized.StatusCode);
|
||||
@@ -237,7 +237,7 @@ public class StateTests
|
||||
[Fact]
|
||||
public async Task Health_endpoint_ok()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var resp = await factory.CreateClient().GetFromJsonAsync<JsonElement>("/health");
|
||||
Assert.Equal("ok", resp.GetProperty("status").GetString());
|
||||
}
|
||||
@@ -245,7 +245,7 @@ public class StateTests
|
||||
[Fact]
|
||||
public async Task GetPhase_aligns_to_results_when_open()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
{
|
||||
var player = new Player
|
||||
@@ -253,8 +253,8 @@ public class StateTests
|
||||
Id = Guid.NewGuid(),
|
||||
Username = "phase",
|
||||
NormalizedUsername = "phase",
|
||||
PasswordHash = new byte[] { 1 },
|
||||
PasswordSalt = new byte[] { 1 },
|
||||
PasswordHash = [1],
|
||||
PasswordSalt = [1],
|
||||
DisplayName = "phase",
|
||||
CurrentPhase = Phase.Vote
|
||||
};
|
||||
@@ -267,7 +267,7 @@ public class StateTests
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
var playerId = await db.Players.Select(p => p.Id).FirstAsync();
|
||||
var phase = await GameList.Endpoints.EndpointHelpers.GetPhase(db, playerId);
|
||||
var phase = await Endpoints.EndpointHelpers.GetPhase(db, playerId);
|
||||
|
||||
Assert.Equal(Phase.Results, phase);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using GameList.Tests.Support;
|
||||
using GameList.Domain;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace GameList.Tests;
|
||||
@@ -12,7 +11,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Player_cannot_exceed_five_suggestions()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("suggestor");
|
||||
|
||||
@@ -50,7 +49,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Rejects_invalid_image_extension_and_player_counts()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("validate");
|
||||
|
||||
@@ -84,7 +83,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Joker_allows_single_extra_suggestion_and_unfinalizes_votes()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var player = factory.CreateClientWithCookies();
|
||||
await player.RegisterAsync("joker");
|
||||
var other = factory.CreateClientWithCookies();
|
||||
@@ -126,7 +125,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Admin_can_update_during_vote_phase()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
|
||||
@@ -152,7 +151,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Phase_gate_blocks_player_update_in_vote_phase()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var player = factory.CreateClientWithCookies();
|
||||
await player.RegisterAsync("phase");
|
||||
var id = await player.CreateSuggestionAsync("Lock");
|
||||
@@ -180,7 +179,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Player_cannot_edit_suggestion_in_results_phase()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var player = factory.CreateClientWithCookies();
|
||||
await player.RegisterAsync("results");
|
||||
var id = await player.CreateSuggestionAsync("Frozen");
|
||||
@@ -217,7 +216,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Player_cannot_edit_other_players_suggestion()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var owner = factory.CreateClientWithCookies();
|
||||
await owner.RegisterAsync("owner");
|
||||
var other = factory.CreateClientWithCookies();
|
||||
@@ -243,7 +242,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Joker_allows_unlimited_extra_suggestions_when_granted_multiple_times()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("sixth");
|
||||
|
||||
@@ -327,7 +326,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Unreachable_screenshot_url_is_rejected()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
factory.HttpHandler.SetResponder(_ => new HttpResponseMessage(HttpStatusCode.BadRequest));
|
||||
|
||||
var client = factory.CreateClientWithCookies();
|
||||
@@ -351,7 +350,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Get_all_requires_vote_phase()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("viewer");
|
||||
|
||||
@@ -362,13 +361,33 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Mine_returns_ordered_list()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("mine");
|
||||
|
||||
await client.PostAsJsonAsync("/api/suggestions", new { Name = "Second", Genre = (string?)null, Description = (string?)null, ScreenshotUrl = (string?)null, YoutubeUrl = (string?)null, GameUrl = (string?)null, MinPlayers = (int?)null, MaxPlayers = (int?)null });
|
||||
await client.PostAsJsonAsync("/api/suggestions", new
|
||||
{
|
||||
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 });
|
||||
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 mine = await client.GetFromJsonAsync<List<JsonElement>>("/api/suggestions/mine");
|
||||
Assert.Equal("Second", mine![0].GetProperty("name").GetString());
|
||||
@@ -377,7 +396,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Create_requires_suggest_phase_and_display_name()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("phasegate");
|
||||
|
||||
@@ -389,7 +408,17 @@ public class SuggestionTests
|
||||
await db.SaveChangesAsync();
|
||||
});
|
||||
|
||||
var badPhase = await client.PostAsJsonAsync("/api/suggestions", new { Name = "Nope", Genre = (string?)null, Description = (string?)null, ScreenshotUrl = (string?)null, YoutubeUrl = (string?)null, GameUrl = (string?)null, MinPlayers = (int?)null, MaxPlayers = (int?)null });
|
||||
var badPhase = await client.PostAsJsonAsync("/api/suggestions", new
|
||||
{
|
||||
Name = "Nope",
|
||||
Genre = (string?)null,
|
||||
Description = (string?)null,
|
||||
ScreenshotUrl = (string?)null,
|
||||
YoutubeUrl = (string?)null,
|
||||
GameUrl = (string?)null,
|
||||
MinPlayers = (int?)null,
|
||||
MaxPlayers = (int?)null
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, badPhase.StatusCode);
|
||||
|
||||
await factory.WithDbContextAsync(async db =>
|
||||
@@ -399,37 +428,97 @@ public class SuggestionTests
|
||||
await db.SaveChangesAsync();
|
||||
});
|
||||
|
||||
var noDisplay = await client.PostAsJsonAsync("/api/suggestions", new { Name = "NoDisplay", Genre = (string?)null, Description = (string?)null, ScreenshotUrl = (string?)null, YoutubeUrl = (string?)null, GameUrl = (string?)null, MinPlayers = (int?)null, MaxPlayers = (int?)null });
|
||||
var noDisplay = await client.PostAsJsonAsync("/api/suggestions", new
|
||||
{
|
||||
Name = "NoDisplay",
|
||||
Genre = (string?)null,
|
||||
Description = (string?)null,
|
||||
ScreenshotUrl = (string?)null,
|
||||
YoutubeUrl = (string?)null,
|
||||
GameUrl = (string?)null,
|
||||
MinPlayers = (int?)null,
|
||||
MaxPlayers = (int?)null
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, noDisplay.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Rejects_invalid_urls_name_length_and_player_counts()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("validate2");
|
||||
|
||||
var badGame = await client.PostAsJsonAsync("/api/suggestions", new { Name = "Bad", Genre = (string?)null, Description = (string?)null, ScreenshotUrl = (string?)null, YoutubeUrl = (string?)null, GameUrl = "ftp://bad", MinPlayers = (int?)null, MaxPlayers = (int?)null });
|
||||
var badGame = await client.PostAsJsonAsync("/api/suggestions", new
|
||||
{
|
||||
Name = "Bad",
|
||||
Genre = (string?)null,
|
||||
Description = (string?)null,
|
||||
ScreenshotUrl = (string?)null,
|
||||
YoutubeUrl = (string?)null,
|
||||
GameUrl = "ftp://bad",
|
||||
MinPlayers = (int?)null,
|
||||
MaxPlayers = (int?)null
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, badGame.StatusCode);
|
||||
|
||||
var badYoutube = await client.PostAsJsonAsync("/api/suggestions", new { Name = "BadYt", Genre = (string?)null, Description = (string?)null, ScreenshotUrl = (string?)null, YoutubeUrl = "file://bad", GameUrl = (string?)null, MinPlayers = (int?)null, MaxPlayers = (int?)null });
|
||||
var badYoutube = await client.PostAsJsonAsync("/api/suggestions", new
|
||||
{
|
||||
Name = "BadYt",
|
||||
Genre = (string?)null,
|
||||
Description = (string?)null,
|
||||
ScreenshotUrl = (string?)null,
|
||||
YoutubeUrl = "file://bad",
|
||||
GameUrl = (string?)null,
|
||||
MinPlayers = (int?)null,
|
||||
MaxPlayers = (int?)null
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, badYoutube.StatusCode);
|
||||
|
||||
var longName = await client.PostAsJsonAsync("/api/suggestions", new { Name = new string('x', 101), Genre = (string?)null, Description = (string?)null, ScreenshotUrl = (string?)null, YoutubeUrl = (string?)null, GameUrl = (string?)null, MinPlayers = (int?)null, MaxPlayers = (int?)null });
|
||||
var longName = await client.PostAsJsonAsync("/api/suggestions", new
|
||||
{
|
||||
Name = new string('x', 101),
|
||||
Genre = (string?)null,
|
||||
Description = (string?)null,
|
||||
ScreenshotUrl = (string?)null,
|
||||
YoutubeUrl = (string?)null,
|
||||
GameUrl = (string?)null,
|
||||
MinPlayers = (int?)null,
|
||||
MaxPlayers = (int?)null
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, longName.StatusCode);
|
||||
|
||||
var minOnly = await client.PostAsJsonAsync("/api/suggestions", new { Name = "MinOnly", Genre = (string?)null, Description = (string?)null, ScreenshotUrl = (string?)null, YoutubeUrl = (string?)null, GameUrl = (string?)null, MinPlayers = 2, MaxPlayers = (int?)null });
|
||||
var minOnly = await client.PostAsJsonAsync("/api/suggestions", new
|
||||
{
|
||||
Name = "MinOnly",
|
||||
Genre = (string?)null,
|
||||
Description = (string?)null,
|
||||
ScreenshotUrl = (string?)null,
|
||||
YoutubeUrl = (string?)null,
|
||||
GameUrl = (string?)null,
|
||||
MinPlayers = 2,
|
||||
MaxPlayers = (int?)null
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, minOnly.StatusCode);
|
||||
|
||||
var maxTooHigh = await client.PostAsJsonAsync("/api/suggestions", new { Name = "MaxHigh", Genre = (string?)null, Description = (string?)null, ScreenshotUrl = (string?)null, YoutubeUrl = (string?)null, GameUrl = (string?)null, MinPlayers = 2, MaxPlayers = 40 });
|
||||
var maxTooHigh = await client.PostAsJsonAsync("/api/suggestions", new
|
||||
{
|
||||
Name = "MaxHigh",
|
||||
Genre = (string?)null,
|
||||
Description = (string?)null,
|
||||
ScreenshotUrl = (string?)null,
|
||||
YoutubeUrl = (string?)null,
|
||||
GameUrl = (string?)null,
|
||||
MinPlayers = 2,
|
||||
MaxPlayers = 40
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, maxTooHigh.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Trims_and_truncates_optional_fields()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("trim");
|
||||
|
||||
@@ -460,7 +549,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Mine_excludes_other_players()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var a = factory.CreateClientWithCookies();
|
||||
await a.RegisterAsync("alice");
|
||||
var b = factory.CreateClientWithCookies();
|
||||
@@ -470,14 +559,15 @@ public class SuggestionTests
|
||||
await b.CreateSuggestionAsync("BobGame");
|
||||
|
||||
var mine = await a.GetFromJsonAsync<List<JsonElement>>("/api/suggestions/mine");
|
||||
Assert.Single(mine!);
|
||||
Assert.NotNull(mine);
|
||||
Assert.Single(mine);
|
||||
Assert.Equal("AliceGame", mine[0].GetProperty("name").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task All_returns_link_metadata_and_ordering()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("owner");
|
||||
|
||||
@@ -507,7 +597,7 @@ public class SuggestionTests
|
||||
[Fact]
|
||||
public async Task Delete_respects_phase_and_clears_links_and_votes()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var owner = factory.CreateClientWithCookies();
|
||||
await owner.RegisterAsync("deleter");
|
||||
var other = factory.CreateClientWithCookies();
|
||||
@@ -524,7 +614,11 @@ public class SuggestionTests
|
||||
|
||||
await owner.PostAsJsonAsync("/api/me/phase/next", new { }); // Vote
|
||||
await other.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await other.PostAsJsonAsync("/api/votes", new { SuggestionId = id, Score = 5 });
|
||||
await other.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = id,
|
||||
Score = 5
|
||||
});
|
||||
|
||||
var blocked = await owner.DeleteAsync($"/api/suggestions/{id}");
|
||||
Assert.Equal(HttpStatusCode.BadRequest, blocked.StatusCode);
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
using System.Net.Http;
|
||||
|
||||
namespace GameList.Tests.Support;
|
||||
|
||||
internal class StubHttpClientFactory : IHttpClientFactory
|
||||
internal class StubHttpClientFactory(StubHttpMessageHandler handler) : IHttpClientFactory
|
||||
{
|
||||
private readonly StubHttpMessageHandler _handler;
|
||||
|
||||
public StubHttpClientFactory(StubHttpMessageHandler handler)
|
||||
{
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
public HttpClient CreateClient(string name)
|
||||
{
|
||||
return new HttpClient(_handler, disposeHandler: false);
|
||||
return new HttpClient(handler, disposeHandler: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,24 +5,16 @@ namespace GameList.Tests.Support;
|
||||
|
||||
internal class StubHttpMessageHandler : HttpMessageHandler
|
||||
{
|
||||
private Func<HttpRequestMessage, HttpResponseMessage> _responder;
|
||||
|
||||
public StubHttpMessageHandler()
|
||||
{
|
||||
_responder = DefaultResponder;
|
||||
}
|
||||
private Func<HttpRequestMessage, HttpResponseMessage> _responder = DefaultResponder;
|
||||
|
||||
public void SetResponder(Func<HttpRequestMessage, HttpResponseMessage> responder)
|
||||
{
|
||||
_responder = responder ?? DefaultResponder;
|
||||
_responder = responder;
|
||||
}
|
||||
|
||||
private static HttpResponseMessage DefaultResponder(HttpRequestMessage _)
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new ByteArrayContent(Array.Empty<byte>())
|
||||
};
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent([]) };
|
||||
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
||||
response.Content.Headers.ContentLength = 0;
|
||||
return response;
|
||||
|
||||
@@ -49,5 +49,4 @@ internal static class TestClientExtensions
|
||||
var me = await client.GetFromJsonAsync<JsonElement>("/api/me");
|
||||
return Guid.Parse(me.GetProperty("id").GetString()!);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GameList.Data;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
@@ -18,13 +16,7 @@ internal class TestWebApplicationFactory : WebApplicationFactory<Program>
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||
{
|
||||
builder.UseEnvironment("Development");
|
||||
builder.ConfigureAppConfiguration((context, config) =>
|
||||
{
|
||||
config.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["ADMIN_PASSWORD"] = "admin-key"
|
||||
});
|
||||
});
|
||||
builder.ConfigureAppConfiguration((_, config) => { config.AddInMemoryCollection(new Dictionary<string, string?> { ["ADMIN_PASSWORD"] = "admin-key" }); });
|
||||
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
@@ -37,10 +29,7 @@ internal class TestWebApplicationFactory : WebApplicationFactory<Program>
|
||||
_connection = new SqliteConnection("Data Source=:memory:;Cache=Shared");
|
||||
_connection.Open();
|
||||
|
||||
services.AddDbContext<AppDbContext>(options =>
|
||||
{
|
||||
options.UseSqlite(_connection);
|
||||
});
|
||||
services.AddDbContext<AppDbContext>(options => { options.UseSqlite(_connection); });
|
||||
|
||||
services.AddSingleton<StubHttpMessageHandler>();
|
||||
services.AddSingleton<IHttpClientFactory, StubHttpClientFactory>();
|
||||
|
||||
@@ -11,7 +11,7 @@ public class VoteTests
|
||||
[Fact]
|
||||
public async Task Finalizing_votes_blocks_further_changes()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("voter");
|
||||
|
||||
@@ -19,13 +19,21 @@ public class VoteTests
|
||||
|
||||
await client.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
|
||||
var vote = await client.PostAsJsonAsync("/api/votes", new { SuggestionId = suggestionId, Score = 7 });
|
||||
var vote = await client.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = suggestionId,
|
||||
Score = 7
|
||||
});
|
||||
vote.EnsureSuccessStatusCode();
|
||||
|
||||
var finalize = await client.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
|
||||
finalize.EnsureSuccessStatusCode();
|
||||
|
||||
var change = await client.PostAsJsonAsync("/api/votes", new { SuggestionId = suggestionId, Score = 5 });
|
||||
var change = await client.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = suggestionId,
|
||||
Score = 5
|
||||
});
|
||||
|
||||
Assert.Equal(HttpStatusCode.BadRequest, change.StatusCode);
|
||||
}
|
||||
@@ -33,45 +41,57 @@ public class VoteTests
|
||||
[Fact]
|
||||
public async Task Score_out_of_range_rejected()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("score");
|
||||
var id = await client.CreateSuggestionAsync("RangeGame");
|
||||
await client.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
|
||||
var resp = await client.PostAsJsonAsync("/api/votes", new { SuggestionId = id, Score = 11 });
|
||||
var resp = await client.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = id,
|
||||
Score = 11
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Negative_score_rejected()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("negative");
|
||||
var id = await client.CreateSuggestionAsync("RangeGame2");
|
||||
await client.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
|
||||
var resp = await client.PostAsJsonAsync("/api/votes", new { SuggestionId = id, Score = -1 });
|
||||
var resp = await client.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = id,
|
||||
Score = -1
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Invalid_suggestion_id_rejected()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("invalid");
|
||||
await client.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
|
||||
var resp = await client.PostAsJsonAsync("/api/votes", new { SuggestionId = 9999, Score = 5 });
|
||||
var resp = await client.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = 9999,
|
||||
Score = 5
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Votes_require_display_name()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("anon");
|
||||
var id = await client.CreateSuggestionAsync("NeedName");
|
||||
@@ -84,14 +104,18 @@ public class VoteTests
|
||||
});
|
||||
|
||||
await client.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
var resp = await client.PostAsJsonAsync("/api/votes", new { SuggestionId = id, Score = 5 });
|
||||
var resp = await client.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = id,
|
||||
Score = 5
|
||||
});
|
||||
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Finalize_only_in_vote_phase()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("phase");
|
||||
|
||||
@@ -102,12 +126,16 @@ public class VoteTests
|
||||
[Fact]
|
||||
public async Task Finalize_toggle_allows_unfinalize()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var client = factory.CreateClientWithCookies();
|
||||
await client.RegisterAsync("toggle");
|
||||
var id = await client.CreateSuggestionAsync("Toggle");
|
||||
await client.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
await client.PostAsJsonAsync("/api/votes", new { SuggestionId = id, Score = 5 });
|
||||
await client.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = id,
|
||||
Score = 5
|
||||
});
|
||||
|
||||
var finalize = await client.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
|
||||
finalize.EnsureSuccessStatusCode();
|
||||
@@ -121,7 +149,7 @@ public class VoteTests
|
||||
[Fact]
|
||||
public async Task Linked_votes_apply_to_all_linked_suggestions()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
await admin.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
@@ -134,23 +162,31 @@ public class VoteTests
|
||||
|
||||
await player.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
|
||||
var linkResponse = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new { SourceSuggestionId = id1, TargetSuggestionId = id2 });
|
||||
var linkResponse = await admin.PostAsJsonAsync("/api/admin/link-suggestions", new
|
||||
{
|
||||
SourceSuggestionId = id1,
|
||||
TargetSuggestionId = id2
|
||||
});
|
||||
linkResponse.EnsureSuccessStatusCode();
|
||||
|
||||
var vote = await player.PostAsJsonAsync("/api/votes", new { SuggestionId = id1, Score = 9 });
|
||||
var vote = await player.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = id1,
|
||||
Score = 9
|
||||
});
|
||||
vote.EnsureSuccessStatusCode();
|
||||
|
||||
var mine = await player.GetFromJsonAsync<List<VoteRecord>>("/api/votes/mine");
|
||||
Assert.NotNull(mine);
|
||||
|
||||
Assert.Equal(2, mine!.Count);
|
||||
Assert.Equal(2, mine.Count);
|
||||
Assert.All(mine, v => Assert.Equal(9, v.Score));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Linked_votes_apply_across_chain()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var admin = factory.CreateClientWithCookies();
|
||||
await admin.RegisterAsync("admin", admin: true);
|
||||
await admin.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
@@ -164,22 +200,34 @@ public class VoteTests
|
||||
|
||||
await player.PostAsJsonAsync("/api/me/phase/next", new { });
|
||||
|
||||
await admin.PostAsJsonAsync("/api/admin/link-suggestions", new { SourceSuggestionId = a, TargetSuggestionId = b });
|
||||
await admin.PostAsJsonAsync("/api/admin/link-suggestions", new { SourceSuggestionId = b, TargetSuggestionId = c });
|
||||
await admin.PostAsJsonAsync("/api/admin/link-suggestions", new
|
||||
{
|
||||
SourceSuggestionId = a,
|
||||
TargetSuggestionId = b
|
||||
});
|
||||
await admin.PostAsJsonAsync("/api/admin/link-suggestions", new
|
||||
{
|
||||
SourceSuggestionId = b,
|
||||
TargetSuggestionId = c
|
||||
});
|
||||
|
||||
var vote = await player.PostAsJsonAsync("/api/votes", new { SuggestionId = c, Score = 6 });
|
||||
var vote = await player.PostAsJsonAsync("/api/votes", new
|
||||
{
|
||||
SuggestionId = c,
|
||||
Score = 6
|
||||
});
|
||||
vote.EnsureSuccessStatusCode();
|
||||
|
||||
var mine = await player.GetFromJsonAsync<List<VoteRecord>>("/api/votes/mine");
|
||||
Assert.NotNull(mine);
|
||||
Assert.Equal(3, mine!.Count);
|
||||
Assert.Equal(3, mine.Count);
|
||||
Assert.All(mine, v => Assert.Equal(6, v.Score));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Votes_mine_requires_vote_phase_and_auth()
|
||||
{
|
||||
using var factory = new TestWebApplicationFactory();
|
||||
await using var factory = new TestWebApplicationFactory();
|
||||
var anon = factory.CreateClient();
|
||||
var unauth = await anon.GetAsync("/api/votes/mine");
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, unauth.StatusCode);
|
||||
@@ -190,5 +238,7 @@ public class VoteTests
|
||||
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
|
||||
}
|
||||
|
||||
// ReSharper disable once NotAccessedPositionalProperty.Local
|
||||
// ReSharper disable once ClassNeverInstantiated.Local
|
||||
private record VoteRecord(int SuggestionId, int Score);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user