Browse Source

Combine local and http into client package, unify tests with table-driven tests

pull/418/head
Ethan Frey 8 years ago
parent
commit
931af6a072
9 changed files with 312 additions and 536 deletions
  1. +0
    -211
      rpc/client/http/rpc_test.go
  2. +33
    -36
      rpc/client/httpclient.go
  3. +7
    -7
      rpc/client/interface.go
  4. +0
    -45
      rpc/client/local/app_test.go
  5. +0
    -22
      rpc/client/local/main_test.go
  6. +0
    -181
      rpc/client/local/rpc_test.go
  7. +31
    -32
      rpc/client/localclient.go
  8. +5
    -2
      rpc/client/main_test.go
  9. +236
    -0
      rpc/client/rpc_test.go

+ 0
- 211
rpc/client/http/rpc_test.go View File

@ -1,211 +0,0 @@
package http_test
import (
"encoding/json"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
merkle "github.com/tendermint/go-merkle"
merktest "github.com/tendermint/merkleeyes/testutil"
"github.com/tendermint/tendermint/rpc/client/http"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctest "github.com/tendermint/tendermint/rpc/test"
"github.com/tendermint/tendermint/types"
)
// GetClient gets a rpc client pointing to the test tendermint rpc
func GetClient() *http.Client {
rpcAddr := rpctest.GetConfig().GetString("rpc_laddr")
return http.New(rpcAddr, "/websocket")
}
// Make sure status is correct (we connect properly)
func TestStatus(t *testing.T) {
c := GetClient()
chainID := rpctest.GetConfig().GetString("chain_id")
status, err := c.Status()
require.Nil(t, err, "%+v", err)
assert.Equal(t, chainID, status.NodeInfo.Network)
}
// Make sure info is correct (we connect properly)
func TestInfo(t *testing.T) {
c := GetClient()
status, err := c.Status()
require.Nil(t, err, "%+v", err)
info, err := c.ABCIInfo()
require.Nil(t, err, "%+v", err)
assert.EqualValues(t, status.LatestBlockHeight, info.Response.LastBlockHeight)
assert.True(t, strings.HasPrefix(info.Response.Data, "size"))
}
func TestNetInfo(t *testing.T) {
c := GetClient()
netinfo, err := c.NetInfo()
require.Nil(t, err, "%+v", err)
assert.True(t, netinfo.Listening)
assert.Equal(t, 0, len(netinfo.Peers))
}
func TestDialSeeds(t *testing.T) {
c := GetClient()
// FIXME: fix server so it doesn't panic on invalid input
_, err := c.DialSeeds([]string{"12.34.56.78:12345"})
require.Nil(t, err, "%+v", err)
}
func TestGenesisAndValidators(t *testing.T) {
c := GetClient()
chainID := rpctest.GetConfig().GetString("chain_id")
// make sure this is the right genesis file
gen, err := c.Genesis()
require.Nil(t, err, "%+v", err)
assert.Equal(t, chainID, gen.Genesis.ChainID)
// get the genesis validator
require.Equal(t, 1, len(gen.Genesis.Validators))
gval := gen.Genesis.Validators[0]
// get the current validators
vals, err := c.Validators()
require.Nil(t, err, "%+v", err)
require.Equal(t, 1, len(vals.Validators))
val := vals.Validators[0]
// make sure the current set is also the genesis set
assert.Equal(t, gval.Amount, val.VotingPower)
assert.Equal(t, gval.PubKey, val.PubKey)
}
// Make some app checks
func TestAppCalls(t *testing.T) {
assert, require := assert.New(t), require.New(t)
c := GetClient()
// get an offset of height to avoid racing and guessing
s, err := c.Status()
require.Nil(err)
// sh is start height or status height
sh := s.LatestBlockHeight
// look for the future
_, err = c.Block(sh + 2)
assert.NotNil(err) // no block yet
// write something
k, v, tx := merktest.MakeTxKV()
_, err = c.BroadcastTxCommit(tx)
require.Nil(err, "%+v", err)
// wait before querying
time.Sleep(time.Second * 1)
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())
}
// +/- 1 making my head hurt
h := int(qres.Response.Height) - 1
// and we can even check the block is added
block, err := c.Block(h)
require.Nil(err, "%+v", err)
appHash := block.BlockMeta.Header.AppHash
assert.True(len(appHash) > 0)
assert.EqualValues(h, block.BlockMeta.Header.Height)
// check blockchain info, now that we know there is info
// TODO: is this commented somewhere that they are returned
// in order of descending height???
info, err := c.BlockchainInfo(h-2, h)
require.Nil(err, "%+v", err)
assert.True(info.LastHeight > 2)
if assert.Equal(3, len(info.BlockMetas)) {
lastMeta := info.BlockMetas[0]
assert.EqualValues(h, lastMeta.Header.Height)
bMeta := block.BlockMeta
assert.Equal(bMeta.Header.AppHash, lastMeta.Header.AppHash)
assert.Equal(bMeta.BlockID, lastMeta.BlockID)
}
// and get the corresponding commit with the same apphash
commit, err := c.Commit(h)
require.Nil(err, "%+v", err)
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(h - 1)
require.Nil(err, "%+v", err)
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)
}
}
}
func TestSubscriptions(t *testing.T) {
require := require.New(t)
c := GetClient()
err := c.StartWebsocket()
require.Nil(err)
defer c.StopWebsocket()
// subscribe to a transaction event
_, _, tx := merktest.MakeTxKV()
eventType := types.EventStringTx(types.Tx(tx))
c.Subscribe(eventType)
// set up a listener
r, e := c.GetEventChannels()
go func() {
// send a tx and wait for it to propogate
_, err = c.BroadcastTxCommit(tx)
require.Nil(err, string(tx))
}()
checkData := func(data []byte, kind byte) {
x := []interface{}{}
err := json.Unmarshal(data, &x)
require.Nil(err)
// gotta love wire's json format
require.EqualValues(kind, x[0])
}
res := <-r
checkData(res, ctypes.ResultTypeSubscribe)
// read one event, must be success
select {
case res := <-r:
checkData(res, ctypes.ResultTypeEvent)
// this is good.. let's get the data... ugh...
// result := new(ctypes.TMResult)
// wire.ReadJSON(result, res, &err)
// require.Nil(err, "%+v", err)
// event, ok := (*result).(*ctypes.ResultEvent)
// require.True(ok)
// assert.Equal("foo", event.Name)
// data, ok := event.Data.(types.EventDataTx)
// require.True(ok)
// assert.EqualValues(0, data.Code)
// assert.EqualValues(tx, data.Tx)
case err := <-e:
// this is a failure
require.Nil(err)
}
}

