84 lines
3.0 KiB
C#
84 lines
3.0 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|