Add comprehensive backend test suite and helper access

This commit is contained in:
2026-02-05 18:03:50 +01:00
parent 330d87b432
commit 912da11809
11 changed files with 591 additions and 4 deletions

View File

@@ -0,0 +1,159 @@
using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using GameList.Domain;
using GameList.Tests.Support;
namespace GameList.Tests;
public class AdminTests
{
[Fact]
public async Task Admin_vote_status_marks_ready_when_all_finalized()
{
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
var p1 = factory.CreateClientWithCookies();
await p1.RegisterAsync("alice");
var p2 = factory.CreateClientWithCookies();
await p2.RegisterAsync("bob");
await p2.PostAsJsonAsync("/api/me/phase/next", new { });
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/finalize", new { Final = true });
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 });
var status = await admin.GetFromJsonAsync<JsonElement>("/api/admin/vote-status");
Assert.True(status.GetProperty("ready").GetBoolean());
Assert.Equal(0, status.GetProperty("waiting").GetArrayLength());
}
[Fact]
public async Task Grant_joker_only_in_vote_phase()
{
using var factory = new TestWebApplicationFactory();
var admin = factory.CreateClientWithCookies();
await admin.RegisterAsync("admin", admin: true);
var p = factory.CreateClientWithCookies();
await p.RegisterAsync("player");
var give = await admin.PostAsJsonAsync("/api/admin/joker", new { playerId = (await p.GetProfileIdAsync()) });
Assert.Equal(HttpStatusCode.BadRequest, give.StatusCode);
}
[Fact]
public async Task Delete_player_cascades_suggestions_and_votes()
{
using var factory = new TestWebApplicationFactory();
var admin = factory.CreateClientWithCookies();
await admin.RegisterAsync("admin", admin: true);
var player = factory.CreateClientWithCookies();
await player.RegisterAsync("deleteme");
var suggestionId = await player.CreateSuggestionAsync("DeleteGame");
await player.PostAsJsonAsync("/api/me/phase/next", new { });
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 =>
{
Assert.Single(db.Players); // admin remains
Assert.Empty(db.Suggestions);
Assert.Empty(db.Votes);
});
}
[Fact]
public async Task Link_suggestions_errors_on_same_id_and_already_linked()
{
using var factory = new TestWebApplicationFactory();
var admin = factory.CreateClientWithCookies();
await admin.RegisterAsync("admin", admin: true);
var player = factory.CreateClientWithCookies();
await player.RegisterAsync("linker");
var a = await player.CreateSuggestionAsync("Game A");
var b = await player.CreateSuggestionAsync("Game B");
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 });
Assert.Equal(HttpStatusCode.BadRequest, same.StatusCode);
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 });
Assert.Equal(HttpStatusCode.BadRequest, already.StatusCode);
}
[Fact]
public async Task Unlink_suggestions_clears_group_votes()
{
using var factory = new TestWebApplicationFactory();
var admin = factory.CreateClientWithCookies();
await admin.RegisterAsync("admin", admin: true);
var player = factory.CreateClientWithCookies();
await player.RegisterAsync("unlinker");
var a = await player.CreateSuggestionAsync("Game A");
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 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 =>
{
Assert.Empty(db.Votes);
Assert.All(db.Suggestions, s => Assert.Null(s.ParentSuggestionId));
});
}
[Fact]
public async Task Reset_and_factory_reset_clear_state()
{
using var factory = new TestWebApplicationFactory();
var admin = factory.CreateClientWithCookies();
await admin.RegisterAsync("admin", admin: true);
var player = factory.CreateClientWithCookies();
await player.RegisterAsync("player");
await player.CreateSuggestionAsync("Keep");
var reset = await admin.PostAsJsonAsync("/api/admin/reset", new { });
reset.EnsureSuccessStatusCode();
await factory.WithDbContextAsync(async db =>
{
Assert.Empty(db.Suggestions);
Assert.Empty(db.Votes);
Assert.All(db.Players, p => Assert.Equal(Phase.Suggest, p.CurrentPhase));
});
var factoryReset = await admin.PostAsJsonAsync("/api/admin/factory-reset", new { });
factoryReset.EnsureSuccessStatusCode();
await factory.WithDbContextAsync(async db =>
{
Assert.Empty(db.Players);
Assert.Single(db.AppState);
});
}
}

