307 lines
7.2 KiB
Go
307 lines
7.2 KiB
Go
|
|
package jsonrpc
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"github.com/stretchr/testify/require"
|
||
|
|
)
|
||
|
|
|
||
|
|
type ComplexData struct {
|
||
|
|
Foo string `json:"foo"`
|
||
|
|
Bar int `json:"bar"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type StaticError struct{}
|
||
|
|
|
||
|
|
func (e *StaticError) Error() string { return "static error" }
|
||
|
|
|
||
|
|
// Define the error types
|
||
|
|
type SimpleError struct {
|
||
|
|
Message string
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *SimpleError) Error() string {
|
||
|
|
return e.Message
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *SimpleError) FromJSONRPCError(jerr JSONRPCError) error {
|
||
|
|
e.Message = jerr.Message
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *SimpleError) ToJSONRPCError() (JSONRPCError, error) {
|
||
|
|
return JSONRPCError{Message: e.Message}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
var _ RPCErrorCodec = (*SimpleError)(nil)
|
||
|
|
|
||
|
|
type DataStringError struct {
|
||
|
|
Message string `json:"message"`
|
||
|
|
Data string `json:"data"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *DataStringError) Error() string {
|
||
|
|
return e.Message
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *DataStringError) FromJSONRPCError(jerr JSONRPCError) error {
|
||
|
|
e.Message = jerr.Message
|
||
|
|
data, ok := jerr.Data.(string)
|
||
|
|
if !ok {
|
||
|
|
return fmt.Errorf("expected string data, got %T", jerr.Data)
|
||
|
|
}
|
||
|
|
|
||
|
|
e.Data = data
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *DataStringError) ToJSONRPCError() (JSONRPCError, error) {
|
||
|
|
return JSONRPCError{Message: e.Message, Data: e.Data}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
var _ RPCErrorCodec = (*DataStringError)(nil)
|
||
|
|
|
||
|
|
type DataComplexError struct {
|
||
|
|
Message string
|
||
|
|
internalData ComplexData
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *DataComplexError) Error() string {
|
||
|
|
return e.Message
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *DataComplexError) FromJSONRPCError(jerr JSONRPCError) error {
|
||
|
|
e.Message = jerr.Message
|
||
|
|
data, ok := jerr.Data.(json.RawMessage)
|
||
|
|
if !ok {
|
||
|
|
return fmt.Errorf("expected string data, got %T", jerr.Data)
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := json.Unmarshal(data, &e.internalData); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *DataComplexError) ToJSONRPCError() (JSONRPCError, error) {
|
||
|
|
data, err := json.Marshal(e.internalData)
|
||
|
|
if err != nil {
|
||
|
|
return JSONRPCError{}, err
|
||
|
|
}
|
||
|
|
return JSONRPCError{Message: e.Message, Data: data}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
var _ RPCErrorCodec = (*DataComplexError)(nil)
|
||
|
|
|
||
|
|
type MetaError struct {
|
||
|
|
Message string
|
||
|
|
Details string
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *MetaError) Error() string {
|
||
|
|
return e.Message
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *MetaError) MarshalJSON() ([]byte, error) {
|
||
|
|
return json.Marshal(struct {
|
||
|
|
Message string `json:"message"`
|
||
|
|
Details string `json:"details"`
|
||
|
|
}{
|
||
|
|
Message: e.Message,
|
||
|
|
Details: e.Details,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *MetaError) UnmarshalJSON(data []byte) error {
|
||
|
|
var temp struct {
|
||
|
|
Message string `json:"message"`
|
||
|
|
Details string `json:"details"`
|
||
|
|
}
|
||
|
|
if err := json.Unmarshal(data, &temp); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
e.Message = temp.Message
|
||
|
|
e.Details = temp.Details
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
type ComplexError struct {
|
||
|
|
Message string
|
||
|
|
Data ComplexData
|
||
|
|
Details string
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *ComplexError) Error() string {
|
||
|
|
return e.Message
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *ComplexError) MarshalJSON() ([]byte, error) {
|
||
|
|
return json.Marshal(struct {
|
||
|
|
Message string `json:"message"`
|
||
|
|
Details string `json:"details"`
|
||
|
|
Data any `json:"data"`
|
||
|
|
}{
|
||
|
|
Details: e.Details,
|
||
|
|
Message: e.Message,
|
||
|
|
Data: e.Data,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *ComplexError) UnmarshalJSON(data []byte) error {
|
||
|
|
var temp struct {
|
||
|
|
Message string `json:"message"`
|
||
|
|
Details string `json:"details"`
|
||
|
|
Data ComplexData `json:"data"`
|
||
|
|
}
|
||
|
|
if err := json.Unmarshal(data, &temp); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
e.Details = temp.Details
|
||
|
|
e.Message = temp.Message
|
||
|
|
e.Data = temp.Data
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRespErrorVal(t *testing.T) {
|
||
|
|
// Initialize the Errors struct and register error types
|
||
|
|
errorsMap := NewErrors()
|
||
|
|
errorsMap.Register(1000, new(*StaticError))
|
||
|
|
errorsMap.Register(1001, new(*SimpleError))
|
||
|
|
errorsMap.Register(1002, new(*DataStringError))
|
||
|
|
errorsMap.Register(1003, new(*DataComplexError))
|
||
|
|
errorsMap.Register(1004, new(*MetaError))
|
||
|
|
errorsMap.Register(1005, new(*ComplexError))
|
||
|
|
|
||
|
|
// Define test cases
|
||
|
|
testCases := []struct {
|
||
|
|
name string
|
||
|
|
respError *JSONRPCError
|
||
|
|
expectedType interface{}
|
||
|
|
expectedMessage string
|
||
|
|
verify func(t *testing.T, err error)
|
||
|
|
}{
|
||
|
|
{
|
||
|
|
name: "StaticError",
|
||
|
|
respError: &JSONRPCError{
|
||
|
|
Code: 1000,
|
||
|
|
Message: "this is ignored",
|
||
|
|
},
|
||
|
|
expectedType: &StaticError{},
|
||
|
|
expectedMessage: "static error",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "SimpleError",
|
||
|
|
respError: &JSONRPCError{
|
||
|
|
Code: 1001,
|
||
|
|
Message: "simple error occurred",
|
||
|
|
},
|
||
|
|
expectedType: &SimpleError{},
|
||
|
|
expectedMessage: "simple error occurred",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "DataStringError",
|
||
|
|
respError: &JSONRPCError{
|
||
|
|
Code: 1002,
|
||
|
|
Message: "data error occurred",
|
||
|
|
Data: "additional data",
|
||
|
|
},
|
||
|
|
expectedType: &DataStringError{},
|
||
|
|
expectedMessage: "data error occurred",
|
||
|
|
verify: func(t *testing.T, err error) {
|
||
|
|
require.IsType(t, &DataStringError{}, err)
|
||
|
|
require.Equal(t, "data error occurred", err.Error())
|
||
|
|
require.Equal(t, "additional data", err.(*DataStringError).Data)
|
||
|
|
},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "DataComplexError",
|
||
|
|
respError: &JSONRPCError{
|
||
|
|
Code: 1003,
|
||
|
|
Message: "data error occurred",
|
||
|
|
Data: json.RawMessage(`{"foo":"boop","bar":101}`),
|
||
|
|
},
|
||
|
|
expectedType: &DataComplexError{},
|
||
|
|
expectedMessage: "data error occurred",
|
||
|
|
verify: func(t *testing.T, err error) {
|
||
|
|
require.Equal(t, ComplexData{Foo: "boop", Bar: 101}, err.(*DataComplexError).internalData)
|
||
|
|
},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "MetaError",
|
||
|
|
respError: &JSONRPCError{
|
||
|
|
Code: 1004,
|
||
|
|
Message: "meta error occurred",
|
||
|
|
Meta: func() json.RawMessage {
|
||
|
|
me := &MetaError{
|
||
|
|
Message: "meta error occurred",
|
||
|
|
Details: "meta details",
|
||
|
|
}
|
||
|
|
metaData, _ := me.MarshalJSON()
|
||
|
|
return metaData
|
||
|
|
}(),
|
||
|
|
},
|
||
|
|
expectedType: &MetaError{},
|
||
|
|
expectedMessage: "meta error occurred",
|
||
|
|
verify: func(t *testing.T, err error) {
|
||
|
|
// details will also be included in the error message since it implements the marshable interface
|
||
|
|
require.Equal(t, "meta details", err.(*MetaError).Details)
|
||
|
|
},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "ComplexError",
|
||
|
|
respError: &JSONRPCError{
|
||
|
|
Code: 1005,
|
||
|
|
Message: "complex error occurred",
|
||
|
|
Data: json.RawMessage(`"complex data"`),
|
||
|
|
Meta: func() json.RawMessage {
|
||
|
|
ce := &ComplexError{
|
||
|
|
Message: "complex error occurred",
|
||
|
|
Details: "complex details",
|
||
|
|
Data: ComplexData{Foo: "foo", Bar: 42},
|
||
|
|
}
|
||
|
|
metaData, _ := ce.MarshalJSON()
|
||
|
|
return metaData
|
||
|
|
}(),
|
||
|
|
},
|
||
|
|
expectedType: &ComplexError{},
|
||
|
|
expectedMessage: "complex error occurred",
|
||
|
|
verify: func(t *testing.T, err error) {
|
||
|
|
require.Equal(t, ComplexData{Foo: "foo", Bar: 42}, err.(*ComplexError).Data)
|
||
|
|
require.Equal(t, "complex details", err.(*ComplexError).Details)
|
||
|
|
},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "UnregisteredError",
|
||
|
|
respError: &JSONRPCError{
|
||
|
|
Code: 9999,
|
||
|
|
Message: "unregistered error occurred",
|
||
|
|
Data: json.RawMessage(`"some data"`),
|
||
|
|
},
|
||
|
|
expectedType: &JSONRPCError{},
|
||
|
|
expectedMessage: "unregistered error occurred",
|
||
|
|
verify: func(t *testing.T, err error) {
|
||
|
|
require.Equal(t, json.RawMessage(`"some data"`), err.(*JSONRPCError).Data)
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, tc := range testCases {
|
||
|
|
tc := tc
|
||
|
|
t.Run(tc.name, func(t *testing.T) {
|
||
|
|
errValue := tc.respError.val(&errorsMap)
|
||
|
|
errInterface := errValue.Interface()
|
||
|
|
err, ok := errInterface.(error)
|
||
|
|
require.True(t, ok, "returned value does not implement error interface")
|
||
|
|
require.IsType(t, tc.expectedType, err)
|
||
|
|
require.Equal(t, tc.expectedMessage, err.Error())
|
||
|
|
if tc.verify != nil {
|
||
|
|
tc.verify(t, err)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|