fix(store-and-forward): gate the retry sweep behind an active-node delivery gate (standby must be passive)
This commit is contained in:
@@ -582,4 +582,60 @@ public class StoreAndForwardServiceTests : IAsyncLifetime, IDisposable
|
||||
Assert.True(handlerCompleted,
|
||||
"Sweep handler must have finished before StopAsync returned.");
|
||||
}
|
||||
|
||||
// ── Task 3 (arch review 02, Stability #2): active-node delivery gate ──
|
||||
// The standby site node applies replicated buffer operations but must never
|
||||
// deliver; the sweep runs only when the gate reports the node is active.
|
||||
|
||||
[Fact]
|
||||
public async Task RetrySweep_SkipsDelivery_WhenDeliveryGateReportsStandby()
|
||||
{
|
||||
var delivered = 0;
|
||||
_service.RegisterDeliveryHandler(StoreAndForwardCategory.ExternalSystem,
|
||||
_ => { delivered++; return Task.FromResult(true); });
|
||||
_service.SetDeliveryGate(() => false); // standby
|
||||
|
||||
var result = await _service.EnqueueAsync(StoreAndForwardCategory.ExternalSystem, "target-x", "{}",
|
||||
attemptImmediateDelivery: false, retryInterval: TimeSpan.Zero);
|
||||
await _service.RetryPendingMessagesAsync();
|
||||
|
||||
Assert.Equal(0, delivered);
|
||||
var row = await _service.GetMessageByIdAsync(result.MessageId);
|
||||
Assert.NotNull(row);
|
||||
Assert.Equal(StoreAndForwardMessageStatus.Pending, row!.Status); // row untouched
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RetrySweep_ResumesDelivery_WhenGateFlipsActive()
|
||||
{
|
||||
var delivered = 0;
|
||||
var active = false;
|
||||
_service.RegisterDeliveryHandler(StoreAndForwardCategory.ExternalSystem,
|
||||
_ => { delivered++; return Task.FromResult(true); });
|
||||
_service.SetDeliveryGate(() => active);
|
||||
|
||||
await _service.EnqueueAsync(StoreAndForwardCategory.ExternalSystem, "target-x", "{}",
|
||||
attemptImmediateDelivery: false, retryInterval: TimeSpan.Zero);
|
||||
|
||||
await _service.RetryPendingMessagesAsync();
|
||||
Assert.Equal(0, delivered);
|
||||
|
||||
active = true; // failover: this node became active
|
||||
await _service.RetryPendingMessagesAsync();
|
||||
Assert.Equal(1, delivered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RetrySweep_TreatsThrowingGateAsStandby()
|
||||
{
|
||||
var delivered = 0;
|
||||
_service.RegisterDeliveryHandler(StoreAndForwardCategory.ExternalSystem,
|
||||
_ => { delivered++; return Task.FromResult(true); });
|
||||
_service.SetDeliveryGate(() => throw new InvalidOperationException("cluster not ready"));
|
||||
|
||||
await _service.EnqueueAsync(StoreAndForwardCategory.ExternalSystem, "t", "{}",
|
||||
attemptImmediateDelivery: false, retryInterval: TimeSpan.Zero);
|
||||
await _service.RetryPendingMessagesAsync(); // must not throw
|
||||
Assert.Equal(0, delivered);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user