This repository has been archived by the owner on Aug 30, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Capture extra info using interface{ExtraInfo() map[string]interface{}} Extract nested extras * Fix tests for Go 1.10 A couple type errors in string formatting prevented the tests from running. Runs 1.10.x on travis. * Add new tests for error with extra Builds on #168 by adding test cases and removing unused interface
- Loading branch information
1 parent
7452746
commit ed7bcb3
Showing
5 changed files
with
291 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ go: | |
- 1.7.x | ||
- 1.8.x | ||
- 1.9.x | ||
- 1.10.x | ||
- tip | ||
|
||
before_install: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package raven | ||
|
||
type causer interface { | ||
Cause() error | ||
} | ||
|
||
type errWrappedWithExtra struct { | ||
err error | ||
extraInfo map[string]interface{} | ||
} | ||
|
||
func (ewx *errWrappedWithExtra) Error() string { | ||
return ewx.err.Error() | ||
} | ||
|
||
func (ewx *errWrappedWithExtra) Cause() error { | ||
return ewx.err | ||
} | ||
|
||
func (ewx *errWrappedWithExtra) ExtraInfo() Extra { | ||
return ewx.extraInfo | ||
} | ||
|
||
// Adds extra data to an error before reporting to Sentry | ||
func WrapWithExtra(err error, extraInfo map[string]interface{}) error { | ||
return &errWrappedWithExtra{ | ||
err: err, | ||
extraInfo: extraInfo, | ||
} | ||
} | ||
|
||
type ErrWithExtra interface { | ||
Error() string | ||
Cause() error | ||
ExtraInfo() Extra | ||
} | ||
|
||
// Iteratively fetches all the Extra data added to an error, | ||
// and it's underlying errors. Extra data defined first is | ||
// respected, and is not overridden when extracting. | ||
func extractExtra(err error) Extra { | ||
extra := Extra{} | ||
|
||
currentErr := err | ||
for currentErr != nil { | ||
if errWithExtra, ok := currentErr.(ErrWithExtra); ok { | ||
for k, v := range errWithExtra.ExtraInfo() { | ||
extra[k] = v | ||
} | ||
} | ||
|
||
if errWithCause, ok := currentErr.(causer); ok { | ||
currentErr = errWithCause.Cause() | ||
} else { | ||
currentErr = nil | ||
} | ||
} | ||
|
||
return extra | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package raven | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"testing" | ||
|
||
pkgErrors "github.com/pkg/errors" | ||
) | ||
|
||
func TestWrapWithExtraGeneratesProperErrWithExtra(t *testing.T) { | ||
errMsg := "This is bad" | ||
baseErr := fmt.Errorf(errMsg) | ||
extraInfo := map[string]interface{}{ | ||
"string": "string", | ||
"int": 1, | ||
"float": 1.001, | ||
"bool": false, | ||
} | ||
|
||
testErr := WrapWithExtra(baseErr, extraInfo) | ||
wrapped, ok := testErr.(ErrWithExtra) | ||
if !ok { | ||
t.Errorf("Wrapped error does not conform to expected protocol.") | ||
} | ||
|
||
if !reflect.DeepEqual(wrapped.Cause(), baseErr) { | ||
t.Errorf("Failed to unwrap error, got %+v, expected %+v", wrapped.Cause(), baseErr) | ||
} | ||
|
||
returnedExtra := wrapped.ExtraInfo() | ||
for expectedKey, expectedVal := range extraInfo { | ||
val, ok := returnedExtra[expectedKey] | ||
if !ok { | ||
t.Errorf("Extra data missing key: %s", expectedKey) | ||
} | ||
if val != expectedVal { | ||
t.Errorf("Extra data [%s]: Got: %+v, expected: %+v", expectedKey, val, expectedVal) | ||
} | ||
} | ||
|
||
if wrapped.Error() != errMsg { | ||
t.Errorf("Wrong error message, got: %q, expected: %q", wrapped.Error(), errMsg) | ||
} | ||
} | ||
|
||
func TestWrapWithExtraGeneratesCausableError(t *testing.T) { | ||
baseErr := fmt.Errorf("this is bad") | ||
testErr := WrapWithExtra(baseErr, nil) | ||
cause := pkgErrors.Cause(testErr) | ||
|
||
if !reflect.DeepEqual(cause, baseErr) { | ||
t.Errorf("Failed to unwrap error, got %+v, expected %+v", cause, baseErr) | ||
} | ||
} | ||
|
||
func TestExtractErrorPullsExtraData(t *testing.T) { | ||
extraInfo := map[string]interface{}{ | ||
"string": "string", | ||
"int": 1, | ||
"float": 1.001, | ||
"bool": false, | ||
} | ||
emptyInfo := map[string]interface{}{} | ||
|
||
testCases := []struct { | ||
Error error | ||
Expected map[string]interface{} | ||
}{ | ||
// Unwrapped error shouldn't include anything | ||
{ | ||
Error: fmt.Errorf("This is bad"), | ||
Expected: emptyInfo, | ||
}, | ||
// Wrapped error with nil map should extract as empty info | ||
{ | ||
Error: WrapWithExtra(fmt.Errorf("This is bad"), nil), | ||
Expected: emptyInfo, | ||
}, | ||
// Wrapped error with empty map should extract as empty info | ||
{ | ||
Error: WrapWithExtra(fmt.Errorf("This is bad"), emptyInfo), | ||
Expected: emptyInfo, | ||
}, | ||
// Wrapped error with extra info should extract with all data | ||
{ | ||
Error: WrapWithExtra(fmt.Errorf("This is bad"), extraInfo), | ||
Expected: extraInfo, | ||
}, | ||
// Nested wrapped error should extract all the info | ||
{ | ||
Error: WrapWithExtra( | ||
WrapWithExtra(fmt.Errorf("This is bad"), | ||
map[string]interface{}{ | ||
"inner": "123", | ||
}), | ||
map[string]interface{}{ | ||
"outer": "456", | ||
}, | ||
), | ||
Expected: map[string]interface{}{ | ||
"inner": "123", | ||
"outer": "456", | ||
}, | ||
}, | ||
// Futher wrapping of errors shouldn't allow for value override | ||
{ | ||
Error: WrapWithExtra( | ||
WrapWithExtra(fmt.Errorf("This is bad"), | ||
map[string]interface{}{ | ||
"dontoverride": "123", | ||
}), | ||
map[string]interface{}{ | ||
"dontoverride": "456", | ||
}, | ||
), | ||
Expected: map[string]interface{}{ | ||
"dontoverride": "123", | ||
}, | ||
}, | ||
} | ||
|
||
for i, test := range testCases { | ||
extracted := extractExtra(test.Error) | ||
if len(test.Expected) != len(extracted) { | ||
t.Errorf( | ||
"Case [%d]: Mismatched amount of data between provided and extracted extra. Got: %+v Expected: %+v", | ||
i, | ||
extracted, | ||
test.Expected, | ||
) | ||
} | ||
|
||
for expectedKey, expectedVal := range test.Expected { | ||
val, ok := extracted[expectedKey] | ||
if !ok { | ||
t.Errorf("Case [%d]: Extra data missing key: %s", i, expectedKey) | ||
} | ||
if val != expectedVal { | ||
t.Errorf("Case [%d]: Wrong extra data for %q. Got: %+v, expected: %+v", i, expectedKey, val, expectedVal) | ||
} | ||
} | ||
} | ||
} |