From 96a68227bdeaf8a951a16020e7cd56380cc99479 Mon Sep 17 00:00:00 2001 From: "John C. Vernaleo" Date: Mon, 12 Aug 2013 12:50:04 -0400 Subject: [PATCH] Add RpcRawCommand and export ReadResultCommand. 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. --- internal_test.go | 71 ----------------------------------------------- jsonapi.go | 35 +++++++++++++++++------ jsonapi_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++ test_coverage.txt | 11 ++++---- 4 files changed, 104 insertions(+), 84 deletions(-) diff --git a/internal_test.go b/internal_test.go index 1647f379..33d51063 100644 --- a/internal_test.go +++ b/internal_test.go @@ -5,7 +5,6 @@ package btcjson import ( - "bytes" "encoding/json" "testing" ) @@ -17,76 +16,6 @@ cases which are either not possible or can't reliably be tested via the public interface. The functions are only exported while the tests are being run. */ -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 := 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 -} - // TestJsonWIthArgs tests jsonWithArgs to ensure that it can generate a json // command as well as sending it something that cannot be marshalled to json // (a channel) to test the failure paths. diff --git a/jsonapi.go b/jsonapi.go index 16fff664..4aa5b320 100644 --- a/jsonapi.go +++ b/jsonapi.go @@ -664,10 +664,10 @@ func CreateMessage(message string, args ...interface{}) ([]byte, error) { return finalMessage, err } -// readResultCmd unmarshalls the json reply with data struct for specific +// ReadResultCmd unmarshalls the json reply with data struct for specific // commands or an interface if it is not a command where we already have a // struct ready. -func readResultCmd(cmd string, message []byte) (Reply, error) { +func ReadResultCmd(cmd string, message []byte) (Reply, error) { var result Reply var err error var objmap map[string]json.RawMessage @@ -797,21 +797,40 @@ func RpcCommand(user string, password string, server string, message []byte) (Re err := fmt.Errorf("Error, no method specified.") return result, err } + body, err := RpcRawCommand(user, password, server, message) + if err != nil { + err := fmt.Errorf("Error getting json reply: %v", err) + return result, err + } + result, err = ReadResultCmd(method, body) + if err != nil { + err := fmt.Errorf("Error reading json message: %v", err) + return result, err + } + return result, err +} + +// RpcRawCommand takes a message generated from one of the routines above +// along with the login/server info, sends it, and gets a reply, returning +// the raw []byte response for use with ReadResultCmd. +func RpcRawCommand(user string, password string, server string, message []byte) ([]byte, error) { + var result []byte + var msg interface{} + err := json.Unmarshal(message, &msg) + if err != nil { + err := fmt.Errorf("Error, message does not appear to be valid json: %v", err) + return result, err + } resp, err := jsonRpcSend(user, password, server, message) if err != nil { err := fmt.Errorf("Error sending json message: " + err.Error()) return result, err } - body, err := GetRaw(resp.Body) + result, err = GetRaw(resp.Body) if err != nil { err := fmt.Errorf("Error getting json reply: %v", err) return result, err } - result, err = readResultCmd(method, body) - if err != nil { - err := fmt.Errorf("Error reading json message: %v", err) - return result, err - } return result, err } diff --git a/jsonapi_test.go b/jsonapi_test.go index 66e8a2cc..108b032c 100644 --- a/jsonapi_test.go +++ b/jsonapi_test.go @@ -6,6 +6,7 @@ package btcjson_test import ( "bytes" + "encoding/json" "github.com/conformal/btcjson" "io" "io/ioutil" @@ -303,3 +304,73 @@ func TestJSONtoAmount(t *testing.T) { } 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 +} diff --git a/test_coverage.txt b/test_coverage.txt index 2b8ab6b6..1fbd79b9 100644 --- a/test_coverage.txt +++ b/test_coverage.txt @@ -1,12 +1,13 @@ -github.com/conformal/btcjson/jsonapi.go CreateMessage 100.00% (310/310) -github.com/conformal/btcjson/jsonapi.go readResultCmd 100.00% (63/63) +github.com/conformal/btcjson/jsonapi.go CreateMessage 100.00% (323/323) +github.com/conformal/btcjson/jsonapi.go ReadResultCmd 100.00% (63/63) github.com/conformal/btcjson/jsonapi.go JSONToAmount 100.00% (15/15) -github.com/conformal/btcjson/jsonfxns.go MarshallAndSend 100.00% (7/7) github.com/conformal/btcjson/jsonfxns.go jsonRpcSend 100.00% (7/7) +github.com/conformal/btcjson/jsonfxns.go MarshallAndSend 100.00% (7/7) github.com/conformal/btcjson/jsonfxns.go GetRaw 100.00% (6/6) github.com/conformal/btcjson/jsonapi.go jsonWithArgs 100.00% (5/5) github.com/conformal/btcjson/jsonapi.go IsValidIdType 100.00% (3/3) -github.com/conformal/btcjson/jsonapi.go RpcCommand 66.67% (18/27) -github.com/conformal/btcjson --------------- 97.97% (434/443) +github.com/conformal/btcjson/jsonapi.go RpcCommand 78.26% (18/23) +github.com/conformal/btcjson/jsonapi.go RpcRawCommand 53.33% (8/15) +github.com/conformal/btcjson --------------- 97.43% (455/467)