55 lines
1.8 KiB
Go
55 lines
1.8 KiB
Go
package entity
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// PLAN.md section 6.3 defines the wait *before the next* attempt: after
|
|
// attempt 1 fails the next try is +5 min, after attempt 3 it is +1 h together
|
|
// with the delay DSN, and from the fifth try on the delay carries +/-20%
|
|
// jitter. A zero delay after the first failure would hammer the destination in
|
|
// a tight loop.
|
|
func TestCalculateNextBackoff_FollowsRetryTable(t *testing.T) {
|
|
now := time.Now()
|
|
expiresAt := now.Add(5 * 24 * time.Hour)
|
|
|
|
cases := []struct {
|
|
failedAttempt int
|
|
wantMin time.Duration
|
|
wantMax time.Duration
|
|
wantDelayDsn bool
|
|
}{
|
|
{1, 5 * time.Minute, 5 * time.Minute, false},
|
|
{2, 15 * time.Minute, 15 * time.Minute, false},
|
|
{3, time.Hour, time.Hour, true},
|
|
{4, 96 * time.Minute, 144 * time.Minute, false}, // 2 h +/- 20%
|
|
{5, 192 * time.Minute, 288 * time.Minute, false}, // 4 h +/- 20%
|
|
{6, 384 * time.Minute, 576 * time.Minute, false}, // 8 h +/- 20%
|
|
{7, 576 * time.Minute, 864 * time.Minute, false}, // 12 h +/- 20%
|
|
{20, 576 * time.Minute, 864 * time.Minute, false}, // steady 12 h
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
next, delayDsn, perm := CalculateNextBackoff(now, tc.failedAttempt, expiresAt)
|
|
if perm {
|
|
t.Errorf("attempt %d: unexpected permanent failure", tc.failedAttempt)
|
|
continue
|
|
}
|
|
got := next.Sub(now)
|
|
if got < tc.wantMin || got > tc.wantMax {
|
|
t.Errorf("attempt %d: delay = %v, want between %v and %v", tc.failedAttempt, got, tc.wantMin, tc.wantMax)
|
|
}
|
|
if delayDsn != tc.wantDelayDsn {
|
|
t.Errorf("attempt %d: delayDsn = %v, want %v", tc.failedAttempt, delayDsn, tc.wantDelayDsn)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCalculateNextBackoff_ExpiredJobIsPermanent(t *testing.T) {
|
|
now := time.Now()
|
|
if _, _, perm := CalculateNextBackoff(now, 5, now.Add(-time.Hour)); !perm {
|
|
t.Error("expected permanent failure for an expired job")
|
|
}
|
|
}
|