@ -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") | |||
} |
@ -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 | |||
} | |||
//-------------------------------------------------------------------------------- |
@ -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. | |||
} | |||
} | |||
//-------------------------------------------------------------------------------- |
@ -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) | |||
} |
@ -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) | |||
} | |||
*/ |