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)