Resolve all high-severity architecture review findings
This commit is contained in:
@@ -42,6 +42,7 @@ public class ApiKeyFormAuditDrillinTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public class ApiKeysListPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ public class SiteFormAuditDrillinTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public class FlowDebuggerDrillinTests : BunitContext
|
||||
Assert.Contains("7–10 audit rows per call", markup);
|
||||
// Load, update and create paths all carry the flag.
|
||||
Assert.Contains("_traceAllCalls = _existing.TraceAllCalls;", markup);
|
||||
Assert.Contains("_existing.TraceAllCalls = _traceAllCalls;", markup);
|
||||
Assert.Contains("TraceAllCalls = _traceAllCalls,", markup);
|
||||
Assert.Contains("new UpdateApiMethodCommand(_existing.Id, _script, _timeoutSeconds, _params?.Trim(), _returns?.Trim(), _traceAllCalls)", markup);
|
||||
Assert.Contains("new CreateApiMethodCommand(methodName, _script, _timeoutSeconds, _params?.Trim(), _returns?.Trim(), _traceAllCalls)", markup);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ public class AuthEndpointsCsrfTests
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
builder.Services.AddRouting();
|
||||
builder.Services.AddAntiforgery();
|
||||
builder.Services.AddSingleton<ZB.MOM.WW.ScadaBridge.Security.SessionActivityTracker>();
|
||||
builder.Services.AddSingleton(TimeProvider.System);
|
||||
// Dispose the host: an undisposed WebApplication leaks its config
|
||||
// PhysicalFileProvider (appsettings reload-watch FileSystemWatcher — a
|
||||
// process-wide macOS run-loop thread) and a ConsoleLoggerProcessor
|
||||
|
||||
@@ -23,6 +23,8 @@ public class AuthPingEndpointTests
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
builder.Services.AddRouting();
|
||||
builder.Services.AddAntiforgery();
|
||||
builder.Services.AddSingleton<ZB.MOM.WW.ScadaBridge.Security.SessionActivityTracker>();
|
||||
builder.Services.AddSingleton(TimeProvider.System);
|
||||
// Dispose the host: an undisposed WebApplication leaks its config
|
||||
// PhysicalFileProvider (appsettings reload-watch FileSystemWatcher — a
|
||||
// process-wide macOS run-loop thread) and a ConsoleLoggerProcessor
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NSubstitute;
|
||||
using ZB.MOM.WW.Auth.Abstractions.Roles;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
|
||||
using ZB.MOM.WW.ScadaBridge.Security;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Auth;
|
||||
|
||||
public class CircuitRevocationRegressionTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ExplicitServerAutoLogin_DoesNotRemapSyntheticCookiePrincipal()
|
||||
{
|
||||
var initial = SessionClaimBuilder.Build("developer", "Developer", [],
|
||||
new RoleMappingResult(Roles.All, [], true), DateTimeOffset.UtcNow);
|
||||
var mapper = Substitute.For<IGroupRoleMapper<string>>();
|
||||
mapper.MapAsync(Arg.Any<IReadOnlyList<string>>(), Arg.Any<CancellationToken>())
|
||||
.Returns(new GroupRoleMapping<string>([], Scope: new RoleMappingResult([], [], false)));
|
||||
var services = new ServiceCollection().AddLogging();
|
||||
services.AddSingleton(mapper);
|
||||
services.AddSingleton<TimeProvider>(TimeProvider.System);
|
||||
services.AddSingleton(Options.Create(new SecurityOptions()));
|
||||
services.AddSingleton(Options.Create(new ZB.MOM.WW.ScadaBridge.Security.Auth.AuthDisableLoginOptions { DisableLogin = true }));
|
||||
services.AddScoped<CookieSessionValidator>();
|
||||
services.AddSingleton<IHttpContextAccessor>(new HttpContextAccessor { HttpContext = new DefaultHttpContext { User = initial } });
|
||||
services.AddScoped<AuthenticationStateProvider, CookieAuthenticationStateProvider>();
|
||||
using var provider = services.BuildServiceProvider();
|
||||
using var scope = provider.CreateScope();
|
||||
var auth = scope.ServiceProvider.GetRequiredService<AuthenticationStateProvider>();
|
||||
var state = await auth.GetAuthoritativeStateAsync();
|
||||
Assert.Contains(state.User.Claims, claim => claim.Type == JwtTokenService.RoleClaimType && claim.Value == Roles.Administrator);
|
||||
await mapper.DidNotReceive().MapAsync(Arg.Any<IReadOnlyList<string>>(), Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SameCircuit_RechecksMappingAndScope_AfterGrantRevocation()
|
||||
{
|
||||
var mapping = new RoleMappingResult([Roles.Deployer, Roles.Administrator], ["1"], false);
|
||||
var mapper = Substitute.For<IGroupRoleMapper<string>>();
|
||||
mapper.MapAsync(Arg.Any<IReadOnlyList<string>>(), Arg.Any<CancellationToken>())
|
||||
.Returns(_ => new GroupRoleMapping<string>(mapping.Roles, Scope: mapping));
|
||||
var initial = SessionClaimBuilder.Build("alice", "Alice", ["group"], mapping, DateTimeOffset.UtcNow);
|
||||
var services = new ServiceCollection().AddLogging();
|
||||
services.AddSingleton(mapper);
|
||||
var transaction = Substitute.For<IConfigurationCommandTransaction>();
|
||||
services.AddSingleton(transaction);
|
||||
services.AddScoped<ConfigurationCommandService>();
|
||||
services.AddSingleton<TimeProvider>(TimeProvider.System);
|
||||
services.AddSingleton(Options.Create(new SecurityOptions()));
|
||||
services.AddScoped<CookieSessionValidator>();
|
||||
services.AddSingleton<IHttpContextAccessor>(new HttpContextAccessor { HttpContext = new DefaultHttpContext { User = initial } });
|
||||
services.AddScoped<AuthenticationStateProvider, CookieAuthenticationStateProvider>();
|
||||
using var provider = services.BuildServiceProvider();
|
||||
using var scope = provider.CreateScope();
|
||||
var auth = scope.ServiceProvider.GetRequiredService<AuthenticationStateProvider>();
|
||||
var sites = new SiteScopeService(auth);
|
||||
Assert.True(await sites.IsSiteAllowedAsync(1));
|
||||
mapping = new RoleMappingResult([Roles.Viewer], ["2"], false);
|
||||
var refreshed = await auth.GetAuthenticationStateAsync();
|
||||
Assert.DoesNotContain(Roles.Deployer, refreshed.User.FindAll(JwtTokenService.RoleClaimType).Select(c => c.Value));
|
||||
Assert.False(await sites.IsSiteAllowedAsync(1));
|
||||
Assert.True(await sites.IsSiteAllowedAsync(2));
|
||||
// The already-created circuit facade must deny before starting any save transaction.
|
||||
var commands = scope.ServiceProvider.GetRequiredService<ConfigurationCommandService>();
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => commands.ExecuteAsync(new CreateRoleMappingCommand("blocked", Roles.Viewer)));
|
||||
await transaction.DidNotReceive().ExecuteAsync(Arg.Any<Func<IServiceProvider, Task<object?>>>());
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Security.Claims;
|
||||
using Akka.Actor;
|
||||
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.CentralUI.Components.Pages.Monitoring;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Messages.RemoteQuery;
|
||||
using ZB.MOM.WW.ScadaBridge.Communication;
|
||||
using ZB.MOM.WW.ScadaBridge.Security;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Auth;
|
||||
|
||||
public class ParkedMessagesMutationRegressionTests : BunitContext
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async Task NonnumericSiteIdentifier_RelaysOnlyWhileCurrentSiteGrantAllows(bool revoke)
|
||||
{
|
||||
using var system = ActorSystem.Create("parked-mutation-" + Guid.NewGuid().ToString("N"));
|
||||
var dispatched = new ConcurrentQueue<SiteEnvelope>();
|
||||
var comms = new CommunicationService(Options.Create(new CommunicationOptions()), NullLogger<CommunicationService>.Instance);
|
||||
comms.SetCommunicationActor(system.ActorOf(Props.Create(() => new ReplyActor(dispatched))));
|
||||
Services.AddSingleton(comms);
|
||||
var sites = Substitute.For<ISiteRepository>();
|
||||
sites.GetAllSitesAsync(Arg.Any<CancellationToken>()).Returns(new List<Site> { new("Plant A", "plant-a") { Id = 42 } });
|
||||
Services.AddSingleton(sites);
|
||||
var auth = new MutableAuth("42");
|
||||
Services.AddSingleton<AuthenticationStateProvider>(auth);
|
||||
Services.AddScoped<SiteScopeService>();
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddLogging();
|
||||
Services.AddSingleton(Substitute.For<IDialogService>());
|
||||
JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var page = Render<ParkedMessages>();
|
||||
await page.Find("select").ChangeAsync(new() { Value = "plant-a" });
|
||||
page.WaitForAssertion(() => Assert.Contains("ERP", page.Markup));
|
||||
await page.FindAll("tr").Single(row => row.TextContent.Contains("ERP")).ClickAsync(new());
|
||||
page.WaitForAssertion(() => Assert.Contains("Retry", page.Markup));
|
||||
if (revoke) auth.SiteId = "99";
|
||||
// The same rendered page still contains the old site and row. The click must
|
||||
// resolve SiteIdentifier through Site.Id and consult the live grant.
|
||||
await page.FindAll("button").Single(button => button.TextContent.Trim() == "Retry").ClickAsync(new());
|
||||
|
||||
if (revoke)
|
||||
{
|
||||
Assert.Empty(dispatched);
|
||||
Assert.Contains("Site access was revoked", page.Markup);
|
||||
}
|
||||
else
|
||||
{
|
||||
var envelope = Assert.Single(dispatched);
|
||||
Assert.Equal("plant-a", envelope.SiteId);
|
||||
var retry = Assert.IsType<ParkedMessageRetryRequest>(envelope.Message);
|
||||
Assert.Equal("parked-message-1", retry.MessageId);
|
||||
Assert.Equal("plant-a", retry.SiteId);
|
||||
}
|
||||
await system.Terminate();
|
||||
}
|
||||
|
||||
private sealed class MutableAuth(string siteId) : AuthenticationStateProvider
|
||||
{
|
||||
public string SiteId { get; set; } = siteId;
|
||||
public override Task<AuthenticationState> GetAuthenticationStateAsync() => Task.FromResult(new AuthenticationState(
|
||||
new ClaimsPrincipal(new ClaimsIdentity(new[] {
|
||||
new Claim(JwtTokenService.UsernameClaimType, "operator"),
|
||||
new Claim(JwtTokenService.RoleClaimType, Roles.Deployer),
|
||||
new Claim(JwtTokenService.SiteIdClaimType, SiteId)
|
||||
}, "test"))));
|
||||
}
|
||||
|
||||
private sealed class ReplyActor : ReceiveActor
|
||||
{
|
||||
public ReplyActor(ConcurrentQueue<SiteEnvelope> dispatched)
|
||||
{
|
||||
Receive<SiteEnvelope>(envelope =>
|
||||
{
|
||||
if (envelope.Message is ParkedMessageQueryRequest query)
|
||||
Sender.Tell(new ParkedMessageQueryResponse(query.CorrelationId, envelope.SiteId,
|
||||
new[] { new ParkedMessageEntry("parked-message-1", "ERP", "Send", "Offline", 2, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow) },
|
||||
1, 1, 25, true, null, DateTimeOffset.UtcNow));
|
||||
else if (envelope.Message is ParkedMessageRetryRequest retry)
|
||||
{
|
||||
dispatched.Enqueue(envelope);
|
||||
Sender.Tell(new ParkedMessageRetryResponse(retry.CorrelationId, true));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using Akka.Actor;
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NSubstitute;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Admin;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
|
||||
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.Messages.Management;
|
||||
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase;
|
||||
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Repositories;
|
||||
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Services;
|
||||
using ZB.MOM.WW.ScadaBridge.ManagementService;
|
||||
using ZB.MOM.WW.ScadaBridge.Security;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Auth;
|
||||
|
||||
public class UiAuditRegressionTests : BunitContext
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async Task LdapCreateFromUi_PersistsAuditWithGeneratedMappingId_OrRollsBack(bool failAudit)
|
||||
{
|
||||
using var connection = new SqliteConnection("Data Source=:memory:");
|
||||
connection.Open();
|
||||
var options = new DbContextOptionsBuilder<ScadaBridgeDbContext>().UseSqlite(connection).Options;
|
||||
Services.AddScoped(_ => new ScadaBridgeDbContext(options));
|
||||
Services.AddScoped<ISecurityRepository, SecurityRepository>();
|
||||
Services.AddScoped<IConfigurationReadSession, ConfigurationReadSession>();
|
||||
Services.AddSingleton(Substitute.For<ISiteRepository>());
|
||||
Services.AddScoped<IAuditCorrelationContext, AuditCorrelationContext>();
|
||||
if (failAudit)
|
||||
{
|
||||
var failing = Substitute.For<IAuditService>();
|
||||
failing.LogAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<object?>(), Arg.Any<CancellationToken>()).Returns(Task.FromException(new InvalidOperationException("audit unavailable")));
|
||||
Services.AddSingleton(failing);
|
||||
}
|
||||
else Services.AddScoped<IAuditService, AuditService>();
|
||||
Services.AddSingleton(Substitute.For<IDialogService>());
|
||||
var user = SessionClaimBuilder.Build("administrator", "Administrator", ["admins"],
|
||||
new RoleMappingResult([Roles.Administrator], [], true), DateTimeOffset.UtcNow);
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new StaticAuth(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddScoped<ZB.MOM.WW.ScadaBridge.CentralUI.Services.ConfigurationCommandService>();
|
||||
Services.AddSingleton<IConfigurationCommandTransaction, ConfigurationCommandTransaction>();
|
||||
Services.AddScoped<ConfigurationCommitActions>();
|
||||
Services.AddScoped<IConfigurationCommitActions>(sp => sp.GetRequiredService<ConfigurationCommitActions>());
|
||||
Services.AddLogging();
|
||||
using var schema = new ScadaBridgeDbContext(options);
|
||||
await schema.Database.EnsureCreatedAsync();
|
||||
var page = Render<LdapMappingForm>();
|
||||
page.Find("input").Change("NewDirectoryGroup");
|
||||
page.Find("select").Change(Roles.Viewer);
|
||||
page.FindAll("button").Single(b => b.TextContent.Contains("Save")).Click();
|
||||
if (failAudit)
|
||||
{
|
||||
page.WaitForAssertion(() => Assert.Contains("audit unavailable", page.Markup));
|
||||
Assert.Empty(await schema.LdapGroupMappings.Where(x => x.LdapGroupName == "NewDirectoryGroup").ToListAsync());
|
||||
Assert.Empty(await schema.AuditLogEntries.ToListAsync());
|
||||
return;
|
||||
}
|
||||
page.WaitForAssertion(() => Assert.Single(schema.LdapGroupMappings.AsNoTracking().Where(x => x.LdapGroupName == "NewDirectoryGroup").ToList()));
|
||||
var mapping = await schema.LdapGroupMappings.AsNoTracking().SingleAsync(x => x.LdapGroupName == "NewDirectoryGroup");
|
||||
var audit = Assert.Single(await schema.AuditLogEntries.AsNoTracking().ToListAsync());
|
||||
Assert.Equal(mapping.Id.ToString(), audit.EntityId);
|
||||
Assert.Equal("Create", audit.Action);
|
||||
// Existing edit form uses the same durable boundary; generated IDs are retained.
|
||||
page.Dispose();
|
||||
var edit = Render<LdapMappingForm>(parameters => parameters.Add(p => p.Id, mapping.Id));
|
||||
edit.Find("input").Change("RenamedDirectoryGroup");
|
||||
edit.FindAll("button").Single(b => b.TextContent.Contains("Save")).Click();
|
||||
edit.WaitForAssertion(() => Assert.Equal("RenamedDirectoryGroup", schema.LdapGroupMappings.AsNoTracking().Single(x => x.Id == mapping.Id).LdapGroupName));
|
||||
edit.Dispose();
|
||||
var list = Render<LdapMappings>();
|
||||
list.WaitForAssertion(() => Assert.Contains("RenamedDirectoryGroup", list.Markup));
|
||||
var row = list.FindAll("tr").Single(r => r.TextContent.Contains("RenamedDirectoryGroup"));
|
||||
await row.QuerySelector("button.dropdown-item")!.ClickAsync(new());
|
||||
list.WaitForAssertion(() => Assert.DoesNotContain("RenamedDirectoryGroup", list.Markup));
|
||||
Assert.False(await schema.LdapGroupMappings.AnyAsync(x => x.Id == mapping.Id));
|
||||
var auditRows = await schema.AuditLogEntries.AsNoTracking().OrderBy(x => x.Id).ToListAsync();
|
||||
Assert.Equal(new[] { "Create", "Update", "Delete" }, auditRows.Select(x => x.Action));
|
||||
Assert.All(auditRows, row => Assert.Equal(mapping.Id.ToString(), row.EntityId));
|
||||
}
|
||||
|
||||
private sealed class StaticAuth(System.Security.Claims.ClaimsPrincipal principal) : AuthenticationStateProvider
|
||||
{
|
||||
public override Task<AuthenticationState> GetAuthenticationStateAsync() => Task.FromResult(new AuthenticationState(principal));
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,7 @@ public class ConnectionCertificatesTests : BunitContext
|
||||
Services.AddSingleton<AuthenticationStateProvider>(
|
||||
new TestAuthStateProvider(BuildPrincipal(roles)));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
Services.AddSingleton<IAuthorizationService, DefaultAuthorizationService>();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public class OpcUaEndpointVerifyTests : BunitContext
|
||||
Services.AddSingleton<AuthenticationStateProvider>(
|
||||
new TestAuthStateProvider(BuildPrincipal(roles)));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
Services.AddSingleton<IAuthorizationService, DefaultAuthorizationService>();
|
||||
_authConfigured = true;
|
||||
|
||||
@@ -78,6 +78,7 @@ public class SecuredWritesTests : BunitContext
|
||||
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
// BunitContext pre-registers a placeholder IAuthorizationService that throws on
|
||||
// policy evaluation; force the real one so the regions are actually gated.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using NSubstitute;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
internal static class ConfigurationMutationTestExtensions
|
||||
{
|
||||
public static IServiceCollection AddTestConfigurationMutations(this IServiceCollection services)
|
||||
{
|
||||
services.TryAddScoped<ConfigurationCommandService>();
|
||||
services.TryAddSingleton<IAuditService>(_ => Substitute.For<IAuditService>());
|
||||
services.TryAddSingleton<IConfigurationCommandTransaction, RepositoryTransaction>();
|
||||
return services;
|
||||
}
|
||||
private sealed class RepositoryTransaction(IServiceScopeFactory scopes) : IConfigurationCommandTransaction
|
||||
{
|
||||
public async Task<object?> ExecuteAsync(Func<IServiceProvider, Task<object?>> operation)
|
||||
{
|
||||
using var scope = scopes.CreateScope();
|
||||
return await operation(scope.ServiceProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,11 +36,12 @@ public class DataConnectionFormTests : BunitContext
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(JwtTokenService.UsernameClaimType, "tester"),
|
||||
new Claim(JwtTokenService.RoleClaimType, "Administrator")
|
||||
new Claim(JwtTokenService.RoleClaimType, "Designer")
|
||||
};
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private IRenderedComponent<DataConnectionForm> RenderForCreateSite(int siteId)
|
||||
|
||||
@@ -64,6 +64,7 @@ public class DataConnectionsPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private void SeedRepos(
|
||||
|
||||
@@ -104,6 +104,7 @@ public class FlowDebuggerPageTests : BunitContext
|
||||
Services.AddSingleton<AuthenticationStateProvider>(stubAuth);
|
||||
Services.AddScoped(_ => new SiteScopeService(stubAuth));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private sealed class StubAuthStateProvider : AuthenticationStateProvider
|
||||
|
||||
+1
@@ -61,6 +61,7 @@ public class InstanceConfigureAuditDrillinTests : BunitContext
|
||||
Services.AddSingleton<AuthenticationStateProvider>(authProvider);
|
||||
Services.AddSingleton(new SiteScopeService(authProvider));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -52,6 +52,7 @@ public class InstanceConfigureBindingBehaviourTests : BunitContext
|
||||
Services.AddSingleton<AuthenticationStateProvider>(authProvider);
|
||||
Services.AddSingleton(new SiteScopeService(authProvider));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ public class InstanceConfigureListOverrideTests
|
||||
Assert.Contains("ClearListOverride", markup);
|
||||
// Repository-direct delete (the page only edits InstanceConfigure; no new
|
||||
// server method) — same pattern as native-alarm-source overrides.
|
||||
Assert.Contains("DeleteInstanceAttributeOverrideAsync", markup);
|
||||
Assert.Contains("DeleteInstanceAttributeOverrideCommand", markup);
|
||||
Assert.Contains("HasOverrideRow", markup);
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -49,11 +49,11 @@ public class InstanceConfigureNativeAlarmTests
|
||||
{
|
||||
var markup = InstanceConfigureMarkup;
|
||||
Assert.Contains("GetNativeAlarmSourceOverridesByInstanceIdAsync", markup);
|
||||
Assert.Contains("GetNativeAlarmSourceOverrideAsync", markup);
|
||||
Assert.Contains("AddInstanceNativeAlarmSourceOverrideAsync", markup);
|
||||
Assert.Contains("UpdateInstanceNativeAlarmSourceOverrideAsync", markup);
|
||||
Assert.Contains("DeleteInstanceNativeAlarmSourceOverrideAsync", markup);
|
||||
Assert.Contains("SaveChangesAsync", markup);
|
||||
Assert.Contains("ExecuteConfigurationAsync", markup);
|
||||
Assert.Contains("SetInstanceNativeAlarmSourceOverrideCommand", markup);
|
||||
Assert.Contains("SetInstanceNativeAlarmSourceOverrideCommand", markup);
|
||||
Assert.Contains("DeleteInstanceNativeAlarmSourceOverrideCommand", markup);
|
||||
Assert.DoesNotContain("TemplateEngineRepository.SaveChangesAsync", markup);
|
||||
Assert.Contains("SaveNativeOverride", markup);
|
||||
Assert.Contains("ClearNativeOverride", markup);
|
||||
}
|
||||
|
||||
+1
@@ -83,6 +83,7 @@ public class InstanceCreateTemplateFieldTests : BunitContext
|
||||
Services.AddSingleton<AuthenticationStateProvider>(
|
||||
new TestAuthStateProvider(new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"))));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
|
||||
JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
}
|
||||
|
||||
+1
@@ -83,6 +83,7 @@ public class TopologyDisableConfirmLabelTests : BunitContext
|
||||
Services.AddSingleton<AuthenticationStateProvider>(
|
||||
new TestAuthStateProvider(new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"))));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
|
||||
JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
}
|
||||
|
||||
+1
@@ -34,6 +34,7 @@ public class ExternalSystemFormAuditDrillinTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -39,6 +39,7 @@ public class ExternalSystemsConnectionStringHidingTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ public class SchemaLibraryPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private void SeedSchemas(params SharedSchema[] schemas) =>
|
||||
|
||||
@@ -76,6 +76,7 @@ public class ApiMethodFormSandboxDisposalTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
|
||||
return (Render<ApiMethodFormPage>(), sandbox);
|
||||
}
|
||||
@@ -179,6 +180,7 @@ public class SharedScriptFormSandboxDisposalTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
|
||||
return (Render<SharedScriptFormPage>(), sandbox);
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ public class TemplateEditInheritedSetTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+4
-4
@@ -87,11 +87,11 @@ public class TemplateNativeAlarmSourceEditorTests
|
||||
public void NativeAlarmsCrud_WiresRepositoryAddUpdateDeleteWithSave()
|
||||
{
|
||||
var markup = TemplateEditMarkup;
|
||||
Assert.Contains("AddTemplateNativeAlarmSourceAsync", markup);
|
||||
Assert.Contains("UpdateTemplateNativeAlarmSourceAsync", markup);
|
||||
Assert.Contains("DeleteTemplateNativeAlarmSourceAsync", markup);
|
||||
Assert.Contains("AddTemplateNativeAlarmSourceCommand", markup);
|
||||
Assert.Contains("UpdateTemplateNativeAlarmSourceCommand", markup);
|
||||
Assert.Contains("DeleteTemplateNativeAlarmSourceCommand", markup);
|
||||
Assert.Contains("GetNativeAlarmSourcesByTemplateIdAsync", markup);
|
||||
Assert.Contains("SaveChangesAsync", markup);
|
||||
Assert.Contains("ExecuteConfigurationAsync", markup);
|
||||
// Add/edit/delete handlers are wired to the UI.
|
||||
Assert.Contains("OpenAddNativeSourceDialog", markup);
|
||||
Assert.Contains("OpenEditNativeSourceDialog", markup);
|
||||
|
||||
@@ -57,6 +57,7 @@ public class NavMenuTests : BunitContext
|
||||
{
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
// BunitContext pre-registers a placeholder IAuthorizationService that
|
||||
// throws when AuthorizeView evaluates a policy. Force the real service
|
||||
|
||||
@@ -77,6 +77,7 @@ public class AlarmSummaryRenderTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
// Renders the page and drives the site picker so the alarm table appears.
|
||||
|
||||
@@ -59,6 +59,7 @@ public class AlarmSummaryVirtualizeTests : BunitContext
|
||||
Services.AddSingleton<AuthenticationStateProvider>(
|
||||
new TestAuthStateProvider(new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"))));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private IRenderedComponent<AlarmSummaryPage> RenderWithSiteSelected()
|
||||
|
||||
@@ -40,6 +40,7 @@ public class FailoverConfirmLabelTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
// BunitContext pre-registers a placeholder IAuthorizationService that throws when
|
||||
// AuthorizeView evaluates a policy; force the real one so the admin gate is real.
|
||||
|
||||
@@ -34,6 +34,7 @@ public class HealthFailoverButtonTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
// BunitContext pre-registers a placeholder IAuthorizationService that throws when
|
||||
// AuthorizeView evaluates a policy. Force the real service so the admin gate is
|
||||
|
||||
@@ -81,6 +81,7 @@ public class AuditLogPagePermissionTests : BunitContext
|
||||
var user = BuildPrincipal(roles);
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
Services.AddSingleton<IAuthorizationService, DefaultAuthorizationService>();
|
||||
WireUpPageDependencies();
|
||||
|
||||
@@ -55,6 +55,7 @@ public class AuditLogPageScaffoldTests : BunitContext
|
||||
var user = BuildPrincipal(roles);
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
Services.AddSingleton<IAuthorizationService, DefaultAuthorizationService>();
|
||||
// The page now hosts AuditFilterBar + AuditResultsGrid which depend on
|
||||
@@ -92,6 +93,7 @@ public class AuditLogPageScaffoldTests : BunitContext
|
||||
var user = BuildPrincipal(roles);
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
Services.AddSingleton<IAuthorizationService, DefaultAuthorizationService>();
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ public class AuditLogPageTrendTests : BunitContext
|
||||
var user = BuildPrincipal("Administrator");
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
Services.AddSingleton<IAuthorizationService, DefaultAuthorizationService>();
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ public class TransportExportPageTests : BunitContext
|
||||
var principal = BuildPrincipal("alice", "Designer");
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(principal));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private static ClaimsPrincipal BuildPrincipal(string username, params string[] roles)
|
||||
|
||||
@@ -87,6 +87,7 @@ public class TransportImportPageTests : BunitContext
|
||||
var principal = BuildPrincipal("alice", "Administrator");
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(principal));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private static ClaimsPrincipal BuildPrincipal(string username, params string[] roles)
|
||||
|
||||
+1
@@ -146,6 +146,7 @@ public class TransportImportSessionDisposeRaceTests : BunitContext
|
||||
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(principal));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private static void SetField(object obj, string name, object? value) =>
|
||||
|
||||
@@ -37,6 +37,7 @@ public class ExecutionTreePageTests : BunitContext
|
||||
var user = BuildPrincipal(roles);
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
Services.AddSingleton<IAuthorizationService, DefaultAuthorizationService>();
|
||||
Services.AddSingleton(_queryService);
|
||||
|
||||
@@ -117,6 +117,7 @@ public class HealthPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
// The failover control's AuthorizeView evaluates the named RequireAdmin policy, so the
|
||||
// real policy set has to be registered or the page throws on any card that renders it.
|
||||
AuthorizationPolicies.AddScadaBridgeAuthorization(Services);
|
||||
|
||||
@@ -101,6 +101,7 @@ public class NotificationKpisPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -43,6 +43,7 @@ public class NotificationListFormTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -29,6 +29,7 @@ public class NotificationListsPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
+1
@@ -94,6 +94,7 @@ public class NotificationReportDetailModalTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
Services.AddScoped<ZB.MOM.WW.ScadaBridge.CentralUI.Auth.SiteScopeService>();
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ public class NotificationReportPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
// CentralUI-028: the page now injects SiteScopeService — the test user
|
||||
// has no SiteId claims, so this resolves to system-wide and the
|
||||
// pre-existing test expectations hold.
|
||||
|
||||
@@ -253,6 +253,7 @@ public sealed class QueryStringDrillInTests
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
Services.AddScoped<ZB.MOM.WW.ScadaBridge.CentralUI.Auth.SiteScopeService>();
|
||||
}
|
||||
|
||||
@@ -314,6 +315,7 @@ public sealed class QueryStringDrillInTests
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
Services.AddScoped<ZB.MOM.WW.ScadaBridge.CentralUI.Auth.SiteScopeService>();
|
||||
}
|
||||
|
||||
@@ -400,6 +402,7 @@ public sealed class QueryStringDrillInTests
|
||||
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(principal));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@ public class SiteCallsReportPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
Services.AddScoped<ZB.MOM.WW.ScadaBridge.CentralUI.Auth.SiteScopeService>();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ public class SmsConfigurationPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private const string SecretToken = "super-secret-auth-token";
|
||||
|
||||
@@ -26,6 +26,7 @@ public class SmtpConfigurationPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private static SmtpConfiguration Sample() =>
|
||||
@@ -61,6 +62,8 @@ public class SmtpConfigurationPageTests : BunitContext
|
||||
{
|
||||
repo.GetAllSmtpConfigurationsAsync()
|
||||
.Returns(Task.FromResult<IReadOnlyList<SmtpConfiguration>>(configs.ToList()));
|
||||
foreach (var config in configs)
|
||||
repo.GetSmtpConfigurationByIdAsync(config.Id).Returns(config);
|
||||
Services.AddSingleton(repo);
|
||||
WireAuth();
|
||||
|
||||
@@ -138,6 +141,7 @@ public class SmtpConfigurationPageTests : BunitContext
|
||||
repo.GetAllSmtpConfigurationsAsync()
|
||||
.Returns(Task.FromResult<IReadOnlyList<SmtpConfiguration>>(
|
||||
new List<SmtpConfiguration> { config }));
|
||||
repo.GetSmtpConfigurationByIdAsync(config.Id).Returns(config);
|
||||
Services.AddSingleton(repo);
|
||||
WireAuth();
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ public class TemplatesPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(identity);
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
+1
@@ -102,6 +102,7 @@ public class TopologyLiveUpdatesTimerDisposeRaceTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private void SeedEmptyRepos()
|
||||
|
||||
@@ -110,6 +110,7 @@ public class TopologyPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddTestConfigurationMutations();
|
||||
}
|
||||
|
||||
private void SeedRepos(
|
||||
@@ -136,6 +137,45 @@ public class TopologyPageTests : BunitContext
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async Task UI13_ExistingTopologyMoveUsesCurrentSiteScope(bool revoked)
|
||||
{
|
||||
var instance = new Instance("Pump") { Id = 100, SiteId = 1, AreaId = 10 };
|
||||
SeedRepos(sites: [new Site("Plant", "plant") { Id = 1 }], instances: [instance]);
|
||||
_repo.GetInstanceByIdAsync(100, Arg.Any<CancellationToken>()).Returns(instance);
|
||||
var cut = Render<TopologyPage>();
|
||||
var auth = Services.GetRequiredService<AuthenticationStateProvider>();
|
||||
var identity = (ClaimsIdentity)(await auth.GetAuthenticationStateAsync()).User.Identity!;
|
||||
if (revoked) identity.AddClaim(new Claim(JwtTokenService.SiteIdClaimType, "2"));
|
||||
|
||||
var handler = typeof(TopologyPage).GetMethod("SubmitMoveInstance",
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!;
|
||||
await cut.InvokeAsync(() => (Task)handler.Invoke(cut.Instance, [(100, (int?)null)])!);
|
||||
|
||||
await _repo.Received(revoked ? 0 : 1).UpdateInstanceAsync(Arg.Any<Instance>(), Arg.Any<CancellationToken>());
|
||||
Assert.Equal(revoked ? 10 : (int?)null, instance.AreaId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async Task UI13_ExistingTopologyCreateUsesCurrentSiteScope(bool revoked)
|
||||
{
|
||||
SeedRepos(sites: [new Site("Plant", "plant") { Id = 1 }]);
|
||||
var cut = Render<TopologyPage>();
|
||||
var auth = Services.GetRequiredService<AuthenticationStateProvider>();
|
||||
var identity = (ClaimsIdentity)(await auth.GetAuthenticationStateAsync()).User.Identity!;
|
||||
if (revoked) identity.AddClaim(new Claim(JwtTokenService.SiteIdClaimType, "2"));
|
||||
|
||||
var handler = typeof(TopologyPage).GetMethod("SubmitCreateArea",
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!;
|
||||
await cut.InvokeAsync(() => (Task)handler.Invoke(cut.Instance, [(1, (int?)null, "New area")])!);
|
||||
|
||||
await _repo.Received(revoked ? 0 : 1).AddAreaAsync(Arg.Any<Area>(), Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Renders_EmptyState_WhenNoSites()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user