test: e2e/client to system tests (#21981)

This commit is contained in:
Julián Toledano 2024-09-30 15:52:18 +02:00 committed by GitHub
parent e5b22e4cda
commit 91b47cb5e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 414 additions and 348 deletions

View File

@ -1,347 +0,0 @@
//go:build e2e
// +build e2e
package cmtservice_test
import (
"context"
"fmt"
"testing"
"github.com/stretchr/testify/suite"
"cosmossdk.io/simapp"
_ "cosmossdk.io/x/gov"
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/testutil/network"
qtypes "github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/version"
)
type E2ETestSuite struct {
suite.Suite
cfg network.Config
network network.NetworkI
queryClient cmtservice.ServiceClient
}
func TestE2ETestSuite(t *testing.T) {
suite.Run(t, new(E2ETestSuite))
}
func (s *E2ETestSuite) SetupSuite() {
s.T().Log("setting up e2e test suite")
cfg := network.DefaultConfig(simapp.NewTestNetworkFixture)
cfg.NumValidators = 1
s.cfg = cfg
var err error
s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg)
s.Require().NoError(err)
s.Require().NoError(s.network.WaitForNextBlock())
s.queryClient = cmtservice.NewServiceClient(s.network.GetValidators()[0].GetClientCtx())
}
func (s *E2ETestSuite) TearDownSuite() {
s.T().Log("tearing down e2e test suite")
s.network.Cleanup()
}
func (s *E2ETestSuite) TestQueryNodeInfo() {
val := s.network.GetValidators()[0]
res, err := s.queryClient.GetNodeInfo(context.Background(), &cmtservice.GetNodeInfoRequest{})
s.Require().NoError(err)
s.Require().Equal(res.ApplicationVersion.AppName, version.NewInfo().AppName)
restRes, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/node_info", val.GetAPIAddress()))
s.Require().NoError(err)
var getInfoRes cmtservice.GetNodeInfoResponse
s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(restRes, &getInfoRes))
s.Require().Equal(getInfoRes.ApplicationVersion.AppName, version.NewInfo().AppName)
}
func (s *E2ETestSuite) TestQuerySyncing() {
val := s.network.GetValidators()[0]
_, err := s.queryClient.GetSyncing(context.Background(), &cmtservice.GetSyncingRequest{})
s.Require().NoError(err)
restRes, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/syncing", val.GetAPIAddress()))
s.Require().NoError(err)
var syncingRes cmtservice.GetSyncingResponse
s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(restRes, &syncingRes))
}
func (s *E2ETestSuite) TestQueryLatestBlock() {
val := s.network.GetValidators()[0]
_, err := s.queryClient.GetLatestBlock(context.Background(), &cmtservice.GetLatestBlockRequest{})
s.Require().NoError(err)
restRes, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/blocks/latest", val.GetAPIAddress()))
s.Require().NoError(err)
var blockInfoRes cmtservice.GetLatestBlockResponse
s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(restRes, &blockInfoRes))
s.Require().Contains(blockInfoRes.SdkBlock.Header.ProposerAddress, "cosmosvalcons")
}
func (s *E2ETestSuite) TestQueryBlockByHeight() {
val := s.network.GetValidators()[0]
_, err := s.queryClient.GetBlockByHeight(context.Background(), &cmtservice.GetBlockByHeightRequest{Height: 1})
s.Require().NoError(err)
restRes, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/blocks/%d", val.GetAPIAddress(), 1))
s.Require().NoError(err)
var blockInfoRes cmtservice.GetBlockByHeightResponse
s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(restRes, &blockInfoRes))
s.Require().Contains(blockInfoRes.SdkBlock.Header.ProposerAddress, "cosmosvalcons")
}
func (s *E2ETestSuite) TestQueryLatestValidatorSet() {
val := s.network.GetValidators()[0]
// nil pagination
res, err := s.queryClient.GetLatestValidatorSet(context.Background(), &cmtservice.GetLatestValidatorSetRequest{
Pagination: nil,
})
s.Require().NoError(err)
s.Require().Equal(1, len(res.Validators))
content, ok := res.Validators[0].PubKey.GetCachedValue().(cryptotypes.PubKey)
s.Require().Equal(true, ok)
s.Require().Equal(content, val.GetPubKey())
// with pagination
_, err = s.queryClient.GetLatestValidatorSet(context.Background(), &cmtservice.GetLatestValidatorSetRequest{Pagination: &qtypes.PageRequest{
Offset: 0,
Limit: 10,
}})
s.Require().NoError(err)
// rest request without pagination
_, err = testutil.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/latest", val.GetAPIAddress()))
s.Require().NoError(err)
// rest request with pagination
restRes, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/latest?pagination.offset=%d&pagination.limit=%d", val.GetAPIAddress(), 0, 1))
s.Require().NoError(err)
var validatorSetRes cmtservice.GetLatestValidatorSetResponse
s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(restRes, &validatorSetRes))
s.Require().Equal(1, len(validatorSetRes.Validators))
anyPub, err := codectypes.NewAnyWithValue(val.GetPubKey())
s.Require().NoError(err)
s.Require().Equal(validatorSetRes.Validators[0].PubKey, anyPub)
}
func (s *E2ETestSuite) TestLatestValidatorSet_GRPC() {
vals := s.network.GetValidators()
testCases := []struct {
name string
req *cmtservice.GetLatestValidatorSetRequest
expErr bool
expErrMsg string
}{
{"nil request", nil, true, "cannot be nil"},
{"no pagination", &cmtservice.GetLatestValidatorSetRequest{}, false, ""},
{"with pagination", &cmtservice.GetLatestValidatorSetRequest{Pagination: &qtypes.PageRequest{Offset: 0, Limit: uint64(len(vals))}}, false, ""},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
grpcRes, err := s.queryClient.GetLatestValidatorSet(context.Background(), tc.req)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expErrMsg)
} else {
s.Require().NoError(err)
s.Require().Len(grpcRes.Validators, len(vals))
s.Require().Equal(grpcRes.Pagination.Total, uint64(len(vals)))
content, ok := grpcRes.Validators[0].PubKey.GetCachedValue().(cryptotypes.PubKey)
s.Require().Equal(true, ok)
s.Require().Equal(content, vals[0].GetPubKey())
}
})
}
}
func (s *E2ETestSuite) TestLatestValidatorSet_GRPCGateway() {
vals := s.network.GetValidators()
testCases := []struct {
name string
url string
expErr bool
expErrMsg string
}{
{"no pagination", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/latest", vals[0].GetAPIAddress()), false, ""},
{"pagination invalid fields", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/latest?pagination.offset=-1&pagination.limit=-2", vals[0].GetAPIAddress()), true, "strconv.ParseUint"},
{"with pagination", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/latest?pagination.offset=0&pagination.limit=2", vals[0].GetAPIAddress()), false, ""},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
res, err := testutil.GetRequest(tc.url)
s.Require().NoError(err)
if tc.expErr {
s.Require().Contains(string(res), tc.expErrMsg)
} else {
var result cmtservice.GetLatestValidatorSetResponse
err = vals[0].GetClientCtx().Codec.UnmarshalJSON(res, &result)
s.Require().NoError(err)
s.Require().Equal(uint64(len(vals)), result.Pagination.Total)
anyPub, err := codectypes.NewAnyWithValue(vals[0].GetPubKey())
s.Require().NoError(err)
s.Require().Equal(result.Validators[0].PubKey, anyPub)
}
})
}
}
func (s *E2ETestSuite) TestValidatorSetByHeight_GRPC() {
vals := s.network.GetValidators()
testCases := []struct {
name string
req *cmtservice.GetValidatorSetByHeightRequest
expErr bool
expErrMsg string
}{
{"nil request", nil, true, "request cannot be nil"},
{"empty request", &cmtservice.GetValidatorSetByHeightRequest{}, true, "height must be greater than 0"},
{"no pagination", &cmtservice.GetValidatorSetByHeightRequest{Height: 1}, false, ""},
{"with pagination", &cmtservice.GetValidatorSetByHeightRequest{Height: 1, Pagination: &qtypes.PageRequest{Offset: 0, Limit: 1}}, false, ""},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
grpcRes, err := s.queryClient.GetValidatorSetByHeight(context.Background(), tc.req)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expErrMsg)
} else {
s.Require().NoError(err)
s.Require().Len(grpcRes.Validators, len(vals))
s.Require().Equal(grpcRes.Pagination.Total, uint64(len(vals)))
}
})
}
}
func (s *E2ETestSuite) TestValidatorSetByHeight_GRPCGateway() {
vals := s.network.GetValidators()
testCases := []struct {
name string
url string
expErr bool
expErrMsg string
}{
{"invalid height", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d", vals[0].GetAPIAddress(), -1), true, "height must be greater than 0"},
{"no pagination", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d", vals[0].GetAPIAddress(), 1), false, ""},
{"pagination invalid fields", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d?pagination.offset=-1&pagination.limit=-2", vals[0].GetAPIAddress(), 1), true, "strconv.ParseUint"},
{"with pagination", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d?pagination.offset=0&pagination.limit=2", vals[0].GetAPIAddress(), 1), false, ""},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
res, err := testutil.GetRequest(tc.url)
s.Require().NoError(err)
if tc.expErr {
s.Require().Contains(string(res), tc.expErrMsg)
} else {
var result cmtservice.GetValidatorSetByHeightResponse
err = vals[0].GetClientCtx().Codec.UnmarshalJSON(res, &result)
s.Require().NoError(err)
s.Require().Equal(uint64(len(vals)), result.Pagination.Total)
}
})
}
}
func (s *E2ETestSuite) TestABCIQuery() {
testCases := []struct {
name string
req *cmtservice.ABCIQueryRequest
expectErr bool
expectedCode uint32
validQuery bool
}{
{
name: "valid request with proof",
req: &cmtservice.ABCIQueryRequest{
Path: "/store/gov/key",
Data: []byte{0x03},
Prove: true,
},
validQuery: true,
},
{
name: "valid request without proof",
req: &cmtservice.ABCIQueryRequest{
Path: "/store/gov/key",
Data: []byte{0x03},
Prove: false,
},
validQuery: true,
},
{
name: "request with invalid path",
req: &cmtservice.ABCIQueryRequest{
Path: "/foo/bar",
Data: []byte{0x03},
},
expectErr: true,
},
{
name: "request with invalid path recursive",
req: &cmtservice.ABCIQueryRequest{
Path: "/cosmos.base.tendermint.v1beta1.Service/ABCIQuery",
Data: s.cfg.Codec.MustMarshal(&cmtservice.ABCIQueryRequest{
Path: "/cosmos.base.tendermint.v1beta1.Service/ABCIQuery",
}),
},
expectErr: true,
},
{
name: "request with invalid broadcast tx path",
req: &cmtservice.ABCIQueryRequest{
Path: "/cosmos.tx.v1beta1.Service/BroadcastTx",
Data: []byte{0x00},
},
expectErr: true,
},
{
name: "request with invalid data",
req: &cmtservice.ABCIQueryRequest{
Path: "/store/gov/key",
Data: []byte{0x0044, 0x00},
},
validQuery: false,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
res, err := s.queryClient.ABCIQuery(context.Background(), tc.req)
if tc.expectErr {
s.Require().Error(err)
s.Require().Nil(res)
} else {
s.Require().NoError(err)
s.Require().NotNil(res)
s.Require().Equal(res.Code, tc.expectedCode)
}
if tc.validQuery {
s.Require().Greater(res.Height, int64(0))
s.Require().Greater(len(res.Key), 0, "expected non-empty key")
s.Require().Greater(len(res.Value), 0, "expected non-empty value")
}
if tc.req.Prove {
s.Require().Greater(len(res.ProofOps.Ops), 0, "expected proofs")
}
})
}
}

View File

@ -0,0 +1,330 @@
//go:build system_test
package systemtests
import (
"context"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/tidwall/gjson"
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/testutil"
qtypes "github.com/cosmos/cosmos-sdk/types/query"
)
func TestQueryNodeInfo(t *testing.T) {
baseurl := fmt.Sprintf("http://localhost:%d", apiPortStart)
sut.ResetChain(t)
sut.StartChain(t)
qc := cmtservice.NewServiceClient(sut.RPCClient(t))
res, err := qc.GetNodeInfo(context.Background(), &cmtservice.GetNodeInfoRequest{})
assert.NoError(t, err)
v := NewCLIWrapper(t, sut, true).Version()
assert.Equal(t, res.ApplicationVersion.Version, v)
// TODO: we should be adding a way to distinguish a v2. Eventually we should skip some v2 system depending on the consensus engine we want to test
restRes, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/node_info", baseurl))
assert.NoError(t, err)
assert.Equal(t, gjson.GetBytes(restRes, "application_version.version").String(), res.ApplicationVersion.Version)
}
func TestQuerySyncing(t *testing.T) {
baseurl := fmt.Sprintf("http://localhost:%d", apiPortStart)
sut.ResetChain(t)
sut.StartChain(t)
qc := cmtservice.NewServiceClient(sut.RPCClient(t))
res, err := qc.GetSyncing(context.Background(), &cmtservice.GetSyncingRequest{})
assert.NoError(t, err)
restRes, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/syncing", baseurl))
assert.NoError(t, err)
assert.Equal(t, gjson.GetBytes(restRes, "syncing").Bool(), res.Syncing)
}
func TestQueryLatestBlock(t *testing.T) {
baseurl := fmt.Sprintf("http://localhost:%d", apiPortStart)
sut.ResetChain(t)
sut.StartChain(t)
qc := cmtservice.NewServiceClient(sut.RPCClient(t))
res, err := qc.GetLatestBlock(context.Background(), &cmtservice.GetLatestBlockRequest{})
assert.NoError(t, err)
assert.Contains(t, res.SdkBlock.Header.ProposerAddress, "cosmosvalcons")
_, err = testutil.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/blocks/latest", baseurl))
assert.NoError(t, err)
}
func TestQueryBlockByHeight(t *testing.T) {
baseurl := fmt.Sprintf("http://localhost:%d", apiPortStart)
sut.ResetChain(t)
sut.StartChain(t)
sut.AwaitNBlocks(t, 2, time.Second*25)
qc := cmtservice.NewServiceClient(sut.RPCClient(t))
res, err := qc.GetBlockByHeight(context.Background(), &cmtservice.GetBlockByHeightRequest{Height: 2})
assert.NoError(t, err)
assert.Equal(t, res.SdkBlock.Header.Height, int64(2))
assert.Contains(t, res.SdkBlock.Header.ProposerAddress, "cosmosvalcons")
restRes, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/blocks/%d", baseurl, 2))
assert.NoError(t, err)
assert.Equal(t, gjson.GetBytes(restRes, "sdk_block.header.height").Int(), int64(2))
assert.Contains(t, gjson.GetBytes(restRes, "sdk_block.header.proposer_address").String(), "cosmosvalcons")
}
func TestQueryLatestValidatorSet(t *testing.T) {
baseurl := fmt.Sprintf("http://localhost:%d", apiPortStart)
sut.ResetChain(t)
sut.StartChain(t)
vals := sut.RPCClient(t).Validators()
qc := cmtservice.NewServiceClient(sut.RPCClient(t))
res, err := qc.GetLatestValidatorSet(context.Background(), &cmtservice.GetLatestValidatorSetRequest{
Pagination: nil,
})
assert.NoError(t, err)
assert.Equal(t, len(res.Validators), len(vals))
// with pagination
res, err = qc.GetLatestValidatorSet(context.Background(), &cmtservice.GetLatestValidatorSetRequest{Pagination: &qtypes.PageRequest{
Offset: 0,
Limit: 2,
}})
assert.NoError(t, err)
assert.Equal(t, len(res.Validators), 2)
restRes, err := testutil.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/latest?pagination.offset=%d&pagination.limit=%d", baseurl, 0, 2))
assert.NoError(t, err)
assert.Equal(t, len(gjson.GetBytes(restRes, "validators").Array()), 2)
}
func TestLatestValidatorSet(t *testing.T) {
sut.ResetChain(t)
sut.StartChain(t)
vals := sut.RPCClient(t).Validators()
qc := cmtservice.NewServiceClient(sut.RPCClient(t))
testCases := []struct {
name string
req *cmtservice.GetLatestValidatorSetRequest
expErr bool
expErrMsg string
}{
{"nil request", nil, true, "cannot be nil"},
{"no pagination", &cmtservice.GetLatestValidatorSetRequest{}, false, ""},
{"with pagination", &cmtservice.GetLatestValidatorSetRequest{Pagination: &qtypes.PageRequest{Offset: 0, Limit: uint64(len(vals))}}, false, ""},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
res, err := qc.GetLatestValidatorSet(context.Background(), tc.req)
if tc.expErr {
assert.Error(t, err)
assert.Contains(t, err.Error(), tc.expErrMsg)
} else {
assert.NoError(t, err)
assert.Equal(t, len(res.Validators), len(vals))
content, ok := res.Validators[0].PubKey.GetCachedValue().(cryptotypes.PubKey)
assert.True(t, ok)
assert.Equal(t, content.Address(), vals[0].PubKey.Address())
}
})
}
}
func TestLatestValidatorSet_GRPCGateway(t *testing.T) {
sut.ResetChain(t)
sut.StartChain(t)
baseurl := fmt.Sprintf("http://localhost:%d", apiPortStart)
vals := sut.RPCClient(t).Validators()
testCases := []struct {
name string
url string
expErr bool
expErrMsg string
}{
{"no pagination", "/cosmos/base/tendermint/v1beta1/validatorsets/latest", false, ""},
{"pagination invalid fields", "/cosmos/base/tendermint/v1beta1/validatorsets/latest?pagination.offset=-1&pagination.limit=-2", true, "strconv.ParseUint"},
{"with pagination", "/cosmos/base/tendermint/v1beta1/validatorsets/latest?pagination.offset=0&pagination.limit=2", false, ""},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
rsp, err := testutil.GetRequest(fmt.Sprintf("%s%s", baseurl, tc.url))
assert.NoError(t, err)
if tc.expErr {
errMsg := gjson.GetBytes(rsp, "message").String()
assert.Contains(t, errMsg, tc.expErrMsg)
} else {
assert.Equal(t, len(vals), int(gjson.GetBytes(rsp, "pagination.total").Int()))
}
})
}
}
func TestValidatorSetByHeight(t *testing.T) {
sut.ResetChain(t)
sut.StartChain(t)
qc := cmtservice.NewServiceClient(sut.RPCClient(t))
vals := sut.RPCClient(t).Validators()
testCases := []struct {
name string
req *cmtservice.GetValidatorSetByHeightRequest
expErr bool
expErrMsg string
}{
{"nil request", nil, true, "request cannot be nil"},
{"empty request", &cmtservice.GetValidatorSetByHeightRequest{}, true, "height must be greater than 0"},
{"no pagination", &cmtservice.GetValidatorSetByHeightRequest{Height: 1}, false, ""},
{"with pagination", &cmtservice.GetValidatorSetByHeightRequest{Height: 1, Pagination: &qtypes.PageRequest{Offset: 0, Limit: uint64(len(vals))}}, false, ""},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
res, err := qc.GetValidatorSetByHeight(context.Background(), tc.req)
if tc.expErr {
assert.Error(t, err)
assert.Contains(t, err.Error(), tc.expErrMsg)
} else {
assert.NoError(t, err)
assert.Equal(t, len(res.Validators), len(vals))
}
})
}
}
func TestValidatorSetByHeight_GRPCGateway(t *testing.T) {
sut.ResetChain(t)
sut.StartChain(t)
vals := sut.RPCClient(t).Validators()
baseurl := fmt.Sprintf("http://localhost:%d", apiPortStart)
block := sut.AwaitNextBlock(t, time.Second*3)
testCases := []struct {
name string
url string
expErr bool
expErrMsg string
}{
{"invalid height", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d", baseurl, -1), true, "height must be greater than 0"},
{"no pagination", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d", baseurl, block), false, ""},
{"pagination invalid fields", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d?pagination.offset=-1&pagination.limit=-2", baseurl, block), true, "strconv.ParseUint"},
{"with pagination", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d?pagination.limit=2", baseurl, 1), false, ""},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
rsp, err := testutil.GetRequest(tc.url)
assert.NoError(t, err)
if tc.expErr {
errMsg := gjson.GetBytes(rsp, "message").String()
assert.Contains(t, errMsg, tc.expErrMsg)
} else {
assert.Equal(t, len(vals), int(gjson.GetBytes(rsp, "pagination.total").Int()))
}
})
}
}
func TestABCIQuery(t *testing.T) {
sut.ResetChain(t)
sut.StartChain(t)
qc := cmtservice.NewServiceClient(sut.RPCClient(t))
cdc := codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
testCases := []struct {
name string
req *cmtservice.ABCIQueryRequest
expectErr bool
expectedCode uint32
validQuery bool
}{
{
name: "valid request with proof",
req: &cmtservice.ABCIQueryRequest{
Path: "/store/gov/key",
Data: []byte{0x03},
Prove: true,
},
validQuery: true,
},
{
name: "valid request without proof",
req: &cmtservice.ABCIQueryRequest{
Path: "/store/gov/key",
Data: []byte{0x03},
Prove: false,
},
validQuery: true,
},
{
name: "request with invalid path",
req: &cmtservice.ABCIQueryRequest{
Path: "/foo/bar",
Data: []byte{0x03},
},
expectErr: true,
},
{
name: "request with invalid path recursive",
req: &cmtservice.ABCIQueryRequest{
Path: "/cosmos.base.tendermint.v1beta1.Service/ABCIQuery",
Data: cdc.MustMarshal(&cmtservice.ABCIQueryRequest{
Path: "/cosmos.base.tendermint.v1beta1.Service/ABCIQuery",
}),
},
expectErr: true,
},
{
name: "request with invalid broadcast tx path",
req: &cmtservice.ABCIQueryRequest{
Path: "/cosmos.tx.v1beta1.Service/BroadcastTx",
Data: []byte{0x00},
},
expectErr: true,
},
{
name: "request with invalid data",
req: &cmtservice.ABCIQueryRequest{
Path: "/store/gov/key",
Data: []byte{0x0044, 0x00},
},
validQuery: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
res, err := qc.ABCIQuery(context.Background(), tc.req)
if tc.expectErr {
assert.Error(t, err)
assert.Nil(t, res)
} else {
assert.NoError(t, err)
assert.NotNil(t, res)
assert.Equal(t, res.Code, tc.expectedCode)
}
if tc.validQuery {
assert.Greater(t, res.Height, int64(0))
assert.Greater(t, len(res.Key), 0)
assert.Greater(t, len(res.Value), 0)
}
})
}
}

View File

@ -20,12 +20,13 @@ require (
github.com/stretchr/testify v1.9.0
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/grpc v1.67.0 // indirect
google.golang.org/grpc v1.67.0
)
require (
cosmossdk.io/math v1.3.0
github.com/cometbft/cometbft v0.38.8
github.com/cometbft/cometbft/api v1.0.0-rc.1
github.com/creachadair/tomledit v0.0.26
github.com/tidwall/gjson v1.14.2
github.com/tidwall/sjson v1.2.5

View File

@ -129,6 +129,8 @@ github.com/cometbft/cometbft v0.38.8 h1:XyJ9Cu3xqap6xtNxiemrO8roXZ+KS2Zlu7qQ0w1t
github.com/cometbft/cometbft v0.38.8/go.mod h1:xOoGZrtUT+A5izWfHSJgl0gYZUE7lu7Z2XIS1vWG/QQ=
github.com/cometbft/cometbft-db v0.9.1 h1:MIhVX5ja5bXNHF8EYrThkG9F7r9kSfv8BX4LWaxWJ4M=
github.com/cometbft/cometbft-db v0.9.1/go.mod h1:iliyWaoV0mRwBJoizElCwwRA9Tf7jZJOURcRZF9m60U=
github.com/cometbft/cometbft/api v1.0.0-rc.1 h1:GtdXwDGlqwHYs16A4egjwylfYOMYyEacLBrs3Zvpt7g=
github.com/cometbft/cometbft/api v1.0.0-rc.1/go.mod h1:NDFKiBBD8HJC6QQLAoUI99YhsiRZtg2+FJWfk6A6m6o=
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=

View File

@ -2,11 +2,23 @@ package systemtests
import (
"context"
"errors"
"reflect"
"strconv"
"testing"
abci "github.com/cometbft/cometbft/api/cometbft/abci/v1"
rpcclient "github.com/cometbft/cometbft/rpc/client"
client "github.com/cometbft/cometbft/rpc/client/http"
cmtypes "github.com/cometbft/cometbft/types"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
)
// RPCClient is a test helper to interact with a node via the RPC endpoint.
@ -31,3 +43,71 @@ func (r RPCClient) Validators() []*cmtypes.Validator {
require.NoError(r.t, err)
return v.Validators
}
func (r RPCClient) Invoke(ctx context.Context, method string, req, reply interface{}, opts ...grpc.CallOption) error {
if reflect.ValueOf(req).IsNil() {
return errors.New("request cannot be nil")
}
ir := types.NewInterfaceRegistry()
cryptocodec.RegisterInterfaces(ir)
cdc := codec.NewProtoCodec(ir).GRPCCodec()
reqBz, err := cdc.Marshal(req)
if err != nil {
return err
}
var height int64
md, _ := metadata.FromOutgoingContext(ctx)
if heights := md.Get(grpctypes.GRPCBlockHeightHeader); len(heights) > 0 {
height, err := strconv.ParseInt(heights[0], 10, 64)
if err != nil {
return err
}
if height < 0 {
return errors.New("height must be greater than or equal to 0")
}
}
abciReq := abci.QueryRequest{
Path: method,
Data: reqBz,
Height: height,
}
abciOpts := rpcclient.ABCIQueryOptions{
Height: height,
Prove: abciReq.Prove,
}
result, err := r.client.ABCIQueryWithOptions(ctx, abciReq.Path, abciReq.Data, abciOpts)
if err != nil {
return err
}
if !result.Response.IsOK() {
return errors.New(result.Response.String())
}
err = cdc.Unmarshal(result.Response.Value, reply)
if err != nil {
return err
}
md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(result.Response.Height, 10))
for _, callOpt := range opts {
header, ok := callOpt.(grpc.HeaderCallOption)
if !ok {
continue
}
*header.HeaderAddr = md
}
return types.UnpackInterfaces(reply, ir)
}
func (r RPCClient) NewStream(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
return nil, errors.New("not implemented")
}