Merge PR #6489: Test Network Testing Framework
This commit is contained in:
parent
3df4dd0cd9
commit
9bf3ff75f5
32
.github/workflows/test.yml
vendored
32
.github/workflows/test.yml
vendored
@ -60,7 +60,7 @@ jobs:
|
||||
if: "env.GIT_DIFF != ''"
|
||||
- name: test & coverage report creation
|
||||
run: |
|
||||
cat xaa.txt | xargs go test -mod=readonly -timeout 8m -race -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock'
|
||||
cat xaa.txt | xargs go test -mod=readonly -timeout 8m -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock'
|
||||
if: "env.GIT_DIFF != ''"
|
||||
- name: filter out DONTCOVER
|
||||
run: |
|
||||
@ -98,7 +98,7 @@ jobs:
|
||||
if: "env.GIT_DIFF != ''"
|
||||
- name: test & coverage report creation
|
||||
run: |
|
||||
cat xab.txt | xargs go test -mod=readonly -timeout 6m -race -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock'
|
||||
cat xab.txt | xargs go test -mod=readonly -timeout 6m -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock'
|
||||
if: "env.GIT_DIFF != ''"
|
||||
- name: filter out DONTCOVER
|
||||
run: |
|
||||
@ -136,7 +136,7 @@ jobs:
|
||||
if: "env.GIT_DIFF != ''"
|
||||
- name: test & coverage report creation
|
||||
run: |
|
||||
cat xac.txt | xargs go test -mod=readonly -timeout 6m -race -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock'
|
||||
cat xac.txt | xargs go test -mod=readonly -timeout 6m -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock'
|
||||
if: "env.GIT_DIFF != ''"
|
||||
- name: filter out DONTCOVER
|
||||
run: |
|
||||
@ -174,7 +174,7 @@ jobs:
|
||||
if: "env.GIT_DIFF != ''"
|
||||
- name: test & coverage report creation
|
||||
run: |
|
||||
cat xad.txt | xargs go test -mod=readonly -timeout 6m -race -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock'
|
||||
cat xad.txt | xargs go test -mod=readonly -timeout 6m -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock'
|
||||
if: "env.GIT_DIFF != ''"
|
||||
- name: filter out DONTCOVER
|
||||
run: |
|
||||
@ -213,27 +213,3 @@ jobs:
|
||||
run: |
|
||||
make test-integration
|
||||
if: "env.GIT_DIFF != ''"
|
||||
|
||||
liveness-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: technote-space/get-diff-action@v1
|
||||
id: git_diff
|
||||
with:
|
||||
SUFFIX_FILTER: |
|
||||
.go
|
||||
.mod
|
||||
.sum
|
||||
- name: build image
|
||||
run: |
|
||||
make build-docker-local-simapp
|
||||
- name: start localnet
|
||||
run: |
|
||||
make clean build-sim-linux localnet-start
|
||||
if: "env.GIT_DIFF != ''"
|
||||
- name: test liveness
|
||||
run: |
|
||||
./contrib/localnet_liveness.sh 100 5 50 localhost
|
||||
if: "env.GIT_DIFF != ''"
|
||||
|
||||
@ -146,6 +146,7 @@ be used to retrieve the actual proposal `Content`. Also the `NewMsgSubmitProposa
|
||||
|
||||
### Features
|
||||
|
||||
* (tests) [\#6489](https://github.com/cosmos/cosmos-sdk/pull/6489) Introduce package `testutil`, new in-process testing network framework for use in integration and unit tests.
|
||||
* (crypto/multisig) [\#6241](https://github.com/cosmos/cosmos-sdk/pull/6241) Add Multisig type directly to the repo. Previously this was in tendermint.
|
||||
* (rest) [\#6167](https://github.com/cosmos/cosmos-sdk/pull/6167) Support `max-body-bytes` CLI flag for the REST service.
|
||||
* (x/ibc) [\#5588](https://github.com/cosmos/cosmos-sdk/pull/5588) Add [ICS 024 - Host State Machine Requirements](https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements) subpackage to `x/ibc` module.
|
||||
|
||||
@ -1,55 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
CNT=0
|
||||
ITER=$1
|
||||
SLEEP=$2
|
||||
NUMBLOCKS=$3
|
||||
NODEADDR=$4
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Need to input number of iterations to run..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$2" ]; then
|
||||
echo "Need to input number of seconds to sleep between iterations"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$3" ]; then
|
||||
echo "Need to input block height to declare completion..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$4" ]; then
|
||||
echo "Need to input node address to poll..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker_containers=( $(docker ps -q -f name=simdnode --format='{{.Names}}') )
|
||||
|
||||
while [ ${CNT} -lt $ITER ]; do
|
||||
curr_block=$(curl -s $NODEADDR:26657/status | jq -r '.result.sync_info.latest_block_height')
|
||||
|
||||
if [ ! -z ${curr_block} ] ; then
|
||||
echo "Number of Blocks: ${curr_block}"
|
||||
fi
|
||||
|
||||
if [ ! -z ${curr_block} ] && [ ${curr_block} -gt ${NUMBLOCKS} ]; then
|
||||
echo "Number of blocks reached. Success!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Emulate network chaos:
|
||||
#
|
||||
# Every 10 blocks, pick a random container and restart it.
|
||||
if ! ((${CNT} % 10)); then
|
||||
rand_container=${docker_containers["$[RANDOM % ${#docker_containers[@]}]"]};
|
||||
echo "Restarting random docker container ${rand_container}"
|
||||
docker restart ${rand_container} &>/dev/null &
|
||||
fi
|
||||
let CNT=CNT+1
|
||||
sleep $SLEEP
|
||||
done
|
||||
echo "Timeout reached. Failure!"
|
||||
exit 1
|
||||
1
go.mod
1
go.mod
@ -39,6 +39,7 @@ require (
|
||||
github.com/tendermint/iavl v0.14.0-rc1
|
||||
github.com/tendermint/tendermint v0.33.5
|
||||
github.com/tendermint/tm-db v0.5.1
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
google.golang.org/grpc v1.30.0
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
)
|
||||
|
||||
1
go.sum
1
go.sum
@ -605,6 +605,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
||||
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -33,11 +32,11 @@ type Server struct {
|
||||
listener net.Listener
|
||||
}
|
||||
|
||||
func New(clientCtx client.Context) *Server {
|
||||
func New(clientCtx client.Context, logger log.Logger) *Server {
|
||||
return &Server{
|
||||
Router: mux.NewRouter(),
|
||||
ClientCtx: clientCtx,
|
||||
logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "api-server"),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@ -199,8 +200,9 @@ func startInProcess(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator
|
||||
return err
|
||||
}
|
||||
|
||||
config := config.GetConfig()
|
||||
var apiSrv *api.Server
|
||||
|
||||
config := config.GetConfig()
|
||||
if config.API.Enable {
|
||||
genDoc, err := genDocProvider()
|
||||
if err != nil {
|
||||
@ -210,18 +212,28 @@ func startInProcess(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator
|
||||
// TODO: Since this is running in process, do we need to provide a verifier
|
||||
// and set TrustNode=false? If so, we need to add additional logic that
|
||||
// waits for a block to be committed first before starting the API server.
|
||||
ctx := client.Context{}.
|
||||
clientCtx := client.Context{}.
|
||||
WithHomeDir(home).
|
||||
WithChainID(genDoc.ChainID).
|
||||
WithJSONMarshaler(cdc).
|
||||
WithClient(local.New(tmNode)).
|
||||
WithTrustNode(true)
|
||||
|
||||
apiSrv = api.New(ctx)
|
||||
apiSrv = api.New(clientCtx, ctx.Logger.With("module", "api-server"))
|
||||
app.RegisterAPIRoutes(apiSrv)
|
||||
|
||||
if err := apiSrv.Start(config); err != nil {
|
||||
errCh := make(chan error)
|
||||
|
||||
go func() {
|
||||
if err := apiSrv.Start(config); err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-time.After(5 * time.Second): // assume server started successfully
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
65
testutil/doc.go
Normal file
65
testutil/doc.go
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Package testutil implements and exposes a fully operational in-process Tendermint
|
||||
test network that consists of at least one or potentially many validators. This
|
||||
test network can be used primarily for integration tests or unit test suites.
|
||||
|
||||
The testnetwork utilizes SimApp as the ABCI application and uses all the modules
|
||||
defined in the Cosmos SDK. An in-process test network can be configured with any
|
||||
number of validators as well as account funds and even custom genesis state.
|
||||
|
||||
When creating a test network, a series of Validator objects are returned. Each
|
||||
Validator object has useful information such as their address and pubkey. A
|
||||
Validator will also provide its RPC, P2P, and API addresses that can be useful
|
||||
for integration testing. In addition, a Tendermint local RPC client is also provided
|
||||
which can be handy for making direct RPC calls to Tendermint.
|
||||
|
||||
Note, due to limitations in concurrency and the design of the RPC layer in
|
||||
Tendermint, only the first Validator object will have an RPC and API client
|
||||
exposed. Due to this exact same limitation, only a single test network can exist
|
||||
at a time. A caller must be certain it calls Cleanup after it no longer needs
|
||||
the network.
|
||||
|
||||
A typical testing flow might look like the following:
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
cfg testutil.Config
|
||||
network *testutil.Network
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) SetupSuite() {
|
||||
s.T().Log("setting up integration test suite")
|
||||
|
||||
cfg := testutil.DefaultConfig()
|
||||
cfg.NumValidators = 1
|
||||
|
||||
s.cfg = cfg
|
||||
s.network = testutil.NewTestNetwork(s.T(), cfg)
|
||||
|
||||
_, err := s.network.WaitForHeight(1)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TearDownSuite() {
|
||||
s.T().Log("tearing down integration test suite")
|
||||
|
||||
// This is important and must be called to ensure other tests can create
|
||||
// a network!
|
||||
s.network.Cleanup()
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestQueryBalancesRequestHandlerFn() {
|
||||
val := s.network.Validators[0]
|
||||
baseURL := val.APIAddress
|
||||
|
||||
// Use baseURL to make API HTTP requests or use val.RPCClient to make direct
|
||||
// Tendermint RPC calls.
|
||||
// ...
|
||||
}
|
||||
|
||||
func TestIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
}
|
||||
*/
|
||||
package testutil
|
||||
384
testutil/network.go
Normal file
384
testutil/network.go
Normal file
@ -0,0 +1,384 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
tmcfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
tmflags "github.com/tendermint/tendermint/libs/cli/flags"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
"github.com/tendermint/tendermint/node"
|
||||
tmclient "github.com/tendermint/tendermint/rpc/client"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
"github.com/cosmos/cosmos-sdk/server/api"
|
||||
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
var (
|
||||
_, cdc = simapp.MakeCodecs()
|
||||
|
||||
// package-wide network lock to only allow one test network at a time
|
||||
lock = new(sync.Mutex)
|
||||
)
|
||||
|
||||
// AppConstructor defines a function which accepts a network configuration and
|
||||
// creates an ABCI Application to provide to Tendermint.
|
||||
type AppConstructor = func(val Validator) server.Application
|
||||
|
||||
func NewSimApp(val Validator) server.Application {
|
||||
return simapp.NewSimApp(
|
||||
val.Ctx.Logger, dbm.NewMemDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0,
|
||||
baseapp.SetPruning(storetypes.NewPruningOptionsFromString(val.AppConfig.Pruning)),
|
||||
baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices),
|
||||
)
|
||||
}
|
||||
|
||||
// Config defines the necessary configuration used to bootstrap and start an
|
||||
// in-process local testing network.
|
||||
type Config struct {
|
||||
AppConstructor AppConstructor // the ABCI application constructor
|
||||
GenesisState map[string]json.RawMessage // custom gensis state to provide
|
||||
TimeoutCommit time.Duration // the consensus commitment timeout
|
||||
ChainID string // the network chain-id
|
||||
NumValidators int // the total number of validators to create and bond
|
||||
BondDenom string // the staking bond denomination
|
||||
MinGasPrices string // the minimum gas prices each validator will accept
|
||||
Passphrase string // the passphrase provided to the test keyring
|
||||
AccountTokens sdk.Int // the amount of unique validator tokens (e.g. 1000node0)
|
||||
StakingTokens sdk.Int // the amount of tokens each validator has available to stake
|
||||
BondedTokens sdk.Int // the amount of tokens each validator stakes
|
||||
PruningStrategy string // the pruning strategy each validator will have
|
||||
EnableLogging bool // enable Tendermint logging to STDOUT
|
||||
CleanupDir bool // remove base temporary directory during cleanup
|
||||
}
|
||||
|
||||
// DefaultConfig returns a sane default configuration suitable for nearly all
|
||||
// testing requirements.
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
AppConstructor: NewSimApp,
|
||||
GenesisState: simapp.ModuleBasics.DefaultGenesis(cdc),
|
||||
TimeoutCommit: 2 * time.Second,
|
||||
ChainID: "chain-" + tmrand.NewRand().Str(6),
|
||||
NumValidators: 4,
|
||||
BondDenom: sdk.DefaultBondDenom,
|
||||
MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom),
|
||||
Passphrase: clientkeys.DefaultKeyPass,
|
||||
AccountTokens: sdk.TokensFromConsensusPower(1000),
|
||||
StakingTokens: sdk.TokensFromConsensusPower(500),
|
||||
BondedTokens: sdk.TokensFromConsensusPower(100),
|
||||
PruningStrategy: storetypes.PruningOptionNothing,
|
||||
CleanupDir: true,
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
// Network defines a local in-process testing network using SimApp. It can be
|
||||
// configured to start any number of validators, each with its own RPC and API
|
||||
// clients. Typically, this test network would be used in client and integration
|
||||
// testing where user input is expected.
|
||||
//
|
||||
// Note, due to Tendermint constraints in regards to RPC functionality, there
|
||||
// may only be one test network running at a time. Thus, any caller must be
|
||||
// sure to Cleanup after testing is finished in order to allow other tests
|
||||
// to create networks. In addition, only the first validator will have a valid
|
||||
// RPC and API server/client.
|
||||
Network struct {
|
||||
T *testing.T
|
||||
BaseDir string
|
||||
Validators []*Validator
|
||||
|
||||
config Config
|
||||
}
|
||||
|
||||
// Validator defines an in-process Tendermint validator node. Through this object,
|
||||
// a client can make RPC and API calls and interact with any client command
|
||||
// or handler.
|
||||
Validator struct {
|
||||
AppConfig *srvconfig.Config
|
||||
ClientCtx client.Context
|
||||
Ctx *server.Context
|
||||
Dir string
|
||||
NodeID string
|
||||
PubKey crypto.PubKey
|
||||
Moniker string
|
||||
APIAddress string
|
||||
RPCAddress string
|
||||
P2PAddress string
|
||||
Address sdk.AccAddress
|
||||
ValAddress sdk.ValAddress
|
||||
RPCClient tmclient.Client
|
||||
|
||||
tmNode *node.Node
|
||||
api *api.Server
|
||||
}
|
||||
)
|
||||
|
||||
func NewTestNetwork(t *testing.T, cfg Config) *Network {
|
||||
// only one caller/test can create and use a network at a time
|
||||
t.Log("acquiring test network lock")
|
||||
lock.Lock()
|
||||
|
||||
baseDir, err := ioutil.TempDir(os.TempDir(), cfg.ChainID)
|
||||
require.NoError(t, err)
|
||||
t.Logf("created temporary directory: %s", baseDir)
|
||||
|
||||
network := &Network{
|
||||
T: t,
|
||||
BaseDir: baseDir,
|
||||
Validators: make([]*Validator, cfg.NumValidators),
|
||||
config: cfg,
|
||||
}
|
||||
|
||||
t.Log("preparing test network...")
|
||||
|
||||
monikers := make([]string, cfg.NumValidators)
|
||||
nodeIDs := make([]string, cfg.NumValidators)
|
||||
valPubKeys := make([]crypto.PubKey, cfg.NumValidators)
|
||||
|
||||
var (
|
||||
genAccounts []authtypes.GenesisAccount
|
||||
genBalances []banktypes.Balance
|
||||
genFiles []string
|
||||
)
|
||||
|
||||
buf := bufio.NewReader(os.Stdin)
|
||||
|
||||
// generate private keys, node IDs, and initial transactions
|
||||
for i := 0; i < cfg.NumValidators; i++ {
|
||||
appCfg := srvconfig.DefaultConfig()
|
||||
appCfg.Pruning = cfg.PruningStrategy
|
||||
appCfg.MinGasPrices = cfg.MinGasPrices
|
||||
appCfg.API.Enable = true
|
||||
appCfg.API.Swagger = false
|
||||
appCfg.Telemetry.Enabled = false
|
||||
|
||||
ctx := server.NewDefaultContext()
|
||||
tmCfg := ctx.Config
|
||||
tmCfg.Consensus.TimeoutCommit = cfg.TimeoutCommit
|
||||
|
||||
// Only allow the first validator to expose an RPC and API server/client
|
||||
// due to Tendermint in-process constraints.
|
||||
apiAddr := ""
|
||||
tmCfg.RPC.ListenAddress = ""
|
||||
if i == 0 {
|
||||
apiListenAddr, _, err := server.FreeTCPAddr()
|
||||
require.NoError(t, err)
|
||||
appCfg.API.Address = apiListenAddr
|
||||
|
||||
apiURL, err := url.Parse(apiListenAddr)
|
||||
require.NoError(t, err)
|
||||
apiAddr = fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port())
|
||||
|
||||
rpcAddr, _, err := server.FreeTCPAddr()
|
||||
require.NoError(t, err)
|
||||
tmCfg.RPC.ListenAddress = rpcAddr
|
||||
}
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
if cfg.EnableLogging {
|
||||
logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
logger, _ = tmflags.ParseLogLevel("info", logger, tmcfg.DefaultLogLevel())
|
||||
}
|
||||
|
||||
ctx.Logger = logger
|
||||
|
||||
nodeDirName := fmt.Sprintf("node%d", i)
|
||||
nodeDir := filepath.Join(network.BaseDir, nodeDirName, "simd")
|
||||
clientDir := filepath.Join(network.BaseDir, nodeDirName, "simcli")
|
||||
gentxsDir := filepath.Join(network.BaseDir, "gentxs")
|
||||
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(nodeDir, "config"), 0755))
|
||||
require.NoError(t, os.MkdirAll(clientDir, 0755))
|
||||
|
||||
tmCfg.SetRoot(nodeDir)
|
||||
tmCfg.Moniker = nodeDirName
|
||||
monikers[i] = nodeDirName
|
||||
|
||||
proxyAddr, _, err := server.FreeTCPAddr()
|
||||
require.NoError(t, err)
|
||||
tmCfg.ProxyApp = proxyAddr
|
||||
|
||||
p2pAddr, _, err := server.FreeTCPAddr()
|
||||
require.NoError(t, err)
|
||||
tmCfg.P2P.ListenAddress = p2pAddr
|
||||
tmCfg.P2P.AddrBookStrict = false
|
||||
tmCfg.P2P.AllowDuplicateIP = true
|
||||
|
||||
nodeID, pubKey, err := genutil.InitializeNodeValidatorFiles(tmCfg)
|
||||
require.NoError(t, err)
|
||||
nodeIDs[i] = nodeID
|
||||
valPubKeys[i] = pubKey
|
||||
|
||||
kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, clientDir, buf)
|
||||
require.NoError(t, err)
|
||||
|
||||
addr, secret, err := server.GenerateSaveCoinKey(kb, nodeDirName, cfg.Passphrase, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
info := map[string]string{"secret": secret}
|
||||
infoBz, err := json.Marshal(info)
|
||||
require.NoError(t, err)
|
||||
|
||||
// save private key seed words
|
||||
require.NoError(t, writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, infoBz))
|
||||
|
||||
balances := sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), cfg.AccountTokens),
|
||||
sdk.NewCoin(cfg.BondDenom, cfg.StakingTokens),
|
||||
)
|
||||
|
||||
genFiles = append(genFiles, tmCfg.GenesisFile())
|
||||
genBalances = append(genBalances, banktypes.Balance{Address: addr, Coins: balances.Sort()})
|
||||
genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0))
|
||||
|
||||
createValMsg := stakingtypes.NewMsgCreateValidator(
|
||||
sdk.ValAddress(addr),
|
||||
valPubKeys[i],
|
||||
sdk.NewCoin(sdk.DefaultBondDenom, cfg.BondedTokens),
|
||||
stakingtypes.NewDescription(nodeDirName, "", "", "", ""),
|
||||
stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()),
|
||||
sdk.OneInt(),
|
||||
)
|
||||
|
||||
p2pURL, err := url.Parse(p2pAddr)
|
||||
require.NoError(t, err)
|
||||
|
||||
memo := fmt.Sprintf("%s@%s:%s", nodeIDs[i], p2pURL.Hostname(), p2pURL.Port())
|
||||
tx := authtypes.NewStdTx([]sdk.Msg{createValMsg}, authtypes.StdFee{}, []authtypes.StdSignature{}, memo) //nolint:staticcheck // SA1019: authtypes.StdFee is deprecated
|
||||
txBldr := authtypes.TxBuilder{}.
|
||||
WithChainID(cfg.ChainID).
|
||||
WithMemo(memo).
|
||||
WithKeybase(kb)
|
||||
|
||||
signedTx, err := txBldr.SignStdTx(nodeDirName, tx, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
txBz, err := cdc.MarshalJSON(signedTx)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz))
|
||||
|
||||
srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config/app.toml"), appCfg)
|
||||
|
||||
network.Validators[i] = &Validator{
|
||||
AppConfig: appCfg,
|
||||
Ctx: ctx,
|
||||
Dir: filepath.Join(network.BaseDir, nodeDirName),
|
||||
NodeID: nodeID,
|
||||
PubKey: pubKey,
|
||||
Moniker: nodeDirName,
|
||||
RPCAddress: tmCfg.RPC.ListenAddress,
|
||||
P2PAddress: tmCfg.P2P.ListenAddress,
|
||||
APIAddress: apiAddr,
|
||||
Address: addr,
|
||||
ValAddress: sdk.ValAddress(addr),
|
||||
}
|
||||
}
|
||||
|
||||
require.NoError(t, initGenFiles(cfg, genAccounts, genBalances, genFiles))
|
||||
require.NoError(t, collectGenFiles(cfg, network.Validators, network.BaseDir))
|
||||
|
||||
t.Log("starting test network...")
|
||||
for _, v := range network.Validators {
|
||||
require.NoError(t, startInProcess(cfg, v))
|
||||
}
|
||||
|
||||
t.Log("started test network")
|
||||
|
||||
// Ensure we cleanup incase any test was abruptly halted (e.g. SIGINT) as any
|
||||
// defer in a test would not be called.
|
||||
server.TrapSignal(network.Cleanup)
|
||||
|
||||
return network
|
||||
}
|
||||
|
||||
// WaitForHeight performs a blocking check where it waits for a block to be
|
||||
// committed after a given block. If that height is not reached within a timeout,
|
||||
// an error is returned. Regardless, the latest height queried is returned.
|
||||
func (n *Network) WaitForHeight(h int64) (int64, error) {
|
||||
return n.WaitForHeightWithTimeout(h, 10*time.Second)
|
||||
}
|
||||
|
||||
// WaitForHeightWithTimeout is the same as WaitForHeight except the caller can
|
||||
// provide a custom timeout.
|
||||
func (n *Network) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, error) {
|
||||
ticker := time.NewTicker(time.Second)
|
||||
timeout := time.After(t)
|
||||
|
||||
if len(n.Validators) == 0 {
|
||||
return 0, errors.New("no validators available")
|
||||
}
|
||||
|
||||
var latestHeight int64
|
||||
val := n.Validators[0]
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
ticker.Stop()
|
||||
return latestHeight, errors.New("timeout exceeded waiting for block")
|
||||
case <-ticker.C:
|
||||
status, err := val.RPCClient.Status()
|
||||
if err == nil && status != nil {
|
||||
latestHeight = status.SyncInfo.LatestBlockHeight
|
||||
if latestHeight >= h {
|
||||
return latestHeight, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup removes the root testing (temporary) directory and stops both the
|
||||
// Tendermint and API services. It allows other callers to create and start
|
||||
// test networks. This method must be called when a test is finished, typically
|
||||
// in a defer.
|
||||
func (n *Network) Cleanup() {
|
||||
defer func() {
|
||||
lock.Unlock()
|
||||
n.T.Log("released test network lock")
|
||||
}()
|
||||
|
||||
n.T.Log("cleaning up test network...")
|
||||
|
||||
for _, v := range n.Validators {
|
||||
if v.tmNode != nil && v.tmNode.IsRunning() {
|
||||
_ = v.tmNode.Stop()
|
||||
}
|
||||
|
||||
if v.api != nil {
|
||||
_ = v.api.Close()
|
||||
}
|
||||
}
|
||||
|
||||
if n.config.CleanupDir {
|
||||
_ = os.RemoveAll(n.BaseDir)
|
||||
}
|
||||
|
||||
n.T.Log("finished cleaning up test network")
|
||||
}
|
||||
38
testutil/network_test.go
Normal file
38
testutil/network_test.go
Normal file
@ -0,0 +1,38 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
network *Network
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) SetupSuite() {
|
||||
s.T().Log("setting up integration test suite")
|
||||
|
||||
s.network = NewTestNetwork(s.T(), DefaultConfig())
|
||||
s.Require().NotNil(s.network)
|
||||
|
||||
_, err := s.network.WaitForHeight(1)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TearDownSuite() {
|
||||
s.T().Log("tearing down integration test suite")
|
||||
s.network.Cleanup()
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestNetwork_Liveness() {
|
||||
h, err := s.network.WaitForHeightWithTimeout(10, time.Minute)
|
||||
s.Require().NoError(err, "expected to reach 10 blocks; got %d", h)
|
||||
}
|
||||
|
||||
func TestIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
}
|
||||
179
testutil/util.go
Normal file
179
testutil/util.go
Normal file
@ -0,0 +1,179 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
"github.com/tendermint/tendermint/node"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
pvm "github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
"github.com/tendermint/tendermint/rpc/client/local"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/server/api"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
)
|
||||
|
||||
func startInProcess(cfg Config, val *Validator) error {
|
||||
logger := val.Ctx.Logger
|
||||
tmCfg := val.Ctx.Config
|
||||
tmCfg.Instrumentation.Prometheus = false
|
||||
|
||||
nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app := cfg.AppConstructor(*val)
|
||||
|
||||
genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg)
|
||||
tmNode, err := node.NewNode(
|
||||
tmCfg,
|
||||
pvm.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()),
|
||||
nodeKey,
|
||||
proxy.NewLocalClientCreator(app),
|
||||
genDocProvider,
|
||||
node.DefaultDBProvider,
|
||||
node.DefaultMetricsProvider(tmCfg.Instrumentation),
|
||||
logger.With("module", val.Moniker),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tmNode.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val.tmNode = tmNode
|
||||
|
||||
if val.RPCAddress != "" {
|
||||
val.RPCClient = local.New(tmNode)
|
||||
}
|
||||
|
||||
if val.APIAddress != "" {
|
||||
val.ClientCtx = client.Context{}.
|
||||
WithHomeDir(tmCfg.RootDir).
|
||||
WithChainID(cfg.ChainID).
|
||||
WithJSONMarshaler(cdc).
|
||||
WithClient(val.RPCClient).
|
||||
WithTrustNode(true)
|
||||
|
||||
apiSrv := api.New(val.ClientCtx, logger.With("module", "api-server"))
|
||||
app.RegisterAPIRoutes(apiSrv)
|
||||
|
||||
errCh := make(chan error)
|
||||
|
||||
go func() {
|
||||
if err := apiSrv.Start(*val.AppConfig); err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-time.After(5 * time.Second): // assume server started successfully
|
||||
}
|
||||
|
||||
val.api = apiSrv
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error {
|
||||
genTime := tmtime.Now()
|
||||
|
||||
for i := 0; i < cfg.NumValidators; i++ {
|
||||
tmCfg := vals[i].Ctx.Config
|
||||
|
||||
nodeDir := filepath.Join(outputDir, vals[i].Moniker, "simd")
|
||||
gentxsDir := filepath.Join(outputDir, "gentxs")
|
||||
|
||||
tmCfg.Moniker = vals[i].Moniker
|
||||
tmCfg.SetRoot(nodeDir)
|
||||
|
||||
initCfg := genutiltypes.NewInitConfig(cfg.ChainID, gentxsDir, vals[i].Moniker, vals[i].NodeID, vals[i].PubKey)
|
||||
|
||||
genFile := tmCfg.GenesisFile()
|
||||
genDoc, err := types.GenesisDocFromFile(genFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
appState, err := genutil.GenAppStateFromConfig(cdc, tmCfg, initCfg, *genDoc, banktypes.GenesisBalancesIterator{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// overwrite each validator's genesis file to have a canonical genesis time
|
||||
if err := genutil.ExportGenesisFileWithTime(genFile, cfg.ChainID, nil, appState, genTime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initGenFiles(cfg Config, genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, genFiles []string) error {
|
||||
|
||||
// set the accounts in the genesis state
|
||||
var authGenState authtypes.GenesisState
|
||||
cdc.MustUnmarshalJSON(cfg.GenesisState[authtypes.ModuleName], &authGenState)
|
||||
|
||||
authGenState.Accounts = genAccounts
|
||||
cfg.GenesisState[authtypes.ModuleName] = cdc.MustMarshalJSON(authGenState)
|
||||
|
||||
// set the balances in the genesis state
|
||||
var bankGenState banktypes.GenesisState
|
||||
cdc.MustUnmarshalJSON(cfg.GenesisState[banktypes.ModuleName], &bankGenState)
|
||||
|
||||
bankGenState.Balances = genBalances
|
||||
cfg.GenesisState[banktypes.ModuleName] = cdc.MustMarshalJSON(bankGenState)
|
||||
|
||||
appGenStateJSON, err := codec.MarshalJSONIndent(cdc, cfg.GenesisState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
genDoc := types.GenesisDoc{
|
||||
ChainID: cfg.ChainID,
|
||||
AppState: appGenStateJSON,
|
||||
Validators: nil,
|
||||
}
|
||||
|
||||
// generate empty genesis files for each validator and save
|
||||
for i := 0; i < cfg.NumValidators; i++ {
|
||||
if err := genDoc.SaveAs(genFiles[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeFile(name string, dir string, contents []byte) error {
|
||||
writePath := filepath.Join(dir)
|
||||
file := filepath.Join(writePath, name)
|
||||
|
||||
err := tmos.EnsureDir(writePath, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tmos.WriteFile(file, contents, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -42,6 +42,17 @@ func NewResponseWithHeight(height int64, result json.RawMessage) ResponseWithHei
|
||||
}
|
||||
}
|
||||
|
||||
// ParseResponseWithHeight returns the raw result from a JSON-encoded
|
||||
// ResponseWithHeight object.
|
||||
func ParseResponseWithHeight(cdc codec.JSONMarshaler, bz []byte) ([]byte, error) {
|
||||
r := ResponseWithHeight{}
|
||||
if err := cdc.UnmarshalJSON(bz, &r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.Result, nil
|
||||
}
|
||||
|
||||
// GasEstimateResponse defines a response definition for tx gas estimation.
|
||||
type GasEstimateResponse struct {
|
||||
GasEstimate uint64 `json:"gas_estimate"`
|
||||
@ -425,3 +436,23 @@ func ParseQueryParamBool(r *http.Request, param string) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// GetRequest defines a wrapper around an HTTP GET request with a provided URL.
|
||||
// An error is returned if the request or reading the body fails.
|
||||
func GetRequest(url string) ([]byte, error) {
|
||||
res, err := http.Get(url) // nolint:gosec
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = res.Body.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
134
x/bank/client/rest/query_test.go
Normal file
134
x/bank/client/rest/query_test.go
Normal file
@ -0,0 +1,134 @@
|
||||
package rest_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
)
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
cfg testutil.Config
|
||||
network *testutil.Network
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) SetupSuite() {
|
||||
s.T().Log("setting up integration test suite")
|
||||
|
||||
cfg := testutil.DefaultConfig()
|
||||
cfg.NumValidators = 1
|
||||
|
||||
s.cfg = cfg
|
||||
s.network = testutil.NewTestNetwork(s.T(), cfg)
|
||||
|
||||
_, err := s.network.WaitForHeight(1)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TearDownSuite() {
|
||||
s.T().Log("tearing down integration test suite")
|
||||
s.network.Cleanup()
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestQueryBalancesRequestHandlerFn() {
|
||||
val := s.network.Validators[0]
|
||||
baseURL := val.APIAddress
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
respType fmt.Stringer
|
||||
expected fmt.Stringer
|
||||
}{
|
||||
{
|
||||
"total account balance",
|
||||
fmt.Sprintf("%s/bank/balances/%s?height=1", baseURL, val.Address),
|
||||
&sdk.Coins{},
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens),
|
||||
sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)),
|
||||
),
|
||||
},
|
||||
{
|
||||
"total account balance of a specific denom",
|
||||
fmt.Sprintf("%s/bank/balances/%s?height=1&denom=%s", baseURL, val.Address, s.cfg.BondDenom),
|
||||
&sdk.Coin{},
|
||||
sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)),
|
||||
},
|
||||
{
|
||||
"total account balance of a bogus denom",
|
||||
fmt.Sprintf("%s/bank/balances/%s?height=1&denom=foobar", baseURL, val.Address),
|
||||
&sdk.Coin{},
|
||||
sdk.NewCoin("foobar", sdk.ZeroInt()),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
s.Run(tc.name, func() {
|
||||
resp, err := rest.GetRequest(tc.url)
|
||||
s.Require().NoError(err)
|
||||
|
||||
bz, err := rest.ParseResponseWithHeight(val.ClientCtx.JSONMarshaler, resp)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(bz, tc.respType))
|
||||
s.Require().Equal(tc.expected.String(), tc.respType.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestTotalSupplyHandlerFn() {
|
||||
val := s.network.Validators[0]
|
||||
baseURL := val.APIAddress
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
respType fmt.Stringer
|
||||
expected fmt.Stringer
|
||||
}{
|
||||
{
|
||||
"total supply",
|
||||
fmt.Sprintf("%s/bank/total?height=1", baseURL),
|
||||
&sdk.Coins{}, sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens),
|
||||
sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(10))),
|
||||
),
|
||||
},
|
||||
{
|
||||
"total supply of a specific denom",
|
||||
fmt.Sprintf("%s/bank/total/%s?height=1", baseURL, s.cfg.BondDenom),
|
||||
&sdk.Coin{},
|
||||
sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(10))),
|
||||
},
|
||||
{
|
||||
"total supply of a bogus denom",
|
||||
fmt.Sprintf("%s/bank/total/foobar?height=1", baseURL),
|
||||
&sdk.Coin{},
|
||||
sdk.NewCoin("foobar", sdk.ZeroInt()),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
s.Run(tc.name, func() {
|
||||
resp, err := rest.GetRequest(tc.url)
|
||||
s.Require().NoError(err)
|
||||
|
||||
bz, err := rest.ParseResponseWithHeight(val.ClientCtx.JSONMarshaler, resp)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(bz, tc.respType))
|
||||
s.Require().Equal(tc.expected.String(), tc.respType.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
}
|
||||
@ -99,12 +99,13 @@ func querySupplyOf(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, er
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
||||
}
|
||||
|
||||
supply := k.GetSupply(ctx).GetTotal().AmountOf(params.Denom)
|
||||
amount := k.GetSupply(ctx).GetTotal().AmountOf(params.Denom)
|
||||
supply := sdk.NewCoin(params.Denom, amount)
|
||||
|
||||
res, err := supply.MarshalJSON()
|
||||
bz, err := codec.MarshalJSONIndent(types.ModuleCdc, supply)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
@ -133,9 +133,9 @@ func (suite *IntegrationTestSuite) TestQuerier_QueryTotalSupplyOf() {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res)
|
||||
|
||||
var resp sdk.Int
|
||||
var resp sdk.Coin
|
||||
suite.Require().NoError(app.Codec().UnmarshalJSON(res, &resp))
|
||||
suite.Require().Equal(test1Supply.Amount, resp)
|
||||
suite.Require().Equal(test1Supply, resp)
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestQuerierRouteNotFound() {
|
||||
|
||||
@ -24,7 +24,6 @@ const (
|
||||
var (
|
||||
// functions aliases
|
||||
RegisterCodec = types.RegisterCodec
|
||||
SetSubModuleCodec = types.SetSubModuleCodec
|
||||
NewClientConsensusStates = types.NewClientConsensusStates
|
||||
NewGenesisState = types.NewGenesisState
|
||||
DefaultGenesisState = types.DefaultGenesisState
|
||||
|
||||
@ -2,12 +2,19 @@ package types
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
|
||||
)
|
||||
|
||||
// SubModuleCdc defines the IBC client codec.
|
||||
var SubModuleCdc *codec.Codec
|
||||
|
||||
func init() {
|
||||
SubModuleCdc = codec.New()
|
||||
cryptocodec.RegisterCrypto(SubModuleCdc)
|
||||
RegisterCodec(SubModuleCdc)
|
||||
}
|
||||
|
||||
// RegisterCodec registers the IBC client interfaces and types
|
||||
func RegisterCodec(cdc *codec.Codec) {
|
||||
cdc.RegisterInterface((*exported.ClientState)(nil), nil)
|
||||
@ -16,10 +23,4 @@ func RegisterCodec(cdc *codec.Codec) {
|
||||
cdc.RegisterInterface((*exported.ConsensusState)(nil), nil)
|
||||
cdc.RegisterInterface((*exported.Header)(nil), nil)
|
||||
cdc.RegisterInterface((*exported.Misbehaviour)(nil), nil)
|
||||
|
||||
SetSubModuleCodec(cdc)
|
||||
}
|
||||
|
||||
func SetSubModuleCodec(cdc *codec.Codec) {
|
||||
SubModuleCdc = cdc
|
||||
}
|
||||
|
||||
@ -2,11 +2,18 @@ package types
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
)
|
||||
|
||||
// SubModuleCdc defines the IBC tendermint client codec.
|
||||
var SubModuleCdc *codec.Codec
|
||||
|
||||
func init() {
|
||||
SubModuleCdc = codec.New()
|
||||
cryptocodec.RegisterCrypto(SubModuleCdc)
|
||||
RegisterCodec(SubModuleCdc)
|
||||
}
|
||||
|
||||
// RegisterCodec registers the Tendermint types
|
||||
func RegisterCodec(cdc *codec.Codec) {
|
||||
cdc.RegisterConcrete(ClientState{}, "ibc/client/tendermint/ClientState", nil)
|
||||
@ -16,11 +23,4 @@ func RegisterCodec(cdc *codec.Codec) {
|
||||
cdc.RegisterConcrete(&MsgCreateClient{}, "ibc/client/tendermint/MsgCreateClient", nil)
|
||||
cdc.RegisterConcrete(&MsgUpdateClient{}, "ibc/client/tendermint/MsgUpdateClient", nil)
|
||||
cdc.RegisterConcrete(&MsgSubmitClientMisbehaviour{}, "ibc/client/tendermint/MsgSubmitClientMisbehaviour", nil)
|
||||
|
||||
SetSubModuleCodec(cdc)
|
||||
}
|
||||
|
||||
// SetSubModuleCodec sets the ibc tendermint client codec
|
||||
func SetSubModuleCodec(cdc *codec.Codec) {
|
||||
SubModuleCdc = cdc
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package types
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -12,14 +13,14 @@ const (
|
||||
// SubModuleCdc defines the IBC localhost client codec.
|
||||
var SubModuleCdc *codec.Codec
|
||||
|
||||
func init() {
|
||||
SubModuleCdc = codec.New()
|
||||
cryptocodec.RegisterCrypto(SubModuleCdc)
|
||||
RegisterCodec(SubModuleCdc)
|
||||
}
|
||||
|
||||
// RegisterCodec registers the localhost types
|
||||
func RegisterCodec(cdc *codec.Codec) {
|
||||
cdc.RegisterConcrete(ClientState{}, "ibc/client/localhost/ClientState", nil)
|
||||
cdc.RegisterConcrete(&MsgCreateClient{}, "ibc/client/localhost/MsgCreateClient", nil)
|
||||
SetSubModuleCodec(cdc)
|
||||
}
|
||||
|
||||
// SetSubModuleCodec sets the ibc localhost client codec
|
||||
func SetSubModuleCodec(cdc *codec.Codec) {
|
||||
SubModuleCdc = cdc
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user