feat: add simulation for nft module (#10522)
<!-- The default pull request template is for types feat, fix, or refactor. For other templates, add one of the following parameters to the url: - template=docs.md - template=other.md --> ## Description Add nft module simulation and unit test,refer #9826 <!-- Add a description of the changes that this PR introduces and the files that are the most critical to review. --> --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
This commit is contained in:
parent
b6d416d871
commit
ba843a351c
|
@ -391,6 +391,7 @@ func NewSimApp(
|
|||
params.NewAppModule(app.ParamsKeeper),
|
||||
evidence.NewAppModule(app.EvidenceKeeper),
|
||||
authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
|
||||
nftmodule.NewAppModule(appCodec, app.NFTKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
|
||||
)
|
||||
|
||||
app.sm.RegisterStoreDecoders()
|
||||
|
|
|
@ -3,6 +3,7 @@ package nft
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
|
@ -15,15 +16,18 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/nft"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft/client/cli"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft/simulation"
|
||||
)
|
||||
|
||||
var (
|
||||
_ module.AppModule = AppModule{}
|
||||
_ module.AppModuleBasic = AppModuleBasic{}
|
||||
_ module.AppModule = AppModule{}
|
||||
_ module.AppModuleBasic = AppModuleBasic{}
|
||||
_ module.AppModuleSimulation = AppModule{}
|
||||
)
|
||||
|
||||
// AppModuleBasic defines the basic application module used by the nft module.
|
||||
|
@ -159,3 +163,37 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {}
|
|||
func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||
return []abci.ValidatorUpdate{}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
|
||||
// AppModuleSimulation functions
|
||||
|
||||
// GenerateGenesisState creates a randomized GenState of the nft module.
|
||||
func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
|
||||
simulation.RandomizedGenState(simState)
|
||||
}
|
||||
|
||||
// ProposalContents returns all the nft content functions used to
|
||||
// simulate governance proposals.
|
||||
func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RandomizedParams creates randomized nft param changes for the simulator.
|
||||
func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterStoreDecoder registers a decoder for nft module's types
|
||||
func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) {
|
||||
sdr[keeper.StoreKey] = simulation.NewDecodeStore(am.cdc)
|
||||
}
|
||||
|
||||
// WeightedOperations returns the all the nft module operations with their respective weights.
|
||||
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
|
||||
return simulation.WeightedOperations(
|
||||
am.registry,
|
||||
simState.AppParams, simState.Cdc,
|
||||
am.accountKeeper, am.bankKeeper, am.keeper,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/kv"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft/keeper"
|
||||
)
|
||||
|
||||
// NewDecodeStore returns a decoder function closure that umarshals the KVPair's
|
||||
// Value to the corresponding nft type.
|
||||
func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string {
|
||||
return func(kvA, kvB kv.Pair) string {
|
||||
switch {
|
||||
case bytes.Equal(kvA.Key[:1], keeper.ClassKey):
|
||||
var classA, classB nft.Class
|
||||
cdc.MustUnmarshal(kvA.Value, &classA)
|
||||
cdc.MustUnmarshal(kvB.Value, &classB)
|
||||
return fmt.Sprintf("%v\n%v", classA, classB)
|
||||
case bytes.Equal(kvA.Key[:1], keeper.NFTKey):
|
||||
var nftA, nftB nft.NFT
|
||||
cdc.MustUnmarshal(kvA.Value, &nftA)
|
||||
cdc.MustUnmarshal(kvB.Value, &nftB)
|
||||
return fmt.Sprintf("%v\n%v", nftA, nftB)
|
||||
case bytes.Equal(kvA.Key[:1], keeper.NFTOfClassByOwnerKey):
|
||||
return fmt.Sprintf("%v\n%v", kvA.Value, kvB.Value)
|
||||
case bytes.Equal(kvA.Key[:1], keeper.OwnerKey):
|
||||
var ownerA, ownerB sdk.AccAddress
|
||||
ownerA = sdk.AccAddress(kvA.Value)
|
||||
ownerB = sdk.AccAddress(kvB.Value)
|
||||
return fmt.Sprintf("%v\n%v", ownerA, ownerB)
|
||||
case bytes.Equal(kvA.Key[:1], keeper.ClassTotalSupply):
|
||||
var supplyA, supplyB uint64
|
||||
supplyA = sdk.BigEndianToUint64(kvA.Value)
|
||||
supplyB = sdk.BigEndianToUint64(kvB.Value)
|
||||
return fmt.Sprintf("%v\n%v", supplyA, supplyB)
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid nft key %X", kvA.Key))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package simulation_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/kv"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft/simulation"
|
||||
)
|
||||
|
||||
var (
|
||||
ownerPk1 = ed25519.GenPrivKey().PubKey()
|
||||
ownerAddr1 = sdk.AccAddress(ownerPk1.Address())
|
||||
)
|
||||
|
||||
func TestDecodeStore(t *testing.T) {
|
||||
cdc := simapp.MakeTestEncodingConfig().Codec
|
||||
dec := simulation.NewDecodeStore(cdc)
|
||||
|
||||
class := nft.Class{
|
||||
Id: "ClassID",
|
||||
Name: "ClassName",
|
||||
Symbol: "ClassSymbol",
|
||||
Description: "ClassDescription",
|
||||
Uri: "ClassURI",
|
||||
}
|
||||
classBz, err := cdc.Marshal(&class)
|
||||
require.NoError(t, err)
|
||||
|
||||
nft := nft.NFT{
|
||||
ClassId: "ClassID",
|
||||
Id: "NFTID",
|
||||
Uri: "NFTURI",
|
||||
}
|
||||
nftBz, err := cdc.Marshal(&nft)
|
||||
require.NoError(t, err)
|
||||
|
||||
nftOfClassByOwnerValue := []byte{0x01}
|
||||
|
||||
totalSupply := 1
|
||||
totalSupplyBz := sdk.Uint64ToBigEndian(1)
|
||||
|
||||
kvPairs := kv.Pairs{
|
||||
Pairs: []kv.Pair{
|
||||
{Key: []byte(keeper.ClassKey), Value: classBz},
|
||||
{Key: []byte(keeper.NFTKey), Value: nftBz},
|
||||
{Key: []byte(keeper.NFTOfClassByOwnerKey), Value: nftOfClassByOwnerValue},
|
||||
{Key: []byte(keeper.OwnerKey), Value: ownerAddr1},
|
||||
{Key: []byte(keeper.ClassTotalSupply), Value: totalSupplyBz},
|
||||
{Key: []byte{0x99}, Value: []byte{0x99}},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
expectErr bool
|
||||
expectedLog string
|
||||
}{
|
||||
{"Class", false, fmt.Sprintf("%v\n%v", class, class)},
|
||||
{"NFT", false, fmt.Sprintf("%v\n%v", nft, nft)},
|
||||
{"NFTOfClassByOwnerKey", false, fmt.Sprintf("%v\n%v", nftOfClassByOwnerValue, nftOfClassByOwnerValue)},
|
||||
{"OwnerKey", false, fmt.Sprintf("%v\n%v", ownerAddr1, ownerAddr1)},
|
||||
{"ClassTotalSupply", false, fmt.Sprintf("%v\n%v", totalSupply, totalSupply)},
|
||||
{"other", true, ""},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
i, tt := i, tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.expectErr {
|
||||
require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name)
|
||||
} else {
|
||||
require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft"
|
||||
)
|
||||
|
||||
// genClasses returns a slice of nft class.
|
||||
func genClasses(r *rand.Rand, accounts []simtypes.Account) []*nft.Class {
|
||||
classes := make([]*nft.Class, len(accounts)-1)
|
||||
for i := 0; i < len(accounts)-1; i++ {
|
||||
classes[i] = &nft.Class{
|
||||
Id: simtypes.RandStringOfLength(r, 10),
|
||||
Name: simtypes.RandStringOfLength(r, 10),
|
||||
Symbol: simtypes.RandStringOfLength(r, 10),
|
||||
Description: simtypes.RandStringOfLength(r, 10),
|
||||
Uri: simtypes.RandStringOfLength(r, 10),
|
||||
}
|
||||
}
|
||||
return classes
|
||||
}
|
||||
|
||||
// genNFT returns a slice of nft.
|
||||
func genNFT(r *rand.Rand, classID string, accounts []simtypes.Account) []*nft.Entry {
|
||||
entries := make([]*nft.Entry, len(accounts)-1)
|
||||
for i := 0; i < len(accounts)-1; i++ {
|
||||
owner := accounts[i]
|
||||
entries[i] = &nft.Entry{
|
||||
Owner: owner.Address.String(),
|
||||
Nfts: []*nft.NFT{
|
||||
{
|
||||
ClassId: classID,
|
||||
Id: simtypes.RandStringOfLength(r, 10),
|
||||
Uri: simtypes.RandStringOfLength(r, 10),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
// RandomizedGenState generates a random GenesisState for nft.
|
||||
func RandomizedGenState(simState *module.SimulationState) {
|
||||
var classes []*nft.Class
|
||||
simState.AppParams.GetOrGenerate(
|
||||
simState.Cdc, "nft", &classes, simState.Rand,
|
||||
func(r *rand.Rand) { classes = genClasses(r, simState.Accounts) },
|
||||
)
|
||||
|
||||
var entries []*nft.Entry
|
||||
simState.AppParams.GetOrGenerate(
|
||||
simState.Cdc, "nft", &entries, simState.Rand,
|
||||
func(r *rand.Rand) {
|
||||
class := classes[r.Int63n(int64(len(classes)))]
|
||||
entries = genNFT(r, class.Id, simState.Accounts)
|
||||
},
|
||||
)
|
||||
|
||||
nftGenesis := &nft.GenesisState{
|
||||
Classes: classes,
|
||||
Entries: entries,
|
||||
}
|
||||
simState.GenState[nft.ModuleName] = simState.Cdc.MustMarshalJSON(nftGenesis)
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package simulation_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft/simulation"
|
||||
)
|
||||
|
||||
func TestRandomizedGenState(t *testing.T) {
|
||||
app := simapp.Setup(t, false)
|
||||
|
||||
s := rand.NewSource(1)
|
||||
r := rand.New(s)
|
||||
|
||||
simState := module.SimulationState{
|
||||
AppParams: make(simtypes.AppParams),
|
||||
Cdc: app.AppCodec(),
|
||||
Rand: r,
|
||||
NumBonded: 3,
|
||||
Accounts: simtypes.RandomAccounts(r, 3),
|
||||
InitialStake: 1000,
|
||||
GenState: make(map[string]json.RawMessage),
|
||||
}
|
||||
|
||||
simulation.RandomizedGenState(&simState)
|
||||
var nftGenesis nft.GenesisState
|
||||
simState.Cdc.MustUnmarshalJSON(simState.GenState[nft.ModuleName], &nftGenesis)
|
||||
|
||||
require.Len(t, nftGenesis.Classes, len(simState.Accounts)-1)
|
||||
require.Len(t, nftGenesis.Entries, len(simState.Accounts)-1)
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
||||
simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/nft/keeper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
const (
|
||||
// Simulation operation weights constants
|
||||
OpWeightMsgSend = "op_weight_msg_send"
|
||||
)
|
||||
|
||||
const (
|
||||
// nft operations weights
|
||||
WeightSend = 100
|
||||
)
|
||||
|
||||
var (
|
||||
TypeMsgSend = sdk.MsgTypeURL(&nft.MsgSend{})
|
||||
)
|
||||
|
||||
// WeightedOperations returns all the operations from the module with their respective weights
|
||||
func WeightedOperations(
|
||||
registry cdctypes.InterfaceRegistry,
|
||||
appParams simtypes.AppParams,
|
||||
cdc codec.JSONCodec,
|
||||
ak nft.AccountKeeper,
|
||||
bk nft.BankKeeper,
|
||||
k keeper.Keeper) simulation.WeightedOperations {
|
||||
|
||||
var (
|
||||
weightMsgSend int
|
||||
)
|
||||
|
||||
appParams.GetOrGenerate(cdc, OpWeightMsgSend, &weightMsgSend, nil,
|
||||
func(_ *rand.Rand) {
|
||||
weightMsgSend = WeightSend
|
||||
},
|
||||
)
|
||||
|
||||
return simulation.WeightedOperations{
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgSend,
|
||||
SimulateMsgSend(codec.NewProtoCodec(registry), ak, bk, k),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgSend generates a MsgSend with random values.
|
||||
func SimulateMsgSend(
|
||||
cdc *codec.ProtoCodec,
|
||||
ak nft.AccountKeeper,
|
||||
bk nft.BankKeeper,
|
||||
k keeper.Keeper) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
sender, _ := simtypes.RandomAcc(r, accs)
|
||||
receiver, _ := simtypes.RandomAcc(r, accs)
|
||||
|
||||
if sender.Address.Equals(receiver.Address) {
|
||||
return simtypes.NoOpMsg(nft.ModuleName, TypeMsgSend, "sender and receiver are same"), nil, nil
|
||||
}
|
||||
|
||||
senderAcc := ak.GetAccount(ctx, sender.Address)
|
||||
spendableCoins := bk.SpendableCoins(ctx, sender.Address)
|
||||
fees, err := simtypes.RandomFees(r, ctx, spendableCoins)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(nft.ModuleName, TypeMsgSend, err.Error()), nil, err
|
||||
}
|
||||
|
||||
spendLimit := spendableCoins.Sub(fees)
|
||||
if spendLimit == nil {
|
||||
return simtypes.NoOpMsg(nft.ModuleName, TypeMsgSend, "spend limit is nil"), nil, nil
|
||||
}
|
||||
|
||||
n, err := randNFT(ctx, r, k, senderAcc.GetAddress())
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(nft.ModuleName, TypeMsgSend, err.Error()), nil, err
|
||||
}
|
||||
|
||||
msg := &nft.MsgSend{
|
||||
ClassId: n.ClassId,
|
||||
Id: n.Id,
|
||||
Sender: senderAcc.GetAddress().String(),
|
||||
Receiver: receiver.Address.String(),
|
||||
}
|
||||
|
||||
txCfg := simappparams.MakeTestEncodingConfig().TxConfig
|
||||
tx, err := helpers.GenTx(
|
||||
txCfg,
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
helpers.DefaultGenTxGas,
|
||||
chainID,
|
||||
[]uint64{senderAcc.GetAccountNumber()},
|
||||
[]uint64{senderAcc.GetSequence()},
|
||||
sender.PrivKey,
|
||||
)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(nft.ModuleName, TypeMsgSend, "unable to generate mock tx"), nil, err
|
||||
}
|
||||
|
||||
_, _, err = app.SimDeliver(txCfg.TxEncoder(), tx)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(nft.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
|
||||
}
|
||||
|
||||
return simtypes.NewOperationMsg(msg, true, "", cdc), nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func randNFT(ctx sdk.Context, r *rand.Rand, k keeper.Keeper, minter sdk.AccAddress) (nft.NFT, error) {
|
||||
c, err := randClass(ctx, r, k)
|
||||
if err != nil {
|
||||
return nft.NFT{}, err
|
||||
}
|
||||
ns := k.GetNFTsOfClassByOwner(ctx, c.Id, minter)
|
||||
if len(ns) > 0 {
|
||||
return ns[r.Intn(len(ns))], nil
|
||||
}
|
||||
|
||||
n := nft.NFT{
|
||||
ClassId: c.Id,
|
||||
Id: simtypes.RandStringOfLength(r, 10),
|
||||
Uri: simtypes.RandStringOfLength(r, 10),
|
||||
}
|
||||
err = k.Mint(ctx, n, minter)
|
||||
if err != nil {
|
||||
return nft.NFT{}, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func randClass(ctx sdk.Context, r *rand.Rand, k keeper.Keeper) (nft.Class, error) {
|
||||
classes := k.GetClasses(ctx)
|
||||
if len(classes) == 0 {
|
||||
c := nft.Class{
|
||||
Id: simtypes.RandStringOfLength(r, 10),
|
||||
Name: simtypes.RandStringOfLength(r, 10),
|
||||
Symbol: simtypes.RandStringOfLength(r, 10),
|
||||
Description: simtypes.RandStringOfLength(r, 10),
|
||||
Uri: simtypes.RandStringOfLength(r, 10),
|
||||
}
|
||||
err := k.SaveClass(ctx, c)
|
||||
if err != nil {
|
||||
return nft.Class{}, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
return *classes[r.Intn(len(classes))], nil
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package simulation_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
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/simapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft"
|
||||
"github.com/cosmos/cosmos-sdk/x/nft/simulation"
|
||||
)
|
||||
|
||||
type SimTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
ctx sdk.Context
|
||||
app *simapp.SimApp
|
||||
}
|
||||
|
||||
func (suite *SimTestSuite) SetupTest() {
|
||||
checkTx := false
|
||||
app := simapp.Setup(suite.T(), checkTx)
|
||||
suite.app = app
|
||||
suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{})
|
||||
}
|
||||
|
||||
func (suite *SimTestSuite) TestWeightedOperations() {
|
||||
weightedOps := simulation.WeightedOperations(
|
||||
suite.app.InterfaceRegistry(),
|
||||
make(simtypes.AppParams),
|
||||
suite.app.AppCodec(),
|
||||
suite.app.AccountKeeper,
|
||||
suite.app.BankKeeper, suite.app.NFTKeeper,
|
||||
)
|
||||
|
||||
// setup 3 accounts
|
||||
s := rand.NewSource(1)
|
||||
r := rand.New(s)
|
||||
accs := suite.getTestingAccounts(r, 3)
|
||||
|
||||
expected := []struct {
|
||||
weight int
|
||||
opMsgRoute string
|
||||
opMsgName string
|
||||
}{
|
||||
{simulation.WeightSend, simulation.TypeMsgSend, simulation.TypeMsgSend},
|
||||
}
|
||||
|
||||
for i, w := range weightedOps {
|
||||
operationMsg, _, _ := w.Op()(r, suite.app.BaseApp, suite.ctx, accs, "")
|
||||
// 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
|
||||
suite.Require().Equal(expected[i].weight, w.Weight(), "weight should be the same")
|
||||
suite.Require().Equal(expected[i].opMsgRoute, operationMsg.Route, "route should be the same")
|
||||
suite.Require().Equal(expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same")
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Account {
|
||||
accounts := simtypes.RandomAccounts(r, n)
|
||||
|
||||
initAmt := suite.app.StakingKeeper.TokensFromConsensusPower(suite.ctx, 200000)
|
||||
initCoins := sdk.NewCoins(sdk.NewCoin("stake", initAmt))
|
||||
|
||||
// add coins to the accounts
|
||||
for _, account := range accounts {
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, account.Address)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
suite.Require().NoError(testutil.FundAccount(suite.app.BankKeeper, suite.ctx, account.Address, initCoins))
|
||||
}
|
||||
|
||||
return accounts
|
||||
}
|
||||
|
||||
func (suite *SimTestSuite) TestSimulateMsgSend() {
|
||||
s := rand.NewSource(1)
|
||||
r := rand.New(s)
|
||||
accounts := suite.getTestingAccounts(r, 2)
|
||||
blockTime := time.Now().UTC()
|
||||
ctx := suite.ctx.WithBlockTime(blockTime)
|
||||
|
||||
// begin a new block
|
||||
suite.app.BeginBlock(abci.RequestBeginBlock{
|
||||
Header: tmproto.Header{
|
||||
Height: suite.app.LastBlockHeight() + 1,
|
||||
AppHash: suite.app.LastCommitID().Hash,
|
||||
},
|
||||
})
|
||||
|
||||
// execute operation
|
||||
registry := suite.app.InterfaceRegistry()
|
||||
op := simulation.SimulateMsgSend(codec.NewProtoCodec(registry), suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.NFTKeeper)
|
||||
operationMsg, futureOperations, err := op(r, suite.app.BaseApp, ctx, accounts, "")
|
||||
suite.Require().NoError(err)
|
||||
|
||||
var msg nft.MsgSend
|
||||
suite.app.AppCodec().UnmarshalJSON(operationMsg.Msg, &msg)
|
||||
suite.Require().True(operationMsg.OK)
|
||||
suite.Require().Len(futureOperations, 0)
|
||||
|
||||
}
|
||||
|
||||
func TestSimTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(SimTestSuite))
|
||||
}
|
Loading…
Reference in New Issue