fix(store-and-forward): resolve StoreAndForward-003, re-triage 002 — fix retry-count off-by-one
This commit is contained in:
@@ -86,7 +86,9 @@ public class StoreAndForwardServiceTests : IAsyncLifetime, IDisposable
|
||||
var msg = await _storage.GetMessageByIdAsync(result.MessageId);
|
||||
Assert.NotNull(msg);
|
||||
Assert.Equal(StoreAndForwardMessageStatus.Pending, msg!.Status);
|
||||
Assert.Equal(1, msg.RetryCount);
|
||||
// StoreAndForward-003: RetryCount counts sweep retries only; the immediate
|
||||
// attempt is attempt 0, so a freshly buffered message has RetryCount 0.
|
||||
Assert.Equal(0, msg.RetryCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -134,6 +136,12 @@ public class StoreAndForwardServiceTests : IAsyncLifetime, IDisposable
|
||||
StoreAndForwardCategory.ExternalSystem, "api", """{}""",
|
||||
maxRetries: 2);
|
||||
|
||||
// StoreAndForward-003: MaxRetries bounds sweep retries (not the immediate
|
||||
// attempt), so a message with MaxRetries=2 needs two retry sweeps to park.
|
||||
await _service.RetryPendingMessagesAsync();
|
||||
var afterFirst = await _storage.GetMessageByIdAsync(result.MessageId);
|
||||
Assert.Equal(StoreAndForwardMessageStatus.Pending, afterFirst!.Status);
|
||||
|
||||
await _service.RetryPendingMessagesAsync();
|
||||
|
||||
var msg = await _storage.GetMessageByIdAsync(result.MessageId);
|
||||
@@ -141,6 +149,34 @@ public class StoreAndForwardServiceTests : IAsyncLifetime, IDisposable
|
||||
Assert.Equal(StoreAndForwardMessageStatus.Parked, msg!.Status);
|
||||
}
|
||||
|
||||
// ── StoreAndForward-003: retry-count accounting ──
|
||||
|
||||
[Fact]
|
||||
public async Task RetryPendingMessagesAsync_MaxRetriesOne_PerformsExactlyOneRetryBeforeParking()
|
||||
{
|
||||
// The immediate attempt is attempt 0; MaxRetries=1 must allow exactly one
|
||||
// retry sweep before parking. The pre-fix off-by-one parked with zero retries.
|
||||
var attempts = 0;
|
||||
_service.RegisterDeliveryHandler(StoreAndForwardCategory.ExternalSystem,
|
||||
_ => { Interlocked.Increment(ref attempts); throw new HttpRequestException("always fails"); });
|
||||
|
||||
var result = await _service.EnqueueAsync(
|
||||
StoreAndForwardCategory.ExternalSystem, "api", """{}""",
|
||||
maxRetries: 1);
|
||||
|
||||
// After the immediate failed attempt the message is buffered, not parked.
|
||||
var buffered = await _storage.GetMessageByIdAsync(result.MessageId);
|
||||
Assert.Equal(StoreAndForwardMessageStatus.Pending, buffered!.Status);
|
||||
Assert.Equal(1, attempts); // only the immediate attempt so far
|
||||
|
||||
await _service.RetryPendingMessagesAsync();
|
||||
|
||||
var msg = await _storage.GetMessageByIdAsync(result.MessageId);
|
||||
Assert.Equal(StoreAndForwardMessageStatus.Parked, msg!.Status);
|
||||
Assert.Equal(2, attempts); // immediate attempt + exactly one retry
|
||||
Assert.Equal(1, msg.RetryCount); // one sweep retry recorded
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RetryPendingMessagesAsync_PermanentFailureOnRetry_ParksMessage()
|
||||
{
|
||||
@@ -332,6 +368,8 @@ public class StoreAndForwardServiceTests : IAsyncLifetime, IDisposable
|
||||
var msg = await _storage.GetMessageByIdAsync(result.MessageId);
|
||||
Assert.NotNull(msg);
|
||||
Assert.Equal(StoreAndForwardMessageStatus.Pending, msg!.Status);
|
||||
Assert.Equal(1, msg.RetryCount); // counts as the caller's first attempt
|
||||
// StoreAndForward-003: the caller's own attempt is attempt 0; RetryCount
|
||||
// counts only sweep retries, so a freshly buffered message has RetryCount 0.
|
||||
Assert.Equal(0, msg.RetryCount);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user