Browse Source

rpc: simplify and consolidate response construction (#7725)

Responses are constructed from requests using MakeResponse, MakeError, and
MakeErrorf. This ensures the response is always paired with the correct ID,
makes cases where there is no ID more explicit at the usage site, and
consolidates the handling of error introspection across transports.

The logic for unpacking errors and assigning JSON-RPC response types was
previously duplicated in three places. Consolidate it in the types package for
the RPC subsystem.

* update test cases
pull/7726/head
M. J. Fromberger 2 years ago
committed by GitHub
parent
commit
fbe86ea645
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 144 additions and 160 deletions
  1. +2
    -2
      internal/rpc/core/events.go
  2. +2
    -2
      light/provider/http/http_test.go
  3. +1
    -5
      light/rpc/client.go
  4. +13
    -28
      rpc/jsonrpc/server/http_json_handler.go
  5. +3
    -5
      rpc/jsonrpc/server/http_json_handler_test.go
  6. +9
    -11
      rpc/jsonrpc/server/http_server_test.go
  7. +5
    -21
      rpc/jsonrpc/server/http_uri_handler.go
  8. +12
    -30
      rpc/jsonrpc/server/ws_handler.go
  9. +79
    -45
      rpc/jsonrpc/types/types.go
  10. +18
    -11
      rpc/jsonrpc/types/types_test.go

+ 2
- 2
internal/rpc/core/events.go View File

@ -67,7 +67,7 @@ func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes
return
} else if errors.Is(err, tmpubsub.ErrTerminated) {
// The subscription was terminated by the publisher.
resp := rpctypes.RPCServerError(subscriptionID, err)
resp := callInfo.RPCRequest.MakeError(err)
ok := callInfo.WSConn.TryWriteRPCResponse(opctx, resp)
if !ok {
env.Logger.Info("Unable to write response (slow client)",
@ -77,7 +77,7 @@ func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes
}
// We have a message to deliver to the client.
resp := rpctypes.NewRPCSuccessResponse(subscriptionID, &coretypes.ResultEvent{
resp := callInfo.RPCRequest.MakeResponse(&coretypes.ResultEvent{
Query: query,
Data: msg.Data(),
Events: msg.Events(),


+ 2
- 2
light/provider/http/http_test.go View File

@ -80,12 +80,12 @@ func TestProvider(t *testing.T) {
lb, err = p.LightBlock(ctx, 9001)
require.Error(t, err)
require.Nil(t, lb)
assert.Equal(t, provider.ErrHeightTooHigh, err)
assert.ErrorIs(t, err, provider.ErrHeightTooHigh)
lb, err = p.LightBlock(ctx, 1)
require.Error(t, err)
require.Nil(t, lb)
assert.Equal(t, provider.ErrLightBlockNotFound, err)
assert.ErrorIs(t, err, provider.ErrLightBlockNotFound)
// if the provider is unable to provide four more blocks then we should return
// an unreliable peer error


+ 1
- 5
light/rpc/client.go View File

@ -655,11 +655,7 @@ func (c *Client) SubscribeWS(ctx context.Context, query string) (*coretypes.Resu
case resultEvent := <-out:
// We should have a switch here that performs a validation
// depending on the event's type.
callInfo.WSConn.TryWriteRPCResponse(bctx,
rpctypes.NewRPCSuccessResponse(
rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", callInfo.RPCRequest.ID)),
resultEvent,
))
callInfo.WSConn.TryWriteRPCResponse(bctx, callInfo.RPCRequest.MakeResponse(resultEvent))
case <-bctx.Done():
return
}


+ 13
- 28
rpc/jsonrpc/server/http_json_handler.go View File

@ -13,7 +13,6 @@ import (
"strconv"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/rpc/coretypes"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
)
@ -25,15 +24,15 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
// For POST requests, reject a non-root URL path. This should not happen
// in the standard configuration, since the wrapper checks the path.
if hreq.URL.Path != "/" {
writeRPCResponse(w, logger, rpctypes.RPCInvalidRequestError(
nil, fmt.Errorf("invalid path: %q", hreq.URL.Path)))
writeRPCResponse(w, logger, rpctypes.RPCRequest{}.MakeErrorf(
rpctypes.CodeInvalidRequest, "invalid path: %q", hreq.URL.Path))
return
}
b, err := io.ReadAll(hreq.Body)
if err != nil {
writeRPCResponse(w, logger, rpctypes.RPCInvalidRequestError(
nil, fmt.Errorf("reading request body: %w", err)))
writeRPCResponse(w, logger, rpctypes.RPCRequest{}.MakeErrorf(
rpctypes.CodeInvalidRequest, "reading request body: %v", err))
return
}
@ -46,7 +45,8 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
requests, err := parseRequests(b)
if err != nil {
writeRPCResponse(w, logger, rpctypes.RPCParseError(fmt.Errorf("decoding request: %w", err)))
writeRPCResponse(w, logger, rpctypes.RPCRequest{}.MakeErrorf(
rpctypes.CodeParseError, "decoding request: %v", err))
return
}
@ -60,7 +60,7 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
rpcFunc, ok := funcMap[req.Method]
if !ok || rpcFunc.ws {
responses = append(responses, rpctypes.RPCMethodNotFoundError(req.ID))
responses = append(responses, req.MakeErrorf(rpctypes.CodeMethodNotFound, req.Method))
continue
}
@ -71,32 +71,17 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
})
args, err := parseParams(ctx, rpcFunc, req.Params)
if err != nil {
responses = append(responses, rpctypes.RPCInvalidParamsError(
req.ID, fmt.Errorf("converting JSON parameters: %w", err)))
responses = append(responses,
req.MakeErrorf(rpctypes.CodeInvalidParams, "converting JSON parameters: %v", err))
continue
}
returns := rpcFunc.f.Call(args)
logger.Debug("HTTPJSONRPC", "method", req.Method, "args", args, "returns", returns)
result, err := unreflectResult(returns)
switch e := err.(type) {
// if no error then return a success response
case nil:
responses = append(responses, rpctypes.NewRPCSuccessResponse(req.ID, result))
// if this already of type RPC error then forward that error
case *rpctypes.RPCError:
responses = append(responses, rpctypes.NewRPCErrorResponse(req.ID, e.Code, e.Message, e.Data))
default: // we need to unwrap the error and parse it accordingly
switch errors.Unwrap(err) {
// check if the error was due to an invald request
case coretypes.ErrZeroOrNegativeHeight, coretypes.ErrZeroOrNegativePerPage,
coretypes.ErrPageOutOfRange, coretypes.ErrInvalidRequest:
responses = append(responses, rpctypes.RPCInvalidRequestError(req.ID, err))
// lastly default all remaining errors as internal errors
default: // includes ctypes.ErrHeightNotAvailable and ctypes.ErrHeightExceedsChainHead
responses = append(responses, rpctypes.RPCInternalError(req.ID, err))
}
if err == nil {
responses = append(responses, req.MakeResponse(result))
} else {
responses = append(responses, req.MakeError(err))
}
}


+ 3
- 5
rpc/jsonrpc/server/http_json_handler_test.go View File

@ -63,14 +63,12 @@ func TestRPCParams(t *testing.T) {
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, req)
res := rec.Result()
defer res.Body.Close()
// Always expecting back a JSONRPCResponse
assert.NotZero(t, res.StatusCode, "#%d: should always return code", i)
blob, err := io.ReadAll(res.Body)
if err != nil {
t.Errorf("#%d: err reading body: %v", i, err)
continue
}
require.NoError(t, err, "#%d: reading body", i)
require.NoError(t, res.Body.Close())
recv := new(rpctypes.RPCResponse)
assert.Nil(t, json.Unmarshal(blob, recv), "#%d: expecting successful parsing of an RPCResponse:\nblob: %s", i, blob)


+ 9
- 11
rpc/jsonrpc/server/http_server_test.go View File

@ -3,7 +3,6 @@ package server
import (
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"net"
@ -126,16 +125,15 @@ func TestServeTLS(t *testing.T) {
}
func TestWriteRPCResponse(t *testing.T) {
id := rpctypes.JSONRPCIntID(-1)
req := rpctypes.RPCRequest{ID: rpctypes.JSONRPCIntID(-1)}
// one argument
w := httptest.NewRecorder()
logger := log.NewNopLogger()
writeRPCResponse(w, logger,
rpctypes.NewRPCSuccessResponse(id, &sampleResult{"hello"}))
writeRPCResponse(w, logger, req.MakeResponse(&sampleResult{"hello"}))
resp := w.Result()
body, err := io.ReadAll(resp.Body)
_ = resp.Body.Close()
require.NoError(t, resp.Body.Close())
require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
@ -145,12 +143,12 @@ func TestWriteRPCResponse(t *testing.T) {
// multiple arguments
w = httptest.NewRecorder()
writeRPCResponse(w, logger,
rpctypes.NewRPCSuccessResponse(id, &sampleResult{"hello"}),
rpctypes.NewRPCSuccessResponse(id, &sampleResult{"world"}),
req.MakeResponse(&sampleResult{"hello"}),
req.MakeResponse(&sampleResult{"world"}),
)
resp = w.Result()
body, err = io.ReadAll(resp.Body)
_ = resp.Body.Close()
require.NoError(t, resp.Body.Close())
require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode)
@ -162,11 +160,11 @@ func TestWriteRPCResponse(t *testing.T) {
func TestWriteHTTPResponse(t *testing.T) {
w := httptest.NewRecorder()
logger := log.NewNopLogger()
writeHTTPResponse(w, logger,
rpctypes.RPCInternalError(rpctypes.JSONRPCIntID(-1), errors.New("foo")))
req := rpctypes.RPCRequest{ID: rpctypes.JSONRPCIntID(-1)}
writeHTTPResponse(w, logger, req.MakeErrorf(rpctypes.CodeInternalError, "foo"))
resp := w.Result()
body, err := io.ReadAll(resp.Body)
_ = resp.Body.Close()
require.NoError(t, resp.Body.Close())
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))


+ 5
- 21
rpc/jsonrpc/server/http_uri_handler.go View File

@ -4,7 +4,6 @@ import (
"context"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"net/http"
"reflect"
@ -12,7 +11,6 @@ import (
"strings"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/rpc/coretypes"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
)
@ -33,29 +31,15 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit
fmt.Fprintln(w, err.Error())
return
}
jreq := rpctypes.RPCRequest{ID: uriReqID}
outs := rpcFunc.f.Call(args)
logger.Debug("HTTPRestRPC", "method", req.URL.Path, "args", args, "returns", outs)
result, err := unreflectResult(outs)
switch e := err.(type) {
// if no error then return a success response
case nil:
writeHTTPResponse(w, logger, rpctypes.NewRPCSuccessResponse(uriReqID, result))
// if this already of type RPC error then forward that error.
case *rpctypes.RPCError:
writeHTTPResponse(w, logger, rpctypes.NewRPCErrorResponse(uriReqID, e.Code, e.Message, e.Data))
default: // we need to unwrap the error and parse it accordingly
switch errors.Unwrap(err) {
case coretypes.ErrZeroOrNegativeHeight,
coretypes.ErrZeroOrNegativePerPage,
coretypes.ErrPageOutOfRange,
coretypes.ErrInvalidRequest:
writeHTTPResponse(w, logger, rpctypes.RPCInvalidRequestError(uriReqID, err))
default: // ctypes.ErrHeightNotAvailable, ctypes.ErrHeightExceedsChainHead:
writeHTTPResponse(w, logger, rpctypes.RPCInternalError(uriReqID, err))
}
if err == nil {
writeHTTPResponse(w, logger, jreq.MakeResponse(result))
} else {
writeHTTPResponse(w, logger, jreq.MakeError(err))
}
}
}


+ 12
- 30
rpc/jsonrpc/server/ws_handler.go View File

@ -3,7 +3,6 @@ package server
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"runtime/debug"
@ -13,7 +12,6 @@ import (
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/rpc/client"
"github.com/tendermint/tendermint/rpc/coretypes"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
)
@ -276,8 +274,10 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) {
if !ok {
err = fmt.Errorf("WSJSONRPC: %v", r)
}
req := rpctypes.RPCRequest{ID: uriReqID}
wsc.Logger.Error("Panic in WSJSONRPC handler", "err", err, "stack", string(debug.Stack()))
if err := wsc.WriteRPCResponse(writeCtx, rpctypes.RPCInternalError(rpctypes.JSONRPCIntID(-1), err)); err != nil {
if err := wsc.WriteRPCResponse(writeCtx,
req.MakeErrorf(rpctypes.CodeInternalError, "Panic in handler: %v", err)); err != nil {
wsc.Logger.Error("error writing RPC response", "err", err)
}
go wsc.readRoutine(ctx)
@ -317,7 +317,7 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) {
err = dec.Decode(&request)
if err != nil {
if err := wsc.WriteRPCResponse(writeCtx,
rpctypes.RPCParseError(fmt.Errorf("error unmarshaling request: %w", err))); err != nil {
request.MakeErrorf(rpctypes.CodeParseError, "unmarshaling request: %v", err)); err != nil {
wsc.Logger.Error("error writing RPC response", "err", err)
}
continue
@ -336,7 +336,8 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) {
// Now, fetch the RPCFunc and execute it.
rpcFunc := wsc.funcMap[request.Method]
if rpcFunc == nil {
if err := wsc.WriteRPCResponse(writeCtx, rpctypes.RPCMethodNotFoundError(request.ID)); err != nil {
if err := wsc.WriteRPCResponse(writeCtx,
request.MakeErrorf(rpctypes.CodeMethodNotFound, request.Method)); err != nil {
wsc.Logger.Error("error writing RPC response", "err", err)
}
continue
@ -348,9 +349,8 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) {
})
args, err := parseParams(fctx, rpcFunc, request.Params)
if err != nil {
if err := wsc.WriteRPCResponse(writeCtx, rpctypes.RPCInvalidParamsError(
request.ID, fmt.Errorf("error converting json params to arguments: %w", err)),
); err != nil {
if err := wsc.WriteRPCResponse(writeCtx, request.MakeErrorf(rpctypes.CodeInvalidParams,
"converting JSON parameters: %v", err)); err != nil {
wsc.Logger.Error("error writing RPC response", "err", err)
}
continue
@ -363,32 +363,14 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) {
var resp rpctypes.RPCResponse
result, err := unreflectResult(returns)
switch e := err.(type) {
// if no error then return a success response
case nil:
resp = rpctypes.NewRPCSuccessResponse(request.ID, result)
// if this already of type RPC error then forward that error
case *rpctypes.RPCError:
resp = rpctypes.NewRPCErrorResponse(request.ID, e.Code, e.Message, e.Data)
default: // we need to unwrap the error and parse it accordingly
switch errors.Unwrap(err) {
// check if the error was due to an invald request
case coretypes.ErrZeroOrNegativeHeight, coretypes.ErrZeroOrNegativePerPage,
coretypes.ErrPageOutOfRange, coretypes.ErrInvalidRequest:
resp = rpctypes.RPCInvalidRequestError(request.ID, err)
// lastly default all remaining errors as internal errors
default: // includes ctypes.ErrHeightNotAvailable and ctypes.ErrHeightExceedsChainHead
resp = rpctypes.RPCInternalError(request.ID, err)
}
if err == nil {
resp = request.MakeResponse(result)
} else {
resp = request.MakeError(err)
}
if err := wsc.WriteRPCResponse(writeCtx, resp); err != nil {
wsc.Logger.Error("error writing RPC response", "err", err)
}
}
}
}


+ 79
- 45
rpc/jsonrpc/types/types.go View File

@ -3,10 +3,13 @@ package types
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"reflect"
"strings"
"github.com/tendermint/tendermint/rpc/coretypes"
)
// a wrapper to emulate a sum type: jsonrpcid = string | int
@ -43,6 +46,33 @@ func idFromInterface(idInterface interface{}) (jsonrpcid, error) {
}
}
// ErrorCode is the type of JSON-RPC error codes.
type ErrorCode int
func (e ErrorCode) String() string {
if s, ok := errorCodeString[e]; ok {
return s
}
return fmt.Sprintf("server error: code %d", e)
}
// Constants defining the standard JSON-RPC error codes.
const (
CodeParseError ErrorCode = -32700 // Invalid JSON received by the server
CodeInvalidRequest ErrorCode = -32600 // The JSON sent is not a valid request object
CodeMethodNotFound ErrorCode = -32601 // The method does not exist or is unavailable
CodeInvalidParams ErrorCode = -32602 // Invalid method parameters
CodeInternalError ErrorCode = -32603 // Internal JSON-RPC error
)
var errorCodeString = map[ErrorCode]string{
CodeParseError: "Parse error",
CodeInvalidRequest: "Invalid request",
CodeMethodNotFound: "Method not found",
CodeInvalidParams: "Invalid params",
CodeInternalError: "Internal error",
}
//----------------------------------------
// REQUEST
@ -94,6 +124,55 @@ func (req RPCRequest) String() string {
return fmt.Sprintf("RPCRequest{%s %s/%X}", req.ID, req.Method, req.Params)
}
// MakeResponse constructs a success response to req with the given result. If
// there is an error marshaling result to JSON, it returns an error response.
func (req RPCRequest) MakeResponse(result interface{}) RPCResponse {
data, err := json.Marshal(result)
if err != nil {
return req.MakeErrorf(CodeInternalError, "marshaling result: %v", err)
}
return RPCResponse{ID: req.ID, Result: data}
}
// MakeErrorf constructs an error response to req with the given code and a
// message constructed by formatting msg with args.
func (req RPCRequest) MakeErrorf(code ErrorCode, msg string, args ...interface{}) RPCResponse {
return RPCResponse{
ID: req.ID,
Error: &RPCError{
Code: int(code),
Message: code.String(),
Data: fmt.Sprintf(msg, args...),
},
}
}
// MakeError constructs an error response to req from the given error value.
// This function will panic if err == nil.
func (req RPCRequest) MakeError(err error) RPCResponse {
if err == nil {
panic("cannot construct an error response for nil")
}
if e, ok := err.(*RPCError); ok {
return RPCResponse{ID: req.ID, Error: e}
}
if errors.Is(err, coretypes.ErrZeroOrNegativeHeight) ||
errors.Is(err, coretypes.ErrZeroOrNegativePerPage) ||
errors.Is(err, coretypes.ErrPageOutOfRange) ||
errors.Is(err, coretypes.ErrInvalidRequest) {
return RPCResponse{ID: req.ID, Error: &RPCError{
Code: int(CodeInvalidRequest),
Message: CodeInvalidRequest.String(),
Data: err.Error(),
}}
}
return RPCResponse{ID: req.ID, Error: &RPCError{
Code: int(CodeInternalError),
Message: CodeInternalError.String(),
Data: err.Error(),
}}
}
// ParamsToRequest constructs a new RPCRequest with the given ID, method, and parameters.
func ParamsToRequest(id jsonrpcid, method string, params interface{}) (RPCRequest, error) {
payload, err := json.Marshal(params)
@ -169,21 +248,6 @@ func (resp RPCResponse) MarshalJSON() ([]byte, error) {
})
}
func NewRPCSuccessResponse(id jsonrpcid, res interface{}) RPCResponse {
result, err := json.Marshal(res)
if err != nil {
return RPCInternalError(id, fmt.Errorf("error marshaling response: %w", err))
}
return RPCResponse{ID: id, Result: result}
}
func NewRPCErrorResponse(id jsonrpcid, code int, msg string, data string) RPCResponse {
return RPCResponse{
ID: id,
Error: &RPCError{Code: code, Message: msg, Data: data},
}
}
func (resp RPCResponse) String() string {
if resp.Error == nil {
return fmt.Sprintf("RPCResponse{%s %X}", resp.ID, resp.Result)
@ -191,36 +255,6 @@ func (resp RPCResponse) String() string {
return fmt.Sprintf("RPCResponse{%s %v}", resp.ID, resp.Error)
}
// From the JSON-RPC 2.0 spec:
// If there was an error in detecting the id in the Request object (e.g. Parse
// error/Invalid Request), it MUST be Null.
func RPCParseError(err error) RPCResponse {
return NewRPCErrorResponse(nil, -32700, "Parse error", err.Error())
}
// From the JSON-RPC 2.0 spec:
// If there was an error in detecting the id in the Request object (e.g. Parse
// error/Invalid Request), it MUST be Null.
func RPCInvalidRequestError(id jsonrpcid, err error) RPCResponse {
return NewRPCErrorResponse(id, -32600, "Invalid Request", err.Error())
}
func RPCMethodNotFoundError(id jsonrpcid) RPCResponse {
return NewRPCErrorResponse(id, -32601, "Method not found", "")
}
func RPCInvalidParamsError(id jsonrpcid, err error) RPCResponse {
return NewRPCErrorResponse(id, -32602, "Invalid params", err.Error())
}
func RPCInternalError(id jsonrpcid, err error) RPCResponse {
return NewRPCErrorResponse(id, -32603, "Internal error", err.Error())
}
func RPCServerError(id jsonrpcid, err error) RPCResponse {
return NewRPCErrorResponse(id, -32000, "Server error", err.Error())
}
//----------------------------------------
// WSRPCConnection represents a websocket connection.


+ 18
- 11
rpc/jsonrpc/types/types_test.go View File

@ -2,7 +2,6 @@ package types
import (
"encoding/json"
"errors"
"fmt"
"testing"
@ -32,20 +31,27 @@ var responseTests = []responseTest{
func TestResponses(t *testing.T) {
for _, tt := range responseTests {
jsonid := tt.id
a := NewRPCSuccessResponse(jsonid, &SampleResult{"hello"})
b, _ := json.Marshal(a)
req := RPCRequest{
ID: tt.id,
Method: "whatever",
}
a := req.MakeResponse(&SampleResult{"hello"})
b, err := json.Marshal(a)
require.NoError(t, err)
s := fmt.Sprintf(`{"jsonrpc":"2.0","id":%v,"result":{"Value":"hello"}}`, tt.expected)
assert.Equal(t, s, string(b))
d := RPCParseError(errors.New("hello world"))
e, _ := json.Marshal(d)
f := `{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":"hello world"}}`
d := req.MakeErrorf(CodeParseError, "hello world")
e, err := json.Marshal(d)
require.NoError(t, err)
f := fmt.Sprintf(`{"jsonrpc":"2.0","id":%v,"error":{"code":-32700,"message":"Parse error","data":"hello world"}}`, tt.expected)
assert.Equal(t, f, string(e))
g := RPCMethodNotFoundError(jsonid)
h, _ := json.Marshal(g)
i := fmt.Sprintf(`{"jsonrpc":"2.0","id":%v,"error":{"code":-32601,"message":"Method not found"}}`, tt.expected)
g := req.MakeErrorf(CodeMethodNotFound, "foo")
h, err := json.Marshal(g)
require.NoError(t, err)
i := fmt.Sprintf(`{"jsonrpc":"2.0","id":%v,"error":{"code":-32601,"message":"Method not found","data":"foo"}}`, tt.expected)
assert.Equal(t, string(h), i)
}
}
@ -59,7 +65,8 @@ func TestUnmarshallResponses(t *testing.T) {
)
require.NoError(t, err)
a := NewRPCSuccessResponse(tt.id, &SampleResult{"hello"})
req := RPCRequest{ID: tt.id}
a := req.MakeResponse(&SampleResult{"hello"})
assert.Equal(t, *response, a)
}
response := &RPCResponse{}


Loading…
Cancel
Save