Files
2026-05-06 10:14:55 +03:00

46 lines
1.4 KiB
Go

package notification
import (
"testing"
"time"
)
// TestRouteBackoffMonotonic locks the documented schedule:
// attempt 1 == ~backoffBase, each subsequent attempt grows by
// backoffFactor up to backoffMax. The check uses the lower bound of
// the jitter window so the assertion is robust under random output.
func TestRouteBackoffMonotonic(t *testing.T) {
t.Parallel()
lower := func(d time.Duration) time.Duration {
return time.Duration(float64(d) * (1 - backoffJitter))
}
upper := func(d time.Duration) time.Duration {
return time.Duration(float64(d) * (1 + backoffJitter))
}
cases := []struct {
attempt int32
want time.Duration
}{
{attempt: 1, want: backoffBase},
{attempt: 2, want: time.Duration(float64(backoffBase) * backoffFactor)},
{attempt: 3, want: time.Duration(float64(backoffBase) * backoffFactor * backoffFactor)},
}
for _, tc := range cases {
got := routeBackoff(tc.attempt)
if got < lower(tc.want) || got > upper(tc.want) {
t.Fatalf("attempt=%d got=%s want ~%s (±%.0f%%)", tc.attempt, got, tc.want, backoffJitter*100)
}
}
}
// TestRouteBackoffCap asserts the schedule clamps at backoffMax.
func TestRouteBackoffCap(t *testing.T) {
t.Parallel()
upper := time.Duration(float64(backoffMax) * (1 + backoffJitter))
got := routeBackoff(50)
if got > upper {
t.Fatalf("attempt=50 got=%s exceeds cap (max=%s)", got, backoffMax)
}
}