View File

@@ -1,5 +1,4 @@
using System.Net; using System.Net;
using System.Net;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Text.Json; using System.Text.Json;
using GameList.Tests.Support; using GameList.Tests.Support;
@@ -47,4 +46,29 @@ public class AuthTests
Assert.Equal(HttpStatusCode.Unauthorized, login.StatusCode); Assert.Equal(HttpStatusCode.Unauthorized, login.StatusCode);
} }
[Fact]
public async Task Register_validates_required_fields()
{
using var factory = new TestWebApplicationFactory();
var client = factory.CreateClientWithCookies();
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" });
Assert.Equal(HttpStatusCode.BadRequest, badKey.StatusCode);
}
[Fact]
public async Task Logout_clears_cookie()
{
using var factory = new TestWebApplicationFactory();
var client = factory.CreateClientWithCookies();
await client.RegisterAsync("logoutme");
var resp = await client.PostAsync("/api/auth/logout", null);
resp.EnsureSuccessStatusCode();
Assert.True(resp.Headers.TryGetValues("Set-Cookie", out var cookies) && cookies.Any(c => c.Contains("player")));
}
} }

View File

@@ -0,0 +1,81 @@
using System.Net;
using System.Net.Http.Headers;
using System.Reflection;
using GameList.Infrastructure;
using GameList.Endpoints;
using GameList.Tests.Support;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using System.Linq;
namespace GameList.Tests;
public class HelperTests
{
[Fact]
public void PasswordHasher_roundtrip_and_empty_guard()
{
var (hash, salt) = PasswordHasher.HashPassword("secret");
Assert.True(PasswordHasher.Verify("secret", hash, salt));
Assert.False(PasswordHasher.Verify("other", hash, salt));
Assert.Throws<ArgumentException>(() => PasswordHasher.HashPassword(""));
}
[Fact]
public void UpdateIndexMetaBase_rewrites_content_value()
{
var webRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(webRoot);
var index = Path.Combine(webRoot, "index.html");
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 text = File.ReadAllText(index);
Assert.Contains("content=\"/pick\"", text);
}
[Fact]
public async Task IsReachableImageAsync_rejects_redirect_and_accepts_image()
{
Assert.True(await EndpointHelpers.IsReachableImageAsync(null, new StubHttpClientFactory(new StubHttpMessageHandler())));
// Private host should be rejected before network call
Assert.False(await EndpointHelpers.IsReachableImageAsync("http://127.0.0.1/img.png", new StubHttpClientFactory(new StubHttpMessageHandler())));
}
[Fact]
public void Link_root_helpers_handle_groups()
{
var roots = EndpointHelpers.BuildLinkRoots(new[] { (1, (int?)null), (2, 1), (3, (int?)null) });
Assert.Equal(1, roots[1]);
Assert.Equal(1, roots[2]);
Assert.Equal(3, roots[3]);
var linked = EndpointHelpers.LinkedIdsFor(2, roots);
Assert.Contains(1, linked);
Assert.Contains(2, linked);
}
[Fact]
public void Url_validation_rules()
{
Assert.True(EndpointHelpers.IsValidImageUrl("https://x.com/img.png"));
Assert.False(EndpointHelpers.IsValidImageUrl("ftp://x/img.png"));
Assert.True(EndpointHelpers.IsValidHttpUrl("http://x"));
Assert.False(EndpointHelpers.IsValidHttpUrl("file://x"));
}
private class FakeEnv : IWebHostEnvironment
{
public string ApplicationName { get; set; } = "";
public IFileProvider WebRootFileProvider { get; set; } = null!;
public string WebRootPath { get; set; } = "";
public string EnvironmentName { get; set; } = "";
public string ContentRootPath { get; set; } = "";
public IFileProvider ContentRootFileProvider { get; set; } = null!;
}
}

View File

