internal/jsonrpc2: test WireError.Is and fix nil target panic (#1079)
# Problem WireError.Is backs errors.Is checks used across the SDK against sentinels such as ErrMethodNotFound, ErrRejected, and ErrClientClosing, but had no direct test. Add a table-driven test for it. While doing so, errors.Is(err, (*WireError)(nil)) was found to panic on a nil pointer dereference, because Is read w.Code without a nil check. Guard against a nil *WireError target so the comparison returns false. ```go errors.Is(err, (*WireError)(nil)) // panic: nil pointer dereference ``` # Approach Is read w.Code after a successful type assertion without checking whether w itself was nil. The fix guards against a nil *WireError target so the comparison returns false instead of panicking. # Test plan - go test ./internal/jsonrpc2/ passes. - The new typed nil WireError case panics without the one-line fix and passes with it, confirming the test exercises the guarded path. Co-authored-by: Yaroslav <yarolegovich@gmail.com>
This commit is contained in:
parent
ce0b198202
commit
b074c7f3af
2 changed files with 25 additions and 1 deletions
|
|
@ -87,7 +87,7 @@ func (err *WireError) Error() string {
|
||||||
|
|
||||||
func (err *WireError) Is(other error) bool {
|
func (err *WireError) Is(other error) bool {
|
||||||
w, ok := other.(*WireError)
|
w, ok := other.(*WireError)
|
||||||
if !ok {
|
if !ok || w == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return err.Code == w.Code
|
return err.Code == w.Code
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ package jsonrpc2_test
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -147,6 +148,29 @@ func TestDecodeIDOnlyMessageIsResponse(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestWireErrorIs checks the errors.Is behavior of WireError, including that
|
||||||
|
// comparing against a typed-nil *WireError does not panic.
|
||||||
|
func TestWireErrorIs(t *testing.T) {
|
||||||
|
err := jsonrpc2.NewError(-32600, "invalid request")
|
||||||
|
for _, test := range []struct {
|
||||||
|
name string
|
||||||
|
target error
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{name: "same code", target: jsonrpc2.NewError(-32600, "other message"), want: true},
|
||||||
|
{name: "different code", target: jsonrpc2.NewError(-32601, "invalid request"), want: false},
|
||||||
|
{name: "typed nil WireError", target: (*jsonrpc2.WireError)(nil), want: false},
|
||||||
|
{name: "untyped nil", target: nil, want: false},
|
||||||
|
{name: "other error type", target: errors.New("invalid request"), want: false},
|
||||||
|
} {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
if got := errors.Is(err, test.target); got != test.want {
|
||||||
|
t.Errorf("errors.Is(%v, %v) = %v, want %v", err, test.target, got, test.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func checkJSON(t *testing.T, got, want []byte) {
|
func checkJSON(t *testing.T, got, want []byte) {
|
||||||
// compare the compact form, to allow for formatting differences
|
// compare the compact form, to allow for formatting differences
|
||||||
g := &bytes.Buffer{}
|
g := &bytes.Buffer{}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue