From 1790f67ebdea0f553b79e000d6e0a982b60ff967 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 21 Aug 2026 07:45:46 -0400 Subject: [PATCH] test(centralui): pin Topology Disable/Delete confirm verbs (register row 28) Claude-Session: https://claude.ai/code/session_01G2GHPnbbKGvyyfbr2XBRVH --- .../TopologyDisableConfirmLabelTests.cs | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/TopologyDisableConfirmLabelTests.cs diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/TopologyDisableConfirmLabelTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/TopologyDisableConfirmLabelTests.cs new file mode 100644 index 00000000..6590cf9f --- /dev/null +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/TopologyDisableConfirmLabelTests.cs @@ -0,0 +1,179 @@ +using System.Security.Claims; +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Rendering; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using NSubstitute; +using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared; +using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances; +using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites; +using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates; +using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories; +using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services; +using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Transport; +using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums; +using ZB.MOM.WW.ScadaBridge.Communication; +using ZB.MOM.WW.ScadaBridge.DeploymentManager; +using ZB.MOM.WW.ScadaBridge.Security; +using ZB.MOM.WW.ScadaBridge.TemplateEngine.Services; +using TopologyPage = ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Deployment.Topology; + +namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Deployment; + +/// +/// Pins the verbs on the Topology instance-action confirmations (deferred-work +/// register row 28): Disable must read "Disable", not the red button's +/// historical "Delete" default, while a genuine Delete must still read "Delete". +/// Like FailoverConfirmLabelTests these run the real +/// behind a real DialogHost, so they pin what the user reads rather than +/// the argument a substitute received. +/// +public class TopologyDisableConfirmLabelTests : BunitContext +{ + private readonly ITemplateEngineRepository _repo = Substitute.For(); + private readonly ISiteRepository _siteRepo = Substitute.For(); + private readonly IDeploymentManagerRepository _deployRepo = Substitute.For(); + private readonly IFlatteningPipeline _pipeline = Substitute.For(); + private readonly IAuditService _audit = Substitute.For(); + private readonly DialogService _dialog = new(); + + public TopologyDisableConfirmLabelTests() + { + Services.AddSingleton(_repo); + Services.AddSingleton(_siteRepo); + Services.AddSingleton(_deployRepo); + Services.AddSingleton(_pipeline); + Services.AddSingleton(_audit); + + // DeploymentService has non-mockable concrete deps; instantiate them directly + // (the same DI block TopologyPageTests uses to render this page). + Services.AddSingleton(new CommunicationService( + Options.Create(new CommunicationOptions()), + NullLogger.Instance)); + Services.AddSingleton(new OperationLockManager()); + Services.AddSingleton(Options.Create(new DeploymentManagerOptions + { + OperationLockTimeout = TimeSpan.FromSeconds(5) + })); + Services.AddSingleton(Options.Create(new CommunicationOptions())); + Services.AddSingleton>(NullLogger.Instance); + Services.AddScoped(); + Services.AddScoped(); + Services.AddSingleton( + new DeploymentStatusNotifier(NullLogger.Instance)); + Services.AddSingleton(Substitute.For()); + Services.AddScoped(); + Services.AddScoped(); + Services.AddScoped(); + Services.AddScoped(); + + // The concrete service registered as the interface, so the page and the + // host share one instance (DialogHost injects IDialogService and casts). + Services.AddSingleton(_dialog); + + var claims = new[] + { + new Claim(JwtTokenService.UsernameClaimType, "tester"), + new Claim(JwtTokenService.RoleClaimType, Roles.Deployer) + }; + Services.AddSingleton( + new TestAuthStateProvider(new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth")))); + Services.AddAuthorizationCore(); + + JSInterop.Mode = JSRuntimeMode.Loose; + } + + /// One site with one enabled instance, so both Disable and Delete are offered. + private void SeedOneEnabledInstance() + { + _siteRepo.GetAllSitesAsync(Arg.Any()) + .Returns(Task.FromResult>( + new List { new("Plant-A", "plant-a") { Id = 1 } })); + _repo.GetAllTemplatesAsync(Arg.Any()) + .Returns(Task.FromResult>(new List