Files
LambdaMail/services/protocols/internal/domain/entity/outbound_job_test.go
T

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")
}
}