rpc/client/http/client.go → rpc/client/httpclient.go View File


+ 7
- 7
rpc/client/interface.go View File

@ -1,21 +1,21 @@
/*
package client provides a general purpose interface for connecting
package client provides a general purpose interface (Client) for connecting
to a tendermint node, as well as higher-level functionality.
The main implementation for production code is http, which connects
via http to the jsonrpc interface of the tendermint node.
The main implementation for production code is client.HTTP, which
connects via http to the jsonrpc interface of the tendermint node.
For connecting to a node running in the same process (eg. when
compiling the abci app in the same process), you can use the local
compiling the abci app in the same process), you can use the client.Local
implementation.
For mocking out server responses during testing to see behavior for
arbitrary return values, use the mock package.
In addition to the Client interface, which should be used externally
for maximum flexibility and testability, this package also provides
a wrapper that accepts any Client implementation and adds some
higher-level functionality.
for maximum flexibility and testability, and two implementations,
this package also provides helper functions that work on any Client
implementation.
*/
package client


+ 0
- 45
rpc/client/local/app_test.go View File

@ -1,45 +0,0 @@
package local_test
import (
"math/rand"
meapp "github.com/tendermint/merkleeyes/app"
wire "github.com/tendermint/go-wire"
)
// MakeTxKV returns a text transaction, allong with expected key, value pair
func MakeTxKV() ([]byte, []byte, []byte) {
k := RandAsciiBytes(8)
v := RandAsciiBytes(8)
return k, v, makeSet(k, v)
}
// blatently copied from merkleeyes/app/app_test.go
// constructs a "set" transaction
func makeSet(key, value []byte) []byte {
tx := make([]byte, 1+wire.ByteSliceSize(key)+wire.ByteSliceSize(value))
buf := tx
buf[0] = meapp.WriteSet // Set TypeByte
buf = buf[1:]
n, err := wire.PutByteSlice(buf, key)
if err != nil {
panic(err)
}
buf = buf[n:]
n, err = wire.PutByteSlice(buf, value)
if err != nil {
panic(err)
}
return tx
}
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandAsciiBytes(n int) []byte {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return b
}

