fix(centralui): pending-updates rail refreshes as soon as a deployment finishes — cache drops its snapshot on Success/Failed and signals the indicator, instead of serving the pre-deploy count for up to TTL + one poll

This commit is contained in:
Joseph Doherty
2026-10-22 17:11:18 -04:00
parent 3e1435dfac
commit 0e5402915b
8 changed files with 305 additions and 12 deletions
@@ -199,4 +199,47 @@ public class SingleFlightMemoTests
released.SetResult(99);
Assert.Equal(99, await patient);
}
/// <summary>Verifies that <c>Invalidate</c> makes a still-fresh value stale, so the next call queries again.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Invalidate_DiscardsAFreshValue()
{
var invocations = 0;
var memo = new SingleFlightMemo<int>(TimeSpan.FromHours(1), () => T0);
var first = await memo.GetAsync(() => Task.FromResult(++invocations));
memo.Invalidate();
var second = await memo.GetAsync(() => Task.FromResult(++invocations));
Assert.Equal(1, first);
Assert.Equal(2, second);
Assert.Equal(2, memo.FlightCount);
}
/// <summary>
/// Verifies that a flight already running when <c>Invalidate</c> is called still answers its
/// callers but is not memoized: it may have read the state the invalidation says changed.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Invalidate_DuringAFlight_ResultIsServedButNotMemoized()
{
var released = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
var invocations = 0;
var memo = new SingleFlightMemo<int>(TimeSpan.FromHours(1), () => T0);
var inFlight = memo.GetAsync(() =>
{
invocations++;
return released.Task;
});
memo.Invalidate();
released.SetResult(7);
Assert.Equal(7, await inFlight);
var next = await memo.GetAsync(() => Task.FromResult(++invocations));
Assert.Equal(2, next);
Assert.Equal(2, memo.FlightCount);
}
}