mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-28 02:21:30 +02:00
92 lines
2.3 KiB
Go
92 lines
2.3 KiB
Go
package grpc
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cenkalti/backoff/v4"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/netbirdio/netbird/client/netstate"
|
|
)
|
|
|
|
func TestRetryWakesOnNetworkChange(t *testing.T) {
|
|
ns := netstate.New()
|
|
attempts := 0
|
|
operation := func() error {
|
|
attempts++
|
|
if attempts == 1 {
|
|
return errors.New("cut by network change")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(20 * time.Millisecond)
|
|
ns.Set(false)
|
|
}()
|
|
|
|
start := time.Now()
|
|
err := Retry(context.Background(), operation, backoff.NewConstantBackOff(time.Minute), ns)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, attempts, "network change must cause one immediate retry")
|
|
assert.Less(t, time.Since(start), time.Second, "the transition must cut the minute-long sleep short")
|
|
}
|
|
|
|
func TestRetryPermanentError(t *testing.T) {
|
|
sentinel := errors.New("permission denied")
|
|
operation := func() error {
|
|
return backoff.Permanent(sentinel)
|
|
}
|
|
|
|
err := Retry(context.Background(), operation, backoff.NewConstantBackOff(time.Millisecond), nil)
|
|
assert.ErrorIs(t, err, sentinel, "permanent errors must stop retries")
|
|
}
|
|
|
|
func TestRetryNilNetState(t *testing.T) {
|
|
attempts := 0
|
|
operation := func() error {
|
|
attempts++
|
|
if attempts < 3 {
|
|
return errors.New("transient")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
err := Retry(context.Background(), operation, backoff.NewConstantBackOff(time.Millisecond), nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 3, attempts, "nil network state must preserve timed retries")
|
|
}
|
|
|
|
func TestRetryStops(t *testing.T) {
|
|
failure := errors.New("still failing")
|
|
operation := func() error {
|
|
return failure
|
|
}
|
|
|
|
err := Retry(context.Background(), operation, &backoff.StopBackOff{}, nil)
|
|
assert.ErrorIs(t, err, failure, "stop backoff must return the operation error")
|
|
}
|
|
|
|
func TestRetryCtxCancelDuringSleep(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
operation := func() error {
|
|
return errors.New("failing")
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(20 * time.Millisecond)
|
|
cancel()
|
|
}()
|
|
|
|
start := time.Now()
|
|
err := Retry(ctx, operation, backoff.NewConstantBackOff(time.Minute), netstate.New())
|
|
|
|
assert.ErrorIs(t, err, context.Canceled, "context cancellation must stop the retry loop")
|
|
assert.Less(t, time.Since(start), time.Second, "context cancellation must interrupt backoff sleep")
|
|
}
|