Skip to content

Commit

Permalink
registry: Add mocks and more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cdamian committed Mar 22, 2023
1 parent 62d3d77 commit 15ad7d4
Show file tree
Hide file tree
Showing 9 changed files with 502 additions and 23 deletions.
35 changes: 25 additions & 10 deletions registry/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,18 @@ func NewRetryableExecutor[T any](opts ...OptsFn) RetryableExecutor[T] {

func (r *retryableExecutor[T]) ExecWithFallback(execFn func() (T, error), fallbackFn func() error) (res T, err error) {
if execFn == nil {
return res, errors.New("no exec function provided")
return res, ErrMissingExecFn
}

if fallbackFn == nil {
return res, errors.New("no fallback function provided")
return res, ErrMissingFallbackFn
}

execErr := &Error{}

for i := uint(0); i < r.opts.maxCount; i++ {
retryCount := uint(0)

for {
res, err = execFn()

if err == nil {
Expand All @@ -47,43 +49,56 @@ func (r *retryableExecutor[T]) ExecWithFallback(execFn func() (T, error), fallba

execErr.AddErr(fmt.Errorf("exec function error: %w", err))

if retryCount == r.opts.maxRetryCount {
return res, execErr
}

if err = fallbackFn(); err != nil && !r.opts.retryOnFallbackError {
execErr.AddErr(fmt.Errorf("fallback function error: %w", err))

return res, execErr
}

retryCount++

time.Sleep(r.opts.errTimeout)
}

return res, execErr
}

var (
ErrMissingExecFn = errors.New("no exec function provided")
ErrMissingFallbackFn = errors.New("no fallback function provided")
)

const (
defaultExecMaxCount = 3
defaultMaxRetryCount = 3
defaultErrTimeout = 0 * time.Second
defaultRetryOnFallbackError = true
)

type Opts struct {
maxCount uint
maxRetryCount uint
errTimeout time.Duration
retryOnFallbackError bool
}

func NewDefaultExecOpts() *Opts {
return &Opts{
maxCount: defaultExecMaxCount,
maxRetryCount: defaultMaxRetryCount,
errTimeout: defaultErrTimeout,
retryOnFallbackError: defaultRetryOnFallbackError,
}
}

type OptsFn func(opts *Opts)

func WithMaxCount(maxCount uint) OptsFn {
func WithMaxRetryCount(maxCount uint) OptsFn {
return func(opts *Opts) {
opts.maxCount = maxCount
if maxCount == 0 {
maxCount = defaultMaxRetryCount
}

opts.maxRetryCount = maxCount
}
}

Expand Down
153 changes: 153 additions & 0 deletions registry/exec/exec_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package exec

import (
"errors"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestRetryableExecutor_ExecWithFallback(t *testing.T) {
exec := NewRetryableExecutor[int]()

execFnCallCount := 0
fallbackFnCallCount := 0

execFnRes := 11

res, err := exec.ExecWithFallback(func() (int, error) {
execFnCallCount++

return execFnRes, nil
}, func() error {
fallbackFnCallCount++

return nil
})

assert.Nil(t, err)
assert.Equal(t, execFnRes, res)
assert.Equal(t, 1, execFnCallCount)
assert.Equal(t, 0, fallbackFnCallCount)
}

func TestRetryableExecutor_ExecWithFallback_RetrySuccess(t *testing.T) {
exec := NewRetryableExecutor[int]()

execFnCallCount := 0
fallbackFnCallCount := 0

execFnRes := 11

res, err := exec.ExecWithFallback(func() (int, error) {
execFnCallCount++

if execFnCallCount < 2 {
return 0, errors.New("boom")
}

return execFnRes, nil
}, func() error {
fallbackFnCallCount++

return nil
})

assert.Nil(t, err)
assert.Equal(t, execFnRes, res)
assert.Equal(t, 2, execFnCallCount)
assert.Equal(t, 1, fallbackFnCallCount)
}

func TestRetryableExecutor_ExecWithFallback_NilFns(t *testing.T) {
exec := NewRetryableExecutor[int]()

res, err := exec.ExecWithFallback(nil, nil)
assert.ErrorIs(t, err, ErrMissingExecFn)
assert.Equal(t, 0, res)

res, err = exec.ExecWithFallback(func() (int, error) {
return 1, nil
}, nil)
assert.ErrorIs(t, err, ErrMissingFallbackFn)
assert.Equal(t, 0, res)

}

func TestRetryableExecutor_ExecWithFallback_ExecFnError(t *testing.T) {
retryCount := uint(5)

exec := NewRetryableExecutor[int](
WithMaxRetryCount(retryCount),
WithErrTimeout(100*time.Millisecond),
)

execFnCallCount := uint(0)
fallbackFnCallCount := uint(0)

res, err := exec.ExecWithFallback(func() (int, error) {
execFnCallCount++

return 0, errors.New("boom")
}, func() error {
fallbackFnCallCount++

return nil
})
assert.NotNil(t, err)
assert.Equal(t, 0, res)
assert.Equal(t, retryCount+1, execFnCallCount)
assert.Equal(t, retryCount, fallbackFnCallCount)

execErr := err.(*Error)
assert.Len(t, execErr.errs, int(retryCount+1))
}

func TestRetryableExecutor_ExecWithFallback_FallBackFnError(t *testing.T) {
exec := NewRetryableExecutor[int]()

execFnCallCount := 0
fallbackFnCallCount := 0

res, err := exec.ExecWithFallback(func() (int, error) {
execFnCallCount++

return 0, errors.New("boom")
}, func() error {
fallbackFnCallCount++

return errors.New("boom")
})
assert.NotNil(t, err)
assert.Equal(t, 0, res)
assert.Equal(t, defaultMaxRetryCount+1, execFnCallCount)
assert.Equal(t, defaultMaxRetryCount, fallbackFnCallCount)

execErr := err.(*Error)
assert.Len(t, execErr.errs, defaultMaxRetryCount+1)
}

func TestRetryableExecutor_ExecWithFallback_FallBackFnError_NoRetry(t *testing.T) {
exec := NewRetryableExecutor[int](WithRetryOnFallBackError(false))

execFnCallCount := 0
fallbackFnCallCount := 0

res, err := exec.ExecWithFallback(func() (int, error) {
execFnCallCount++

return 0, errors.New("boom")
}, func() error {
fallbackFnCallCount++

return errors.New("boom")
})
assert.NotNil(t, err)
assert.Equal(t, 0, res)
assert.Equal(t, 1, execFnCallCount)
assert.Equal(t, 1, fallbackFnCallCount)

execErr := err.(*Error)
assert.Len(t, execErr.errs, 2)
}
97 changes: 97 additions & 0 deletions registry/factory_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions registry/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type EventParser interface {
}

type eventParser struct {
stateProvider state.StateProvider
stateProvider state.Provider
registryFactory registry.Factory

eventStorageExecutor exec.RetryableExecutor[*types.StorageDataRaw]
Expand All @@ -37,7 +37,7 @@ type eventParser struct {
}

func NewParser(
stateProvider state.StateProvider,
stateProvider state.Provider,
registryFactory registry.Factory,
eventStorageExecutor exec.RetryableExecutor[*types.StorageDataRaw],
eventParsingExecutor exec.RetryableExecutor[[]*Event],
Expand All @@ -56,9 +56,9 @@ func NewParser(
return parser, nil
}

func NewDefaultParser(stateProvider state.StateProvider, registryFactory registry.Factory) (EventParser, error) {
func NewDefaultParser(stateProvider state.Provider, registryFactory registry.Factory) (EventParser, error) {
eventStorageExecutor := exec.NewRetryableExecutor[*types.StorageDataRaw](exec.WithErrTimeout(1 * time.Second))
eventParsingExecutor := exec.NewRetryableExecutor[[]*Event](exec.WithMaxCount(1))
eventParsingExecutor := exec.NewRetryableExecutor[[]*Event](exec.WithMaxRetryCount(1))

return NewParser(stateProvider, registryFactory, eventStorageExecutor, eventParsingExecutor)
}
Expand Down
Loading

0 comments on commit 15ad7d4

Please sign in to comment.