Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Auth/SessionExpiryComponentTests.cs
T
Joseph Doherty 513fa1aa16 docs(tests): fix CommentChecker findings across tests/ — 13,392 → 0 (repo-wide scan clean)
Comment/XML-doc changes only:
- add summaries to ~10k test methods, fixtures, fakes and helpers
  (+ <param> on theories, <returns> on async Task methods,
  <inheritdoc/> on interface implementations the checker can resolve)
- strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N,
  #NN, Component-0NN) from comments, keeping the explanatory text;
  reword false positives in comments only
- real docs on fakes implementing BCL/library interfaces
  (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime)
- reword the last 5 src/ false positives (CWE-1236, failures-1,
  year-0001, M365)

Only non-comment edits: four one-line nested classes split across lines
so members can carry docs. Build 0 errors / 0 warnings.
2026-10-07 15:05:49 -04:00

97 lines
4.1 KiB
C#

using Bunit;
using Bunit.TestDoubles;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Auth;
/// <summary>
/// Regression tests for the session-expiry redirect. <c>SessionExpiry</c>
/// used to poll the Blazor <c>AuthenticationStateProvider</c>, which (via
/// <c>CookieAuthenticationStateProvider</c>) serves a frozen constructor-time
/// principal — so the polled state could never become "expired" and the
/// idle-logout redirect never fired. The component now polls the server
/// <c>GET /auth/ping</c> endpoint, which reflects the live cookie session: a
/// 401 response triggers a redirect to <c>/login</c>. These tests exercise that
/// redirect path directly (the path was previously untested).
/// </summary>
public class SessionExpiryComponentTests : BunitContext
{
private const string ModulePath = "./_content/ZB.MOM.WW.ScadaBridge.CentralUI/js/session-expiry.js";
/// <summary>Verifies that an expired session redirects to the login page.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task CheckSession_ExpiredSession_RedirectsToLogin()
{
// The server reports the cookie has lapsed: ping returns HTTP 401.
var module = JSInterop.SetupModule(ModulePath);
module.Setup<int>("ping", "/auth/ping").SetResult(401);
var nav = Services.GetRequiredService<NavigationManager>();
var cut = Render<SessionExpiry>();
await cut.InvokeAsync(() => cut.Instance.CheckSessionAsync());
Assert.EndsWith("/login", nav.Uri);
}
/// <summary>Verifies that a live session does not redirect.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task CheckSession_LiveSession_DoesNotRedirect()
{
// The server reports the session is still valid: ping returns HTTP 200.
var module = JSInterop.SetupModule(ModulePath);
module.Setup<int>("ping", "/auth/ping").SetResult(200);
var nav = Services.GetRequiredService<NavigationManager>();
var before = nav.Uri;
var cut = Render<SessionExpiry>();
await cut.InvokeAsync(() => cut.Instance.CheckSessionAsync());
Assert.Equal(before, nav.Uri);
Assert.DoesNotContain("/login", nav.Uri);
}
/// <summary>Verifies that a transient network failure does not redirect.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task CheckSession_TransientNetworkFailure_DoesNotRedirect()
{
// A network blip surfaces as status 0 — inconclusive. The component must
// NOT log an authenticated user out on a transient failure.
var module = JSInterop.SetupModule(ModulePath);
module.Setup<int>("ping", "/auth/ping").SetResult(0);
var nav = Services.GetRequiredService<NavigationManager>();
var before = nav.Uri;
var cut = Render<SessionExpiry>();
await cut.InvokeAsync(() => cut.Instance.CheckSessionAsync());
Assert.Equal(before, nav.Uri);
}
/// <summary>Verifies that checking the session on the login page neither pings the server nor redirects.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task CheckSession_OnLoginPage_DoesNotPingOrRedirect()
{
// On /login the component must neither poll nor redirect (a /login →
// /login redirect would loop). JSInterop is left in Strict mode with no
// module setup, so any ping call would throw and fail the test.
var nav = (BunitNavigationManager)Services
.GetRequiredService<NavigationManager>();
nav.NavigateTo("login");
var cut = Render<SessionExpiry>();
await cut.InvokeAsync(() => cut.Instance.CheckSessionAsync());
// No JS module import was attempted and the URL is unchanged.
Assert.EndsWith("/login", nav.Uri);
}
}