@@ -0,0 +1,28 @@
using System.Net;
using GameList.Tests.Support;
namespace GameList.Tests;
public class MiddlewareTests
{
[Fact]
public async Task Deleted_player_cookie_is_signed_out()
{
using var factory = new TestWebApplicationFactory();
var client = factory.CreateClientWithCookies();
await client.RegisterAsync("ghost");
var playerId = await client.GetProfileIdAsync();
await factory.WithDbContextAsync(async db =>
{
var player = await db.Players.FindAsync(playerId);
db.Players.Remove(player!);
await db.SaveChangesAsync();
});
var resp = await client.GetAsync("/api/state");
Assert.Equal(HttpStatusCode.Unauthorized, resp.StatusCode);
Assert.Contains(resp.Headers, h => h.Key.Equals("Set-Cookie", StringComparison.OrdinalIgnoreCase));
}
}

View File

@@ -32,4 +32,15 @@ public class ResultsTests
Assert.Equal("ResultGame", first.GetProperty("name").GetString()); Assert.Equal("ResultGame", first.GetProperty("name").GetString());
Assert.Equal(8, (int)first.GetProperty("average").GetDouble()); Assert.Equal(8, (int)first.GetProperty("average").GetDouble());
} }
[Fact]
public async Task Results_locked_returns_error()
{
using var factory = new TestWebApplicationFactory();
var client = factory.CreateClientWithCookies();
await client.RegisterAsync("user");
await client.PostAsJsonAsync("/api/me/phase/next", new { });
var resp = await client.GetAsync("/api/results");
Assert.Equal(System.Net.HttpStatusCode.BadRequest, resp.StatusCode);
}
} }

View File

@@ -1,5 +1,4 @@
using System.Net; using System.Net;
using System.Net;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Text.Json; using System.Text.Json;
using GameList.Domain; using GameList.Domain;
@@ -42,4 +41,65 @@ public class StateTests
Assert.Equal(Phase.Results.ToString(), state.GetProperty("currentPhase").GetString()); Assert.Equal(Phase.Results.ToString(), state.GetProperty("currentPhase").GetString());
Assert.True(state.GetProperty("resultsOpen").GetBoolean()); Assert.True(state.GetProperty("resultsOpen").GetBoolean());
} }
[Fact]
public async Task Name_endpoint_trims_and_rejects_blank()
{
using var factory = new TestWebApplicationFactory();
var client = factory.CreateClientWithCookies();
await client.RegisterAsync("nametest");
var bad = await client.PostAsJsonAsync("/api/me/name", new { name = " " });
Assert.Equal(HttpStatusCode.BadRequest, bad.StatusCode);
var ok = await client.PostAsJsonAsync("/api/me/name", new { name = " Alice " });
ok.EnsureSuccessStatusCode();
var me = await client.GetFromJsonAsync<JsonElement>("/api/me");
Assert.Equal("Alice", me.GetProperty("displayName").GetString());
}
[Fact]
public async Task Phase_prev_admin_only()
{
using var factory = new TestWebApplicationFactory();
var player = factory.CreateClientWithCookies();
await player.RegisterAsync("phase");
var notAdmin = await player.PostAsJsonAsync("/api/me/phase/prev", new { });
Assert.Equal(HttpStatusCode.BadRequest, notAdmin.StatusCode);
var admin = factory.CreateClientWithCookies();
await admin.RegisterAsync("admin", admin: true);
await admin.PostAsJsonAsync("/api/me/phase/next", new { }); // to Vote
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());
}
[Fact]
public async Task State_endpoint_requires_auth_and_counts()
{
using var factory = new TestWebApplicationFactory();
var anon = factory.CreateClient();
var unauthorized = await anon.GetAsync("/api/state");
Assert.NotEqual(HttpStatusCode.OK, unauthorized.StatusCode);
var client = factory.CreateClientWithCookies();
await client.RegisterAsync("counting");
await client.CreateSuggestionAsync("One");
var state = await client.GetFromJsonAsync<JsonElement>("/api/state");
Assert.True(state.TryGetProperty("Players", out var players) || state.TryGetProperty("players", out players));
Assert.True(players.GetInt32() >= 1);
Assert.True(state.TryGetProperty("Suggestions", out var suggestions) || state.TryGetProperty("suggestions", out suggestions));
Assert.True(suggestions.GetInt32() >= 1);
}
[Fact]
public async Task Health_endpoint_ok()
{
using var factory = new TestWebApplicationFactory();
var resp = await factory.CreateClient().GetFromJsonAsync<JsonElement>("/health");
Assert.Equal("ok", resp.GetProperty("status").GetString());
}
} }