+ 0
- 22
rpc/client/local/main_test.go View File

@ -1,22 +0,0 @@
package local_test
import (
"os"
"testing"
meapp "github.com/tendermint/merkleeyes/app"
nm "github.com/tendermint/tendermint/node"
rpctest "github.com/tendermint/tendermint/rpc/test"
)
var node *nm.Node
func TestMain(m *testing.M) {
// configure a node, but don't start the server
app := meapp.NewMerkleEyesApp("", 100)
node = rpctest.StartTendermint(app)
code := m.Run()
// and shut down proper at the end
os.Exit(code)
}

+ 0
- 181
rpc/client/local/rpc_test.go View File

@ -1,181 +0,0 @@
package local_test
import (
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
merkle "github.com/tendermint/go-merkle"
"github.com/tendermint/tendermint/rpc/client/local"
rpctest "github.com/tendermint/tendermint/rpc/test"
)
// GetClient gets a rpc client pointing to the test tendermint rpc
func GetClient() local.Client {
return local.New(node)
}
// Make sure status is correct (we connect properly)
func TestStatus(t *testing.T) {
c := GetClient()
status, err := c.Status()
require.Nil(t, err, "%+v", err)
require.NotNil(t, status.PubKey)
}
// Make sure info is correct (we connect properly)
func TestInfo(t *testing.T) {
c := GetClient()
status, err := c.Status()
require.Nil(t, err, "%+v", err)
info, err := c.ABCIInfo()
require.Nil(t, err, "%+v", err)
assert.EqualValues(t, status.LatestBlockHeight, info.Response.LastBlockHeight)
assert.True(t, strings.HasPrefix(info.Response.Data, "size:"))
}
func TestNetInfo(t *testing.T) {
c := GetClient()
netinfo, err := c.NetInfo()
require.Nil(t, err, "%+v", err)
assert.True(t, netinfo.Listening)
assert.Equal(t, 0, len(netinfo.Peers))
}
func TestDialSeeds(t *testing.T) {
c := GetClient()
// FIXME: fix server so it doesn't panic on invalid input
_, err := c.DialSeeds([]string{"12.34.56.78:12345"})
require.Nil(t, err, "%+v", err)
}
func TestGenesisAndValidators(t *testing.T) {
c := GetClient()
chainID := rpctest.GetConfig().GetString("chain_id")
// make sure this is the right genesis file
gen, err := c.Genesis()
require.Nil(t, err, "%+v", err)
assert.Equal(t, chainID, gen.Genesis.ChainID)
// get the genesis validator
require.Equal(t, 1, len(gen.Genesis.Validators))
gval := gen.Genesis.Validators[0]
// get the current validators
vals, err := c.Validators()
require.Nil(t, err, "%+v", err)
require.Equal(t, 1, len(vals.Validators))
val := vals.Validators[0]
// make sure the current set is also the genesis set
assert.Equal(t, gval.Amount, val.VotingPower)
assert.Equal(t, gval.PubKey, val.PubKey)
}
// 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 := MakeTxKV()
_, err = c.BroadcastTxCommit(tx)
require.Nil(err, "%+v", err)
// wait before querying
time.Sleep(time.Second * 1)
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)
require.Nil(err, "%+v", err)
appHash := block.BlockMeta.Header.AppHash
assert.True(len(appHash) > 0)
assert.EqualValues(3, block.BlockMeta.Header.Height)
// check blockchain info, now that we know there is info
// TODO: is this commented somewhere that they are returned
// in order of descending height???
info, err := c.BlockchainInfo(1, 3)
require.Nil(err, "%+v", err)
assert.True(info.LastHeight > 2)
if assert.Equal(3, len(info.BlockMetas)) {
lastMeta := info.BlockMetas[0]
assert.EqualValues(3, lastMeta.Header.Height)
bMeta := block.BlockMeta
assert.Equal(bMeta.Header.AppHash, lastMeta.Header.AppHash)
assert.Equal(bMeta.BlockID, lastMeta.BlockID)
}
// and get the corresponding commit with the same apphash
commit, err := c.Commit(3)
require.Nil(err, "%+v", err)
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)
require.Nil(err, "%+v", err)
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)
}
}
}
/*
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 := MakeTxKV()
// 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)
}
*/

