513fa1aa16
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.
125 lines
5.0 KiB
C#
125 lines
5.0 KiB
C#
using System.Security.Claims;
|
|
using Bunit;
|
|
using Microsoft.AspNetCore.Components.Authorization;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using NSubstitute;
|
|
using ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
|
using ZB.MOM.WW.ScadaBridge.Communication;
|
|
using ZB.MOM.WW.ScadaBridge.Security;
|
|
using AlarmSummaryPage = ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Monitoring.AlarmSummary;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Monitoring;
|
|
|
|
/// <summary>
|
|
/// Covers the Alarm Summary flat table's two rendering paths. A short
|
|
/// list renders every row with a plain foreach; past the threshold the same row template is
|
|
/// handed to <c>Virtualize</c> so a site with thousands of alarms stops materialising the
|
|
/// whole table into the circuit's render tree on every delta. Row markup — including the
|
|
/// <c>data-test</c> hook the operator tests key off — is identical either way.
|
|
/// </summary>
|
|
public class AlarmSummaryVirtualizeTests : BunitContext
|
|
{
|
|
private readonly IAlarmSummaryService _summary = Substitute.For<IAlarmSummaryService>();
|
|
private readonly ISiteRepository _siteRepo = Substitute.For<ISiteRepository>();
|
|
|
|
private void Arrange(int rowCount)
|
|
{
|
|
JSInterop.Mode = JSRuntimeMode.Loose;
|
|
|
|
var rows = Enumerable.Range(0, rowCount)
|
|
.Select(i => new AlarmSummaryRow(
|
|
$"inst-{i:D4}",
|
|
new AlarmStateChanged($"inst-{i:D4}", $"alarm-{i:D4}", AlarmState.Active, i, DateTimeOffset.UtcNow)))
|
|
.ToList();
|
|
|
|
_summary.GetSiteAlarmsAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
|
|
.Returns(Task.FromResult(new AlarmSummaryResult(rows, Array.Empty<string>())));
|
|
_summary.ComputeRollup(Arg.Any<IReadOnlyList<AlarmSummaryRow>>())
|
|
.Returns(new AlarmRollup(rowCount, rowCount, 0, new Dictionary<AlarmKind, int>()));
|
|
Services.AddSingleton(_summary);
|
|
|
|
_siteRepo.GetAllSitesAsync(Arg.Any<CancellationToken>())
|
|
.Returns(Task.FromResult<IReadOnlyList<Site>>(new List<Site>
|
|
{
|
|
new("Site 1", "site1") { Id = 1 },
|
|
}));
|
|
Services.AddSingleton(_siteRepo);
|
|
Services.AddSingleton<ISiteAlarmLiveCache>(new InertLiveCache());
|
|
Services.AddScoped<ZB.MOM.WW.ScadaBridge.CentralUI.Auth.SiteScopeService>();
|
|
|
|
var claims = new[]
|
|
{
|
|
new Claim(JwtTokenService.UsernameClaimType, "tester"),
|
|
new Claim(JwtTokenService.RoleClaimType, "Administrator"),
|
|
};
|
|
Services.AddSingleton<AuthenticationStateProvider>(
|
|
new TestAuthStateProvider(new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"))));
|
|
Services.AddAuthorizationCore();
|
|
Services.AddTestConfigurationMutations();
|
|
}
|
|
|
|
private IRenderedComponent<AlarmSummaryPage> RenderWithSiteSelected()
|
|
{
|
|
var cut = Render<AlarmSummaryPage>();
|
|
cut.Find("[data-test='alarm-summary-site']").Change("1");
|
|
return cut;
|
|
}
|
|
|
|
/// <summary>Verifies that a short alarm list renders every row inline.</summary>
|
|
[Fact]
|
|
public void ShortList_RendersEveryRowInline()
|
|
{
|
|
Arrange(rowCount: 20);
|
|
|
|
var cut = RenderWithSiteSelected();
|
|
|
|
cut.WaitForAssertion(() =>
|
|
Assert.Equal(20, cut.FindAll("tr[data-test='alarm-summary-row']").Count));
|
|
// The row-count line is unchanged by either path.
|
|
Assert.Contains("Showing 20 of 20", cut.Markup);
|
|
}
|
|
|
|
/// <summary>Verifies that a long alarm list is virtualized and does not render every row.</summary>
|
|
[Fact]
|
|
public void LongList_VirtualizesWithoutRenderingEveryRow()
|
|
{
|
|
Arrange(rowCount: 2000);
|
|
|
|
var cut = RenderWithSiteSelected();
|
|
|
|
cut.WaitForAssertion(() => Assert.Contains("Showing 2000 of 2000", cut.Markup));
|
|
|
|
var rendered = cut.FindAll("tr[data-test='alarm-summary-row']").Count;
|
|
Assert.InRange(rendered, 1, 1999);
|
|
// Rows still carry the same shape — instance name, alarm name, severity.
|
|
Assert.Contains("alarm-", cut.Markup);
|
|
}
|
|
|
|
/// <summary>Never goes live, so the page keeps its poll snapshot for these tests.</summary>
|
|
private sealed class InertLiveCache : ISiteAlarmLiveCache
|
|
{
|
|
/// <inheritdoc />
|
|
public IDisposable Subscribe(int siteId, Action onChanged) => new NoOp();
|
|
|
|
/// <inheritdoc />
|
|
public IReadOnlyList<AlarmStateChanged> GetCurrentAlarms(int siteId) =>
|
|
Array.Empty<AlarmStateChanged>();
|
|
|
|
/// <inheritdoc />
|
|
public bool IsLive(int siteId) => false;
|
|
|
|
/// <inheritdoc />
|
|
public IReadOnlyList<string> GetNotReportingInstances(int siteId) => Array.Empty<string>();
|
|
|
|
private sealed class NoOp : IDisposable
|
|
{
|
|
/// <summary>Does nothing; the no-op subscription holds no resources.</summary>
|
|
public void Dispose() { }
|
|
}
|
|
}
|
|
}
|