perf(adminui): drift chip recomposes only when the config-edit watermark moves
This commit is contained in:
+3
-1
@@ -40,7 +40,9 @@ public class PageAuthorizationGuardTests
|
||||
[typeof(NodeEdit)] = AdminUiPolicies.ConfigEditor,
|
||||
[typeof(NamespaceEdit)] = AdminUiPolicies.ConfigEditor,
|
||||
[typeof(AclEdit)] = AdminUiPolicies.ConfigEditor,
|
||||
[typeof(Deployments)] = AdminUiPolicies.ConfigEditor,
|
||||
// Fully qualified for the same reason as Certificates/Hosts below: the AdminUI.Deployments
|
||||
// namespace (DeploymentDriftProbe) shadows the page's simple name from here.
|
||||
[typeof(global::ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Deployments)] = AdminUiPolicies.ConfigEditor,
|
||||
[typeof(Scripts)] = AdminUiPolicies.ConfigEditor,
|
||||
[typeof(ScriptEdit)] = AdminUiPolicies.ConfigEditor,
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.AdminUI.Deployments;
|
||||
using ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Deployments;
|
||||
|
||||
/// <summary>
|
||||
/// Pins the <see cref="DeploymentDriftProbe"/> watermark contract: the expensive
|
||||
/// <c>ConfigComposer</c> recomposition runs only when the <c>ConfigEdit</c> audit watermark (or the
|
||||
/// sealed revision hash) has moved, and it definitely still runs when it has.
|
||||
/// </summary>
|
||||
public sealed class DeploymentDriftProbeTests
|
||||
{
|
||||
private const string SealedHash = "sealed-hash-aaaa";
|
||||
|
||||
[Fact]
|
||||
public async Task An_unchanged_watermark_reuses_the_cached_verdict()
|
||||
{
|
||||
var name = $"drift-{Guid.NewGuid():N}";
|
||||
var factory = UnsTreeTestDb.Factory(name);
|
||||
var compose = new CountingCompose(SealedHash);
|
||||
var probe = new DeploymentDriftProbe(factory, compose.ComposeAsync);
|
||||
|
||||
var first = await probe.GetDriftAsync(SealedHash, CancellationToken.None);
|
||||
var second = await probe.GetDriftAsync(SealedHash, CancellationToken.None);
|
||||
|
||||
// Nothing mutated between the calls, so the second call must be served from the cache.
|
||||
compose.Count.ShouldBe(1);
|
||||
first.ShouldBe(false);
|
||||
second.ShouldBe(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_new_config_edit_recomposes()
|
||||
{
|
||||
var name = $"drift-{Guid.NewGuid():N}";
|
||||
var factory = UnsTreeTestDb.Factory(name);
|
||||
var compose = new CountingCompose(SealedHash);
|
||||
var probe = new DeploymentDriftProbe(factory, compose.ComposeAsync);
|
||||
|
||||
var before = await probe.GetDriftAsync(SealedHash, CancellationToken.None);
|
||||
before.ShouldBe(false);
|
||||
|
||||
// An edit lands: the audit row moves the watermark, and the (faked) composer now reports a
|
||||
// different revision — the probe must recompose and flip the verdict.
|
||||
await using (var db = UnsTreeTestDb.CreateNamed(name))
|
||||
{
|
||||
db.ConfigEdits.Add(new ConfigEdit
|
||||
{
|
||||
EntityType = "Equipment",
|
||||
EntityId = Guid.NewGuid(),
|
||||
FieldsJson = "{}",
|
||||
EditedBy = "test",
|
||||
SourceNode = "node-1",
|
||||
EditedAtUtc = DateTime.UtcNow,
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
compose.Result = "a-different-hash";
|
||||
var after = await probe.GetDriftAsync(SealedHash, CancellationToken.None);
|
||||
|
||||
compose.Count.ShouldBe(2);
|
||||
after.ShouldBe(true);
|
||||
}
|
||||
|
||||
/// <summary>Stand-in for the <c>ConfigComposer</c> seam that counts how often it was invoked.</summary>
|
||||
private sealed class CountingCompose(string result)
|
||||
{
|
||||
public string Result { get; set; } = result;
|
||||
|
||||
public int Count { get; private set; }
|
||||
|
||||
public Task<string> ComposeAsync(OtOpcUaConfigDbContext db)
|
||||
{
|
||||
Count++;
|
||||
return Task.FromResult(Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user