View File

@@ -1,7 +1,9 @@
using System.Net; using System.Net;
using System.Net;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Text.Json;
using GameList.Tests.Support; using GameList.Tests.Support;
using GameList.Domain;
using Microsoft.EntityFrameworkCore;
namespace GameList.Tests; namespace GameList.Tests;
@@ -45,6 +47,129 @@ public class SuggestionTests
Assert.Equal(HttpStatusCode.BadRequest, sixth.StatusCode); Assert.Equal(HttpStatusCode.BadRequest, sixth.StatusCode);
} }
[Fact]
public async Task Rejects_invalid_image_extension_and_player_counts()
{
using var factory = new TestWebApplicationFactory();
var client = factory.CreateClientWithCookies();
await client.RegisterAsync("validate");
var badExt = await client.PostAsJsonAsync("/api/suggestions", new
{
Name = "BadImg",
Genre = (string?)null,
Description = (string?)null,
ScreenshotUrl = "http://example.com/file.txt",
YoutubeUrl = (string?)null,
GameUrl = (string?)null,
MinPlayers = (int?)null,
MaxPlayers = (int?)null
});
Assert.Equal(HttpStatusCode.BadRequest, badExt.StatusCode);
var badPlayers = await client.PostAsJsonAsync("/api/suggestions", new
{
Name = "BadPlayers",
Genre = (string?)null,
Description = (string?)null,
ScreenshotUrl = (string?)null,
YoutubeUrl = (string?)null,
GameUrl = (string?)null,
MinPlayers = 4,
MaxPlayers = 2
});
Assert.Equal(HttpStatusCode.BadRequest, badPlayers.StatusCode);
}
[Fact]
public async Task Joker_allows_single_extra_suggestion_and_unfinalizes_votes()
{
using var factory = new TestWebApplicationFactory();
var player = factory.CreateClientWithCookies();
await player.RegisterAsync("joker");
await factory.WithDbContextAsync(async db =>
{
var p = await db.Players.FirstAsync();
p.HasJoker = true;
p.CurrentPhase = Domain.Phase.Vote;
await db.SaveChangesAsync();
});
var suggestion = await player.PostAsJsonAsync("/api/suggestions", new
{
Name = "JokerGame",
Genre = (string?)null,
Description = (string?)null,
ScreenshotUrl = (string?)null,
YoutubeUrl = (string?)null,
GameUrl = (string?)null,
MinPlayers = (int?)null,
MaxPlayers = (int?)null
});
suggestion.EnsureSuccessStatusCode();
await factory.WithDbContextAsync(async db =>
{
var p = await db.Players.FirstAsync();
Assert.False(p.HasJoker);
Assert.False(p.VotesFinal);
});
}
[Fact]
public async Task Admin_can_update_during_vote_phase()
{
using var factory = new TestWebApplicationFactory();
var admin = factory.CreateClientWithCookies();
await admin.RegisterAsync("admin", admin: true);
var player = factory.CreateClientWithCookies();
await player.RegisterAsync("author");
var id = await player.CreateSuggestionAsync("OldName");
await player.PostAsJsonAsync("/api/me/phase/next", new { });
var update = await admin.PutAsJsonAsync($"/api/suggestions/{id}", new
{
Name = "NewName",
Genre = (string?)null,
Description = (string?)null,
ScreenshotUrl = (string?)null,
YoutubeUrl = (string?)null,
GameUrl = (string?)null,
MinPlayers = (int?)null,
MaxPlayers = (int?)null
});
update.EnsureSuccessStatusCode();
}
[Fact]
public async Task Phase_gate_blocks_player_update_in_vote_phase()
{
using var factory = new TestWebApplicationFactory();
var player = factory.CreateClientWithCookies();
await player.RegisterAsync("phase");
var id = await player.CreateSuggestionAsync("Lock");
await player.PostAsJsonAsync("/api/me/phase/next", new { });
var update = await player.PutAsJsonAsync($"/api/suggestions/{id}", new
{
Name = "Blocked",
Genre = "NewGenre",
Description = (string?)null,
ScreenshotUrl = (string?)null,
YoutubeUrl = (string?)null,
GameUrl = (string?)null,
MinPlayers = (int?)null,
MaxPlayers = (int?)null
});
update.EnsureSuccessStatusCode();
var loaded = await factory.WithDbContextAsync(async db => await db.Suggestions.FindAsync(id));
Assert.Equal("Lock", loaded!.Name); // title locked
}
[Fact] [Fact]
public async Task Unreachable_screenshot_url_is_rejected() public async Task Unreachable_screenshot_url_is_rejected()
{ {
@@ -68,4 +193,30 @@ public class SuggestionTests
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
} }
[Fact]
public async Task Get_all_requires_vote_phase()
{
using var factory = new TestWebApplicationFactory();
var client = factory.CreateClientWithCookies();
await client.RegisterAsync("viewer");
var resp = await client.GetAsync("/api/suggestions/all");
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
}
[Fact]
public async Task Mine_returns_ordered_list()
{
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 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 mine = await client.GetFromJsonAsync<List<JsonElement>>("/api/suggestions/mine");
Assert.Equal("Second", mine![0].GetProperty("name").GetString());
}
} }

