synapsecns/sanguine

View on GitHub
core/retry/retry_test.go

Summary

Maintainability
A
3 hrs
Test Coverage
package retry_test

import (
    "context"
    "errors"
    "fmt"
    "github.com/brianvoe/gofakeit/v6"
    . "github.com/stretchr/testify/assert"
    "github.com/synapsecns/sanguine/core/retry"
    "testing"
    "time"
)

// nolint: cyclop
func TestRetryWithBackoff(t *testing.T) {
    // Test a function that succeeds on the first attempt.
    t.Run("Success", func(t *testing.T) {
        err := retry.WithBackoff(context.Background(), func(ctx context.Context) error {
            return nil
        })
        if err != nil {
            t.Errorf("Unexpected error: %v", err)
        }
    })

    // Test a function that fails and eventually succeeds.
    t.Run("SuccessAfterRetries", func(t *testing.T) {
        var i int
        err := retry.WithBackoff(context.Background(), func(ctx context.Context) error {
            if i < 2 {
                i++
                return errors.New("simulated error")
            }
            return nil
        }, retry.WithMaxAttempts(3))
        if err != nil {
            t.Errorf("Unexpected error: %v", err)
        }
    })

    // Test a function that always fails.
    t.Run("Failure", func(t *testing.T) {
        err := retry.WithBackoff(context.Background(), func(ctx context.Context) error {
            return errors.New("simulated error")
        }, retry.WithMaxAttempts(3))
        if err == nil {
            t.Errorf("Expected error, but got nil")
        }
    })

    // Test a function that times out.
    t.Run("Timeout", func(t *testing.T) {
        ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
        defer cancel()

        err := retry.WithBackoff(ctx, func(ctx context.Context) error {
            return errors.New("simulated error")
        })
        if err == nil {
            t.Errorf("Expected error, but got nil")
        }
    })

    t.Run("WithFactor", func(t *testing.T) {
        cfg := retry.DefaultConfig()
        factor := gofakeit.Float64Range(1, 10)
        newCfg := retry.Configurator(t, cfg, retry.WithFactor(factor))
        Equal(t, factor, newCfg.GetFactor())
    })

    t.Run("WithJitter", func(t *testing.T) {
        cfg := retry.DefaultConfig()
        jitter := gofakeit.Bool()
        newCfg := retry.Configurator(t, cfg, retry.WithJitter(jitter))
        Equal(t, jitter, newCfg.GetJitter())
    })

    t.Run("WithMin", func(t *testing.T) {
        cfg := retry.DefaultConfig()
        withMin := time.Duration(gofakeit.Uint64())
        newCfg := retry.Configurator(t, cfg, retry.WithMin(withMin))
        Equal(t, withMin, newCfg.GetMin())
    })

    t.Run("WithMax", func(t *testing.T) {
        cfg := retry.DefaultConfig()
        withMax := time.Duration(gofakeit.Uint64())
        newCfg := retry.Configurator(t, cfg, retry.WithMax(withMax))
        Equal(t, withMax, newCfg.GetMax())
    })

    t.Run("WithMaxAttemptTime", func(t *testing.T) {
        err := retry.WithBackoff(context.Background(), func(ctx context.Context) error {
            select {
            case <-ctx.Done():
                return fmt.Errorf("context canceled: %w", ctx.Err())
            case <-time.After(time.Millisecond):
                return nil
            }
        }, retry.WithMaxAttemptTime(1), retry.WithMaxAttempts(3))
        NotNil(t, err)
    })

    t.Run("WithMaxTotalTime", func(t *testing.T) {
        startTime := time.Now()
        const testDuration = time.Second

        err := retry.WithBackoff(context.Background(), func(ctx context.Context) error {
            select {
            case <-ctx.Done():
                return fmt.Errorf("context canceled: %w", ctx.Err())
            case <-time.After(time.Millisecond):
                return fmt.Errorf("ima fail")
            }
        }, retry.WithMaxTotalTime(testDuration))
        NotNil(t, err)

        if time.Since(startTime) < time.Second {
            t.Errorf("Expected to run for at least %s second, but ran for %s", testDuration.String(), time.Since(startTime))
        }
    })
}