fix(sitestream): deathwatch resets live-cache liveness on aggregator termination (plan R2-07 T10)

This commit is contained in:
Joseph Doherty
2026-07-13 11:01:54 -04:00
parent 76ef97f729
commit 5b16429635
4 changed files with 97 additions and 2 deletions
@@ -302,4 +302,40 @@ public class SiteAlarmLiveCacheServiceTests : TestKit
AwaitCondition(() => service.IsLive(3), TimeSpan.FromSeconds(5));
Assert.True(factory.GetOrCreateCount >= 1); // a client was created for the single endpoint
}
// ── R2 T10: deathwatch un-sticks IsLive on aggregator death (N6) ──
[Fact]
public async Task Aggregator_Death_Resets_IsLive_And_Clears_The_Snapshot()
{
var service = CreateService(TimeSpan.FromMinutes(5), out _);
using var sub = service.Subscribe(SiteId, () => { });
AwaitAssert(() => Assert.True(service.IsLive(SiteId)), TimeSpan.FromSeconds(10));
// Kill the aggregator out from under the service (crash-death, NOT a linger stop).
var aggregator = await Sys.ActorSelection($"/user/site-alarm-aggregator-{SiteId}-*")
.ResolveOne(TimeSpan.FromSeconds(5));
Sys.Stop(aggregator);
AwaitAssert(() =>
{
Assert.False(service.IsLive(SiteId)); // sticky no more (R2 N6)
Assert.Empty(service.GetCurrentAlarms(SiteId)); // frozen snapshot never grafted
}, TimeSpan.FromSeconds(10));
}
[Fact]
public void Linger_Stop_Does_Not_Resurrect_The_Aggregator()
{
// Last viewer leaves, linger fires, entry removed: the deathwatch on the
// deliberately-stopped actor must be a no-op (entry gone), not a restart.
var service = CreateService(TimeSpan.FromMilliseconds(50), out var factory);
var sub = service.Subscribe(SiteId, () => { });
AwaitAssert(() => Assert.True(service.IsLive(SiteId)), TimeSpan.FromSeconds(10));
sub.Dispose();
AwaitAssert(() => Assert.False(service.IsLive(SiteId)), TimeSpan.FromSeconds(10));
var startsAfterStop = factory.GetOrCreateCount;
Thread.Sleep(300);
Assert.Equal(startsAfterStop, factory.GetOrCreateCount); // no self-heal restart fired
}
}