perf(adminui): one guard round-trip per bulk reference add

This commit is contained in:
Joseph Doherty
2026-08-16 04:03:49 -04:00
parent 541a223b96
commit fa36d64e8f
5 changed files with 251 additions and 28 deletions
@@ -190,6 +190,59 @@ public sealed class EffectiveNameGuardTests
result.ShouldBeNull();
}
/// <summary>
/// The batch check is a pure round-trip saving: for every proposed name it must return exactly
/// what <see cref="EffectiveNameGuard.CheckAsync"/> returns for the FIRST colliding name in input
/// order — including the source-specific message text — across all three source kinds.
/// </summary>
[Theory]
// First collision is the no-override reference ("speed"), even though an alarm name follows it.
[InlineData(new[] { "free-a", "speed", "highpress", "temperature" }, "speed")]
// First collision is the OVERRIDE-derived reference effective name.
[InlineData(new[] { "free-a", "temperature", "speed" }, "temperature")]
// First collision is the VirtualTag.
[InlineData(new[] { "free-a", "computed", "speed" }, "computed")]
// First collision is the ScriptedAlarm.
[InlineData(new[] { "free-a", "highpress", "speed" }, "highpress")]
public async Task CheckMany_matches_CheckAsync_verdicts(string[] names, string expectedCollidingName)
{
var factory = SeedFactory();
var guard = Guard(factory);
var expected = await guard.CheckAsync(
EquipmentId, expectedCollidingName, EffectiveNameSourceKind.Reference, excludeSourceId: null);
expected.ShouldNotBeNull();
var actual = await guard.CheckManyAsync(EquipmentId, names, EffectiveNameSourceKind.Reference);
actual.ShouldBe(expected);
}
/// <summary>A batch where every proposed name is free reports no collision.</summary>
[Fact]
public async Task CheckMany_all_free_returns_null()
{
var guard = Guard(SeedFactory());
var result = await guard.CheckManyAsync(
EquipmentId,
["free-a", "free-b", "Speed" /* ordinal: capital S does not collide */, "free-c"],
EffectiveNameSourceKind.Reference);
result.ShouldBeNull();
}
/// <summary>An empty batch is trivially free (and must not throw).</summary>
[Fact]
public async Task CheckMany_empty_batch_returns_null()
{
var guard = Guard(SeedFactory());
var result = await guard.CheckManyAsync(EquipmentId, [], EffectiveNameSourceKind.Reference);
result.ShouldBeNull();
}
private sealed class NamedFactory(string name) : IDbContextFactory<OtOpcUaConfigDbContext>
{
public OtOpcUaConfigDbContext CreateDbContext() =>