You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
520 lines
12 KiB
520 lines
12 KiB
9 years ago
|
// 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 btcjson_test
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"math"
|
||
|
"reflect"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/btcsuite/btcd/btcjson"
|
||
|
)
|
||
|
|
||
|
// 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 := btcjson.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 btcjson.Error
|
||
|
}{
|
||
|
{
|
||
|
name: "general incompatible int -> string",
|
||
|
dest: string(0),
|
||
|
src: int(0),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "overflow source int -> dest int",
|
||
|
dest: int8(0),
|
||
|
src: int(128),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "overflow source int -> dest uint",
|
||
|
dest: uint8(0),
|
||
|
src: int(256),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "int -> float",
|
||
|
dest: float32(0),
|
||
|
src: int(256),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "overflow source uint64 -> dest int64",
|
||
|
dest: int64(0),
|
||
|
src: uint64(1 << 63),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "overflow source uint -> dest int",
|
||
|
dest: int8(0),
|
||
|
src: uint(128),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "overflow source uint -> dest uint",
|
||
|
dest: uint8(0),
|
||
|
src: uint(256),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "uint -> float",
|
||
|
dest: float32(0),
|
||
|
src: uint(256),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "float -> int",
|
||
|
dest: int(0),
|
||
|
src: float32(1.0),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "overflow float64 -> float32",
|
||
|
dest: float32(0),
|
||
|
src: float64(math.MaxFloat64),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "invalid string -> bool",
|
||
|
dest: true,
|
||
|
src: "foo",
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "invalid string -> int",
|
||
|
dest: int8(0),
|
||
|
src: "foo",
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "overflow string -> int",
|
||
|
dest: int8(0),
|
||
|
src: "128",
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "invalid string -> uint",
|
||
|
dest: uint8(0),
|
||
|
src: "foo",
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "overflow string -> uint",
|
||
|
dest: uint8(0),
|
||
|
src: "256",
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "invalid string -> float",
|
||
|
dest: float32(0),
|
||
|
src: "foo",
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "overflow string -> float",
|
||
|
dest: float32(0),
|
||
|
src: "1.7976931348623157e+308",
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "invalid string -> array",
|
||
|
dest: [3]int{},
|
||
|
src: "foo",
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "invalid string -> slice",
|
||
|
dest: []int{},
|
||
|
src: "foo",
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "invalid string -> struct",
|
||
|
dest: struct{ A int }{},
|
||
|
src: "foo",
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "invalid string -> map",
|
||
|
dest: map[string]int{},
|
||
|
src: "foo",
|
||
|
err: btcjson.Error{ErrorCode: btcjson.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 := btcjson.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
|
||
|
}
|
||
|
gotErrorCode := err.(btcjson.Error).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
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestNewCmdErrors ensures the error paths of NewCmd behave as expected.
|
||
|
func TestNewCmdErrors(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
method string
|
||
|
args []interface{}
|
||
|
err btcjson.Error
|
||
|
}{
|
||
|
{
|
||
|
name: "unregistered command",
|
||
|
method: "boguscommand",
|
||
|
args: []interface{}{},
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod},
|
||
|
},
|
||
|
{
|
||
|
name: "too few parameters to command with required + optional",
|
||
|
method: "getblock",
|
||
|
args: []interface{}{},
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrNumParams},
|
||
|
},
|
||
|
{
|
||
|
name: "too many parameters to command with no optional",
|
||
|
method: "getblockcount",
|
||
|
args: []interface{}{"123"},
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrNumParams},
|
||
|
},
|
||
|
{
|
||
|
name: "incorrect parameter type",
|
||
|
method: "getblock",
|
||
|
args: []interface{}{1},
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
t.Logf("Running %d tests", len(tests))
|
||
|
for i, test := range tests {
|
||
|
_, err := btcjson.NewCmd(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
|
||
|
}
|
||
|
gotErrorCode := err.(btcjson.Error).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
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestMarshalCmdErrors tests the error paths of the MarshalCmd function.
|
||
|
func TestMarshalCmdErrors(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
id interface{}
|
||
|
cmd interface{}
|
||
|
err btcjson.Error
|
||
|
}{
|
||
|
{
|
||
|
name: "unregistered type",
|
||
|
id: 1,
|
||
|
cmd: (*int)(nil),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod},
|
||
|
},
|
||
|
{
|
||
|
name: "nil instance of registered type",
|
||
|
id: 1,
|
||
|
cmd: (*btcjson.GetBlockCmd)(nil),
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "nil instance of registered type",
|
||
|
id: []int{0, 1},
|
||
|
cmd: &btcjson.GetBlockCountCmd{},
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
t.Logf("Running %d tests", len(tests))
|
||
|
for i, test := range tests {
|
||
|
_, err := btcjson.MarshalCmd(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
|
||
|
}
|
||
|
gotErrorCode := err.(btcjson.Error).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
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestUnmarshalCmdErrors tests the error paths of the UnmarshalCmd function.
|
||
|
func TestUnmarshalCmdErrors(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
request btcjson.Request
|
||
|
err btcjson.Error
|
||
|
}{
|
||
|
{
|
||
|
name: "unregistered type",
|
||
|
request: btcjson.Request{
|
||
|
Jsonrpc: "1.0",
|
||
|
Method: "bogusmethod",
|
||
|
Params: nil,
|
||
|
ID: nil,
|
||
|
},
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod},
|
||
|
},
|
||
|
{
|
||
|
name: "incorrect number of params",
|
||
|
request: btcjson.Request{
|
||
|
Jsonrpc: "1.0",
|
||
|
Method: "getblockcount",
|
||
|
Params: []json.RawMessage{[]byte(`"bogusparam"`)},
|
||
|
ID: nil,
|
||
|
},
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrNumParams},
|
||
|
},
|
||
|
{
|
||
|
name: "invalid type for a parameter",
|
||
|
request: btcjson.Request{
|
||
|
Jsonrpc: "1.0",
|
||
|
Method: "getblock",
|
||
|
Params: []json.RawMessage{[]byte("1")},
|
||
|
ID: nil,
|
||
|
},
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
{
|
||
|
name: "invalid JSON for a parameter",
|
||
|
request: btcjson.Request{
|
||
|
Jsonrpc: "1.0",
|
||
|
Method: "getblock",
|
||
|
Params: []json.RawMessage{[]byte(`"1`)},
|
||
|
ID: nil,
|
||
|
},
|
||
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
t.Logf("Running %d tests", len(tests))
|
||
|
for i, test := range tests {
|
||
|
_, err := btcjson.UnmarshalCmd(&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
|
||
|
}
|
||
|
gotErrorCode := err.(btcjson.Error).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
|
||
|
}
|
||
|
}
|
||
|
}
|