rpc/client/local/client.go → rpc/client/localclient.go View File


rpc/client/http/main_test.go → rpc/client/main_test.go View File


+ 236
- 0
rpc/client/rpc_test.go View File

@ -0,0 +1,236 @@
package client_test
import (
"encoding/json"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
merkle "github.com/tendermint/go-merkle"
merktest "github.com/tendermint/merkleeyes/testutil"
"github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctest "github.com/tendermint/tendermint/rpc/test"
"github.com/tendermint/tendermint/types"
)
func getHTTPClient() *client.HTTP {
rpcAddr := rpctest.GetConfig().GetString("rpc_laddr")
return client.NewHTTP(rpcAddr, "/websocket")
}
func getLocalClient() client.Local {
return client.NewLocal(node)
}
// GetClients returns a slice of clients for table-driven tests
func GetClients() []client.Client {
return []client.Client{
getHTTPClient(),
getLocalClient(),
}
}
// Make sure status is correct (we connect properly)
func TestStatus(t *testing.T) {
for i, c := range GetClients() {
chainID := rpctest.GetConfig().GetString("chain_id")
status, err := c.Status()
require.Nil(t, err, "%d: %+v", i, err)
assert.Equal(t, chainID, status.NodeInfo.Network)
}
}
// Make sure info is correct (we connect properly)
func TestInfo(t *testing.T) {
for i, c := range GetClients() {
// status, err := c.Status()
// require.Nil(t, err, "%+v", err)
info, err := c.ABCIInfo()
require.Nil(t, err, "%d: %+v", i, err)
// TODO: this is not correct - fix merkleeyes!
// assert.EqualValues(t, status.LatestBlockHeight, info.Response.LastBlockHeight)
assert.True(t, strings.HasPrefix(info.Response.Data, "size"))
}
}
func TestNetInfo(t *testing.T) {
for i, c := range GetClients() {
nc, ok := c.(client.NetworkClient)
require.True(t, ok, "%d", i)
netinfo, err := nc.NetInfo()
require.Nil(t, err, "%d: %+v", i, err)
assert.True(t, netinfo.Listening)
assert.Equal(t, 0, len(netinfo.Peers))
}
}
func TestDialSeeds(t *testing.T) {
for i, c := range GetClients() {
// FIXME: fix server so it doesn't panic on invalid input
nc, ok := c.(client.NetworkClient)
require.True(t, ok, "%d", i)
_, err := nc.DialSeeds([]string{"12.34.56.78:12345"})
require.Nil(t, err, "%d: %+v", i, err)
}
}
func TestGenesisAndValidators(t *testing.T) {
for i, c := range GetClients() {
chainID := rpctest.GetConfig().GetString("chain_id")
// make sure this is the right genesis file
gen, err := c.Genesis()
require.Nil(t, err, "%d: %+v", i, err)
assert.Equal(t, chainID, gen.Genesis.ChainID)
// get the genesis validator
require.Equal(t, 1, len(gen.Genesis.Validators))
gval := gen.Genesis.Validators[0]
// get the current validators
vals, err := c.Validators()
require.Nil(t, err, "%d: %+v", i, err)
require.Equal(t, 1, len(vals.Validators))
val := vals.Validators[0]
// make sure the current set is also the genesis set
assert.Equal(t, gval.Amount, val.VotingPower)
assert.Equal(t, gval.PubKey, val.PubKey)
}
}
// Make some app checks
func TestAppCalls(t *testing.T) {
assert, require := assert.New(t), require.New(t)
for i, c := range GetClients() {
// get an offset of height to avoid racing and guessing
s, err := c.Status()
require.Nil(err, "%d: %+v", i, err)
// sh is start height or status height
sh := s.LatestBlockHeight
// look for the future
_, err = c.Block(sh + 2)
assert.NotNil(err) // no block yet
// write something
k, v, tx := merktest.MakeTxKV()
_, err = c.BroadcastTxCommit(tx)
require.Nil(err, "%d: %+v", i, err)
// wait before querying
time.Sleep(time.Second * 1)
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())
}
// +/- 1 making my head hurt
h := int(qres.Response.Height) - 1
// and we can even check the block is added
block, err := c.Block(h)
require.Nil(err, "%d: %+v", i, err)
appHash := block.BlockMeta.Header.AppHash
assert.True(len(appHash) > 0)
assert.EqualValues(h, block.BlockMeta.Header.Height)
// check blockchain info, now that we know there is info
// TODO: is this commented somewhere that they are returned
// in order of descending height???
info, err := c.BlockchainInfo(h-2, h)
require.Nil(err, "%d: %+v", i, err)
assert.True(info.LastHeight > 2)
if assert.Equal(3, len(info.BlockMetas)) {
lastMeta := info.BlockMetas[0]
assert.EqualValues(h, lastMeta.Header.Height)
bMeta := block.BlockMeta
assert.Equal(bMeta.Header.AppHash, lastMeta.Header.AppHash)
assert.Equal(bMeta.BlockID, lastMeta.BlockID)
}
// and get the corresponding commit with the same apphash
commit, err := c.Commit(h)
require.Nil(err, "%d: %+v", i, err)
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(h - 1)
require.Nil(err, "%d: %+v", i, err)
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)
}
}
}
}
// TestSubscriptions only works for HTTPClient
//
// TODO: generalize this functionality -> Local and Client
func TestSubscriptions(t *testing.T) {
require := require.New(t)
c := getHTTPClient()
err := c.StartWebsocket()
require.Nil(err)
defer c.StopWebsocket()
// subscribe to a transaction event
_, _, tx := merktest.MakeTxKV()
eventType := types.EventStringTx(types.Tx(tx))
c.Subscribe(eventType)
// set up a listener
r, e := c.GetEventChannels()
go func() {
// send a tx and wait for it to propogate
_, err = c.BroadcastTxCommit(tx)
require.Nil(err, string(tx))
}()
checkData := func(data []byte, kind byte) {
x := []interface{}{}
err := json.Unmarshal(data, &x)
require.Nil(err)
// gotta love wire's json format
require.EqualValues(kind, x[0])
}
res := <-r
checkData(res, ctypes.ResultTypeSubscribe)
// read one event, must be success
select {
case res := <-r:
checkData(res, ctypes.ResultTypeEvent)
// this is good.. let's get the data... ugh...
// result := new(ctypes.TMResult)
// wire.ReadJSON(result, res, &err)
// require.Nil(err, "%+v", err)
// event, ok := (*result).(*ctypes.ResultEvent)
// require.True(ok)
// assert.Equal("foo", event.Name)
// data, ok := event.Data.(types.EventDataTx)
// require.True(ok)
// assert.EqualValues(0, data.Code)
// assert.EqualValues(tx, data.Tx)
case err := <-e:
// this is a failure
require.Nil(err)
}
}

Loading…
Cancel
Save