test(playwright): token-endpoint tests tolerate DisableLogin

Under DisableLogin /auth/token deliberately answers 404 (lane K). The tests
now probe it: on a login-disabled rig TokenEndpoint_ReturnsJwt skips and the
bad-credentials test asserts 404 (the exchange is closed); with login enabled
they keep the 200 + JWT and 401 assertions.
This commit is contained in:
Joseph Doherty
2026-10-22 14:01:40 -04:00
parent 4ae3ed3fdd
commit a04eb3a752
@@ -74,15 +74,17 @@ public class LoginTests
Assert.Equal(200, status); // redirect followed to login page Assert.Equal(200, status); // redirect followed to login page
} }
/// <summary>Verifies that the token endpoint returns a JWT for valid credentials.</summary> /// <summary>
/// Verifies that the token endpoint returns a JWT for valid credentials. Skipped when the
/// rig runs with <c>DisableLogin</c>, where <c>/auth/token</c> deliberately answers 404.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns> /// <returns>A task that represents the asynchronous operation.</returns>
[Fact] [SkippableFact]
public async Task TokenEndpoint_ReturnsJwt() public async Task TokenEndpoint_ReturnsJwt()
{ {
var page = await _fixture.NewPageAsync(); var page = await OpenLoginPageAsync();
Skip.If(await TokenEndpointDisabledAsync(page),
await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/login"); "Login is disabled on this rig: POST /auth/token answers 404 by design, so no JWT is issued.");
await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);
var result = await page.EvaluateAsync<JsonElement>(@" var result = await page.EvaluateAsync<JsonElement>(@"
async () => { async () => {
@@ -101,29 +103,50 @@ public class LoginTests
Assert.Equal("multi-role", result.GetProperty("username").GetString()); Assert.Equal("multi-role", result.GetProperty("username").GetString());
} }
/// <summary>Verifies that the token endpoint returns HTTP 401 for invalid credentials.</summary> /// <summary>
/// Verifies that the token endpoint rejects invalid credentials: HTTP 401 with login
/// enabled, or HTTP 404 under <c>DisableLogin</c> (the exchange is closed entirely, so
/// even bad credentials can never reach a credential check).
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns> /// <returns>A task that represents the asynchronous operation.</returns>
[Fact] [Fact]
public async Task TokenEndpoint_InvalidCredentials_Returns401() public async Task TokenEndpoint_InvalidCredentials_Returns401()
{ {
var page = await _fixture.NewPageAsync(); var page = await OpenLoginPageAsync();
var disabled = await TokenEndpointDisabledAsync(page);
var status = await PostTokenAsync(page, "baduser", "badpass");
Assert.Equal(disabled ? 404 : 401, status);
}
private async Task<IPage> OpenLoginPageAsync()
{
var page = await _fixture.NewPageAsync();
await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/login"); await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/login");
await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);
return page;
}
var status = await page.EvaluateAsync<int>(@" /// <summary>
async () => { /// Probes whether the rig runs with <c>DisableLogin</c>: the token exchange then answers
/// 404 for any request (AuthEndpoints.HandleTokenAsync), while with login enabled a
/// valid-credential POST is a 200 and never a 404.
/// </summary>
private static async Task<bool> TokenEndpointDisabledAsync(IPage page) =>
await PostTokenAsync(page, PlaywrightFixture.TestUsername, PlaywrightFixture.TestPassword) == 404;
private static Task<int> PostTokenAsync(IPage page, string username, string password) =>
page.EvaluateAsync<int>(@"
async ([u, p]) => {
const resp = await fetch('/auth/token', { const resp = await fetch('/auth/token', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'username=baduser&password=badpass' body: new URLSearchParams({ username: u, password: p }).toString()
}); });
return resp.status; return resp.status;
} }
"); ", new object[] { username, password });
Assert.Equal(401, status);
}
private static ILocatorAssertions Expect(ILocator locator) => private static ILocatorAssertions Expect(ILocator locator) =>
Assertions.Expect(locator); Assertions.Expect(locator);