Skip to content

Commit 6910129

Browse files
authored
Use wrap errors and update tests (#1032)
Use wrap error verb (`%w`) in some `fmt.Errorf` calls and update some test to avoid confusion about how to get errors and compared them. This resolves #988
1 parent 5f5dc19 commit 6910129

File tree

5 files changed

+66
-14
lines changed

5 files changed

+66
-14
lines changed

annotated.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ func (e *annotationError) Error() string {
134134
return e.err.Error()
135135
}
136136

137+
// Unwrap the wrapped error.
138+
func (e *annotationError) Unwrap() error {
139+
return e.err
140+
}
141+
137142
type paramTagsAnnotation struct {
138143
tags []string
139144
}

app.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ type withTimeoutParams struct {
762762
}
763763

764764
// errHookCallbackExited is returned when a hook callback does not finish executing
765-
var errHookCallbackExited = fmt.Errorf("goroutine exited without returning")
765+
var errHookCallbackExited = errors.New("goroutine exited without returning")
766766

767767
func withTimeout(ctx context.Context, param *withTimeoutParams) error {
768768
c := make(chan error, 1)

app_internal_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package fx
2222

2323
import (
24+
"errors"
2425
"fmt"
2526
"os"
2627
"sync"
@@ -105,3 +106,13 @@ func (o withClockOption) apply(m *module) {
105106
func (o withClockOption) String() string {
106107
return fmt.Sprintf("WithClock(%v)", o.clock)
107108
}
109+
110+
func TestAnnotationError(t *testing.T) {
111+
wantErr := errors.New("want error")
112+
err := &annotationError{
113+
err: wantErr,
114+
}
115+
require.Error(t, err)
116+
assert.ErrorIs(t, err, wantErr)
117+
assert.Contains(t, err.Error(), wantErr.Error())
118+
}

app_test.go

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,18 @@ type errHandlerFunc func(error)
654654

655655
func (f errHandlerFunc) HandleError(err error) { f(err) }
656656

657+
type customError struct {
658+
err error
659+
}
660+
661+
func (e *customError) Error() string {
662+
return fmt.Sprintf("custom error: %v", e.err)
663+
}
664+
665+
func (e *customError) Unwrap() error {
666+
return e.err
667+
}
668+
657669
func TestInvokes(t *testing.T) {
658670
t.Parallel()
659671

@@ -673,16 +685,18 @@ func TestInvokes(t *testing.T) {
673685
t.Run("Failure event", func(t *testing.T) {
674686
t.Parallel()
675687

688+
wantErr := errors.New("great sadness")
676689
app, spy := NewSpied(
677690
Invoke(func() error {
678-
return errors.New("great sadness")
691+
return wantErr
679692
}),
680693
)
681694
require.Error(t, app.Err())
682695

683696
invoked := spy.Events().SelectByTypeName("Invoked")
684697
require.Len(t, invoked, 1)
685-
assert.Error(t, invoked[0].(*fxevent.Invoked).Err)
698+
require.Error(t, invoked[0].(*fxevent.Invoked).Err)
699+
require.ErrorIs(t, invoked[0].(*fxevent.Invoked).Err, wantErr)
686700
})
687701

688702
t.Run("ErrorsAreNotOverriden", func(t *testing.T) {
@@ -716,6 +730,23 @@ func TestInvokes(t *testing.T) {
716730
)
717731
assert.Equal(t, 1, count)
718732
})
733+
734+
t.Run("ErrorsAreWrapped", func(t *testing.T) {
735+
t.Parallel()
736+
wantErr := errors.New("err")
737+
738+
app := NewForTest(t,
739+
Invoke(func() error {
740+
return &customError{err: wantErr}
741+
}),
742+
)
743+
744+
err := app.Err()
745+
require.Error(t, err)
746+
assert.ErrorIs(t, err, wantErr)
747+
var ce *customError
748+
assert.ErrorAs(t, err, &ce)
749+
})
719750
}
720751

721752
func TestError(t *testing.T) {
@@ -738,19 +769,24 @@ func TestError(t *testing.T) {
738769
t.Run("SingleErrorOption", func(t *testing.T) {
739770
t.Parallel()
740771

772+
wantErr := errors.New("module failure")
741773
app := NewForTest(t,
742-
Error(errors.New("module failure")),
774+
Error(wantErr),
743775
Invoke(func() { t.Errorf("Invoke should not be called") }),
744776
)
745777
err := app.Err()
778+
require.Error(t, err)
746779
assert.EqualError(t, err, "module failure")
780+
assert.ErrorIs(t, err, wantErr)
747781
})
748782

749783
t.Run("MultipleErrorOption", func(t *testing.T) {
750784
t.Parallel()
751785

752786
type A struct{}
753787

788+
errA := errors.New("module A failure")
789+
errB := errors.New("module B failure")
754790
app := NewForTest(t,
755791
Provide(func() A {
756792
t.Errorf("Provide should not be called")
@@ -759,14 +795,14 @@ func TestError(t *testing.T) {
759795
),
760796
Invoke(func(A) { t.Errorf("Invoke should not be called") }),
761797
Error(
762-
errors.New("module A failure"),
763-
errors.New("module B failure"),
798+
errA,
799+
errB,
764800
),
765801
)
766802
err := app.Err()
767803
require.Error(t, err)
768-
assert.Contains(t, err.Error(), "module A failure")
769-
assert.Contains(t, err.Error(), "module B failure")
804+
assert.ErrorIs(t, err, errA)
805+
assert.ErrorIs(t, err, errB)
770806
assert.NotContains(t, err.Error(), "not in the container")
771807
})
772808

@@ -1759,7 +1795,7 @@ func TestHookConstructors(t *testing.T) {
17591795
}))
17601796
}))
17611797
require.NoError(t, app.Start(ctx))
1762-
require.ErrorContains(t, app.Stop(ctx), wantErr.Error())
1798+
require.ErrorIs(t, app.Stop(ctx), wantErr)
17631799
})
17641800

17651801
t.Run("stop deadline", func(t *testing.T) {

provide.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,18 +131,18 @@ func runProvide(c container, p provide, opts ...dig.ProvideOption) error {
131131
case annotationError:
132132
// fx.Annotate failed. Turn it into an Fx error.
133133
return fmt.Errorf(
134-
"encountered error while applying annotation using fx.Annotate to %s: %+v",
134+
"encountered error while applying annotation using fx.Annotate to %s: %w",
135135
fxreflect.FuncName(constructor.target), constructor.err)
136136

137137
case annotated:
138138
ctor, err := constructor.Build()
139139
if err != nil {
140-
return fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %v", constructor, p.Stack, err)
140+
return fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %w", constructor, p.Stack, err)
141141
}
142142

143143
opts = append(opts, dig.LocationForPC(constructor.FuncPtr))
144144
if err := c.Provide(ctor, opts...); err != nil {
145-
return fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %v", constructor, p.Stack, err)
145+
return fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %w", constructor, p.Stack, err)
146146
}
147147

148148
case Annotated:
@@ -159,7 +159,7 @@ func runProvide(c container, p provide, opts ...dig.ProvideOption) error {
159159
}
160160

161161
if err := c.Provide(ann.Target, opts...); err != nil {
162-
return fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %v", ann, p.Stack, err)
162+
return fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %w", ann, p.Stack, err)
163163
}
164164

165165
default:
@@ -180,7 +180,7 @@ func runProvide(c container, p provide, opts ...dig.ProvideOption) error {
180180
}
181181

182182
if err := c.Provide(constructor, opts...); err != nil {
183-
return fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %v", fxreflect.FuncName(constructor), p.Stack, err)
183+
return fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %w", fxreflect.FuncName(constructor), p.Stack, err)
184184
}
185185
}
186186
return nil

0 commit comments

Comments
 (0)