using System.Security.Claims; using Bunit; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Rendering; using Microsoft.Extensions.DependencyInjection; using ScadaLink.Security; using NavMenu = ScadaLink.CentralUI.Components.Layout.NavMenu; namespace ScadaLink.CentralUI.Tests.Layout; /// /// bUnit rendering tests for the sidebar . They verify the /// new Notifications section: its items are gated per-policy, and the old /// /admin/smtp and /monitoring/notification-outbox routes are gone. /// The AuthorizeView Policy=... blocks evaluate the real policies, which /// require a claim of type ("Role"), /// so the test principal carries claims of that exact type. /// public class NavMenuTests : BunitContext { /// /// Renders under a principal holding the given roles. /// 's top-level AuthorizeView requires the /// cascading , so it is rendered inside a /// ; the real policies are /// registered so the per-item AuthorizeView Policy=... blocks are /// genuinely evaluated. /// private IRenderedComponent RenderWithRoles(params string[] roles) { var claims = new List { new("Username", "tester") }; claims.AddRange(roles.Select(r => new Claim(JwtTokenService.RoleClaimType, r))); var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth")); Services.AddSingleton(new TestAuthStateProvider(user)); Services.AddAuthorizationCore(); AuthorizationPolicies.AddScadaLinkAuthorization(Services); // BunitContext pre-registers a placeholder IAuthorizationService that // throws when AuthorizeView evaluates a policy. Force the real service // so the per-item policy gating is genuinely exercised. Services.AddSingleton(); var host = Render(parameters => parameters .Add(p => p.ChildContent, (RenderFragment)(builder => { builder.OpenComponent(0); builder.CloseComponent(); }))); return host.FindComponent(); } [Fact] public void NotificationsSection_ShowsAllItems_ForMultiRoleUser() { var cut = RenderWithRoles("Admin", "Design", "Deployment"); cut.WaitForAssertion(() => { Assert.Contains("Notifications", cut.Markup); Assert.Contains("/notifications/smtp", cut.Markup); Assert.Contains("/notifications/lists", cut.Markup); Assert.Contains("/notifications/report", cut.Markup); Assert.Contains("/notifications/kpis", cut.Markup); }); } [Fact] public void NotificationsSection_AdminOnlyUser_SeesOnlySmtp() { var cut = RenderWithRoles("Admin"); cut.WaitForAssertion(() => { Assert.Contains("/notifications/smtp", cut.Markup); Assert.DoesNotContain("/notifications/report", cut.Markup); Assert.DoesNotContain("/notifications/lists", cut.Markup); Assert.DoesNotContain("/notifications/kpis", cut.Markup); }); } [Fact] public void OldRoutes_AreNoLongerLinked() { var cut = RenderWithRoles("Admin", "Design", "Deployment"); cut.WaitForAssertion(() => { Assert.DoesNotContain("/admin/smtp", cut.Markup); Assert.DoesNotContain("/monitoring/notification-outbox", cut.Markup); }); } }