Unit Tests
Unit tests are the lowest test category of the test pyramid. All packages and modules should have unit test coverage. Modules should have their dependencies mocked: this means mocking keepers. The SDK usesmockgen
to generate mocks for keepers:
Copy
Ask AI
#!/usr/bin/env bash
mockgen_cmd="mockgen"
$mockgen_cmd -source=client/account_retriever.go -package mock -destination testutil/mock/account_retriever.go
$mockgen_cmd -package mock -destination testutil/mock/tendermint_tm_db_DB.go github.com/tendermint/tm-db DB
$mockgen_cmd -source=types/module/module.go -package mock -destination testutil/mock/types_module_module.go
$mockgen_cmd -source=types/module/mock_appmodule_test.go -package mock -destination testutil/mock/types_mock_appmodule.go
$mockgen_cmd -source=types/invariant.go -package mock -destination testutil/mock/types_invariant.go
$mockgen_cmd -package mock -destination testutil/mock/grpc_server.go github.com/cosmos/gogoproto/grpc Server
$mockgen_cmd -package mock -destination testutil/mock/tendermint_tendermint_libs_log_DB.go github.com/tendermint/tendermint/libs/log Logger
$mockgen_cmd -source=orm/model/ormtable/hooks.go -package ormmocks -destination orm/testing/ormmocks/hooks.go
$mockgen_cmd -source=x/nft/expected_keepers.go -package testutil -destination x/nft/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/feegrant/expected_keepers.go -package testutil -destination x/feegrant/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/mint/types/expected_keepers.go -package testutil -destination x/mint/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/params/proposal_handler_test.go -package testutil -destination x/params/testutil/staking_keeper_mock.go
$mockgen_cmd -source=x/crisis/types/expected_keepers.go -package testutil -destination x/crisis/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/auth/types/expected_keepers.go -package testutil -destination x/auth/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/auth/ante/expected_keepers.go -package testutil -destination x/auth/ante/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/authz/expected_keepers.go -package testutil -destination x/authz/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/bank/types/expected_keepers.go -package testutil -destination x/bank/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/group/testutil/expected_keepers.go -package testutil -destination x/group/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/evidence/types/expected_keepers.go -package testutil -destination x/evidence/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/distribution/types/expected_keepers.go -package testutil -destination x/distribution/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/slashing/types/expected_keepers.go -package testutil -destination x/slashing/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/genutil/types/expected_keepers.go -package testutil -destination x/genutil/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/gov/testutil/expected_keepers.go -package testutil -destination x/gov/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/staking/types/expected_keepers.go -package testutil -destination x/staking/testutil/expected_keepers_mocks.go
$mockgen_cmd -source=x/auth/vesting/types/expected_keepers.go -package testutil -destination x/auth/vesting/testutil/expected_keepers_mocks.go
Example
As an example, we will walkthrough the keeper tests of thex/gov
module.
The x/gov
module has a Keeper
type requires a few external dependencies (ie. imports outside x/gov
to work properly).
Copy
Ask AI
package keeper
import (
"fmt"
"time"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
)
/ Keeper defines the governance module Keeper
type Keeper struct {
authKeeper types.AccountKeeper
bankKeeper types.BankKeeper
/ The reference to the DelegationSet and ValidatorSet to get information about validators and delegators
sk types.StakingKeeper
/ GovHooks
hooks types.GovHooks
/ The (unexposed)
keys used to access the stores from the Context.
storeKey storetypes.StoreKey
/ The codec for binary encoding/decoding.
cdc codec.BinaryCodec
/ Legacy Proposal router
legacyRouter v1beta1.Router
/ Msg server router
router *baseapp.MsgServiceRouter
config types.Config
/ the address capable of executing a MsgUpdateParams message. Typically, this
/ should be the x/gov module account.
authority string
}
/ GetAuthority returns the x/gov module's authority.
func (k Keeper)
GetAuthority()
string {
return k.authority
}
/ NewKeeper returns a governance keeper. It handles:
/ - submitting governance proposals
/ - depositing funds into proposals, and activating upon sufficient funds being deposited
/ - users voting on proposals, with weight proportional to stake in the system
/ - and tallying the result of the vote.
/
/ CONTRACT: the parameter Subspace must have the param key table already initialized
func NewKeeper(
cdc codec.BinaryCodec, key storetypes.StoreKey, authKeeper types.AccountKeeper,
bankKeeper types.BankKeeper, sk types.StakingKeeper,
router *baseapp.MsgServiceRouter, config types.Config, authority string,
) *Keeper {
/ ensure governance module account is set
if addr := authKeeper.GetModuleAddress(types.ModuleName); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
}
if _, err := sdk.AccAddressFromBech32(authority); err != nil {
panic(fmt.Sprintf("invalid authority address: %s", authority))
}
/ If MaxMetadataLen not set by app developer, set to default value.
if config.MaxMetadataLen == 0 {
config.MaxMetadataLen = types.DefaultConfig().MaxMetadataLen
}
return &Keeper{
storeKey: key,
authKeeper: authKeeper,
bankKeeper: bankKeeper,
sk: sk,
cdc: cdc,
router: router,
config: config,
authority: authority,
}
}
/ Hooks gets the hooks for governance *Keeper {
func (keeper *Keeper)
Hooks()
types.GovHooks {
if keeper.hooks == nil {
/ return a no-op implementation if no hooks are set
return types.MultiGovHooks{
}
}
return keeper.hooks
}
/ SetHooks sets the hooks for governance
func (keeper *Keeper)
SetHooks(gh types.GovHooks) *Keeper {
if keeper.hooks != nil {
panic("cannot set governance hooks twice")
}
keeper.hooks = gh
return keeper
}
/ SetLegacyRouter sets the legacy router for governance
func (keeper *Keeper)
SetLegacyRouter(router v1beta1.Router) {
/ It is vital to seal the governance proposal router here as to not allow
/ further handlers to be registered after the keeper is created since this
/ could create invalid or non-deterministic behavior.
router.Seal()
keeper.legacyRouter = router
}
/ Logger returns a module-specific logger.
func (keeper Keeper)
Logger(ctx sdk.Context)
log.Logger {
return ctx.Logger().With("module", "x/"+types.ModuleName)
}
/ Router returns the gov keeper's router
func (keeper Keeper)
Router() *baseapp.MsgServiceRouter {
return keeper.router
}
/ LegacyRouter returns the gov keeper's legacy router
func (keeper Keeper)
LegacyRouter()
v1beta1.Router {
return keeper.legacyRouter
}
/ GetGovernanceAccount returns the governance ModuleAccount
func (keeper Keeper)
GetGovernanceAccount(ctx sdk.Context)
authtypes.ModuleAccountI {
return keeper.authKeeper.GetModuleAccount(ctx, types.ModuleName)
}
/ ProposalQueues
/ InsertActiveProposalQueue inserts a proposalID into the active proposal queue at endTime
func (keeper Keeper)
InsertActiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) {
store := ctx.KVStore(keeper.storeKey)
bz := types.GetProposalIDBytes(proposalID)
store.Set(types.ActiveProposalQueueKey(proposalID, endTime), bz)
}
/ RemoveFromActiveProposalQueue removes a proposalID from the Active Proposal Queue
func (keeper Keeper)
RemoveFromActiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) {
store := ctx.KVStore(keeper.storeKey)
store.Delete(types.ActiveProposalQueueKey(proposalID, endTime))
}
/ InsertInactiveProposalQueue inserts a proposalID into the inactive proposal queue at endTime
func (keeper Keeper)
InsertInactiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) {
store := ctx.KVStore(keeper.storeKey)
bz := types.GetProposalIDBytes(proposalID)
store.Set(types.InactiveProposalQueueKey(proposalID, endTime), bz)
}
/ RemoveFromInactiveProposalQueue removes a proposalID from the Inactive Proposal Queue
func (keeper Keeper)
RemoveFromInactiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) {
store := ctx.KVStore(keeper.storeKey)
store.Delete(types.InactiveProposalQueueKey(proposalID, endTime))
}
/ Iterators
/ IterateActiveProposalsQueue iterates over the proposals in the active proposal queue
/ and performs a callback function
func (keeper Keeper)
IterateActiveProposalsQueue(ctx sdk.Context, endTime time.Time, cb func(proposal v1.Proposal) (stop bool)) {
iterator := keeper.ActiveProposalQueueIterator(ctx, endTime)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
proposalID, _ := types.SplitActiveProposalQueueKey(iterator.Key())
proposal, found := keeper.GetProposal(ctx, proposalID)
if !found {
panic(fmt.Sprintf("proposal %d does not exist", proposalID))
}
if cb(proposal) {
break
}
}
}
/ IterateInactiveProposalsQueue iterates over the proposals in the inactive proposal queue
/ and performs a callback function
func (keeper Keeper)
IterateInactiveProposalsQueue(ctx sdk.Context, endTime time.Time, cb func(proposal v1.Proposal) (stop bool)) {
iterator := keeper.InactiveProposalQueueIterator(ctx, endTime)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
proposalID, _ := types.SplitInactiveProposalQueueKey(iterator.Key())
proposal, found := keeper.GetProposal(ctx, proposalID)
if !found {
panic(fmt.Sprintf("proposal %d does not exist", proposalID))
}
if cb(proposal) {
break
}
}
}
/ ActiveProposalQueueIterator returns an sdk.Iterator for all the proposals in the Active Queue that expire by endTime
func (keeper Keeper)
ActiveProposalQueueIterator(ctx sdk.Context, endTime time.Time)
sdk.Iterator {
store := ctx.KVStore(keeper.storeKey)
return store.Iterator(types.ActiveProposalQueuePrefix, sdk.PrefixEndBytes(types.ActiveProposalByTimeKey(endTime)))
}
/ InactiveProposalQueueIterator returns an sdk.Iterator for all the proposals in the Inactive Queue that expire by endTime
func (keeper Keeper)
InactiveProposalQueueIterator(ctx sdk.Context, endTime time.Time)
sdk.Iterator {
store := ctx.KVStore(keeper.storeKey)
return store.Iterator(types.InactiveProposalQueuePrefix, sdk.PrefixEndBytes(types.InactiveProposalByTimeKey(endTime)))
}
/ assertMetadataLength returns an error if given metadata length
/ is greater than a pre-defined MaxMetadataLen.
func (keeper Keeper)
assertMetadataLength(metadata string)
error {
if metadata != "" && uint64(len(metadata)) > keeper.config.MaxMetadataLen {
return types.ErrMetadataTooLong.Wrapf("got metadata with length %d", len(metadata))
}
return nil
}
x/gov
, we mock the expected keepers and instantiate the Keeper
with the mocked dependencies. Note that we may need to configure the mocked dependencies to return the expected values:
Copy
Ask AI
package keeper_test
import (
"fmt"
"testing"
"cosmossdk.io/math"
"github.com/golang/mock/gomock"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
govtestutil "github.com/cosmos/cosmos-sdk/x/gov/testutil"
"github.com/cosmos/cosmos-sdk/x/gov/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
)
var (
_, _, addr = testdata.KeyTestPubAddr()
govAcct = authtypes.NewModuleAddress(types.ModuleName)
TestProposal = getTestProposal()
)
/ getTestProposal creates and returns a test proposal message.
func getTestProposal() []sdk.Msg {
legacyProposalMsg, err := v1.NewLegacyContent(v1beta1.NewTextProposal("Title", "description"), authtypes.NewModuleAddress(types.ModuleName).String())
if err != nil {
panic(err)
}
return []sdk.Msg{
banktypes.NewMsgSend(govAcct, addr, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000)))),
legacyProposalMsg,
}
}
/ setupGovKeeper creates a govKeeper as well as all its dependencies.
func setupGovKeeper(t *testing.T) (
*keeper.Keeper,
*govtestutil.MockAccountKeeper,
*govtestutil.MockBankKeeper,
*govtestutil.MockStakingKeeper,
moduletestutil.TestEncodingConfig,
sdk.Context,
) {
key := sdk.NewKVStoreKey(types.StoreKey)
testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test"))
ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{
Time: tmtime.Now()
})
encCfg := moduletestutil.MakeTestEncodingConfig()
v1.RegisterInterfaces(encCfg.InterfaceRegistry)
v1beta1.RegisterInterfaces(encCfg.InterfaceRegistry)
banktypes.RegisterInterfaces(encCfg.InterfaceRegistry)
/ Create MsgServiceRouter, but don't populate it before creating the gov
/ keeper.
msr := baseapp.NewMsgServiceRouter()
/ gomock initializations
ctrl := gomock.NewController(t)
acctKeeper := govtestutil.NewMockAccountKeeper(ctrl)
bankKeeper := govtestutil.NewMockBankKeeper(ctrl)
stakingKeeper := govtestutil.NewMockStakingKeeper(ctrl)
acctKeeper.EXPECT().GetModuleAddress(types.ModuleName).Return(govAcct).AnyTimes()
acctKeeper.EXPECT().GetModuleAccount(gomock.Any(), types.ModuleName).Return(authtypes.NewEmptyModuleAccount(types.ModuleName)).AnyTimes()
trackMockBalances(bankKeeper)
stakingKeeper.EXPECT().TokensFromConsensusPower(ctx, gomock.Any()).DoAndReturn(func(ctx sdk.Context, power int64)
math.Int {
return sdk.TokensFromConsensusPower(power, math.NewIntFromUint64(1000000))
}).AnyTimes()
stakingKeeper.EXPECT().BondDenom(ctx).Return("stake").AnyTimes()
stakingKeeper.EXPECT().IterateBondedValidatorsByPower(gomock.Any(), gomock.Any()).AnyTimes()
stakingKeeper.EXPECT().IterateDelegations(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
stakingKeeper.EXPECT().TotalBondedTokens(gomock.Any()).Return(math.NewInt(10000000)).AnyTimes()
/ Gov keeper initializations
govKeeper := keeper.NewKeeper(encCfg.Codec, key, acctKeeper, bankKeeper, stakingKeeper, msr, types.DefaultConfig(), govAcct.String())
govKeeper.SetProposalID(ctx, 1)
govRouter := v1beta1.NewRouter() / Also register legacy gov handlers to test them too.
govRouter.AddRoute(types.RouterKey, v1beta1.ProposalHandler)
govKeeper.SetLegacyRouter(govRouter)
govKeeper.SetParams(ctx, v1.DefaultParams())
/ Register all handlers for the MegServiceRouter.
msr.SetInterfaceRegistry(encCfg.InterfaceRegistry)
v1.RegisterMsgServer(msr, keeper.NewMsgServerImpl(govKeeper))
banktypes.RegisterMsgServer(msr, nil) / Nil is fine here as long as we never execute the proposal's Msgs.
return govKeeper, acctKeeper, bankKeeper, stakingKeeper, encCfg, ctx
}
/ trackMockBalances sets up expected calls on the Mock BankKeeper, and also
/ locally tracks accounts balances (not modules balances).
func trackMockBalances(bankKeeper *govtestutil.MockBankKeeper) {
balances := make(map[string]sdk.Coins)
/ We don't track module account balances.
bankKeeper.EXPECT().MintCoins(gomock.Any(), minttypes.ModuleName, gomock.Any()).AnyTimes()
bankKeeper.EXPECT().BurnCoins(gomock.Any(), types.ModuleName, gomock.Any()).AnyTimes()
bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), minttypes.ModuleName, types.ModuleName, gomock.Any()).AnyTimes()
/ But we do track normal account balances.
bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), gomock.Any(), types.ModuleName, gomock.Any()).DoAndReturn(func(_ sdk.Context, sender sdk.AccAddress, _ string, coins sdk.Coins)
error {
newBalance, negative := balances[sender.String()].SafeSub(coins...)
if negative {
return fmt.Errorf("not enough balance")
}
balances[sender.String()] = newBalance
return nil
}).AnyTimes()
bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ sdk.Context, module string, rcpt sdk.AccAddress, coins sdk.Coins)
error {
balances[rcpt.String()] = balances[rcpt.String()].Add(coins...)
return nil
}).AnyTimes()
bankKeeper.EXPECT().GetAllBalances(gomock.Any(), gomock.Any()).DoAndReturn(func(_ sdk.Context, addr sdk.AccAddress)
sdk.Coins {
return balances[addr.String()]
}).AnyTimes()
}
x/gov
module without having to import other modules.
Copy
Ask AI
package keeper_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
govtestutil "github.com/cosmos/cosmos-sdk/x/gov/testutil"
"github.com/cosmos/cosmos-sdk/x/gov/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
)
type KeeperTestSuite struct {
suite.Suite
cdc codec.Codec
ctx sdk.Context
govKeeper *keeper.Keeper
acctKeeper *govtestutil.MockAccountKeeper
bankKeeper *govtestutil.MockBankKeeper
stakingKeeper *govtestutil.MockStakingKeeper
queryClient v1.QueryClient
legacyQueryClient v1beta1.QueryClient
addrs []sdk.AccAddress
msgSrvr v1.MsgServer
legacyMsgSrvr v1beta1.MsgServer
}
func (suite *KeeperTestSuite)
SetupSuite() {
suite.reset()
}
func (suite *KeeperTestSuite)
reset() {
govKeeper, acctKeeper, bankKeeper, stakingKeeper, encCfg, ctx := setupGovKeeper(suite.T())
/ Populate the gov account with some coins, as the TestProposal we have
/ is a MsgSend from the gov account.
coins := sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100000)))
err := bankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, coins)
suite.NoError(err)
err = bankKeeper.SendCoinsFromModuleToModule(ctx, minttypes.ModuleName, types.ModuleName, coins)
suite.NoError(err)
queryHelper := baseapp.NewQueryServerTestHelper(ctx, encCfg.InterfaceRegistry)
v1.RegisterQueryServer(queryHelper, govKeeper)
legacyQueryHelper := baseapp.NewQueryServerTestHelper(ctx, encCfg.InterfaceRegistry)
v1beta1.RegisterQueryServer(legacyQueryHelper, keeper.NewLegacyQueryServer(govKeeper))
queryClient := v1.NewQueryClient(queryHelper)
legacyQueryClient := v1beta1.NewQueryClient(legacyQueryHelper)
suite.ctx = ctx
suite.govKeeper = govKeeper
suite.acctKeeper = acctKeeper
suite.bankKeeper = bankKeeper
suite.stakingKeeper = stakingKeeper
suite.cdc = encCfg.Codec
suite.queryClient = queryClient
suite.legacyQueryClient = legacyQueryClient
suite.msgSrvr = keeper.NewMsgServerImpl(suite.govKeeper)
suite.legacyMsgSrvr = keeper.NewLegacyMsgServerImpl(govAcct.String(), suite.msgSrvr)
suite.addrs = simtestutil.AddTestAddrsIncremental(bankKeeper, stakingKeeper, ctx, 3, sdk.NewInt(30000000))
}
func TestIncrementProposalNumber(t *testing.T) {
govKeeper, _, _, _, _, ctx := setupGovKeeper(t)
tp := TestProposal
_, err := govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
_, err = govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
_, err = govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
_, err = govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
_, err = govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
proposal6, err := govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
require.Equal(t, uint64(6), proposal6.Id)
}
func TestProposalQueues(t *testing.T) {
govKeeper, _, _, _, _, ctx := setupGovKeeper(t)
/ create test proposals
tp := TestProposal
proposal, err := govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
inactiveIterator := govKeeper.InactiveProposalQueueIterator(ctx, *proposal.DepositEndTime)
require.True(t, inactiveIterator.Valid())
proposalID := types.GetProposalIDFromBytes(inactiveIterator.Value())
require.Equal(t, proposalID, proposal.Id)
inactiveIterator.Close()
govKeeper.ActivateVotingPeriod(ctx, proposal)
proposal, ok := govKeeper.GetProposal(ctx, proposal.Id)
require.True(t, ok)
activeIterator := govKeeper.ActiveProposalQueueIterator(ctx, *proposal.VotingEndTime)
require.True(t, activeIterator.Valid())
proposalID, _ = types.SplitActiveProposalQueueKey(activeIterator.Key())
require.Equal(t, proposalID, proposal.Id)
activeIterator.Close()
}
func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}
Keeper
instance.
Copy
Ask AI
package keeper_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
govtestutil "github.com/cosmos/cosmos-sdk/x/gov/testutil"
"github.com/cosmos/cosmos-sdk/x/gov/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
)
type KeeperTestSuite struct {
suite.Suite
cdc codec.Codec
ctx sdk.Context
govKeeper *keeper.Keeper
acctKeeper *govtestutil.MockAccountKeeper
bankKeeper *govtestutil.MockBankKeeper
stakingKeeper *govtestutil.MockStakingKeeper
queryClient v1.QueryClient
legacyQueryClient v1beta1.QueryClient
addrs []sdk.AccAddress
msgSrvr v1.MsgServer
legacyMsgSrvr v1beta1.MsgServer
}
func (suite *KeeperTestSuite)
SetupSuite() {
suite.reset()
}
func (suite *KeeperTestSuite)
reset() {
govKeeper, acctKeeper, bankKeeper, stakingKeeper, encCfg, ctx := setupGovKeeper(suite.T())
/ Populate the gov account with some coins, as the TestProposal we have
/ is a MsgSend from the gov account.
coins := sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100000)))
err := bankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, coins)
suite.NoError(err)
err = bankKeeper.SendCoinsFromModuleToModule(ctx, minttypes.ModuleName, types.ModuleName, coins)
suite.NoError(err)
queryHelper := baseapp.NewQueryServerTestHelper(ctx, encCfg.InterfaceRegistry)
v1.RegisterQueryServer(queryHelper, govKeeper)
legacyQueryHelper := baseapp.NewQueryServerTestHelper(ctx, encCfg.InterfaceRegistry)
v1beta1.RegisterQueryServer(legacyQueryHelper, keeper.NewLegacyQueryServer(govKeeper))
queryClient := v1.NewQueryClient(queryHelper)
legacyQueryClient := v1beta1.NewQueryClient(legacyQueryHelper)
suite.ctx = ctx
suite.govKeeper = govKeeper
suite.acctKeeper = acctKeeper
suite.bankKeeper = bankKeeper
suite.stakingKeeper = stakingKeeper
suite.cdc = encCfg.Codec
suite.queryClient = queryClient
suite.legacyQueryClient = legacyQueryClient
suite.msgSrvr = keeper.NewMsgServerImpl(suite.govKeeper)
suite.legacyMsgSrvr = keeper.NewLegacyMsgServerImpl(govAcct.String(), suite.msgSrvr)
suite.addrs = simtestutil.AddTestAddrsIncremental(bankKeeper, stakingKeeper, ctx, 3, sdk.NewInt(30000000))
}
func TestIncrementProposalNumber(t *testing.T) {
govKeeper, _, _, _, _, ctx := setupGovKeeper(t)
tp := TestProposal
_, err := govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
_, err = govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
_, err = govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
_, err = govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
_, err = govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
proposal6, err := govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
require.Equal(t, uint64(6), proposal6.Id)
}
func TestProposalQueues(t *testing.T) {
govKeeper, _, _, _, _, ctx := setupGovKeeper(t)
/ create test proposals
tp := TestProposal
proposal, err := govKeeper.SubmitProposal(ctx, tp, "", "test", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
inactiveIterator := govKeeper.InactiveProposalQueueIterator(ctx, *proposal.DepositEndTime)
require.True(t, inactiveIterator.Valid())
proposalID := types.GetProposalIDFromBytes(inactiveIterator.Value())
require.Equal(t, proposalID, proposal.Id)
inactiveIterator.Close()
govKeeper.ActivateVotingPeriod(ctx, proposal)
proposal, ok := govKeeper.GetProposal(ctx, proposal.Id)
require.True(t, ok)
activeIterator := govKeeper.ActiveProposalQueueIterator(ctx, *proposal.VotingEndTime)
require.True(t, activeIterator.Valid())
proposalID, _ = types.SplitActiveProposalQueueKey(activeIterator.Key())
require.Equal(t, proposalID, proposal.Id)
activeIterator.Close()
}
func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}
Integration Tests
Integration tests are at the second level of the test pyramid. In the SDK, we locate our integration tests under/tests/integrations
.
The goal of these integration tests is to test how a component interacts with other dependencies. Compared to unit tests, integration tests do not mock dependencies. Instead, they use the direct dependencies of the component. This differs as well from end-to-end tests, which test the component with a full application.
Integration tests interact with the tested module via the defined Msg
and Query
services. The result of the test can be verified by checking the state of the application, by checking the emitted events or the response. It is adviced to combine two of these methods to verify the result of the test.
The SDK provides small helpers for quickly setting up an integration tests. These helpers can be found at Link.
Example
Copy
Ask AI
package integration_test
import (
"fmt"
"io"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil/integration"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/x/auth"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/mint"
mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
"github.com/google/go-cmp/cmp"
)
/ Example shows how to use the integration test framework to test the integration of a modules.
/ Panics are used in this example, but in a real test case, you should use the testing.T object and assertions.
func Example() {
/ in this example we are testing the integration of the following modules:
/ - mint, which directly depends on auth, bank and staking
encodingCfg := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{
}, mint.AppModuleBasic{
})
keys := storetypes.NewKVStoreKeys(authtypes.StoreKey, minttypes.StoreKey)
authority := authtypes.NewModuleAddress("gov").String()
accountKeeper := authkeeper.NewAccountKeeper(
encodingCfg.Codec,
runtime.NewKVStoreService(keys[authtypes.StoreKey]),
authtypes.ProtoBaseAccount,
map[string][]string{
minttypes.ModuleName: {
authtypes.Minter
}},
"cosmos",
authority,
)
/ subspace is nil because we don't test params (which is legacy anyway)
authModule := auth.NewAppModule(encodingCfg.Codec, accountKeeper, authsims.RandomGenesisAccounts, nil)
/ here bankkeeper and staking keeper is nil because we are not testing them
/ subspace is nil because we don't test params (which is legacy anyway)
mintKeeper := mintkeeper.NewKeeper(encodingCfg.Codec, keys[minttypes.StoreKey], nil, accountKeeper, nil, authtypes.FeeCollectorName, authority)
mintModule := mint.NewAppModule(encodingCfg.Codec, mintKeeper, accountKeeper, nil, nil)
/ create the application and register all the modules from the previous step
/ replace the name and the logger by testing values in a real test case (e.g. t.Name()
and log.NewTestLogger(t))
integrationApp := integration.NewIntegrationApp("example", log.NewLogger(io.Discard), keys, authModule, mintModule)
/ register the message and query servers
authtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), authkeeper.NewMsgServerImpl(accountKeeper))
minttypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), mintkeeper.NewMsgServerImpl(mintKeeper))
minttypes.RegisterQueryServer(integrationApp.QueryHelper(), mintKeeper)
params := minttypes.DefaultParams()
params.BlocksPerYear = 10000
/ now we can use the application to test a mint message
result, err := integrationApp.RunMsg(&minttypes.MsgUpdateParams{
Authority: authority,
Params: params,
})
if err != nil {
panic(err)
}
/ in this example the result is an empty response, a nil check is enough
/ in other cases, it is recommended to check the result value.
if result == nil {
panic(fmt.Errorf("unexpected nil result"))
}
/ we now check the result
resp := minttypes.MsgUpdateParamsResponse{
}
err = encodingCfg.Codec.Unmarshal(result.Value, &resp)
if err != nil {
panic(err)
}
/ we should also check the state of the application
got := mintKeeper.GetParams(integrationApp.SDKContext())
if diff := cmp.Diff(got, params); diff != "" {
panic(diff)
}
fmt.Println(got.BlocksPerYear)
/ Output: 10000
}
/ ExampleOneModule shows how to use the integration test framework to test the integration of a single module.
/ That module has no dependency on other modules.
func Example_oneModule() {
/ in this example we are testing the integration of the auth module:
encodingCfg := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{
})
keys := storetypes.NewKVStoreKeys(authtypes.StoreKey)
authority := authtypes.NewModuleAddress("gov").String()
accountKeeper := authkeeper.NewAccountKeeper(
encodingCfg.Codec,
runtime.NewKVStoreService(keys[authtypes.StoreKey]),
authtypes.ProtoBaseAccount,
map[string][]string{
minttypes.ModuleName: {
authtypes.Minter
}},
"cosmos",
authority,
)
/ subspace is nil because we don't test params (which is legacy anyway)
authModule := auth.NewAppModule(encodingCfg.Codec, accountKeeper, authsims.RandomGenesisAccounts, nil)
/ create the application and register all the modules from the previous step
/ replace the name and the logger by testing values in a real test case (e.g. t.Name()
and log.NewTestLogger(t))
integrationApp := integration.NewIntegrationApp("example-one-module", log.NewLogger(io.Discard), keys, authModule)
/ register the message and query servers
authtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), authkeeper.NewMsgServerImpl(accountKeeper))
params := authtypes.DefaultParams()
params.MaxMemoCharacters = 1000
/ now we can use the application to test a mint message
result, err := integrationApp.RunMsg(&authtypes.MsgUpdateParams{
Authority: authority,
Params: params,
})
if err != nil {
panic(err)
}
/ in this example the result is an empty response, a nil check is enough
/ in other cases, it is recommended to check the result value.
if result == nil {
panic(fmt.Errorf("unexpected nil result"))
}
/ we now check the result
resp := authtypes.MsgUpdateParamsResponse{
}
err = encodingCfg.Codec.Unmarshal(result.Value, &resp)
if err != nil {
panic(err)
}
/ we should also check the state of the application
got := accountKeeper.GetParams(integrationApp.SDKContext())
if diff := cmp.Diff(got, params); diff != "" {
panic(diff)
}
fmt.Println(got.MaxMemoCharacters)
/ Output: 1000
}
Deterministic and Regression tests
Tests are written for queries in the Cosmos SDK which havemodule_query_safe
Protobuf annotation.
Each query is tested using 2 methods:
- Use property-based testing with the
rapid
library. The property that is tested is that the query response and gas consumption are the same upon 1000 query calls. - Regression tests are written with hardcoded responses and gas, and verify they don’t change upon 1000 calls and between SDK patch versions.
Copy
Ask AI
package keeper_test
import (
"testing"
"github.com/stretchr/testify/suite"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"pgregory.net/rapid"
"github.com/cosmos/cosmos-sdk/baseapp"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/configurator"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank/keeper"
banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
_ "github.com/cosmos/cosmos-sdk/x/auth"
_ "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
_ "github.com/cosmos/cosmos-sdk/x/bank"
_ "github.com/cosmos/cosmos-sdk/x/consensus"
_ "github.com/cosmos/cosmos-sdk/x/params"
_ "github.com/cosmos/cosmos-sdk/x/staking"
)
type DeterministicTestSuite struct {
suite.Suite
ctx sdk.Context
bankKeeper keeper.BaseKeeper
queryClient banktypes.QueryClient
}
var (
denomRegex = sdk.DefaultCoinDenomRegex()
addr1 = sdk.MustAccAddressFromBech32("cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5")
coin1 = sdk.NewCoin("denom", sdk.NewInt(10))
metadataAtom = banktypes.Metadata{
Description: "The native staking token of the Cosmos Hub.",
DenomUnits: []*banktypes.DenomUnit{
{
Denom: "uatom",
Exponent: 0,
Aliases: []string{"microatom"
},
},
{
Denom: "atom",
Exponent: 6,
Aliases: []string{"ATOM"
},
},
},
Base: "uatom",
Display: "atom",
}
)
func TestDeterministicTestSuite(t *testing.T) {
suite.Run(t, new(DeterministicTestSuite))
}
func (suite *DeterministicTestSuite)
SetupTest() {
var interfaceRegistry codectypes.InterfaceRegistry
app, err := simtestutil.Setup(
configurator.NewAppConfig(
configurator.AuthModule(),
configurator.TxModule(),
configurator.ParamsModule(),
configurator.ConsensusModule(),
configurator.BankModule(),
configurator.StakingModule(),
),
&suite.bankKeeper,
&interfaceRegistry,
)
suite.Require().NoError(err)
ctx := app.BaseApp.NewContext(false, tmproto.Header{
})
suite.ctx = ctx
queryHelper := baseapp.NewQueryServerTestHelper(ctx, interfaceRegistry)
banktypes.RegisterQueryServer(queryHelper, suite.bankKeeper)
suite.queryClient = banktypes.NewQueryClient(queryHelper)
}
func (suite *DeterministicTestSuite)
fundAccount(addr sdk.AccAddress, coin ...sdk.Coin) {
err := banktestutil.FundAccount(suite.bankKeeper, suite.ctx, addr, sdk.NewCoins(coin...))
suite.Require().NoError(err)
}
func (suite *DeterministicTestSuite)
getCoin(t *rapid.T)
sdk.Coin {
return sdk.NewCoin(
rapid.StringMatching(denomRegex).Draw(t, "denom"),
sdk.NewInt(rapid.Int64Min(1).Draw(t, "amount")),
)
}
func (suite *DeterministicTestSuite)
TestGRPCQueryBalance() {
rapid.Check(suite.T(), func(t *rapid.T) {
addr := testdata.AddressGenerator(t).Draw(t, "address")
coin := suite.getCoin(t)
suite.fundAccount(addr, coin)
req := banktypes.NewQueryBalanceRequest(addr, coin.GetDenom())
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.Balance, 0, true)
})
suite.fundAccount(addr1, coin1)
req := banktypes.NewQueryBalanceRequest(addr1, coin1.GetDenom())
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.Balance, 1087, false)
}
func (suite *DeterministicTestSuite)
TestGRPCQueryAllBalances() {
rapid.Check(suite.T(), func(t *rapid.T) {
addr := testdata.AddressGenerator(t).Draw(t, "address")
numCoins := rapid.IntRange(1, 10).Draw(t, "num-count")
coins := make(sdk.Coins, 0, numCoins)
for i := 0; i < numCoins; i++ {
coin := suite.getCoin(t)
/ NewCoins sorts the denoms
coins = sdk.NewCoins(append(coins, coin)...)
}
suite.fundAccount(addr, coins...)
req := banktypes.NewQueryAllBalancesRequest(addr, testdata.PaginationGenerator(t, uint64(numCoins)).Draw(t, "pagination"))
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.AllBalances, 0, true)
})
coins := sdk.NewCoins(
sdk.NewCoin("stake", sdk.NewInt(10)),
sdk.NewCoin("denom", sdk.NewInt(100)),
)
suite.fundAccount(addr1, coins...)
req := banktypes.NewQueryAllBalancesRequest(addr1, nil)
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.AllBalances, 357, false)
}
func (suite *DeterministicTestSuite)
TestGRPCQuerySpendableBalances() {
rapid.Check(suite.T(), func(t *rapid.T) {
addr := testdata.AddressGenerator(t).Draw(t, "address")
numCoins := rapid.IntRange(1, 10).Draw(t, "num-count")
coins := make(sdk.Coins, 0, numCoins)
for i := 0; i < numCoins; i++ {
coin := sdk.NewCoin(
rapid.StringMatching(denomRegex).Draw(t, "denom"),
sdk.NewInt(rapid.Int64Min(1).Draw(t, "amount")),
)
/ NewCoins sorts the denoms
coins = sdk.NewCoins(append(coins, coin)...)
}
err := banktestutil.FundAccount(suite.bankKeeper, suite.ctx, addr, coins)
suite.Require().NoError(err)
req := banktypes.NewQuerySpendableBalancesRequest(addr, testdata.PaginationGenerator(t, uint64(numCoins)).Draw(t, "pagination"))
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.SpendableBalances, 0, true)
})
coins := sdk.NewCoins(
sdk.NewCoin("stake", sdk.NewInt(10)),
sdk.NewCoin("denom", sdk.NewInt(100)),
)
err := banktestutil.FundAccount(suite.bankKeeper, suite.ctx, addr1, coins)
suite.Require().NoError(err)
req := banktypes.NewQuerySpendableBalancesRequest(addr1, nil)
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.SpendableBalances, 2032, false)
}
func (suite *DeterministicTestSuite)
TestGRPCQueryTotalSupply() {
res, err := suite.queryClient.TotalSupply(suite.ctx, &banktypes.QueryTotalSupplyRequest{
})
suite.Require().NoError(err)
initialSupply := res.GetSupply()
rapid.Check(suite.T(), func(t *rapid.T) {
numCoins := rapid.IntRange(1, 3).Draw(t, "num-count")
coins := make(sdk.Coins, 0, numCoins)
for i := 0; i < numCoins; i++ {
coin := sdk.NewCoin(
rapid.StringMatching(denomRegex).Draw(t, "denom"),
sdk.NewInt(rapid.Int64Min(1).Draw(t, "amount")),
)
coins = coins.Add(coin)
}
suite.Require().NoError(suite.bankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, coins))
initialSupply = initialSupply.Add(coins...)
req := &banktypes.QueryTotalSupplyRequest{
Pagination: testdata.PaginationGenerator(t, uint64(len(initialSupply))).Draw(t, "pagination"),
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.TotalSupply, 0, true)
})
suite.SetupTest() / reset
coins := sdk.NewCoins(
sdk.NewCoin("foo", sdk.NewInt(10)),
sdk.NewCoin("bar", sdk.NewInt(100)),
)
suite.Require().NoError(suite.bankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, coins))
req := &banktypes.QueryTotalSupplyRequest{
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.TotalSupply, 243, false)
}
func (suite *DeterministicTestSuite)
TestGRPCQueryTotalSupplyOf() {
rapid.Check(suite.T(), func(t *rapid.T) {
coin := sdk.NewCoin(
rapid.StringMatching(denomRegex).Draw(t, "denom"),
sdk.NewInt(rapid.Int64Min(1).Draw(t, "amount")),
)
suite.Require().NoError(suite.bankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.NewCoins(coin)))
req := &banktypes.QuerySupplyOfRequest{
Denom: coin.GetDenom()
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.SupplyOf, 0, true)
})
coin := sdk.NewCoin("bar", sdk.NewInt(100))
suite.Require().NoError(suite.bankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.NewCoins(coin)))
req := &banktypes.QuerySupplyOfRequest{
Denom: coin.GetDenom()
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.SupplyOf, 1021, false)
}
func (suite *DeterministicTestSuite)
TestGRPCQueryParams() {
rapid.Check(suite.T(), func(t *rapid.T) {
enabledStatus := banktypes.SendEnabled{
Denom: rapid.StringMatching(denomRegex).Draw(t, "denom"),
Enabled: rapid.Bool().Draw(t, "status"),
}
params := banktypes.Params{
SendEnabled: []*banktypes.SendEnabled{&enabledStatus
},
DefaultSendEnabled: rapid.Bool().Draw(t, "send"),
}
suite.bankKeeper.SetParams(suite.ctx, params)
req := &banktypes.QueryParamsRequest{
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.Params, 0, true)
})
enabledStatus := banktypes.SendEnabled{
Denom: "denom",
Enabled: true,
}
params := banktypes.Params{
SendEnabled: []*banktypes.SendEnabled{&enabledStatus
},
DefaultSendEnabled: false,
}
suite.bankKeeper.SetParams(suite.ctx, params)
req := &banktypes.QueryParamsRequest{
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.Params, 1003, false)
}
func (suite *DeterministicTestSuite)
createAndReturnMetadatas(t *rapid.T, count int) []banktypes.Metadata {
denomsMetadata := make([]banktypes.Metadata, 0, count)
for i := 0; i < count; i++ {
denom := rapid.StringMatching(denomRegex).Draw(t, "denom")
aliases := rapid.SliceOf(rapid.String()).Draw(t, "aliases")
/ In the GRPC server code, empty arrays are returned as nil
if len(aliases) == 0 {
aliases = nil
}
metadata := banktypes.Metadata{
Description: rapid.StringN(1, 100, 100).Draw(t, "desc"),
DenomUnits: []*banktypes.DenomUnit{
{
Denom: denom,
Exponent: rapid.Uint32().Draw(t, "exponent"),
Aliases: aliases,
},
},
Base: denom,
Display: denom,
Name: rapid.String().Draw(t, "name"),
Symbol: rapid.String().Draw(t, "symbol"),
URI: rapid.String().Draw(t, "uri"),
URIHash: rapid.String().Draw(t, "uri-hash"),
}
denomsMetadata = append(denomsMetadata, metadata)
}
return denomsMetadata
}
func (suite *DeterministicTestSuite)
TestGRPCDenomsMetadata() {
rapid.Check(suite.T(), func(t *rapid.T) {
count := rapid.IntRange(1, 3).Draw(t, "count")
denomsMetadata := suite.createAndReturnMetadatas(t, count)
suite.Require().Len(denomsMetadata, count)
for i := 0; i < count; i++ {
suite.bankKeeper.SetDenomMetaData(suite.ctx, denomsMetadata[i])
}
req := &banktypes.QueryDenomsMetadataRequest{
Pagination: testdata.PaginationGenerator(t, uint64(count)).Draw(t, "pagination"),
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.DenomsMetadata, 0, true)
})
suite.SetupTest() / reset
suite.bankKeeper.SetDenomMetaData(suite.ctx, metadataAtom)
req := &banktypes.QueryDenomsMetadataRequest{
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.DenomsMetadata, 660, false)
}
func (suite *DeterministicTestSuite)
TestGRPCDenomMetadata() {
rapid.Check(suite.T(), func(t *rapid.T) {
denomMetadata := suite.createAndReturnMetadatas(t, 1)
suite.Require().Len(denomMetadata, 1)
suite.bankKeeper.SetDenomMetaData(suite.ctx, denomMetadata[0])
req := &banktypes.QueryDenomMetadataRequest{
Denom: denomMetadata[0].Base,
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.DenomMetadata, 0, true)
})
suite.bankKeeper.SetDenomMetaData(suite.ctx, metadataAtom)
req := &banktypes.QueryDenomMetadataRequest{
Denom: metadataAtom.Base,
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.DenomMetadata, 1300, false)
}
func (suite *DeterministicTestSuite)
TestGRPCSendEnabled() {
allDenoms := []string{
}
rapid.Check(suite.T(), func(t *rapid.T) {
count := rapid.IntRange(0, 10).Draw(t, "count")
denoms := make([]string, 0, count)
for i := 0; i < count; i++ {
coin := banktypes.SendEnabled{
Denom: rapid.StringMatching(denomRegex).Draw(t, "denom"),
Enabled: rapid.Bool().Draw(t, "enabled-status"),
}
suite.bankKeeper.SetSendEnabled(suite.ctx, coin.Denom, coin.Enabled)
denoms = append(denoms, coin.Denom)
}
allDenoms = append(allDenoms, denoms...)
req := &banktypes.QuerySendEnabledRequest{
Denoms: denoms,
/ Pagination is only taken into account when `denoms` is an empty array
Pagination: testdata.PaginationGenerator(t, uint64(len(allDenoms))).Draw(t, "pagination"),
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.SendEnabled, 0, true)
})
coin1 := banktypes.SendEnabled{
Denom: "falsecoin",
Enabled: false,
}
coin2 := banktypes.SendEnabled{
Denom: "truecoin",
Enabled: true,
}
suite.bankKeeper.SetSendEnabled(suite.ctx, coin1.Denom, false)
suite.bankKeeper.SetSendEnabled(suite.ctx, coin2.Denom, true)
req := &banktypes.QuerySendEnabledRequest{
Denoms: []string{
coin1.GetDenom(), coin2.GetDenom()
},
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.SendEnabled, 4063, false)
}
func (suite *DeterministicTestSuite)
TestGRPCDenomOwners() {
rapid.Check(suite.T(), func(t *rapid.T) {
denom := rapid.StringMatching(denomRegex).Draw(t, "denom")
numAddr := rapid.IntRange(1, 10).Draw(t, "number-address")
for i := 0; i < numAddr; i++ {
addr := testdata.AddressGenerator(t).Draw(t, "address")
coin := sdk.NewCoin(
denom,
sdk.NewInt(rapid.Int64Min(1).Draw(t, "amount")),
)
err := banktestutil.FundAccount(suite.bankKeeper, suite.ctx, addr, sdk.NewCoins(coin))
suite.Require().NoError(err)
}
req := &banktypes.QueryDenomOwnersRequest{
Denom: denom,
Pagination: testdata.PaginationGenerator(t, uint64(numAddr)).Draw(t, "pagination"),
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.DenomOwners, 0, true)
})
denomOwners := []*banktypes.DenomOwner{
{
Address: "cosmos1qg65a9q6k2sqq7l3ycp428sqqpmqcucgzze299",
Balance: coin1,
},
{
Address: "cosmos1qglnsqgpq48l7qqzgs8qdshr6fh3gqq9ej3qut",
Balance: coin1,
},
}
for i := 0; i < len(denomOwners); i++ {
addr, err := sdk.AccAddressFromBech32(denomOwners[i].Address)
suite.Require().NoError(err)
err = banktestutil.FundAccount(suite.bankKeeper, suite.ctx, addr, sdk.NewCoins(coin1))
suite.Require().NoError(err)
}
req := &banktypes.QueryDenomOwnersRequest{
Denom: coin1.GetDenom(),
}
testdata.DeterministicIterations(suite.ctx, suite.Require(), req, suite.queryClient.DenomOwners, 2525, false)
}
Simulations
Simulations uses as well a minimal application, built withdepinject
:
You can as well use the
AppConfig
configurator
for creating an AppConfig
inline. There is no difference between those two ways, use whichever you prefer.x/gov/
simulations:
Copy
Ask AI
package simulation_test
import (
"fmt"
"math/rand"
"testing"
"time"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil/configurator"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
_ "github.com/cosmos/cosmos-sdk/x/auth"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
_ "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
_ "github.com/cosmos/cosmos-sdk/x/bank"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
_ "github.com/cosmos/cosmos-sdk/x/consensus"
govcodec "github.com/cosmos/cosmos-sdk/x/gov/codec"
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
"github.com/cosmos/cosmos-sdk/x/gov/simulation"
"github.com/cosmos/cosmos-sdk/x/gov/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
_ "github.com/cosmos/cosmos-sdk/x/params"
_ "github.com/cosmos/cosmos-sdk/x/staking"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
)
type MockWeightedProposalContent struct {
n int
}
func (m MockWeightedProposalContent)
AppParamsKey()
string {
return fmt.Sprintf("AppParamsKey-%d", m.n)
}
func (m MockWeightedProposalContent)
DefaultWeight()
int {
return m.n
}
func (m MockWeightedProposalContent)
ContentSimulatorFn()
simtypes.ContentSimulatorFn {
return func(r *rand.Rand, _ sdk.Context, _ []simtypes.Account)
simtypes.Content {
return v1beta1.NewTextProposal(
fmt.Sprintf("title-%d: %s", m.n, simtypes.RandStringOfLength(r, 100)),
fmt.Sprintf("description-%d: %s", m.n, simtypes.RandStringOfLength(r, 4000)),
)
}
}
/ make sure the MockWeightedProposalContent satisfied the WeightedProposalContent interface
var _ simtypes.WeightedProposalContent = MockWeightedProposalContent{
}
func mockWeightedProposalContent(n int) []simtypes.WeightedProposalContent {
wpc := make([]simtypes.WeightedProposalContent, n)
for i := 0; i < n; i++ {
wpc[i] = MockWeightedProposalContent{
i
}
}
return wpc
}
/ TestWeightedOperations tests the weights of the operations.
func TestWeightedOperations(t *testing.T) {
suite, ctx := createTestSuite(t, false)
app := suite.App
ctx.WithChainID("test-chain")
cdc := suite.cdc
appParams := make(simtypes.AppParams)
weightesOps := simulation.WeightedOperations(appParams, cdc, suite.AccountKeeper,
suite.BankKeeper, suite.GovKeeper, mockWeightedProposalContent(3),
)
/ setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
accs := getTestingAccounts(t, r, suite.AccountKeeper, suite.BankKeeper, suite.StakingKeeper, ctx, 3)
expected := []struct {
weight int
opMsgRoute string
opMsgName string
}{
{0, types.ModuleName, simulation.TypeMsgSubmitProposal
},
{1, types.ModuleName, simulation.TypeMsgSubmitProposal
},
{2, types.ModuleName, simulation.TypeMsgSubmitProposal
},
{
simulation.DefaultWeightMsgDeposit, types.ModuleName, simulation.TypeMsgDeposit
},
{
simulation.DefaultWeightMsgVote, types.ModuleName, simulation.TypeMsgVote
},
{
simulation.DefaultWeightMsgVoteWeighted, types.ModuleName, simulation.TypeMsgVoteWeighted
},
}
for i, w := range weightesOps {
operationMsg, _, _ := w.Op()(r, app.BaseApp, ctx, accs, ctx.ChainID())
/ require.NoError(t, err) / TODO check if it should be NoError
/ the following checks are very much dependent from the ordering of the output given
/ by WeightedOperations. if the ordering in WeightedOperations changes some tests
/ will fail
require.Equal(t, expected[i].weight, w.Weight(), "weight should be the same")
require.Equal(t, expected[i].opMsgRoute, operationMsg.Route, "route should be the same")
require.Equal(t, expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same")
}
}
/ TestSimulateMsgSubmitProposal tests the normal scenario of a valid message of type TypeMsgSubmitProposal.
/ Abnormal scenarios, where errors occur, are not tested here.
func TestSimulateMsgSubmitProposal(t *testing.T) {
suite, ctx := createTestSuite(t, false)
app := suite.App
/ setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
accounts := getTestingAccounts(t, r, suite.AccountKeeper, suite.BankKeeper, suite.StakingKeeper, ctx, 3)
/ begin a new block
app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: app.LastBlockHeight() + 1,
AppHash: app.LastCommitID().Hash
}})
/ execute operation
op := simulation.SimulateMsgSubmitProposal(suite.AccountKeeper, suite.BankKeeper, suite.GovKeeper, MockWeightedProposalContent{3
}.ContentSimulatorFn())
operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "")
require.NoError(t, err)
var msg v1.MsgSubmitProposal
err = govcodec.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
require.NoError(t, err)
require.True(t, operationMsg.OK)
require.Equal(t, "cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.Proposer)
require.NotEqual(t, len(msg.InitialDeposit), 0)
require.Equal(t, "2686011stake", msg.InitialDeposit[0].String())
require.Equal(t, "title-3: ZBSpYuLyYggwexjxusrBqDOTtGTOWeLrQKjLxzIivHSlcxgdXhhuTSkuxKGLwQvuyNhYFmBZHeAerqyNEUzXPFGkqEGqiQWIXnku", msg.Messages[0].GetCachedValue().(*v1.MsgExecLegacyContent).Content.GetCachedValue().(v1beta1.Content).GetTitle())
require.Equal(t, "description-3: NJWzHdBNpAXKJPHWQdrGYcAHSctgVlqwqHoLfHsXUdStwfefwzqLuKEhmMyYLdbZrcPgYqjNHxPexsruwEGStAneKbWkQDDIlCWBLSiAASNhZqNFlPtfqPJoxKsgMdzjWqLWdqKQuJqWPMvwPQWZUtVMOTMYKJbfdlZsjdsomuScvDmbDkgRualsxDvRJuCAmPOXitIbcyWsKGSdrEunFAOdmXnsuyFVgJqEjbklvmwrUlsxjRSfKZxGcpayDdgoFcnVSutxjRgOSFzPwidAjubMncNweqpbxhXGchpZUxuFDOtpnhNUycJICRYqsPhPSCjPTWZFLkstHWJxvdPEAyEIxXgLwbNOjrgzmaujiBABBIXvcXpLrbcEWNNQsbjvgJFgJkflpRohHUutvnaUqoopuKjTDaemDeSdqbnOzcfJpcTuAQtZoiLZOoAIlboFDAeGmSNwkvObPRvRWQgWkGkxwtPauYgdkmypLjbqhlHJIQTntgWjXwZdOyYEdQRRLfMSdnxqppqUofqLbLQDUjwKVKfZJUJQPsWIPwIVaSTrmKskoAhvmZyJgeRpkaTfGgrJzAigcxtfshmiDCFkuiluqtMOkidknnTBtumyJYlIsWLnCQclqdVmikUoMOPdPWwYbJxXyqUVicNxFxyqJTenNblyyKSdlCbiXxUiYUiMwXZASYfvMDPFgxniSjWaZTjHkqlJvtBsXqwPpyVxnJVGFWhfSxgOcduoxkiopJvFjMmFabrGYeVtTXLhxVUEiGwYUvndjFGzDVntUvibiyZhfMQdMhgsiuysLMiePBNXifRLMsSmXPkwlPloUbJveCvUlaalhZHuvdkCnkSHbMbmOnrfEGPwQiACiPlnihiaOdbjPqPiTXaHDoJXjSlZmltGqNHHNrcKdlFSCdmVOuvDcBLdSklyGJmcLTbSFtALdGlPkqqecJrpLCXNPWefoTJNgEJlyMEPneVaxxduAAEqQpHWZodWyRkDAxzyMnFMcjSVqeRXLqsNyNtQBbuRvunZflWSbbvXXdkyLikYqutQhLPONXbvhcQZJPSWnOulqQaXmbfFxAkqfYeseSHOQidHwbcsOaMnSrrmGjjRmEMQNuknupMxJiIeVjmgZvbmjPIQTEhQFULQLBMPrxcFPvBinaOPYWGvYGRKxLZdwamfRQQFngcdSlvwjfaPbURasIsGJVHtcEAxnIIrhSriiXLOlbEBLXFElXJFGxHJczRBIxAuPKtBisjKBwfzZFagdNmjdwIRvwzLkFKWRTDPxJCmpzHUcrPiiXXHnOIlqNVoGSXZewdnCRhuxeYGPVTfrNTQNOxZmxInOazUYNTNDgzsxlgiVEHPKMfbesvPHUqpNkUqbzeuzfdrsuLDpKHMUbBMKczKKWOdYoIXoPYtEjfOnlQLoGnbQUCuERdEFaptwnsHzTJDsuZkKtzMpFaZobynZdzNydEeJJHDYaQcwUxcqvwfWwNUsCiLvkZQiSfzAHftYgAmVsXgtmcYgTqJIawstRYJrZdSxlfRiqTufgEQVambeZZmaAyRQbcmdjVUZZCgqDrSeltJGXPMgZnGDZqISrGDOClxXCxMjmKqEPwKHoOfOeyGmqWqihqjINXLqnyTesZePQRqaWDQNqpLgNrAUKulklmckTijUltQKuWQDwpLmDyxLppPVMwsmBIpOwQttYFMjgJQZLYFPmxWFLIeZihkRNnkzoypBICIxgEuYsVWGIGRbbxqVasYnstWomJnHwmtOhAFSpttRYYzBmyEtZXiCthvKvWszTXDbiJbGXMcrYpKAgvUVFtdKUfvdMfhAryctklUCEdjetjuGNfJjajZtvzdYaqInKtFPPLYmRaXPdQzxdSQfmZDEVHlHGEGNSPRFJuIfKLLfUmnHxHnRjmzQPNlqrXgifUdzAGKVabYqvcDeYoTYgPsBUqehrBhmQUgTvDnsdpuhUoxskDdppTsYMcnDIPSwKIqhXDCIxOuXrywahvVavvHkPuaenjLmEbMgrkrQLHEAwrhHkPRNvonNQKqprqOFVZKAtpRSpvQUxMoXCMZLSSbnLEFsjVfANdQNQVwTmGxqVjVqRuxREAhuaDrFgEZpYKhwWPEKBevBfsOIcaZKyykQafzmGPLRAKDtTcJxJVgiiuUkmyMYuDUNEUhBEdoBLJnamtLmMJQgmLiUELIhLpiEvpOXOvXCPUeldLFqkKOwfacqIaRcnnZvERKRMCKUkMABbDHytQqQblrvoxOZkwzosQfDKGtIdfcXRJNqlBNwOCWoQBcEWyqrMlYZIAXYJmLfnjoJepgSFvrgajaBAIksoyeHqgqbGvpAstMIGmIhRYGGNPRIfOQKsGoKgxtsidhTaAePRCBFqZgPDWCIkqOJezGVkjfYUCZTlInbxBXwUAVRsxHTQtJFnnpmMvXDYCVlEmnZBKhmmxQOIQzxFWpJQkQoSAYzTEiDWEOsVLNrbfzeHFRyeYATakQQWmFDLPbVMCJcWjFGJjfqCoVzlbNNEsqxdSmNPjTjHYOkuEMFLkXYGaoJlraLqayMeCsTjWNRDPBywBJLAPVkGQqTwApVVwYAetlwSbzsdHWsTwSIcctkyKDuRWYDQikRqsKTMJchrliONJeaZIzwPQrNbTwxsGdwuduvibtYndRwpdsvyCktRHFalvUuEKMqXbItfGcNGWsGzubdPMYayOUOINjpcFBeESdwpdlTYmrPsLsVDhpTzoMegKrytNVZkfJRPuDCUXxSlSthOohmsuxmIZUedzxKmowKOdXTMcEtdpHaPWgIsIjrViKrQOCONlSuazmLuCUjLltOGXeNgJKedTVrrVCpWYWHyVrdXpKgNaMJVjbXxnVMSChdWKuZdqpisvrkBJPoURDYxWOtpjzZoOpWzyUuYNhCzRoHsMjmmWDcXzQiHIyjwdhPNwiPqFxeUfMVFQGImhykFgMIlQEoZCaRoqSBXTSWAeDumdbsOGtATwEdZlLfoBKiTvodQBGOEcuATWXfiinSjPmJKcWgQrTVYVrwlyMWhxqNbCMpIQNoSMGTiWfPTCezUjYcdWppnsYJihLQCqbNLRGgqrwHuIvsazapTpoPZIyZyeeSueJuTIhpHMEJfJpScshJubJGfkusuVBgfTWQoywSSliQQSfbvaHKiLnyjdSbpMkdBgXepoSsHnCQaYuHQqZsoEOmJCiuQUpJkmfyfbIShzlZpHFmLCsbknEAkKXKfRTRnuwdBeuOGgFbJLbDksHVapaRayWzwoYBEpmrlAxrUxYMUekKbpjPNfjUCjhbdMAnJmYQVZBQZkFVweHDAlaqJjRqoQPoOMLhyvYCzqEuQsAFoxWrzRnTVjStPadhsESlERnKhpEPsfDxNvxqcOyIulaCkmPdambLHvGhTZzysvqFauEgkFRItPfvisehFmoBhQqmkfbHVsgfHXDPJVyhwPllQpuYLRYvGodxKjkarnSNgsXoKEMlaSKxKdcVgvOkuLcfLFfdtXGTclqfPOfeoVLbqcjcXCUEBgAGplrkgsmIEhWRZLlGPGCwKWRaCKMkBHTAcypUrYjWwCLtOPVygMwMANGoQwFnCqFrUGMCRZUGJKTZIGPyldsifauoMnJPLTcDHmilcmahlqOELaAUYDBuzsVywnDQfwRLGIWozYaOAilMBcObErwgTDNGWnwQMUgFFSKtPDMEoEQCTKVREqrXZSGLqwTMcxHfWotDllNkIJPMbXzjDVjPOOjCFuIvTyhXKLyhUScOXvYthRXpPfKwMhptXaxIxgqBoUqzrWbaoLTVpQoottZyPFfNOoMioXHRuFwMRYUiKvcWPkrayyTLOCFJlAyslDameIuqVAuxErqFPEWIScKpBORIuZqoXlZuTvAjEdlEWDODFRregDTqGNoFBIHxvimmIZwLfFyKUfEWAnNBdtdzDmTPXtpHRGdIbuucfTjOygZsTxPjfweXhSUkMhPjMaxKlMIJMOXcnQfyzeOcbWwNbeH", msg.Messages[0].GetCachedValue().(*v1.MsgExecLegacyContent).Content.GetCachedValue().(v1beta1.Content).GetDescription())
require.Equal(t, "gov", msg.Route())
require.Equal(t, simulation.TypeMsgSubmitProposal, msg.Type())
}
/ TestSimulateMsgDeposit tests the normal scenario of a valid message of type TypeMsgDeposit.
/ Abnormal scenarios, where errors occur, are not tested here.
func TestSimulateMsgDeposit(t *testing.T) {
suite, ctx := createTestSuite(t, false)
app := suite.App
blockTime := time.Now().UTC()
ctx = ctx.WithBlockTime(blockTime)
/ setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
accounts := getTestingAccounts(t, r, suite.AccountKeeper, suite.BankKeeper, suite.StakingKeeper, ctx, 3)
/ setup a proposal
content := v1beta1.NewTextProposal("Test", "description")
contentMsg, err := v1.NewLegacyContent(content, suite.GovKeeper.GetGovernanceAccount(ctx).GetAddress().String())
require.NoError(t, err)
submitTime := ctx.BlockHeader().Time
depositPeriod := suite.GovKeeper.GetParams(ctx).MaxDepositPeriod
proposal, err := v1.NewProposal([]sdk.Msg{
contentMsg
}, 1, "", submitTime, submitTime.Add(*depositPeriod), "text proposal", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
suite.GovKeeper.SetProposal(ctx, proposal)
/ begin a new block
app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: app.LastBlockHeight() + 1,
AppHash: app.LastCommitID().Hash,
Time: blockTime
}})
/ execute operation
op := simulation.SimulateMsgDeposit(suite.AccountKeeper, suite.BankKeeper, suite.GovKeeper)
operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "")
require.NoError(t, err)
var msg v1.MsgDeposit
err = govcodec.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
require.NoError(t, err)
require.True(t, operationMsg.OK)
require.Equal(t, uint64(1), msg.ProposalId)
require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Depositor)
require.NotEqual(t, len(msg.Amount), 0)
require.Equal(t, "560969stake", msg.Amount[0].String())
require.Equal(t, "gov", msg.Route())
require.Equal(t, simulation.TypeMsgDeposit, msg.Type())
}
/ TestSimulateMsgVote tests the normal scenario of a valid message of type TypeMsgVote.
/ Abnormal scenarios, where errors occur, are not tested here.
func TestSimulateMsgVote(t *testing.T) {
suite, ctx := createTestSuite(t, false)
app := suite.App
blockTime := time.Now().UTC()
ctx = ctx.WithBlockTime(blockTime)
/ setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
accounts := getTestingAccounts(t, r, suite.AccountKeeper, suite.BankKeeper, suite.StakingKeeper, ctx, 3)
/ setup a proposal
govAcc := suite.GovKeeper.GetGovernanceAccount(ctx).GetAddress().String()
contentMsg, err := v1.NewLegacyContent(v1beta1.NewTextProposal("Test", "description"), govAcc)
require.NoError(t, err)
submitTime := ctx.BlockHeader().Time
depositPeriod := suite.GovKeeper.GetParams(ctx).MaxDepositPeriod
proposal, err := v1.NewProposal([]sdk.Msg{
contentMsg
}, 1, "", submitTime, submitTime.Add(*depositPeriod), "text proposal", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
suite.GovKeeper.ActivateVotingPeriod(ctx, proposal)
/ begin a new block
app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: app.LastBlockHeight() + 1,
AppHash: app.LastCommitID().Hash,
Time: blockTime
}})
/ execute operation
op := simulation.SimulateMsgVote(suite.AccountKeeper, suite.BankKeeper, suite.GovKeeper)
operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "")
require.NoError(t, err)
var msg v1.MsgVote
govcodec.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
require.True(t, operationMsg.OK)
require.Equal(t, uint64(1), msg.ProposalId)
require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Voter)
require.Equal(t, v1.OptionYes, msg.Option)
require.Equal(t, "gov", msg.Route())
require.Equal(t, simulation.TypeMsgVote, msg.Type())
}
/ TestSimulateMsgVoteWeighted tests the normal scenario of a valid message of type TypeMsgVoteWeighted.
/ Abnormal scenarios, where errors occur, are not tested here.
func TestSimulateMsgVoteWeighted(t *testing.T) {
suite, ctx := createTestSuite(t, false)
app := suite.App
blockTime := time.Now().UTC()
ctx = ctx.WithBlockTime(blockTime)
/ setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
accounts := getTestingAccounts(t, r, suite.AccountKeeper, suite.BankKeeper, suite.StakingKeeper, ctx, 3)
/ setup a proposal
govAcc := suite.GovKeeper.GetGovernanceAccount(ctx).GetAddress().String()
contentMsg, err := v1.NewLegacyContent(v1beta1.NewTextProposal("Test", "description"), govAcc)
require.NoError(t, err)
submitTime := ctx.BlockHeader().Time
depositPeriod := suite.GovKeeper.GetParams(ctx).MaxDepositPeriod
proposal, err := v1.NewProposal([]sdk.Msg{
contentMsg
}, 1, "", submitTime, submitTime.Add(*depositPeriod), "text proposal", "test", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
suite.GovKeeper.ActivateVotingPeriod(ctx, proposal)
/ begin a new block
app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: app.LastBlockHeight() + 1,
AppHash: app.LastCommitID().Hash,
Time: blockTime
}})
/ execute operation
op := simulation.SimulateMsgVoteWeighted(suite.AccountKeeper, suite.BankKeeper, suite.GovKeeper)
operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "")
require.NoError(t, err)
var msg v1.MsgVoteWeighted
govcodec.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
require.True(t, operationMsg.OK)
require.Equal(t, uint64(1), msg.ProposalId)
require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Voter)
require.True(t, len(msg.Options) >= 1)
require.Equal(t, "gov", msg.Route())
require.Equal(t, simulation.TypeMsgVoteWeighted, msg.Type())
}
type suite struct {
cdc codec.Codec
AccountKeeper authkeeper.AccountKeeper
BankKeeper bankkeeper.Keeper
GovKeeper *keeper.Keeper
StakingKeeper *stakingkeeper.Keeper
App *runtime.App
}
/ returns context and an app with updated mint keeper
func createTestSuite(t *testing.T, isCheckTx bool) (suite, sdk.Context) {
res := suite{
}
app, err := simtestutil.Setup(configurator.NewAppConfig(
configurator.AuthModule(),
configurator.TxModule(),
configurator.ParamsModule(),
configurator.BankModule(),
configurator.StakingModule(),
configurator.ConsensusModule(),
configurator.GovModule(),
), &res.AccountKeeper, &res.BankKeeper, &res.GovKeeper, &res.StakingKeeper, &res.cdc)
require.NoError(t, err)
ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{
})
res.App = app
return res, ctx
}
func getTestingAccounts(
t *testing.T, r *rand.Rand,
accountKeeper authkeeper.AccountKeeper, bankKeeper bankkeeper.Keeper, stakingKeeper *stakingkeeper.Keeper,
ctx sdk.Context, n int,
) []simtypes.Account {
accounts := simtypes.RandomAccounts(r, n)
initAmt := stakingKeeper.TokensFromConsensusPower(ctx, 200)
initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt))
/ add coins to the accounts
for _, account := range accounts {
acc := accountKeeper.NewAccountWithAddress(ctx, account.Address)
accountKeeper.SetAccount(ctx, acc)
require.NoError(t, testutil.FundAccount(bankKeeper, ctx, account.Address, initCoins))
}
return accounts
}
Copy
Ask AI
package simulation_test
import (
"fmt"
"math/rand"
"testing"
"time"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil/configurator"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
_ "github.com/cosmos/cosmos-sdk/x/auth"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
_ "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
_ "github.com/cosmos/cosmos-sdk/x/bank"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
_ "github.com/cosmos/cosmos-sdk/x/consensus"
govcodec "github.com/cosmos/cosmos-sdk/x/gov/codec"
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
"github.com/cosmos/cosmos-sdk/x/gov/simulation"
"github.com/cosmos/cosmos-sdk/x/gov/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
_ "github.com/cosmos/cosmos-sdk/x/params"
_ "github.com/cosmos/cosmos-sdk/x/staking"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
)
type MockWeightedProposalContent struct {
n int
}
func (m MockWeightedProposalContent)
AppParamsKey()
string {
return fmt.Sprintf("AppParamsKey-%d", m.n)
}
func (m MockWeightedProposalContent)
DefaultWeight()
int {
return m.n
}
func (m MockWeightedProposalContent)
ContentSimulatorFn()
simtypes.ContentSimulatorFn {
return func(r *rand.Rand, _ sdk.Context, _ []simtypes.Account)
simtypes.Content {
return v1beta1.NewTextProposal(
fmt.Sprintf("title-%d: %s", m.n, simtypes.RandStringOfLength(r, 100)),
fmt.Sprintf("description-%d: %s", m.n, simtypes.RandStringOfLength(r, 4000)),
)
}
}
/ make sure the MockWeightedProposalContent satisfied the WeightedProposalContent interface
var _ simtypes.WeightedProposalContent = MockWeightedProposalContent{
}
func mockWeightedProposalContent(n int) []simtypes.WeightedProposalContent {
wpc := make([]simtypes.WeightedProposalContent, n)
for i := 0; i < n; i++ {
wpc[i] = MockWeightedProposalContent{
i
}
}
return wpc
}
/ TestWeightedOperations tests the weights of the operations.
func TestWeightedOperations(t *testing.T) {
suite, ctx := createTestSuite(t, false)
app := suite.App
ctx.WithChainID("test-chain")
cdc := suite.cdc
appParams := make(simtypes.AppParams)
weightesOps := simulation.WeightedOperations(appParams, cdc, suite.AccountKeeper,
suite.BankKeeper, suite.GovKeeper, mockWeightedProposalContent(3),
)
/ setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
accs := getTestingAccounts(t, r, suite.AccountKeeper, suite.BankKeeper, suite.StakingKeeper, ctx, 3)
expected := []struct {
weight int
opMsgRoute string
opMsgName string
}{
{0, types.ModuleName, simulation.TypeMsgSubmitProposal
},
{1, types.ModuleName, simulation.TypeMsgSubmitProposal
},
{2, types.ModuleName, simulation.TypeMsgSubmitProposal
},
{
simulation.DefaultWeightMsgDeposit, types.ModuleName, simulation.TypeMsgDeposit
},
{
simulation.DefaultWeightMsgVote, types.ModuleName, simulation.TypeMsgVote
},
{
simulation.DefaultWeightMsgVoteWeighted, types.ModuleName, simulation.TypeMsgVoteWeighted
},
}
for i, w := range weightesOps {
operationMsg, _, _ := w.Op()(r, app.BaseApp, ctx, accs, ctx.ChainID())
/ require.NoError(t, err) / TODO check if it should be NoError
/ the following checks are very much dependent from the ordering of the output given
/ by WeightedOperations. if the ordering in WeightedOperations changes some tests
/ will fail
require.Equal(t, expected[i].weight, w.Weight(), "weight should be the same")
require.Equal(t, expected[i].opMsgRoute, operationMsg.Route, "route should be the same")
require.Equal(t, expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same")
}
}
/ TestSimulateMsgSubmitProposal tests the normal scenario of a valid message of type TypeMsgSubmitProposal.
/ Abnormal scenarios, where errors occur, are not tested here.
func TestSimulateMsgSubmitProposal(t *testing.T) {
suite, ctx := createTestSuite(t, false)
app := suite.App
/ setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
accounts := getTestingAccounts(t, r, suite.AccountKeeper, suite.BankKeeper, suite.StakingKeeper, ctx, 3)
/ begin a new block
app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: app.LastBlockHeight() + 1,
AppHash: app.LastCommitID().Hash
}})
/ execute operation
op := simulation.SimulateMsgSubmitProposal(suite.AccountKeeper, suite.BankKeeper, suite.GovKeeper, MockWeightedProposalContent{3
}.ContentSimulatorFn())
operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "")
require.NoError(t, err)
var msg v1.MsgSubmitProposal
err = govcodec.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
require.NoError(t, err)
require.True(t, operationMsg.OK)
require.Equal(t, "cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.Proposer)
require.NotEqual(t, len(msg.InitialDeposit), 0)
require.Equal(t, "2686011stake", msg.InitialDeposit[0].String())
require.Equal(t, "title-3: ZBSpYuLyYggwexjxusrBqDOTtGTOWeLrQKjLxzIivHSlcxgdXhhuTSkuxKGLwQvuyNhYFmBZHeAerqyNEUzXPFGkqEGqiQWIXnku", msg.Messages[0].GetCachedValue().(*v1.MsgExecLegacyContent).Content.GetCachedValue().(v1beta1.Content).GetTitle())
require.Equal(t, "description-3: NJWzHdBNpAXKJPHWQdrGYcAHSctgVlqwqHoLfHsXUdStwfefwzqLuKEhmMyYLdbZrcPgYqjNHxPexsruwEGStAneKbWkQDDIlCWBLSiAASNhZqNFlPtfqPJoxKsgMdzjWqLWdqKQuJqWPMvwPQWZUtVMOTMYKJbfdlZsjdsomuScvDmbDkgRualsxDvRJuCAmPOXitIbcyWsKGSdrEunFAOdmXnsuyFVgJqEjbklvmwrUlsxjRSfKZxGcpayDdgoFcnVSutxjRgOSFzPwidAjubMncNweqpbxhXGchpZUxuFDOtpnhNUycJICRYqsPhPSCjPTWZFLkstHWJxvdPEAyEIxXgLwbNOjrgzmaujiBABBIXvcXpLrbcEWNNQsbjvgJFgJkflpRohHUutvnaUqoopuKjTDaemDeSdqbnOzcfJpcTuAQtZoiLZOoAIlboFDAeGmSNwkvObPRvRWQgWkGkxwtPauYgdkmypLjbqhlHJIQTntgWjXwZdOyYEdQRRLfMSdnxqppqUofqLbLQDUjwKVKfZJUJQPsWIPwIVaSTrmKskoAhvmZyJgeRpkaTfGgrJzAigcxtfshmiDCFkuiluqtMOkidknnTBtumyJYlIsWLnCQclqdVmikUoMOPdPWwYbJxXyqUVicNxFxyqJTenNblyyKSdlCbiXxUiYUiMwXZASYfvMDPFgxniSjWaZTjHkqlJvtBsXqwPpyVxnJVGFWhfSxgOcduoxkiopJvFjMmFabrGYeVtTXLhxVUEiGwYUvndjFGzDVntUvibiyZhfMQdMhgsiuysLMiePBNXifRLMsSmXPkwlPloUbJveCvUlaalhZHuvdkCnkSHbMbmOnrfEGPwQiACiPlnihiaOdbjPqPiTXaHDoJXjSlZmltGqNHHNrcKdlFSCdmVOuvDcBLdSklyGJmcLTbSFtALdGlPkqqecJrpLCXNPWefoTJNgEJlyMEPneVaxxduAAEqQpHWZodWyRkDAxzyMnFMcjSVqeRXLqsNyNtQBbuRvunZflWSbbvXXdkyLikYqutQhLPONXbvhcQZJPSWnOulqQaXmbfFxAkqfYeseSHOQidHwbcsOaMnSrrmGjjRmEMQNuknupMxJiIeVjmgZvbmjPIQTEhQFULQLBMPrxcFPvBinaOPYWGvYGRKxLZdwamfRQQFngcdSlvwjfaPbURasIsGJVHtcEAxnIIrhSriiXLOlbEBLXFElXJFGxHJczRBIxAuPKtBisjKBwfzZFagdNmjdwIRvwzLkFKWRTDPxJCmpzHUcrPiiXXHnOIlqNVoGSXZewdnCRhuxeYGPVTfrNTQNOxZmxInOazUYNTNDgzsxlgiVEHPKMfbesvPHUqpNkUqbzeuzfdrsuLDpKHMUbBMKczKKWOdYoIXoPYtEjfOnlQLoGnbQUCuERdEFaptwnsHzTJDsuZkKtzMpFaZobynZdzNydEeJJHDYaQcwUxcqvwfWwNUsCiLvkZQiSfzAHftYgAmVsXgtmcYgTqJIawstRYJrZdSxlfRiqTufgEQVambeZZmaAyRQbcmdjVUZZCgqDrSeltJGXPMgZnGDZqISrGDOClxXCxMjmKqEPwKHoOfOeyGmqWqihqjINXLqnyTesZePQRqaWDQNqpLgNrAUKulklmckTijUltQKuWQDwpLmDyxLppPVMwsmBIpOwQttYFMjgJQZLYFPmxWFLIeZihkRNnkzoypBICIxgEuYsVWGIGRbbxqVasYnstWomJnHwmtOhAFSpttRYYzBmyEtZXiCthvKvWszTXDbiJbGXMcrYpKAgvUVFtdKUfvdMfhAryctklUCEdjetjuGNfJjajZtvzdYaqInKtFPPLYmRaXPdQzxdSQfmZDEVHlHGEGNSPRFJuIfKLLfUmnHxHnRjmzQPNlqrXgifUdzAGKVabYqvcDeYoTYgPsBUqehrBhmQUgTvDnsdpuhUoxskDdppTsYMcnDIPSwKIqhXDCIxOuXrywahvVavvHkPuaenjLmEbMgrkrQLHEAwrhHkPRNvonNQKqprqOFVZKAtpRSpvQUxMoXCMZLSSbnLEFsjVfANdQNQVwTmGxqVjVqRuxREAhuaDrFgEZpYKhwWPEKBevBfsOIcaZKyykQafzmGPLRAKDtTcJxJVgiiuUkmyMYuDUNEUhBEdoBLJnamtLmMJQgmLiUELIhLpiEvpOXOvXCPUeldLFqkKOwfacqIaRcnnZvERKRMCKUkMABbDHytQqQblrvoxOZkwzosQfDKGtIdfcXRJNqlBNwOCWoQBcEWyqrMlYZIAXYJmLfnjoJepgSFvrgajaBAIksoyeHqgqbGvpAstMIGmIhRYGGNPRIfOQKsGoKgxtsidhTaAePRCBFqZgPDWCIkqOJezGVkjfYUCZTlInbxBXwUAVRsxHTQtJFnnpmMvXDYCVlEmnZBKhmmxQOIQzxFWpJQkQoSAYzTEiDWEOsVLNrbfzeHFRyeYATakQQWmFDLPbVMCJcWjFGJjfqCoVzlbNNEsqxdSmNPjTjHYOkuEMFLkXYGaoJlraLqayMeCsTjWNRDPBywBJLAPVkGQqTwApVVwYAetlwSbzsdHWsTwSIcctkyKDuRWYDQikRqsKTMJchrliONJeaZIzwPQrNbTwxsGdwuduvibtYndRwpdsvyCktRHFalvUuEKMqXbItfGcNGWsGzubdPMYayOUOINjpcFBeESdwpdlTYmrPsLsVDhpTzoMegKrytNVZkfJRPuDCUXxSlSthOohmsuxmIZUedzxKmowKOdXTMcEtdpHaPWgIsIjrViKrQOCONlSuazmLuCUjLltOGXeNgJKedTVrrVCpWYWHyVrdXpKgNaMJVjbXxnVMSChdWKuZdqpisvrkBJPoURDYxWOtpjzZoOpWzyUuYNhCzRoHsMjmmWDcXzQiHIyjwdhPNwiPqFxeUfMVFQGImhykFgMIlQEoZCaRoqSBXTSWAeDumdbsOGtATwEdZlLfoBKiTvodQBGOEcuATWXfiinSjPmJKcWgQrTVYVrwlyMWhxqNbCMpIQNoSMGTiWfPTCezUjYcdWppnsYJihLQCqbNLRGgqrwHuIvsazapTpoPZIyZyeeSueJuTIhpHMEJfJpScshJubJGfkusuVBgfTWQoywSSliQQSfbvaHKiLnyjdSbpMkdBgXepoSsHnCQaYuHQqZsoEOmJCiuQUpJkmfyfbIShzlZpHFmLCsbknEAkKXKfRTRnuwdBeuOGgFbJLbDksHVapaRayWzwoYBEpmrlAxrUxYMUekKbpjPNfjUCjhbdMAnJmYQVZBQZkFVweHDAlaqJjRqoQPoOMLhyvYCzqEuQsAFoxWrzRnTVjStPadhsESlERnKhpEPsfDxNvxqcOyIulaCkmPdambLHvGhTZzysvqFauEgkFRItPfvisehFmoBhQqmkfbHVsgfHXDPJVyhwPllQpuYLRYvGodxKjkarnSNgsXoKEMlaSKxKdcVgvOkuLcfLFfdtXGTclqfPOfeoVLbqcjcXCUEBgAGplrkgsmIEhWRZLlGPGCwKWRaCKMkBHTAcypUrYjWwCLtOPVygMwMANGoQwFnCqFrUGMCRZUGJKTZIGPyldsifauoMnJPLTcDHmilcmahlqOELaAUYDBuzsVywnDQfwRLGIWozYaOAilMBcObErwgTDNGWnwQMUgFFSKtPDMEoEQCTKVREqrXZSGLqwTMcxHfWotDllNkIJPMbXzjDVjPOOjCFuIvTyhXKLyhUScOXvYthRXpPfKwMhptXaxIxgqBoUqzrWbaoLTVpQoottZyPFfNOoMioXHRuFwMRYUiKvcWPkrayyTLOCFJlAyslDameIuqVAuxErqFPEWIScKpBORIuZqoXlZuTvAjEdlEWDODFRregDTqGNoFBIHxvimmIZwLfFyKUfEWAnNBdtdzDmTPXtpHRGdIbuucfTjOygZsTxPjfweXhSUkMhPjMaxKlMIJMOXcnQfyzeOcbWwNbeH", msg.Messages[0].GetCachedValue().(*v1.MsgExecLegacyContent).Content.GetCachedValue().(v1beta1.Content).GetDescription())
require.Equal(t, "gov", msg.Route())
require.Equal(t, simulation.TypeMsgSubmitProposal, msg.Type())
}
/ TestSimulateMsgDeposit tests the normal scenario of a valid message of type TypeMsgDeposit.
/ Abnormal scenarios, where errors occur, are not tested here.
func TestSimulateMsgDeposit(t *testing.T) {
suite, ctx := createTestSuite(t, false)
app := suite.App
blockTime := time.Now().UTC()
ctx = ctx.WithBlockTime(blockTime)
/ setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
accounts := getTestingAccounts(t, r, suite.AccountKeeper, suite.BankKeeper, suite.StakingKeeper, ctx, 3)
/ setup a proposal
content := v1beta1.NewTextProposal("Test", "description")
contentMsg, err := v1.NewLegacyContent(content, suite.GovKeeper.GetGovernanceAccount(ctx).GetAddress().String())
require.NoError(t, err)
submitTime := ctx.BlockHeader().Time
depositPeriod := suite.GovKeeper.GetParams(ctx).MaxDepositPeriod
proposal, err := v1.NewProposal([]sdk.Msg{
contentMsg
}, 1, "", submitTime, submitTime.Add(*depositPeriod), "text proposal", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
suite.GovKeeper.SetProposal(ctx, proposal)
/ begin a new block
app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: app.LastBlockHeight() + 1,
AppHash: app.LastCommitID().Hash,
Time: blockTime
}})
/ execute operation
op := simulation.SimulateMsgDeposit(suite.AccountKeeper, suite.BankKeeper, suite.GovKeeper)
operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "")
require.NoError(t, err)
var msg v1.MsgDeposit
err = govcodec.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
require.NoError(t, err)
require.True(t, operationMsg.OK)
require.Equal(t, uint64(1), msg.ProposalId)
require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Depositor)
require.NotEqual(t, len(msg.Amount), 0)
require.Equal(t, "560969stake", msg.Amount[0].String())
require.Equal(t, "gov", msg.Route())
require.Equal(t, simulation.TypeMsgDeposit, msg.Type())
}
/ TestSimulateMsgVote tests the normal scenario of a valid message of type TypeMsgVote.
/ Abnormal scenarios, where errors occur, are not tested here.
func TestSimulateMsgVote(t *testing.T) {
suite, ctx := createTestSuite(t, false)
app := suite.App
blockTime := time.Now().UTC()
ctx = ctx.WithBlockTime(blockTime)
/ setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
accounts := getTestingAccounts(t, r, suite.AccountKeeper, suite.BankKeeper, suite.StakingKeeper, ctx, 3)
/ setup a proposal
govAcc := suite.GovKeeper.GetGovernanceAccount(ctx).GetAddress().String()
contentMsg, err := v1.NewLegacyContent(v1beta1.NewTextProposal("Test", "description"), govAcc)
require.NoError(t, err)
submitTime := ctx.BlockHeader().Time
depositPeriod := suite.GovKeeper.GetParams(ctx).MaxDepositPeriod
proposal, err := v1.NewProposal([]sdk.Msg{
contentMsg
}, 1, "", submitTime, submitTime.Add(*depositPeriod), "text proposal", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
suite.GovKeeper.ActivateVotingPeriod(ctx, proposal)
/ begin a new block
app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: app.LastBlockHeight() + 1,
AppHash: app.LastCommitID().Hash,
Time: blockTime
}})
/ execute operation
op := simulation.SimulateMsgVote(suite.AccountKeeper, suite.BankKeeper, suite.GovKeeper)
operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "")
require.NoError(t, err)
var msg v1.MsgVote
govcodec.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
require.True(t, operationMsg.OK)
require.Equal(t, uint64(1), msg.ProposalId)
require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Voter)
require.Equal(t, v1.OptionYes, msg.Option)
require.Equal(t, "gov", msg.Route())
require.Equal(t, simulation.TypeMsgVote, msg.Type())
}
/ TestSimulateMsgVoteWeighted tests the normal scenario of a valid message of type TypeMsgVoteWeighted.
/ Abnormal scenarios, where errors occur, are not tested here.
func TestSimulateMsgVoteWeighted(t *testing.T) {
suite, ctx := createTestSuite(t, false)
app := suite.App
blockTime := time.Now().UTC()
ctx = ctx.WithBlockTime(blockTime)
/ setup 3 accounts
s := rand.NewSource(1)
r := rand.New(s)
accounts := getTestingAccounts(t, r, suite.AccountKeeper, suite.BankKeeper, suite.StakingKeeper, ctx, 3)
/ setup a proposal
govAcc := suite.GovKeeper.GetGovernanceAccount(ctx).GetAddress().String()
contentMsg, err := v1.NewLegacyContent(v1beta1.NewTextProposal("Test", "description"), govAcc)
require.NoError(t, err)
submitTime := ctx.BlockHeader().Time
depositPeriod := suite.GovKeeper.GetParams(ctx).MaxDepositPeriod
proposal, err := v1.NewProposal([]sdk.Msg{
contentMsg
}, 1, "", submitTime, submitTime.Add(*depositPeriod), "text proposal", "test", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"))
require.NoError(t, err)
suite.GovKeeper.ActivateVotingPeriod(ctx, proposal)
/ begin a new block
app.BeginBlock(abci.RequestBeginBlock{
Header: tmproto.Header{
Height: app.LastBlockHeight() + 1,
AppHash: app.LastCommitID().Hash,
Time: blockTime
}})
/ execute operation
op := simulation.SimulateMsgVoteWeighted(suite.AccountKeeper, suite.BankKeeper, suite.GovKeeper)
operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "")
require.NoError(t, err)
var msg v1.MsgVoteWeighted
govcodec.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg)
require.True(t, operationMsg.OK)
require.Equal(t, uint64(1), msg.ProposalId)
require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Voter)
require.True(t, len(msg.Options) >= 1)
require.Equal(t, "gov", msg.Route())
require.Equal(t, simulation.TypeMsgVoteWeighted, msg.Type())
}
type suite struct {
cdc codec.Codec
AccountKeeper authkeeper.AccountKeeper
BankKeeper bankkeeper.Keeper
GovKeeper *keeper.Keeper
StakingKeeper *stakingkeeper.Keeper
App *runtime.App
}
/ returns context and an app with updated mint keeper
func createTestSuite(t *testing.T, isCheckTx bool) (suite, sdk.Context) {
res := suite{
}
app, err := simtestutil.Setup(configurator.NewAppConfig(
configurator.AuthModule(),
configurator.TxModule(),
configurator.ParamsModule(),
configurator.BankModule(),
configurator.StakingModule(),
configurator.ConsensusModule(),
configurator.GovModule(),
), &res.AccountKeeper, &res.BankKeeper, &res.GovKeeper, &res.StakingKeeper, &res.cdc)
require.NoError(t, err)
ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{
})
res.App = app
return res, ctx
}
func getTestingAccounts(
t *testing.T, r *rand.Rand,
accountKeeper authkeeper.AccountKeeper, bankKeeper bankkeeper.Keeper, stakingKeeper *stakingkeeper.Keeper,
ctx sdk.Context, n int,
) []simtypes.Account {
accounts := simtypes.RandomAccounts(r, n)
initAmt := stakingKeeper.TokensFromConsensusPower(ctx, 200)
initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt))
/ add coins to the accounts
for _, account := range accounts {
acc := accountKeeper.NewAccountWithAddress(ctx, account.Address)
accountKeeper.SetAccount(ctx, acc)
require.NoError(t, testutil.FundAccount(bankKeeper, ctx, account.Address, initCoins))
}
return accounts
}
End-to-end Tests
End-to-end tests are at the top of the test pyramid. They must test the whole application flow, from the user perspective (for instance, CLI tests). They are located under/tests/e2e
.
For that, the SDK is using simapp
but you should use your own application (appd
).
Here are some examples:
warning
The SDK is in the process of creating its E2E tests, as defined in ADR-59. This page will eventually be updated with better examples.