using System.Reflection; using System.Security.Claims; using Bunit; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using NSubstitute; using ZB.MOM.WW.ScadaBridge.CentralUI.Auth; using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites; using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories; using ZB.MOM.WW.ScadaBridge.Commons.Messages.DebugView; using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming; using ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms; using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums; using ZB.MOM.WW.ScadaBridge.Communication; using ZB.MOM.WW.ScadaBridge.Communication.Grpc; using DebugViewPage = ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Deployment.DebugView; namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Deployment; /// /// Task 23: the DebugView alarm table surfaces enriched native alarm state — /// kind, severity, source reference, and a composite condition badge set /// (Unacked / Shelved / Suppressed). /// public class DebugViewAlarmTableTests : BunitContext { private IRenderedComponent RenderPage() { JSInterop.Mode = JSRuntimeMode.Loose; var repo = Substitute.For(); var siteRepo = Substitute.For(); siteRepo.GetAllSitesAsync().Returns(new List()); Services.AddSingleton(repo); Services.AddSingleton(siteRepo); var comms = new CommunicationService( Options.Create(new CommunicationOptions()), NullLogger.Instance); Services.AddSingleton(comms); var grpcFactory = new SiteStreamGrpcClientFactory(NullLoggerFactory.Instance); var debugStream = new DebugStreamService( comms, new ServiceCollection().BuildServiceProvider(), grpcFactory, NullLogger.Instance); Services.AddSingleton(debugStream); var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "deployer") }, "TestCookie"); var stubAuth = new StubAuthStateProvider(new AuthenticationState(new ClaimsPrincipal(identity))); Services.AddSingleton(stubAuth); Services.AddScoped(_ => new SiteScopeService(stubAuth)); return Render(); } private sealed class StubAuthStateProvider : AuthenticationStateProvider { private readonly AuthenticationState _state; public StubAuthStateProvider(AuthenticationState state) => _state = state; public override Task GetAuthenticationStateAsync() => Task.FromResult(_state); } private static void SetField(DebugViewPage c, string name, object? value) => typeof(DebugViewPage).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic)!.SetValue(c, value); private static Dictionary AlarmStates(DebugViewPage c) => (Dictionary)typeof(DebugViewPage) .GetField("_alarmStates", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(c)!; [Fact] public void AlarmTable_RendersNativeAlarm_WithSeverityKindAndShelvedBadge() { var cut = RenderPage(); var native = new AlarmStateChanged("inst", "T01.Hi", AlarmState.Active, 800, DateTimeOffset.UtcNow) { Kind = AlarmKind.NativeOpcUa, SourceReference = "ns=2;s=Tank01.Level.HiHi", Condition = new AlarmConditionState( Active: true, Acknowledged: false, Confirmed: null, Shelve: AlarmShelveState.OneShotShelved, Suppressed: false, Severity: 800) }; cut.InvokeAsync(() => { SetField(cut.Instance, "_connected", true); SetField(cut.Instance, "_snapshot", new DebugViewSnapshot("inst", new List(), new List { native }, DateTimeOffset.UtcNow)); AlarmStates(cut.Instance)["T01.Hi"] = native; }); cut.Render(); var markup = cut.Markup; Assert.Contains("800", markup); // severity surfaced Assert.Contains("Shelved", markup); // composite condition badge Assert.Contains("OPC UA", markup); // native kind badge Assert.Contains("ns=2;s=Tank01.Level.HiHi", markup); // source reference Assert.Contains("Unacked", markup); // active + unacknowledged } }