kaspad/rpc/model/command_parse_test.go
stasatdaglabs 3d45c8de50
[NOD-1130] Integrate RPC with the new architecture (#807)
* [NOD-1130] Delete rpcadapters.go.

* [NOD-1130] Delete p2p. Move rpc to top level.

* [NOD-1130] Remove DAGParams from rpcserverConfig.

* [NOD-1130] Remove rpcserverPeer, rpcserverConnManager, rpcserverSyncManager, and rpcserverConfig.

* [NOD-1130] Remove wallet RPC commands.

* [NOD-1130] Remove wallet RPC commands.

* [NOD-1130] Remove connmgr and peer.

* [NOD-1130] Move rpcmodel into rpc.

* [NOD-1130] Implement ConnectionCount.

* [NOD-1130] Remove ping and node RPC commands.

* [NOD-1130] Dummify handleGetNetTotals.

* [NOD-1130] Add NetConnection to Peer.

* [NOD-1130] Fix merge errors.

* [NOD-1130] Implement Peers.

* [NOD-1130] Fix HandleGetConnectedPeerInfo.

* [NOD-1130] Fix SendRawTransaction.

* [NOD-1130] Rename addManualNode to connect and removeManualNode to disconnect.

* [NOD-1130] Add a stub for AddBlock.

* [NOD-1130] Fix tests.

* [NOD-1130] Replace half-baked contents of RemoveConnection with a stub.

* [NOD-1130] Fix merge errors.

* [NOD-1130] Make golint happy.

* [NOD-1130] Get rid of something weird.

* [NOD-1130] Rename minerClient back to client.

* [NOD-1130] Add a few fields to GetConnectedPeerInfoResult.

* [NOD-1130] Rename oneTry to isPermanent.

* [NOD-1130] Implement ConnectionCount in NetAdapter.

* [NOD-1130] Move RawMempoolVerbose out of mempool.

* [NOD-1130] Move isSynced into the mining package.

* [NOD-1130] Fix a compilation error.

* [NOD-1130] Make golint happy.

* [NOD-1130] Fix merge errors.
2020-07-22 10:26:39 +03:00

529 lines
12 KiB
Go

// Copyright (c) 2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package model_test
import (
"encoding/json"
"github.com/pkg/errors"
"math"
"reflect"
"testing"
"github.com/kaspanet/kaspad/rpc/model"
)
// TestAssignField tests the assignField function handles supported combinations
// properly.
func TestAssignField(t *testing.T) {
t.Parallel()
tests := []struct {
name string
dest interface{}
src interface{}
expected interface{}
}{
{
name: "same types",
dest: int8(0),
src: int8(100),
expected: int8(100),
},
{
name: "same types - more source pointers",
dest: int8(0),
src: func() interface{} {
i := int8(100)
return &i
}(),
expected: int8(100),
},
{
name: "same types - more dest pointers",
dest: func() interface{} {
i := int8(0)
return &i
}(),
src: int8(100),
expected: int8(100),
},
{
name: "convertible types - more source pointers",
dest: int16(0),
src: func() interface{} {
i := int8(100)
return &i
}(),
expected: int16(100),
},
{
name: "convertible types - both pointers",
dest: func() interface{} {
i := int8(0)
return &i
}(),
src: func() interface{} {
i := int16(100)
return &i
}(),
expected: int8(100),
},
{
name: "convertible types - int16 -> int8",
dest: int8(0),
src: int16(100),
expected: int8(100),
},
{
name: "convertible types - int16 -> uint8",
dest: uint8(0),
src: int16(100),
expected: uint8(100),
},
{
name: "convertible types - uint16 -> int8",
dest: int8(0),
src: uint16(100),
expected: int8(100),
},
{
name: "convertible types - uint16 -> uint8",
dest: uint8(0),
src: uint16(100),
expected: uint8(100),
},
{
name: "convertible types - float32 -> float64",
dest: float64(0),
src: float32(1.5),
expected: float64(1.5),
},
{
name: "convertible types - float64 -> float32",
dest: float32(0),
src: float64(1.5),
expected: float32(1.5),
},
{
name: "convertible types - string -> bool",
dest: false,
src: "true",
expected: true,
},
{
name: "convertible types - string -> int8",
dest: int8(0),
src: "100",
expected: int8(100),
},
{
name: "convertible types - string -> uint8",
dest: uint8(0),
src: "100",
expected: uint8(100),
},
{
name: "convertible types - string -> float32",
dest: float32(0),
src: "1.5",
expected: float32(1.5),
},
{
name: "convertible types - typecase string -> string",
dest: "",
src: func() interface{} {
type foo string
return foo("foo")
}(),
expected: "foo",
},
{
name: "convertible types - string -> array",
dest: [2]string{},
src: `["test","test2"]`,
expected: [2]string{"test", "test2"},
},
{
name: "convertible types - string -> slice",
dest: []string{},
src: `["test","test2"]`,
expected: []string{"test", "test2"},
},
{
name: "convertible types - string -> struct",
dest: struct{ A int }{},
src: `{"A":100}`,
expected: struct{ A int }{100},
},
{
name: "convertible types - string -> map",
dest: map[string]float64{},
src: `{"1Address":1.5}`,
expected: map[string]float64{"1Address": 1.5},
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
dst := reflect.New(reflect.TypeOf(test.dest)).Elem()
src := reflect.ValueOf(test.src)
err := model.TstAssignField(1, "testField", dst, src)
if err != nil {
t.Errorf("Test #%d (%s) unexpected error: %v", i,
test.name, err)
continue
}
// Inidirect through to the base types to ensure their values
// are the same.
for dst.Kind() == reflect.Ptr {
dst = dst.Elem()
}
if !reflect.DeepEqual(dst.Interface(), test.expected) {
t.Errorf("Test #%d (%s) unexpected value - got %v, "+
"want %v", i, test.name, dst.Interface(),
test.expected)
continue
}
}
}
// TestAssignFieldErrors tests the assignField function error paths.
func TestAssignFieldErrors(t *testing.T) {
t.Parallel()
tests := []struct {
name string
dest interface{}
src interface{}
err model.Error
}{
{
name: "general incompatible int -> string",
dest: string(0),
src: int(0),
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "overflow source int -> dest int",
dest: int8(0),
src: int(128),
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "overflow source int -> dest uint",
dest: uint8(0),
src: int(256),
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "int -> float",
dest: float32(0),
src: int(256),
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "overflow source uint64 -> dest int64",
dest: int64(0),
src: uint64(1 << 63),
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "overflow source uint -> dest int",
dest: int8(0),
src: uint(128),
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "overflow source uint -> dest uint",
dest: uint8(0),
src: uint(256),
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "uint -> float",
dest: float32(0),
src: uint(256),
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "float -> int",
dest: int(0),
src: float32(1.0),
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "overflow float64 -> float32",
dest: float32(0),
src: float64(math.MaxFloat64),
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "invalid string -> bool",
dest: true,
src: "foo",
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "invalid string -> int",
dest: int8(0),
src: "foo",
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "overflow string -> int",
dest: int8(0),
src: "128",
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "invalid string -> uint",
dest: uint8(0),
src: "foo",
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "overflow string -> uint",
dest: uint8(0),
src: "256",
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "invalid string -> float",
dest: float32(0),
src: "foo",
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "overflow string -> float",
dest: float32(0),
src: "1.7976931348623157e+308",
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "invalid string -> array",
dest: [3]int{},
src: "foo",
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "invalid string -> slice",
dest: []int{},
src: "foo",
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "invalid string -> struct",
dest: struct{ A int }{},
src: "foo",
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "invalid string -> map",
dest: map[string]int{},
src: "foo",
err: model.Error{ErrorCode: model.ErrInvalidType},
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
dst := reflect.New(reflect.TypeOf(test.dest)).Elem()
src := reflect.ValueOf(test.src)
err := model.TstAssignField(1, "testField", dst, src)
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+
"want %T", i, test.name, err, test.err)
continue
}
var gotRPCModelErr model.Error
errors.As(err, &gotRPCModelErr)
gotErrorCode := gotRPCModelErr.ErrorCode
if gotErrorCode != test.err.ErrorCode {
t.Errorf("Test #%d (%s) mismatched error code - got "+
"%v (%v), want %v", i, test.name, gotErrorCode,
err, test.err.ErrorCode)
continue
}
}
}
// TestNewCommandErrors ensures the error paths of NewCommand behave as expected.
func TestNewCommandErrors(t *testing.T) {
t.Parallel()
tests := []struct {
name string
method string
args []interface{}
err model.Error
}{
{
name: "unregistered command",
method: "bogusCommand",
args: []interface{}{},
err: model.Error{ErrorCode: model.ErrUnregisteredMethod},
},
{
name: "too few parameters to command with required + optional",
method: "getBlock",
args: []interface{}{},
err: model.Error{ErrorCode: model.ErrNumParams},
},
{
name: "too many parameters to command with no optional",
method: "getBlockCount",
args: []interface{}{"123"},
err: model.Error{ErrorCode: model.ErrNumParams},
},
{
name: "incorrect parameter type",
method: "getBlock",
args: []interface{}{1},
err: model.Error{ErrorCode: model.ErrInvalidType},
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
_, err := model.NewCommand(test.method, test.args...)
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+
"want %T", i, test.name, err, test.err)
continue
}
var gotRPCModelErr model.Error
errors.As(err, &gotRPCModelErr)
gotErrorCode := gotRPCModelErr.ErrorCode
if gotErrorCode != test.err.ErrorCode {
t.Errorf("Test #%d (%s) mismatched error code - got "+
"%v (%v), want %v", i, test.name, gotErrorCode,
err, test.err.ErrorCode)
continue
}
}
}
// TestMarshalCommandErrors tests the error paths of the MarshalCommand function.
func TestMarshalCommandErrors(t *testing.T) {
t.Parallel()
tests := []struct {
name string
id interface{}
cmd interface{}
err model.Error
}{
{
name: "unregistered type",
id: 1,
cmd: (*int)(nil),
err: model.Error{ErrorCode: model.ErrUnregisteredMethod},
},
{
name: "nil instance of registered type",
id: 1,
cmd: (*model.GetBlockCmd)(nil),
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "nil instance of registered type",
id: []int{0, 1},
cmd: &model.GetBlockCountCmd{},
err: model.Error{ErrorCode: model.ErrInvalidType},
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
_, err := model.MarshalCommand(test.id, test.cmd)
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+
"want %T", i, test.name, err, test.err)
continue
}
var gotRPCModelErr model.Error
errors.As(err, &gotRPCModelErr)
gotErrorCode := gotRPCModelErr.ErrorCode
if gotErrorCode != test.err.ErrorCode {
t.Errorf("Test #%d (%s) mismatched error code - got "+
"%v (%v), want %v", i, test.name, gotErrorCode,
err, test.err.ErrorCode)
continue
}
}
}
// TestUnmarshalCommandErrors tests the error paths of the UnmarshalCommand function.
func TestUnmarshalCommandErrors(t *testing.T) {
t.Parallel()
tests := []struct {
name string
request model.Request
err model.Error
}{
{
name: "unregistered type",
request: model.Request{
JSONRPC: "1.0",
Method: "bogusMethod",
Params: nil,
ID: nil,
},
err: model.Error{ErrorCode: model.ErrUnregisteredMethod},
},
{
name: "incorrect number of params",
request: model.Request{
JSONRPC: "1.0",
Method: "getBlockCount",
Params: []json.RawMessage{[]byte(`"bogusparam"`)},
ID: nil,
},
err: model.Error{ErrorCode: model.ErrNumParams},
},
{
name: "invalid type for a parameter",
request: model.Request{
JSONRPC: "1.0",
Method: "getBlock",
Params: []json.RawMessage{[]byte("1")},
ID: nil,
},
err: model.Error{ErrorCode: model.ErrInvalidType},
},
{
name: "invalid JSON for a parameter",
request: model.Request{
JSONRPC: "1.0",
Method: "getBlock",
Params: []json.RawMessage{[]byte(`"1`)},
ID: nil,
},
err: model.Error{ErrorCode: model.ErrInvalidType},
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
_, err := model.UnmarshalCommand(&test.request)
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+
"want %T", i, test.name, err, test.err)
continue
}
var gotRPCModelErr model.Error
errors.As(err, &gotRPCModelErr)
gotErrorCode := gotRPCModelErr.ErrorCode
if gotErrorCode != test.err.ErrorCode {
t.Errorf("Test #%d (%s) mismatched error code - got "+
"%v (%v), want %v", i, test.name, gotErrorCode,
err, test.err.ErrorCode)
continue
}
}
}