96a68227bd
Add new RpcRawCommand to get []byte represtation of reply instead of btcjson.Result. Export ReadResultCommand to allow caller of RpcRawCommand to deal with the reply in the same way RpcCommand does. Adjust test code to for the above changes.
376 lines
14 KiB
Go
376 lines
14 KiB
Go
// Copyright (c) 2013 Conformal Systems LLC.
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package btcjson_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"github.com/conformal/btcjson"
|
|
"io"
|
|
"io/ioutil"
|
|
"testing"
|
|
)
|
|
|
|
// cmdtests is a table of all the possible commands and a list of inputs,
|
|
// some of which should work, some of which should not (indicated by the
|
|
// pass variable). This mainly checks the type and number of the arguments,
|
|
// it does not actually check to make sure the values are correct (i.e., that
|
|
// addresses are reasonable) as the bitcoin client must be able to deal with
|
|
// that.
|
|
var cmdtests = []struct {
|
|
cmd string
|
|
args []interface{}
|
|
pass bool
|
|
}{
|
|
{"getinfo", nil, true},
|
|
{"getinfo", []interface{}{1}, false},
|
|
{"listaccounts", nil, true},
|
|
{"listaccounts", []interface{}{1}, true},
|
|
{"listaccounts", []interface{}{"test"}, false},
|
|
{"listaccounts", []interface{}{1, 2}, false},
|
|
{"getblockhash", nil, false},
|
|
{"getblockhash", []interface{}{1}, true},
|
|
{"getblockhash", []interface{}{1, 2}, false},
|
|
{"getblockhash", []interface{}{1.1}, false},
|
|
{"settxfee", nil, false},
|
|
{"settxfee", []interface{}{1.0}, true},
|
|
{"settxfee", []interface{}{1.0, 2.0}, false},
|
|
{"settxfee", []interface{}{1}, false},
|
|
{"getmemorypool", nil, true},
|
|
{"getmemorypool", []interface{}{"test"}, true},
|
|
{"getmemorypool", []interface{}{1}, false},
|
|
{"getmemorypool", []interface{}{"test", 2}, false},
|
|
{"backupwallet", nil, false},
|
|
{"backupwallet", []interface{}{1, 2}, false},
|
|
{"backupwallet", []interface{}{1}, false},
|
|
{"backupwallet", []interface{}{"testpath"}, true},
|
|
{"setaccount", nil, false},
|
|
{"setaccount", []interface{}{1}, false},
|
|
{"setaccount", []interface{}{1, 2, 3}, false},
|
|
{"setaccount", []interface{}{1, "test"}, false},
|
|
{"setaccount", []interface{}{"test", "test"}, true},
|
|
{"verifymessage", nil, false},
|
|
{"verifymessage", []interface{}{1}, false},
|
|
{"verifymessage", []interface{}{1, 2}, false},
|
|
{"verifymessage", []interface{}{1, 2, 3, 4}, false},
|
|
{"verifymessage", []interface{}{"test", "test", "test"}, true},
|
|
{"verifymessage", []interface{}{"test", "test", 1}, false},
|
|
{"getaddednodeinfo", nil, false},
|
|
{"getaddednodeinfo", []interface{}{1}, false},
|
|
{"getaddednodeinfo", []interface{}{true}, true},
|
|
{"getaddednodeinfo", []interface{}{true, 1}, false},
|
|
{"getaddednodeinfo", []interface{}{true, "test"}, true},
|
|
{"setgenerate", nil, false},
|
|
{"setgenerate", []interface{}{1, 2, 3}, false},
|
|
{"setgenerate", []interface{}{true}, true},
|
|
{"setgenerate", []interface{}{true, 1}, true},
|
|
{"setgenerate", []interface{}{true, 1.1}, false},
|
|
{"setgenerate", []interface{}{"true", 1}, false},
|
|
{"getbalance", nil, true},
|
|
{"getbalance", []interface{}{"test"}, true},
|
|
{"getbalance", []interface{}{"test", 1}, true},
|
|
{"getbalance", []interface{}{"test", 1.0}, false},
|
|
{"getbalance", []interface{}{1, 1}, false},
|
|
{"getbalance", []interface{}{"test", 1, 2}, false},
|
|
{"getbalance", []interface{}{1}, false},
|
|
{"addnode", nil, false},
|
|
{"addnode", []interface{}{1, 2, 3}, false},
|
|
{"addnode", []interface{}{"test"}, true},
|
|
{"addnode", []interface{}{1}, false},
|
|
{"addnode", []interface{}{"test", 1}, true},
|
|
{"addnode", []interface{}{"test", 1.0}, false},
|
|
{"listreceivedbyaccount", nil, true},
|
|
{"listreceivedbyaccount", []interface{}{1, 2, 3}, false},
|
|
{"listreceivedbyaccount", []interface{}{1}, true},
|
|
{"listreceivedbyaccount", []interface{}{1.0}, false},
|
|
{"listreceivedbyaccount", []interface{}{1, false}, true},
|
|
{"listreceivedbyaccount", []interface{}{1, "false"}, false},
|
|
{"listtransactions", nil, true},
|
|
{"listtransactions", []interface{}{"test"}, true},
|
|
{"listtransactions", []interface{}{"test", 1}, true},
|
|
{"listtransactions", []interface{}{"test", 1, 2}, true},
|
|
{"listtransactions", []interface{}{"test", 1, 2, 3}, false},
|
|
{"listtransactions", []interface{}{1}, false},
|
|
{"listtransactions", []interface{}{"test", 1.0}, false},
|
|
{"listtransactions", []interface{}{"test", 1, "test"}, false},
|
|
{"importprivkey", nil, false},
|
|
{"importprivkey", []interface{}{"test"}, true},
|
|
{"importprivkey", []interface{}{1}, false},
|
|
{"importprivkey", []interface{}{"test", "test"}, true},
|
|
{"importprivkey", []interface{}{"test", "test", true}, true},
|
|
{"importprivkey", []interface{}{"test", "test", true, 1}, false},
|
|
{"importprivkey", []interface{}{"test", 1.0, true}, false},
|
|
{"importprivkey", []interface{}{"test", "test", "true"}, false},
|
|
{"listunspent", nil, true},
|
|
{"listunspent", []interface{}{1}, true},
|
|
{"listunspent", []interface{}{1, 2}, true},
|
|
{"listunspent", []interface{}{1, 2, 3}, false},
|
|
{"listunspent", []interface{}{1.0}, false},
|
|
{"listunspent", []interface{}{1, 2.0}, false},
|
|
{"sendfrom", nil, false},
|
|
{"sendfrom", []interface{}{"test"}, false},
|
|
{"sendfrom", []interface{}{"test", "test"}, false},
|
|
{"sendfrom", []interface{}{"test", "test", 1.0}, true},
|
|
{"sendfrom", []interface{}{"test", 1, 1.0}, false},
|
|
{"sendfrom", []interface{}{1, "test", 1.0}, false},
|
|
{"sendfrom", []interface{}{"test", "test", 1}, false},
|
|
{"sendfrom", []interface{}{"test", "test", 1.0, 1}, true},
|
|
{"sendfrom", []interface{}{"test", "test", 1.0, 1, "test"}, true},
|
|
{"sendfrom", []interface{}{"test", "test", 1.0, 1, "test", "test"}, true},
|
|
{"move", nil, false},
|
|
{"move", []interface{}{1, 2, 3, 4, 5, 6}, false},
|
|
{"move", []interface{}{1, 2}, false},
|
|
{"move", []interface{}{"test", "test", 1.0}, true},
|
|
{"move", []interface{}{"test", "test", 1.0, 1, "test"}, true},
|
|
{"move", []interface{}{"test", "test", 1.0, 1}, true},
|
|
{"move", []interface{}{1, "test", 1.0}, false},
|
|
{"move", []interface{}{"test", 1, 1.0}, false},
|
|
{"move", []interface{}{"test", "test", 1}, false},
|
|
{"move", []interface{}{"test", "test", 1.0, 1.0, "test"}, false},
|
|
{"move", []interface{}{"test", "test", 1.0, 1, true}, false},
|
|
{"sendtoaddress", nil, false},
|
|
{"sendtoaddress", []interface{}{"test"}, false},
|
|
{"sendtoaddress", []interface{}{"test", 1.0}, true},
|
|
{"sendtoaddress", []interface{}{"test", 1.0, "test"}, true},
|
|
{"sendtoaddress", []interface{}{"test", 1.0, "test", "test"}, true},
|
|
{"sendtoaddress", []interface{}{1, 1.0, "test", "test"}, false},
|
|
{"sendtoaddress", []interface{}{"test", 1, "test", "test"}, false},
|
|
{"sendtoaddress", []interface{}{"test", 1.0, 1.0, "test"}, false},
|
|
{"sendtoaddress", []interface{}{"test", 1.0, "test", 1.0}, false},
|
|
{"sendtoaddress", []interface{}{"test", 1.0, "test", "test", 1}, false},
|
|
{"addmultisignaddress", []interface{}{1, "test", "test"}, true},
|
|
{"addmultisignaddress", []interface{}{1, "test"}, false},
|
|
{"addmultisignaddress", []interface{}{1, 1.0, "test"}, false},
|
|
{"addmultisignaddress", []interface{}{1, "test", "test", "test"}, true},
|
|
{"createrawtransaction", []interface{}{"in1", 0, "a1", 1.0}, true},
|
|
{"createrawtransaction", []interface{}{"in1", "out1", "a1", 1.0, "test"}, false},
|
|
{"createrawtransaction", []interface{}{}, false},
|
|
{"createrawtransaction", []interface{}{"in1", 1.0, "a1", 1.0}, false},
|
|
{"sendmany", []interface{}{"in1", "out1", 1.0, 1, "comment"}, true},
|
|
{"sendmany", []interface{}{"in1", "out1", 1.0, "comment"}, true},
|
|
{"sendmany", []interface{}{"in1", "out1"}, false},
|
|
{"sendmany", []interface{}{true, "out1", 1.0, 1, "comment"}, false},
|
|
{"sendmany", []interface{}{"in1", "out1", "test", 1, "comment"}, false},
|
|
{"lockunspent", []interface{}{true, "something"}, true},
|
|
{"lockunspent", []interface{}{true}, false},
|
|
{"lockunspent", []interface{}{1.0, "something"}, false},
|
|
{"signrawtransaction", []interface{}{"hexstring"}, true},
|
|
{"signrawtransaction", []interface{}{"hexstring", "test", "test2", "test3", "test4"}, true},
|
|
{"signrawtransaction", []interface{}{"hexstring", "test", "test2", "test3"}, false},
|
|
{"signrawtransaction", []interface{}{1.2, "test", "test2", "test3", "test4"}, false},
|
|
{"signrawtransaction", []interface{}{"hexstring", 1, "test2", "test3", "test4"}, false},
|
|
{"signrawtransaction", []interface{}{"hexstring", "test", 2, "test3", "test4"}, false},
|
|
{"signrawtransaction", []interface{}{"hexstring", "test", "test2", 3, "test4"}, false},
|
|
{"listsinceblock", []interface{}{"test", "test"}, true},
|
|
{"listsinceblock", []interface{}{"test", "test", "test"}, false},
|
|
{"listsinceblock", []interface{}{"test"}, true},
|
|
{"listsinceblock", []interface{}{}, true},
|
|
{"listsinceblock", []interface{}{1, "test"}, false},
|
|
{"fakecommand", nil, false},
|
|
}
|
|
|
|
// TestRpcCreateMessage tests CreateMessage using the table of messages
|
|
// in cmdtests.
|
|
func TestRpcCreateMessage(t *testing.T) {
|
|
var err error
|
|
for i, tt := range cmdtests {
|
|
if tt.args == nil {
|
|
_, err = btcjson.CreateMessage(tt.cmd)
|
|
} else {
|
|
_, err = btcjson.CreateMessage(tt.cmd, tt.args...)
|
|
}
|
|
if tt.pass {
|
|
if err != nil {
|
|
t.Errorf("Could not create command %d: %s %v.", i, tt.cmd, err)
|
|
}
|
|
} else {
|
|
if err == nil {
|
|
t.Errorf("Should create command. %d: %s", i, tt.cmd)
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// TestRpcCommand tests RpcCommand by generating some commands and
|
|
// trying to send them off.
|
|
func TestRpcCommand(t *testing.T) {
|
|
user := "something"
|
|
pass := "something"
|
|
server := "invalid"
|
|
var msg []byte
|
|
_, err := btcjson.RpcCommand(user, pass, server, msg)
|
|
if err == nil {
|
|
t.Errorf("Should fail.")
|
|
}
|
|
msg, err = btcjson.CreateMessage("getinfo")
|
|
if err != nil {
|
|
t.Errorf("Cannot create valid json message")
|
|
}
|
|
_, err = btcjson.RpcCommand(user, pass, server, msg)
|
|
if err == nil {
|
|
t.Errorf("Should not connect to server.")
|
|
}
|
|
|
|
badMsg := []byte("{\"jsonrpc\":\"1.0\",\"id\":\"btcd\",\"method\":\"\"}")
|
|
_, err = btcjson.RpcCommand(user, pass, server, badMsg)
|
|
if err == nil {
|
|
t.Errorf("Cannot have no method in msg..")
|
|
}
|
|
return
|
|
}
|
|
|
|
// FailingReadClose is a type used for testing so we can get something that
|
|
// fails past Go's type system.
|
|
type FailingReadCloser struct{}
|
|
|
|
func (f *FailingReadCloser) Close() error {
|
|
return io.ErrUnexpectedEOF
|
|
}
|
|
|
|
func (f *FailingReadCloser) Read(p []byte) (n int, err error) {
|
|
return 0, io.ErrUnexpectedEOF
|
|
}
|
|
|
|
// TestRpcReply tests JsonGetRaw by sending both a good and a bad buffer
|
|
// to it.
|
|
func TestRpcReply(t *testing.T) {
|
|
buffer := new(bytes.Buffer)
|
|
buffer2 := ioutil.NopCloser(buffer)
|
|
_, err := btcjson.GetRaw(buffer2)
|
|
if err != nil {
|
|
t.Errorf("Error reading rpc reply.")
|
|
}
|
|
failBuf := &FailingReadCloser{}
|
|
_, err = btcjson.GetRaw(failBuf)
|
|
if err == nil {
|
|
t.Errorf("Error, this should fail.")
|
|
}
|
|
return
|
|
}
|
|
|
|
var idtests = []struct {
|
|
testId []interface{}
|
|
pass bool
|
|
}{
|
|
{[]interface{}{"string test"}, true},
|
|
{[]interface{}{1}, true},
|
|
{[]interface{}{1.0}, true},
|
|
{[]interface{}{nil}, true},
|
|
{[]interface{}{make(chan int)}, false},
|
|
}
|
|
|
|
// TestIsValidIdType tests that IsValidIdType allows (and disallows the correct
|
|
// types).
|
|
func TestIsValidIdType(t *testing.T) {
|
|
for _, tt := range idtests {
|
|
res := btcjson.IsValidIdType(tt.testId[0])
|
|
if res != tt.pass {
|
|
t.Errorf("Incorrect type result %v.", tt)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
var floattests = []struct {
|
|
in float64
|
|
out int64
|
|
pass bool
|
|
}{
|
|
{1.0, 100000000, true},
|
|
{-1.0, -100000000, true},
|
|
{0.0, 0, true},
|
|
{0.00000001, 1, true},
|
|
{-0.00000001, -1, true},
|
|
{-1.0e307, 0, false},
|
|
{1.0e307, 0, false},
|
|
}
|
|
|
|
// TestJSONtoAmount tests that JSONtoAmount returns the proper values.
|
|
func TestJSONtoAmount(t *testing.T) {
|
|
for _, tt := range floattests {
|
|
res, err := btcjson.JSONToAmount(tt.in)
|
|
if tt.pass {
|
|
if res != tt.out || err != nil {
|
|
t.Errorf("Should not fail: %v", tt.in)
|
|
}
|
|
} else {
|
|
if err == nil {
|
|
t.Errorf("Should not pass: %v", tt.in)
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
var resulttests = []struct {
|
|
cmd string
|
|
msg []byte
|
|
comp bool
|
|
pass bool
|
|
}{
|
|
// Generate a fake message to make sure we can encode and decode it and
|
|
// get the same thing back.
|
|
{"getblockcount",
|
|
[]byte(`{"result":226790,"error":{"code":1,"message":"No Error"},"id":"btcd"}`),
|
|
true, true},
|
|
// Generate a fake message to make sure we don't make a command from it.
|
|
{"anycommand", []byte(`{"result":"test","id":1}`), false, false},
|
|
{"anycommand", []byte(`{some junk}`), false, false},
|
|
{"anycommand", []byte(`{"error":null,"result":null,"id":"test"}`), false, true},
|
|
{"getinfo", []byte(`{"error":null,"result":null,"id":"test"}`), false, true},
|
|
{"getinfo", []byte(`{"error":null,"result":null}`), false, false},
|
|
{"getinfo", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
{"getblock", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
{"getblock", []byte(`{"result":{"hash":"000000","confirmations":16007,"size":325648},"error":null,"id":1}`), false, true},
|
|
{"getrawtransaction", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
{"getrawtransaction", []byte(`{"error":null,"id":1,"result":{"hex":"somejunk","version":1}}`), false, true},
|
|
{"decoderawtransaction", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
{"decoderawtransaction", []byte(`{"error":null,"id":1,"result":{"Txid":"something"}}`), false, true},
|
|
{"getaddressesbyaccount", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
{"getaddressesbyaccount", []byte(`{"error":null,"id":1,"result":["test"]}`), false, true},
|
|
{"getmininginfo", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
{"getmininginfo", []byte(`{"error":null,"id":1,"result":{"generate":true}}`), false, true},
|
|
{"getrawmempool", []byte(`{"error":null,"id":1,"result":[{"a":"b"}]}`), false, false},
|
|
{"getrawmempool", []byte(`{"error":null,"id":1,"result":["test"]}`), false, true},
|
|
{"validateaddress", []byte(`{"error":null,"id":1,"result":{"isvalid":false}}`), false, true},
|
|
{"validateaddress", []byte(`{"error":null,"id":1,"result":{false}}`), false, false},
|
|
{"signrawtransaction", []byte(`{"error":null,"id":1,"result":{"hex":"something","complete":false}}`), false, true},
|
|
{"signrawtransaction", []byte(`{"error":null,"id":1,"result":{false}}`), false, false},
|
|
{"listunspent", []byte(`{"error":null,"id":1,"result":[{"txid":"something"}]}`), false, true},
|
|
{"listunspent", []byte(`{"error":null,"id":1,"result":[{"txid"}]}`), false, false},
|
|
}
|
|
|
|
// TestReadResultCmd tests that readResultCmd can properly unmarshall the
|
|
// returned []byte that contains a json reply for both known and unknown
|
|
// messages.
|
|
func TestReadResultCmd(t *testing.T) {
|
|
for i, tt := range resulttests {
|
|
result, err := btcjson.ReadResultCmd(tt.cmd, tt.msg)
|
|
if tt.pass {
|
|
if err != nil {
|
|
t.Errorf("Should read result: %d %v", i, err)
|
|
}
|
|
// Due to the pointer for the Error and other structs,
|
|
// we can't always guarantee byte for byte comparison.
|
|
if tt.comp {
|
|
msg2, err := json.Marshal(result)
|
|
if err != nil {
|
|
t.Errorf("Should unmarshal result: %d %v", i, err)
|
|
}
|
|
if bytes.Compare(tt.msg, msg2) != 0 {
|
|
t.Errorf("json byte arrays differ. %d %v %v", i, tt.msg, msg2)
|
|
}
|
|
}
|
|
|
|
} else {
|
|
if err == nil {
|
|
t.Errorf("Should fail: %d, %s", i, tt.msg)
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|