fix(centralui): short negative TTL after a faulted pending-updates flight (review 2026-10-21 04/C4)

This commit is contained in:
Joseph Doherty
2026-10-22 08:35:10 -04:00
parent d36f360d35
commit 0bd856552c
4 changed files with 57 additions and 12 deletions
@@ -115,10 +115,10 @@ public class PendingUpdatesCacheTests : IDisposable
await _evaluator.Received(1).EvaluateAsync(Arg.Any<CancellationToken>());
}
/// <summary>Verifies that a failed evaluation surfaces to the caller and is not memoized.</summary>
/// <summary>Verifies that a failed evaluation surfaces to the caller and is remembered only for the short back-off (review 2026-10-21 04/C4).</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task FailedEvaluation_SurfacesToTheCaller_AndIsNotMemoized()
public async Task FailedEvaluation_SurfacesToTheCaller_AndIsBackedOffNotRetriedPerPoll()
{
_evaluator.EvaluateAsync(Arg.Any<CancellationToken>())
.Returns(
@@ -128,10 +128,12 @@ public class PendingUpdatesCacheTests : IDisposable
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.GetAsync());
// Same instant, well inside the TTL — a cached fault would starve every dashboard
// for the rest of the window, so the next caller must start a new evaluation.
var recovered = await sut.GetAsync();
// Inside the negative TTL a poll is served the same fault with no new flatten.
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.GetAsync());
await _evaluator.Received(1).EvaluateAsync(Arg.Any<CancellationToken>());
// An operator Re-check bypasses the back-off.
var recovered = await sut.GetAsync(forceRefresh: true);
Assert.NotNull(recovered);
await _evaluator.Received(2).EvaluateAsync(Arg.Any<CancellationToken>());
}