View File

@@ -43,4 +43,11 @@ internal static class TestClientExtensions
var json = await response.Content.ReadFromJsonAsync<JsonElement>(); var json = await response.Content.ReadFromJsonAsync<JsonElement>();
return json.GetProperty("id").GetInt32(); return json.GetProperty("id").GetInt32();
} }
public static async Task<Guid> GetProfileIdAsync(this HttpClient client)
{
var me = await client.GetFromJsonAsync<JsonElement>("/api/me");
return Guid.Parse(me.GetProperty("id").GetString()!);
}
} }

View File

@@ -79,6 +79,13 @@ internal class TestWebApplicationFactory : WebApplicationFactory<Program>
return action(db); return action(db);
} }
public Task<T> WithDbContextAsync<T>(Func<AppDbContext, Task<T>> action)
{
using var scope = Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return action(db);
}
public HttpClient CreateClientWithCookies() public HttpClient CreateClientWithCookies()
{ {
return CreateClient(new WebApplicationFactoryClientOptions return CreateClient(new WebApplicationFactoryClientOptions

View File

@@ -1,7 +1,7 @@
using System.Net; using System.Net;
using System.Net;
using System.Net.Http.Json; using System.Net.Http.Json;
using GameList.Tests.Support; using GameList.Tests.Support;
using Microsoft.EntityFrameworkCore;
namespace GameList.Tests; namespace GameList.Tests;
@@ -29,6 +29,62 @@ public class VoteTests
Assert.Equal(HttpStatusCode.BadRequest, change.StatusCode); Assert.Equal(HttpStatusCode.BadRequest, change.StatusCode);
} }
[Fact]
public async Task Score_out_of_range_rejected()
{
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 });
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
}
[Fact]
public async Task Invalid_suggestion_id_rejected()
{
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 });
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
}
[Fact]
public async Task Votes_require_display_name()
{
using var factory = new TestWebApplicationFactory();
var client = factory.CreateClientWithCookies();
await client.RegisterAsync("anon");
var id = await client.CreateSuggestionAsync("NeedName");
await factory.WithDbContextAsync(async db =>
{
var p = await db.Players.FirstAsync();
p.DisplayName = null;
await db.SaveChangesAsync();
});
await client.PostAsJsonAsync("/api/me/phase/next", new { });
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();
var client = factory.CreateClientWithCookies();
await client.RegisterAsync("phase");
var resp = await client.PostAsJsonAsync("/api/votes/finalize", new { Final = true });
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
}
[Fact] [Fact]
public async Task Linked_votes_apply_to_all_linked_suggestions() public async Task Linked_votes_apply_to_all_linked_suggestions()
{ {

View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("GameList.Tests")]