fix(site-runtime): batch native-alarm deletes into the coalesced flush transaction; docs for scripts/cache/scheduler (review 2026-10-21 01/SR-P2)

This commit is contained in:
Joseph Doherty
2026-10-22 08:33:30 -04:00
parent 010d2ea438
commit 37c25db2f1
5 changed files with 148 additions and 18 deletions
@@ -240,6 +240,32 @@ public class NativeAlarmActorTests : TestKit, IDisposable
Assert.Contains(rows, r => r.ConditionJson.Contains("49"));
}
/// <summary>A snapshot swap that drops several conditions deletes their rows via the batched flush, and a delete cancels a pending upsert (review 2026-10-21 01/SR-P2).</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task SnapshotSwap_DroppedConditions_AreDeletedInTheBatchedFlush()
{
var instance = CreateTestProbe();
var dcl = CreateTestProbe();
var actor = SpawnWithFlush(instance.Ref, dcl.Ref, TimeSpan.FromMilliseconds(100));
dcl.ExpectMsg<SubscribeAlarmsRequest>();
var t0 = DateTimeOffset.UtcNow;
for (var i = 0; i < 4; i++)
DeliverLiveTransition(actor, $"ref-{i}", severity: 800, t0.AddMilliseconds(i));
await Task.Delay(400);
Assert.Equal(4, (await _storage.GetNativeAlarmsAsync("inst", Source().CanonicalName)).Count);
// Empty snapshot: every condition is dropped.
actor.Tell(new NativeAlarmTransitionUpdate("Opc", Transition(
"ref-0", AlarmTransitionKind.SnapshotComplete,
new AlarmConditionState(true, false, null, AlarmShelveState.Unshelved, false, 800))));
await Task.Delay(500);
var rows = await _storage.GetNativeAlarmsAsync("inst", Source().CanonicalName);
Assert.Empty(rows);
}
// ── Rehydration restores display metadata ────────
/// <summary>Verifies that rehydration restores the display metadata.</summary>
@@ -95,6 +95,23 @@ public class NativeAlarmStateStoreTests : IAsyncLifetime, IDisposable
Assert.Equal("Tank01.Lo", rows[0].SourceReference);
}
/// <summary>Upserts and deletes apply together in one batch (review 2026-10-21 01/SR-P2).</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyChanges_UpsertsAndDeletesInOneBatch()
{
await _storage.UpsertNativeAlarmAsync("inst", "Src", "A", "{}", DateTimeOffset.UnixEpoch);
await _storage.UpsertNativeAlarmAsync("inst", "Src", "B", "{}", DateTimeOffset.UnixEpoch);
await _storage.UpsertNativeAlarmAsync("inst", "Src", "C", "{}", DateTimeOffset.UnixEpoch);
await _storage.ApplyNativeAlarmChangesAsync("inst", "Src",
[("D", "{\"n\":1}", null, DateTimeOffset.UnixEpoch)],
["A", "B"]);
var refs = (await _storage.GetNativeAlarmsAsync("inst", "Src")).Select(r => r.SourceReference).OrderBy(x => x).ToArray();
Assert.Equal(new[] { "C", "D" }, refs);
}
/// <summary>Verifies that clearing an instance removes all of its sources and leaves other instances untouched.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]