diff --git a/rpc/client/client.go b/rpc/client/client.go new file mode 100644 index 000000000..b2d4ee219 --- /dev/null +++ b/rpc/client/client.go @@ -0,0 +1,178 @@ +package rpcclient + +import ( + "encoding/json" + + "github.com/pkg/errors" + "github.com/tendermint/go-rpc/client" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + "github.com/tendermint/tendermint/types" +) + +type HTTPClient struct { + remote string + endpoint string + rpc *rpcclient.ClientJSONRPC + ws *rpcclient.WSClient +} + +func New(remote, wsEndpoint string) *HTTPClient { + return &HTTPClient{ + rpc: rpcclient.NewClientJSONRPC(remote), + remote: remote, + endpoint: wsEndpoint, + } +} + +func (c *HTTPClient) Status() (*ctypes.ResultStatus, error) { + tmResult := new(ctypes.TMResult) + _, err := c.rpc.Call("status", []interface{}{}, tmResult) + if err != nil { + return nil, errors.Wrap(err, "Status") + } + // note: panics if rpc doesn't match. okay??? + return (*tmResult).(*ctypes.ResultStatus), nil +} + +func (c *HTTPClient) ABCIInfo() (*ctypes.ResultABCIInfo, error) { + tmResult := new(ctypes.TMResult) + _, err := c.rpc.Call("abci_info", []interface{}{}, tmResult) + if err != nil { + return nil, errors.Wrap(err, "ABCIInfo") + } + return (*tmResult).(*ctypes.ResultABCIInfo), nil +} + +func (c *HTTPClient) ABCIQuery(path string, data []byte, prove bool) (*ctypes.ResultABCIQuery, error) { + tmResult := new(ctypes.TMResult) + _, err := c.rpc.Call("abci_query", []interface{}{path, data, prove}, tmResult) + if err != nil { + return nil, errors.Wrap(err, "ABCIQuery") + } + return (*tmResult).(*ctypes.ResultABCIQuery), nil +} + +func (c *HTTPClient) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + return c.broadcastTX("broadcast_tx_commit", tx) +} + +func (c *HTTPClient) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + return c.broadcastTX("broadcast_tx_async", tx) +} + +func (c *HTTPClient) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + return c.broadcastTX("broadcast_tx_sync", tx) +} + +func (c *HTTPClient) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + tmResult := new(ctypes.TMResult) + _, err := c.rpc.Call(route, []interface{}{tx}, tmResult) + if err != nil { + return nil, errors.Wrap(err, route) + } + return (*tmResult).(*ctypes.ResultBroadcastTxCommit), nil +} + +func (c *HTTPClient) NetInfo() (*ctypes.ResultNetInfo, error) { + tmResult := new(ctypes.TMResult) + _, err := c.rpc.Call("net_info", nil, tmResult) + if err != nil { + return nil, errors.Wrap(err, "NetInfo") + } + return (*tmResult).(*ctypes.ResultNetInfo), nil +} + +func (c *HTTPClient) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { + tmResult := new(ctypes.TMResult) + // TODO: is this the correct way to marshall seeds? + _, err := c.rpc.Call("dial_seeds", []interface{}{seeds}, tmResult) + if err != nil { + return nil, errors.Wrap(err, "DialSeeds") + } + return (*tmResult).(*ctypes.ResultDialSeeds), nil +} + +func (c *HTTPClient) BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, error) { + tmResult := new(ctypes.TMResult) + _, err := c.rpc.Call("blockchain", []interface{}{minHeight, maxHeight}, tmResult) + if err != nil { + return nil, errors.Wrap(err, "BlockchainInfo") + } + return (*tmResult).(*ctypes.ResultBlockchainInfo), nil +} + +func (c *HTTPClient) Genesis() (*ctypes.ResultGenesis, error) { + tmResult := new(ctypes.TMResult) + _, err := c.rpc.Call("genesis", nil, tmResult) + if err != nil { + return nil, errors.Wrap(err, "Genesis") + } + return (*tmResult).(*ctypes.ResultGenesis), nil +} + +func (c *HTTPClient) Block(height int) (*ctypes.ResultBlock, error) { + tmResult := new(ctypes.TMResult) + _, err := c.rpc.Call("block", []interface{}{height}, tmResult) + if err != nil { + return nil, errors.Wrap(err, "Block") + } + return (*tmResult).(*ctypes.ResultBlock), nil +} + +func (c *HTTPClient) Commit(height int) (*ctypes.ResultCommit, error) { + tmResult := new(ctypes.TMResult) + _, err := c.rpc.Call("commit", []interface{}{height}, tmResult) + if err != nil { + return nil, errors.Wrap(err, "Commit") + } + return (*tmResult).(*ctypes.ResultCommit), nil +} + +func (c *HTTPClient) Validators() (*ctypes.ResultValidators, error) { + tmResult := new(ctypes.TMResult) + _, err := c.rpc.Call("validators", nil, tmResult) + if err != nil { + return nil, errors.Wrap(err, "Validators") + } + return (*tmResult).(*ctypes.ResultValidators), nil +} + +/** websocket event stuff here... **/ + +// StartWebsocket starts up a websocket and a listener goroutine +// if already started, do nothing +func (c *HTTPClient) StartWebsocket() error { + var err error + if c.ws == nil { + ws := rpcclient.NewWSClient(c.remote, c.endpoint) + _, err = ws.Start() + if err == nil { + c.ws = ws + } + } + return errors.Wrap(err, "StartWebsocket") +} + +// StopWebsocket stops the websocket connection +func (c *HTTPClient) StopWebsocket() { + if c.ws != nil { + c.ws.Stop() + c.ws = nil + } +} + +// GetEventChannels returns the results and error channel from the websocket +func (c *HTTPClient) GetEventChannels() (chan json.RawMessage, chan error) { + if c.ws == nil { + return nil, nil + } + return c.ws.ResultsCh, c.ws.ErrorsCh +} + +func (c *HTTPClient) Subscribe(event string) error { + return errors.Wrap(c.ws.Subscribe(event), "Subscribe") +} + +func (c *HTTPClient) Unsubscribe(event string) error { + return errors.Wrap(c.ws.Unsubscribe(event), "Unsubscribe") +} diff --git a/rpc/test/client_test.go b/rpc/test/client_test.go index b17252dfa..a173bf297 100644 --- a/rpc/test/client_test.go +++ b/rpc/test/client_test.go @@ -24,7 +24,7 @@ import ( func TestURIStatus(t *testing.T) { tmResult := new(ctypes.TMResult) - _, err := clientURI.Call("status", map[string]interface{}{}, tmResult) + _, err := GetURIClient().Call("status", map[string]interface{}{}, tmResult) if err != nil { panic(err) } @@ -33,7 +33,7 @@ func TestURIStatus(t *testing.T) { func TestJSONStatus(t *testing.T) { tmResult := new(ctypes.TMResult) - _, err := clientJSON.Call("status", []interface{}{}, tmResult) + _, err := GetJSONClient().Call("status", []interface{}{}, tmResult) if err != nil { panic(err) } @@ -41,6 +41,8 @@ func TestJSONStatus(t *testing.T) { } func testStatus(t *testing.T, statusI interface{}) { + chainID := GetConfig().GetString("chain_id") + tmRes := statusI.(*ctypes.TMResult) status := (*tmRes).(*ctypes.ResultStatus) if status.NodeInfo.Network != chainID { @@ -68,7 +70,7 @@ func TestURIBroadcastTxSync(t *testing.T) { defer config.Set("block_size", -1) tmResult := new(ctypes.TMResult) tx := randBytes() - _, err := clientURI.Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, tmResult) + _, err := GetURIClient().Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, tmResult) if err != nil { panic(err) } @@ -80,7 +82,7 @@ func TestJSONBroadcastTxSync(t *testing.T) { defer config.Set("block_size", -1) tmResult := new(ctypes.TMResult) tx := randBytes() - _, err := clientJSON.Call("broadcast_tx_sync", []interface{}{tx}, tmResult) + _, err := GetJSONClient().Call("broadcast_tx_sync", []interface{}{tx}, tmResult) if err != nil { panic(err) } @@ -118,13 +120,10 @@ func testTxKV() ([]byte, []byte, []byte) { func sendTx() ([]byte, []byte) { tmResult := new(ctypes.TMResult) k, v, tx := testTxKV() - _, err := clientJSON.Call("broadcast_tx_commit", []interface{}{tx}, tmResult) + _, err := GetJSONClient().Call("broadcast_tx_commit", []interface{}{tx}, tmResult) if err != nil { panic(err) } - fmt.Println("SENT TX", tx) - fmt.Printf("SENT TX %X\n", tx) - fmt.Printf("k %X; v %X", k, v) return k, v } @@ -132,7 +131,7 @@ func TestURIABCIQuery(t *testing.T) { k, v := sendTx() time.Sleep(time.Second) tmResult := new(ctypes.TMResult) - _, err := clientURI.Call("abci_query", map[string]interface{}{"path": "", "data": k, "prove": false}, tmResult) + _, err := GetURIClient().Call("abci_query", map[string]interface{}{"path": "", "data": k, "prove": false}, tmResult) if err != nil { panic(err) } @@ -142,7 +141,7 @@ func TestURIABCIQuery(t *testing.T) { func TestJSONABCIQuery(t *testing.T) { k, v := sendTx() tmResult := new(ctypes.TMResult) - _, err := clientJSON.Call("abci_query", []interface{}{"", k, false}, tmResult) + _, err := GetJSONClient().Call("abci_query", []interface{}{"", k, false}, tmResult) if err != nil { panic(err) } @@ -168,7 +167,7 @@ func testABCIQuery(t *testing.T, statusI interface{}, value []byte) { func TestURIBroadcastTxCommit(t *testing.T) { tmResult := new(ctypes.TMResult) tx := randBytes() - _, err := clientURI.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult) + _, err := GetURIClient().Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult) if err != nil { panic(err) } @@ -178,7 +177,7 @@ func TestURIBroadcastTxCommit(t *testing.T) { func TestJSONBroadcastTxCommit(t *testing.T) { tmResult := new(ctypes.TMResult) tx := randBytes() - _, err := clientJSON.Call("broadcast_tx_commit", []interface{}{tx}, tmResult) + _, err := GetJSONClient().Call("broadcast_tx_commit", []interface{}{tx}, tmResult) if err != nil { panic(err) } @@ -211,13 +210,13 @@ var wsTyp = "JSONRPC" // make a simple connection to the server func TestWSConnect(t *testing.T) { - wsc := newWSClient(t) + wsc := GetWSClient() wsc.Stop() } // receive a new block message func TestWSNewBlock(t *testing.T) { - wsc := newWSClient(t) + wsc := GetWSClient() eid := types.EventStringNewBlock() subscribe(t, wsc, eid) defer func() { @@ -225,7 +224,7 @@ func TestWSNewBlock(t *testing.T) { wsc.Stop() }() waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error { - fmt.Println("Check:", b) + // fmt.Println("Check:", b) return nil }) } @@ -235,7 +234,7 @@ func TestWSBlockchainGrowth(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } - wsc := newWSClient(t) + wsc := GetWSClient() eid := types.EventStringNewBlock() subscribe(t, wsc, eid) defer func() { @@ -263,7 +262,7 @@ func TestWSBlockchainGrowth(t *testing.T) { } func TestWSTxEvent(t *testing.T) { - wsc := newWSClient(t) + wsc := GetWSClient() tx := randBytes() // listen for the tx I am about to submit @@ -276,7 +275,7 @@ func TestWSTxEvent(t *testing.T) { // send an tx tmResult := new(ctypes.TMResult) - _, err := clientJSON.Call("broadcast_tx_sync", []interface{}{tx}, tmResult) + _, err := GetJSONClient().Call("broadcast_tx_sync", []interface{}{tx}, tmResult) if err != nil { t.Fatal("Error submitting event") } @@ -341,7 +340,7 @@ var testCasesUnsafeSetConfig = [][]string{ func TestURIUnsafeSetConfig(t *testing.T) { for _, testCase := range testCasesUnsafeSetConfig { tmResult := new(ctypes.TMResult) - _, err := clientURI.Call("unsafe_set_config", map[string]interface{}{ + _, err := GetURIClient().Call("unsafe_set_config", map[string]interface{}{ "type": testCase[0], "key": testCase[1], "value": testCase[2], @@ -356,7 +355,7 @@ func TestURIUnsafeSetConfig(t *testing.T) { func TestJSONUnsafeSetConfig(t *testing.T) { for _, testCase := range testCasesUnsafeSetConfig { tmResult := new(ctypes.TMResult) - _, err := clientJSON.Call("unsafe_set_config", []interface{}{testCase[0], testCase[1], testCase[2]}, tmResult) + _, err := GetJSONClient().Call("unsafe_set_config", []interface{}{testCase[0], testCase[1], testCase[2]}, tmResult) if err != nil { panic(err) } diff --git a/rpc/test/grpc_test.go b/rpc/test/grpc_test.go index 73ba22a78..fa57b1fb7 100644 --- a/rpc/test/grpc_test.go +++ b/rpc/test/grpc_test.go @@ -11,7 +11,7 @@ import ( //------------------------------------------- func TestBroadcastTx(t *testing.T) { - res, err := clientGRPC.BroadcastTx(context.Background(), &core_grpc.RequestBroadcastTx{[]byte("this is a tx")}) + res, err := GetGRPCClient().BroadcastTx(context.Background(), &core_grpc.RequestBroadcastTx{[]byte("this is a tx")}) if err != nil { t.Fatal(err) } diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 76fcd4b3b..287228e04 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -1,160 +1,91 @@ package rpctest import ( - "testing" - "time" + "fmt" - . "github.com/tendermint/go-common" - cfg "github.com/tendermint/go-config" - "github.com/tendermint/go-wire" + logger "github.com/tendermint/go-logger" + abci "github.com/tendermint/abci/types" + cfg "github.com/tendermint/go-config" client "github.com/tendermint/go-rpc/client" "github.com/tendermint/tendermint/config/tendermint_test" nm "github.com/tendermint/tendermint/node" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - "github.com/tendermint/tendermint/rpc/grpc" + "github.com/tendermint/tendermint/proxy" + rpcclient "github.com/tendermint/tendermint/rpc/client" + core_grpc "github.com/tendermint/tendermint/rpc/grpc" + "github.com/tendermint/tendermint/types" ) -// global variables for use across all tests var ( - config cfg.Config - node *nm.Node - chainID string - rpcAddr string - requestAddr string - websocketAddr string - websocketEndpoint string - grpcAddr string - clientURI *client.ClientURI - clientJSON *client.ClientJSONRPC - clientGRPC core_grpc.BroadcastAPIClient + config cfg.Config + node *nm.Node ) -// initialize config and create new node -func init() { - config = tendermint_test.ResetConfig("rpc_test_client_test") - chainID = config.GetString("chain_id") - rpcAddr = config.GetString("rpc_laddr") - grpcAddr = config.GetString("grpc_laddr") - requestAddr = rpcAddr - websocketAddr = rpcAddr - websocketEndpoint = "/websocket" - - clientURI = client.NewClientURI(requestAddr) - clientJSON = client.NewClientJSONRPC(requestAddr) - clientGRPC = core_grpc.StartGRPCClient(grpcAddr) - - // TODO: change consensus/state.go timeouts to be shorter +const tmLogLevel = "error" - // start a node - ready := make(chan struct{}) - go newNode(ready) - <-ready +// GetConfig returns a config for the test cases as a singleton +func GetConfig() cfg.Config { + if config == nil { + config = tendermint_test.ResetConfig("rpc_test_client_test") + // Shut up the logging + logger.SetLogLevel(tmLogLevel) + } + return config } -// create a new node and sleep forever -func newNode(ready chan struct{}) { - // Create & start node - node = nm.NewNodeDefault(config) - node.Start() - - time.Sleep(time.Second) +// GetClient gets a rpc client pointing to the test tendermint rpc +func GetClient() *rpcclient.HTTPClient { + rpcAddr := GetConfig().GetString("rpc_laddr") + return rpcclient.New(rpcAddr, "/websocket") +} - ready <- struct{}{} +// GetURIClient gets a uri client pointing to the test tendermint rpc +func GetURIClient() *client.ClientURI { + rpcAddr := GetConfig().GetString("rpc_laddr") + return client.NewClientURI(rpcAddr) +} - // Sleep forever - ch := make(chan struct{}) - <-ch +// GetJSONClient gets a http/json client pointing to the test tendermint rpc +func GetJSONClient() *client.ClientJSONRPC { + rpcAddr := GetConfig().GetString("rpc_laddr") + return client.NewClientJSONRPC(rpcAddr) } -//-------------------------------------------------------------------------------- -// Utilities for testing the websocket service +func GetGRPCClient() core_grpc.BroadcastAPIClient { + grpcAddr := config.GetString("grpc_laddr") + return core_grpc.StartGRPCClient(grpcAddr) +} -// create a new connection -func newWSClient(t *testing.T) *client.WSClient { - wsc := client.NewWSClient(websocketAddr, websocketEndpoint) +func GetWSClient() *client.WSClient { + rpcAddr := GetConfig().GetString("rpc_laddr") + wsc := client.NewWSClient(rpcAddr, "/websocket") if _, err := wsc.Start(); err != nil { panic(err) } return wsc } -// subscribe to an event -func subscribe(t *testing.T, wsc *client.WSClient, eventid string) { - if err := wsc.Subscribe(eventid); err != nil { - panic(err) - } -} +// StartTendermint starts a test tendermint server in a go routine and returns when it is initialized +// TODO: can one pass an Application in???? +func StartTendermint(app abci.Application) *nm.Node { + // start a node + fmt.Println("Starting Tendermint...") -// unsubscribe from an event -func unsubscribe(t *testing.T, wsc *client.WSClient, eventid string) { - if err := wsc.Unsubscribe(eventid); err != nil { - panic(err) - } + node = NewTendermint(app) + fmt.Println("Tendermint running!") + return node } -// wait for an event; do things that might trigger events, and check them when they are received -// the check function takes an event id and the byte slice read off the ws -func waitForEvent(t *testing.T, wsc *client.WSClient, eventid string, dieOnTimeout bool, f func(), check func(string, interface{}) error) { - // go routine to wait for webscoket msg - goodCh := make(chan interface{}) - errCh := make(chan error) - - // Read message - go func() { - var err error - LOOP: - for { - select { - case r := <-wsc.ResultsCh: - result := new(ctypes.TMResult) - wire.ReadJSONPtr(result, r, &err) - if err != nil { - errCh <- err - break LOOP - } - event, ok := (*result).(*ctypes.ResultEvent) - if ok && event.Name == eventid { - goodCh <- event.Data - break LOOP - } - case err := <-wsc.ErrorsCh: - errCh <- err - break LOOP - case <-wsc.Quit: - break LOOP - } - } - }() - - // do stuff (transactions) - f() - - // wait for an event or timeout - timeout := time.NewTimer(10 * time.Second) - select { - case <-timeout.C: - if dieOnTimeout { - wsc.Stop() - panic(Fmt("%s event was not received in time", eventid)) - } - // else that's great, we didn't hear the event - // and we shouldn't have - case eventData := <-goodCh: - if dieOnTimeout { - // message was received and expected - // run the check - if err := check(eventid, eventData); err != nil { - panic(err) // Show the stack trace. - } - } else { - wsc.Stop() - panic(Fmt("%s event was not expected", eventid)) - } - case err := <-errCh: - panic(err) // Show the stack trace. +// NewTendermint creates a new tendermint server and sleeps forever +func NewTendermint(app abci.Application) *nm.Node { + // Create & start node + config := GetConfig() + privValidatorFile := config.GetString("priv_validator_file") + privValidator := types.LoadOrGenPrivValidator(privValidatorFile) + papp := proxy.NewLocalClientCreator(app) + node := nm.NewNode(config, privValidator, papp) - } + // node.Start now does everything including the RPC server + node.Start() + return node } - -//-------------------------------------------------------------------------------- diff --git a/rpc/test/helpers_old.go b/rpc/test/helpers_old.go new file mode 100644 index 000000000..8795fb5dc --- /dev/null +++ b/rpc/test/helpers_old.go @@ -0,0 +1,157 @@ +package rpctest + +import ( + "testing" + "time" + + . "github.com/tendermint/go-common" + "github.com/tendermint/go-wire" + + client "github.com/tendermint/go-rpc/client" + ctypes "github.com/tendermint/tendermint/rpc/core/types" +) + +/* +// global variables for use across all tests +var ( + config cfg.Config + node *nm.Node + chainID string + rpcAddr string + requestAddr string + websocketAddr string + websocketEndpoint string + grpcAddr string + clientURI *client.ClientURI + clientJSON *client.ClientJSONRPC + clientGRPC core_grpc.BroadcastAPIClient +) + +// initialize config and create new node +func init() { + config = tendermint_test.ResetConfig("rpc_test_client_test") + chainID = config.GetString("chain_id") + rpcAddr = config.GetString("rpc_laddr") + grpcAddr = config.GetString("grpc_laddr") + requestAddr = rpcAddr + websocketAddr = rpcAddr + websocketEndpoint = "/websocket" + + clientURI = client.NewClientURI(requestAddr) + clientJSON = client.NewClientJSONRPC(requestAddr) + clientGRPC = core_grpc.StartGRPCClient(grpcAddr) + + // TODO: change consensus/state.go timeouts to be shorter + + // start a node + ready := make(chan struct{}) + go newNode(ready) + <-ready +} + +// create a new node and sleep forever +func newNode(ready chan struct{}) { + // Create & start node + node = nm.NewNodeDefault(config) + node.Start() + + time.Sleep(time.Second) + + ready <- struct{}{} + + // Sleep forever + ch := make(chan struct{}) + <-ch +} + +//-------------------------------------------------------------------------------- +// Utilities for testing the websocket service + +// create a new connection +func newWSClient(t *testing.T) *client.WSClient { + wsc := client.NewWSClient(websocketAddr, websocketEndpoint) + if _, err := wsc.Start(); err != nil { + panic(err) + } + return wsc +} +*/ +// subscribe to an event +func subscribe(t *testing.T, wsc *client.WSClient, eventid string) { + if err := wsc.Subscribe(eventid); err != nil { + panic(err) + } +} + +// unsubscribe from an event +func unsubscribe(t *testing.T, wsc *client.WSClient, eventid string) { + if err := wsc.Unsubscribe(eventid); err != nil { + panic(err) + } +} + +// wait for an event; do things that might trigger events, and check them when they are received +// the check function takes an event id and the byte slice read off the ws +func waitForEvent(t *testing.T, wsc *client.WSClient, eventid string, dieOnTimeout bool, f func(), check func(string, interface{}) error) { + // go routine to wait for webscoket msg + goodCh := make(chan interface{}) + errCh := make(chan error) + + // Read message + go func() { + var err error + LOOP: + for { + select { + case r := <-wsc.ResultsCh: + result := new(ctypes.TMResult) + wire.ReadJSONPtr(result, r, &err) + if err != nil { + errCh <- err + break LOOP + } + event, ok := (*result).(*ctypes.ResultEvent) + if ok && event.Name == eventid { + goodCh <- event.Data + break LOOP + } + case err := <-wsc.ErrorsCh: + errCh <- err + break LOOP + case <-wsc.Quit: + break LOOP + } + } + }() + + // do stuff (transactions) + f() + + // wait for an event or timeout + timeout := time.NewTimer(10 * time.Second) + select { + case <-timeout.C: + if dieOnTimeout { + wsc.Stop() + panic(Fmt("%s event was not received in time", eventid)) + } + // else that's great, we didn't hear the event + // and we shouldn't have + case eventData := <-goodCh: + if dieOnTimeout { + // message was received and expected + // run the check + if err := check(eventid, eventData); err != nil { + panic(err) // Show the stack trace. + } + } else { + wsc.Stop() + panic(Fmt("%s event was not expected", eventid)) + } + case err := <-errCh: + panic(err) // Show the stack trace. + + } +} + +//-------------------------------------------------------------------------------- diff --git a/rpc/test/main_test.go b/rpc/test/main_test.go new file mode 100644 index 000000000..e78cd17a1 --- /dev/null +++ b/rpc/test/main_test.go @@ -0,0 +1,33 @@ +/* +package tests contain integration tests and helper functions for testing +the RPC interface + +In particular, it allows us to spin up a tendermint node in process, with +a live RPC server, which we can use to verify our rpc calls. It provides +all data structures, enabling us to do more complex tests (like node_test.go) +that introspect the blocks themselves to validate signatures and the like. + +It currently only spins up one node, it would be interesting to expand it +to multiple nodes to see the real effects of validating partially signed +blocks. +*/ +package rpctest + +import ( + "os" + "testing" + + "github.com/tendermint/abci/example/dummy" +) + +func TestMain(m *testing.M) { + // start a tendermint node (and merkleeyes) in the background to test against + app := dummy.NewDummyApplication() + node := StartTendermint(app) + code := m.Run() + + // and shut down proper at the end + node.Stop() + node.Wait() + os.Exit(code) +} diff --git a/rpc/test/rpc_test.go b/rpc/test/rpc_test.go new file mode 100644 index 000000000..3896fffd6 --- /dev/null +++ b/rpc/test/rpc_test.go @@ -0,0 +1,130 @@ +package rpctest + +import ( + "testing" + + "github.com/stretchr/testify/assert" + // "github.com/stretchr/testify/require" + // merkle "github.com/tendermint/go-merkle" + // "github.com/tendermint/tendermint/types" +) + +// Make sure status is correct (we connect properly) +func TestStatus(t *testing.T) { + c := GetClient() + status, err := c.Status() + if assert.Nil(t, err) { + assert.Equal(t, GetConfig().GetString("chain_id"), status.NodeInfo.Network) + } +} + +/* +// Make some app checks +func TestAppCalls(t *testing.T) { + assert, require := assert.New(t), require.New(t) + c := GetClient() + _, err := c.Block(1) + assert.NotNil(err) // no block yet + k, v, tx := TestTxKV() + _, err = c.BroadcastTxCommit(tx) + require.Nil(err) + // wait before querying + time.Sleep(time.Second * 2) + qres, err := c.ABCIQuery("/key", k, false) + if assert.Nil(err) && assert.True(qres.Response.Code.IsOK()) { + data := qres.Response + // assert.Equal(k, data.GetKey()) // only returned for proofs + assert.Equal(v, data.GetValue()) + } + // and we can even check the block is added + block, err := c.Block(3) + assert.Nil(err) // now it's good :) + appHash := block.BlockMeta.Header.AppHash + assert.True(len(appHash) > 0) + + // and get the corresponding commit with the same apphash + commit, err := c.Commit(3) + assert.Nil(err) // now it's good :) + cappHash := commit.Header.AppHash + assert.Equal(appHash, cappHash) + assert.NotNil(commit.Commit) + + // compare the commits (note Commit(2) has commit from Block(3)) + commit2, err := c.Commit(2) + assert.Nil(err) // now it's good :) + assert.Equal(block.Block.LastCommit, commit2.Commit) + + // and we got a proof that works! + pres, err := c.ABCIQuery("/key", k, true) + if assert.Nil(err) && assert.True(pres.Response.Code.IsOK()) { + proof, err := merkle.ReadProof(pres.Response.GetProof()) + if assert.Nil(err) { + key := pres.Response.GetKey() + value := pres.Response.GetValue() + assert.Equal(appHash, proof.RootHash) + valid := proof.Verify(key, value, appHash) + assert.True(valid) + } + } +} + +// run most calls just to make sure no syntax errors +func TestNoErrors(t *testing.T) { + assert := assert.New(t) + c := GetClient() + _, err := c.NetInfo() + assert.Nil(err) + _, err = c.BlockchainInfo(0, 4) + assert.Nil(err) + // TODO: check with a valid height + _, err = c.Block(1000) + assert.NotNil(err) + // maybe this is an error??? + // _, err = c.DialSeeds([]string{"one", "two"}) + // assert.Nil(err) + gen, err := c.Genesis() + if assert.Nil(err) { + assert.Equal(GetConfig().GetString("chain_id"), gen.Genesis.ChainID) + } +} + +func TestSubscriptions(t *testing.T) { + assert, require := assert.New(t), require.New(t) + c := GetClient() + err := c.StartWebsocket() + require.Nil(err) + defer c.StopWebsocket() + + // subscribe to a transaction event + _, _, tx := TestTxKV() + // this causes a panic in tendermint core!!! + eventType := types.EventStringTx(types.Tx(tx)) + c.Subscribe(eventType) + read := 0 + + // set up a listener + r, e := c.GetEventChannels() + go func() { + // read one event in the background + select { + case <-r: + // TODO: actually parse this or something + read += 1 + case err := <-e: + panic(err) + } + }() + + // make sure nothing has happened yet. + assert.Equal(0, read) + + // send a tx and wait for it to propogate + _, err = c.BroadcastTxCommit(tx) + assert.Nil(err, string(tx)) + // wait before querying + time.Sleep(time.Second) + + // now make sure the event arrived + assert.Equal(1, read) +} +*/