namespace RpgRoller.Tests; public sealed class AuthApiTests(WebApplicationFactory factory) : ApiTestBase(factory) { [Fact] public async Task RegisterLoginAndMeFlow_WorksWithDuplicateUsernameGuard() { using var factory = CreateFactory(4, 4, 4); using var client = factory.CreateClient(new() { AllowAutoRedirect = false }); var registerResult = await RegisterAsync(client, "alice", "Password123", "Alice"); Assert.Equal("alice", registerResult.Username); Assert.Contains(registerResult.Roles, role => string.Equals(role, "admin", StringComparison.OrdinalIgnoreCase)); var duplicate = await client.PostAsJsonAsync("/api/auth/register", new RegisterRequest("alice", "Password123", "Alice 2")); Assert.Equal(HttpStatusCode.BadRequest, duplicate.StatusCode); var loginResult = await client.PostAsJsonAsync("/api/auth/login", new LoginRequest("alice", "Password123")); Assert.Equal(HttpStatusCode.OK, loginResult.StatusCode); var me = await GetAsync(client, "/api/me"); Assert.Equal(registerResult.Id, me.User.Id); Assert.Null(me.ActiveCharacterId); Assert.Null(me.CurrentCampaignId); var invalidLogin = await client.PostAsJsonAsync("/api/auth/login", new LoginRequest("alice", "wrong-password")); Assert.Equal(HttpStatusCode.BadRequest, invalidLogin.StatusCode); } [Fact] public async Task UsernamesEndpoint_RequiresAuthAndReturnsAlphabeticalList() { using var factory = CreateFactory(); using var client = factory.CreateClient(new() { AllowAutoRedirect = false }); await RegisterAsync(client, "zoe", "Password123", "Zoe"); await RegisterAsync(client, "amy", "Password123", "Amy"); await RegisterAsync(client, "bob", "Password123", "Bob"); var unauthorized = await client.GetAsync("/api/users/usernames"); Assert.Equal(HttpStatusCode.Unauthorized, unauthorized.StatusCode); await LoginAsync(client, "bob", "Password123"); var usernames = await GetAsync>(client, "/api/users/usernames"); Assert.Equal(["amy", "bob", "zoe"], usernames); } [Fact] public async Task LoginCookie_IsMarkedSecure_WhenForwardedProtoIsHttps() { using var factory = CreateFactory(); using var client = factory.CreateClient(new() { AllowAutoRedirect = false }); await RegisterAsync(client, "proxy-user", "Password123", "Proxy User"); using var request = new HttpRequestMessage(HttpMethod.Post, "/api/auth/login") { Content = JsonContent.Create(new LoginRequest("proxy-user", "Password123")) }; request.Headers.TryAddWithoutValidation("X-Forwarded-Proto", "https"); using var response = await client.SendAsync(request); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.NotNull(response.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value); var setCookie = Assert.Single(response.Headers.GetValues("Set-Cookie")); Assert.Contains("rpgroller_session=", setCookie); Assert.Contains("secure", setCookie, StringComparison.OrdinalIgnoreCase); } }