Files
kportal/internal/retry/backoff_test.go
T
lukaszraczylo 7df161aee0 bugfixes nov2025 (#3)
* Fix enter misbehaving.
* Cleanup after previous tui implementation.
* Fix race condition and improve logging
* Add filtering of the namespaces by text input in the wizard UI
2025-11-24 11:09:23 +00:00

170 lines
4.4 KiB
Go

package retry
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestBackoff_Next(t *testing.T) {
tests := []struct {
name string
attempt int
minDelay time.Duration
maxDelay time.Duration
}{
{
name: "first attempt returns ~1s",
attempt: 0,
minDelay: 900 * time.Millisecond, // 1s - 10% jitter
maxDelay: 1100 * time.Millisecond, // 1s + 10% jitter
},
{
name: "second attempt returns ~2s",
attempt: 1,
minDelay: 1800 * time.Millisecond, // 2s - 10% jitter
maxDelay: 2200 * time.Millisecond, // 2s + 10% jitter
},
{
name: "third attempt returns ~4s",
attempt: 2,
minDelay: 3600 * time.Millisecond, // 4s - 10% jitter
maxDelay: 4400 * time.Millisecond, // 4s + 10% jitter
},
{
name: "fourth attempt returns ~8s",
attempt: 3,
minDelay: 7200 * time.Millisecond, // 8s - 10% jitter
maxDelay: 8800 * time.Millisecond, // 8s + 10% jitter
},
{
name: "fifth attempt returns ~10s (max)",
attempt: 4,
minDelay: 9000 * time.Millisecond, // 10s - 10% jitter
maxDelay: 11000 * time.Millisecond, // 10s + 10% jitter
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := NewBackoff()
// Advance to the desired attempt
for i := 0; i < tt.attempt; i++ {
b.Next()
}
delay := b.Next()
assert.GreaterOrEqual(t, delay, tt.minDelay, "delay should be >= min with jitter")
assert.LessOrEqual(t, delay, tt.maxDelay, "delay should be <= max with jitter")
})
}
}
func TestBackoff_Next_StaysAtMax(t *testing.T) {
b := NewBackoff()
// Go through several attempts past the max
for i := 0; i < 10; i++ {
delay := b.Next()
// After the 5th attempt (index 4), should always be at max (10s ± jitter)
if i >= 4 {
assert.GreaterOrEqual(t, delay, 9*time.Second, "should stay at max delay")
assert.LessOrEqual(t, delay, 11*time.Second, "should stay at max delay with jitter")
}
}
}
func TestBackoff_Reset(t *testing.T) {
b := NewBackoff()
// Advance through several attempts
for i := 0; i < 5; i++ {
b.Next()
}
// Should be at attempt 5
assert.Equal(t, 5, b.Attempt(), "should be at attempt 5")
// Reset
b.Reset()
// Should be back to attempt 0
assert.Equal(t, 0, b.Attempt(), "should be reset to attempt 0")
// Next call should return ~1s (first attempt)
delay := b.Next()
assert.GreaterOrEqual(t, delay, 900*time.Millisecond, "after reset should return ~1s")
assert.LessOrEqual(t, delay, 1100*time.Millisecond, "after reset should return ~1s with jitter")
}
func TestBackoff_Jitter_IsWithinExpectedRange(t *testing.T) {
b := NewBackoff()
// Test multiple times to ensure jitter varies
delays := make([]time.Duration, 20)
for i := 0; i < 20; i++ {
b.Reset()
delays[i] = b.Next()
}
// All delays should be within the jitter range for 1s
for _, delay := range delays {
assert.GreaterOrEqual(t, delay, 900*time.Millisecond, "jitter should not go below 10%")
assert.LessOrEqual(t, delay, 1100*time.Millisecond, "jitter should not go above 10%")
}
// Check that not all delays are identical (jitter is working)
allSame := true
first := delays[0]
for _, d := range delays[1:] {
if d != first {
allSame = false
break
}
}
assert.False(t, allSame, "jitter should produce varying delays")
}
func TestBackoff_Attempt(t *testing.T) {
b := NewBackoff()
assert.Equal(t, 0, b.Attempt(), "initial attempt should be 0")
b.Next()
assert.Equal(t, 1, b.Attempt(), "attempt should increment after Next()")
b.Next()
assert.Equal(t, 2, b.Attempt(), "attempt should increment after Next()")
b.Reset()
assert.Equal(t, 0, b.Attempt(), "attempt should reset to 0")
}
func TestBackoff_ExponentialProgression(t *testing.T) {
b := NewBackoff()
// Track the progression
var delays []time.Duration
for i := 0; i < 5; i++ {
delays = append(delays, b.Next())
}
// Verify exponential growth (each should be roughly 2x the previous)
// We allow for jitter by checking a range
for i := 1; i < len(delays)-1; i++ {
// Each delay should be roughly double the previous (accounting for jitter)
// With 10% jitter on each value:
// Lower bound: (2.0 * 0.9) / 1.1 ≈ 1.636
// Upper bound: (2.0 * 1.1) / 0.9 ≈ 2.444
// We use 1.6x to 2.5x as a reasonable range to account for jitter variance
ratio := float64(delays[i]) / float64(delays[i-1])
assert.GreaterOrEqual(t, ratio, 1.6, "exponential growth should be ~2x")
assert.LessOrEqual(t, ratio, 2.5, "exponential growth should be ~2x")
}
}