test: v2 services helpers and demo using x/bank (#23057)

Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com>
This commit is contained in:
Alex | Interchain Labs 2025-01-13 19:45:41 -05:00 committed by GitHub
parent 265cb94e8d
commit b4e88cc517
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 463 additions and 147 deletions

View File

@ -4,21 +4,65 @@ import (
"context"
"cosmossdk.io/core/event"
"cosmossdk.io/core/gas"
"cosmossdk.io/core/header"
"cosmossdk.io/core/store"
"cosmossdk.io/core/transaction"
)
type dummyKey struct{}
func Context() context.Context {
var _ context.Context = &TestContext{}
type TestContext struct {
context.Context
}
func Context() TestContext {
dummy := &dummyCtx{
stores: map[string]store.KVStore{},
events: map[string][]event.Event{},
protoEvents: map[string][]transaction.Msg{},
header: header.Info{},
execMode: transaction.ExecModeFinalize,
gasConfig: gas.GasConfig{},
gasMeter: nil,
}
ctx := context.WithValue(context.Background(), dummyKey{}, dummy)
return ctx
return TestContext{
Context: context.WithValue(context.Background(), dummyKey{}, dummy),
}
}
// WithHeaderInfo sets the header on a testing ctx and returns the updated ctx.
func (t TestContext) WithHeaderInfo(info header.Info) TestContext {
dummy := unwrap(t.Context)
dummy.header = info
return TestContext{
Context: context.WithValue(t.Context, dummyKey{}, dummy),
}
}
// WithExecMode sets the exec mode on a testing ctx and returns the updated ctx.
func (t TestContext) WithExecMode(mode transaction.ExecMode) TestContext {
dummy := unwrap(t.Context)
dummy.execMode = mode
return TestContext{
Context: context.WithValue(t.Context, dummyKey{}, dummy),
}
}
// WithGas sets the gas config and meter on a testing ctx and returns the updated ctx.
func (t TestContext) WithGas(gasConfig gas.GasConfig, gasMeter gas.Meter) TestContext {
dummy := unwrap(t.Context)
dummy.gasConfig = gasConfig
dummy.gasMeter = gasMeter
return TestContext{
Context: context.WithValue(t.Context, dummyKey{}, dummy),
}
}
type dummyCtx struct {
@ -28,6 +72,12 @@ type dummyCtx struct {
events map[string][]event.Event
// maps proto events emitted by the actor.
protoEvents map[string][]transaction.Msg
header header.Info
execMode transaction.ExecMode
gasMeter gas.Meter
gasConfig gas.GasConfig
}
func unwrap(ctx context.Context) *dummyCtx {

View File

@ -0,0 +1,65 @@
package coretesting
import (
"context"
appmodulev2 "cosmossdk.io/core/appmodule/v2"
corecontext "cosmossdk.io/core/context"
corelog "cosmossdk.io/core/log"
"cosmossdk.io/core/router"
"cosmossdk.io/core/store"
)
type TestEnvironmentConfig struct {
ModuleName string
Logger corelog.Logger
MsgRouter router.Service
QueryRouter router.Service
}
type TestEnvironment struct {
appmodulev2.Environment
testEventService TestEventService
testHeaderService TestHeaderService
}
func NewTestEnvironment(cfg TestEnvironmentConfig) (TestContext, TestEnvironment) {
ctx := Context()
testEventService := NewTestEventService(ctx, cfg.ModuleName)
testHeaderService := TestHeaderService{}
env := TestEnvironment{
Environment: appmodulev2.Environment{
Logger: cfg.Logger,
BranchService: nil,
EventService: testEventService,
GasService: TestGasService{},
HeaderService: testHeaderService,
QueryRouterService: cfg.QueryRouter,
MsgRouterService: cfg.MsgRouter,
TransactionService: TestTransactionService{},
KVStoreService: KVStoreService(ctx, cfg.ModuleName),
MemStoreService: nil,
},
testEventService: testEventService,
testHeaderService: testHeaderService,
}
// set internal context to point to environment
ctx.Context = context.WithValue(ctx.Context, corecontext.EnvironmentContextKey, env.Environment)
return ctx, env
}
func (env TestEnvironment) EventService() TestEventService {
return env.testEventService
}
func (env TestEnvironment) KVStoreService() store.KVStoreService {
return env.Environment.KVStoreService
}
func (env TestEnvironment) HeaderService() TestHeaderService {
return env.testHeaderService
}

View File

@ -7,29 +7,29 @@ import (
"cosmossdk.io/core/transaction"
)
var _ event.Service = (*MemEventsService)(nil)
var _ event.Service = &TestEventService{}
// EventsService attaches an event service to the context.
// Adding an existing module will reset the events.
func EventsService(ctx context.Context, moduleName string) MemEventsService {
unwrap(ctx).events[moduleName] = nil
unwrap(ctx).protoEvents[moduleName] = nil
return MemEventsService{moduleName: moduleName}
}
type MemEventsService struct {
type TestEventService struct {
moduleName string
}
func (e MemEventsService) EventManager(ctx context.Context) event.Manager {
// NewTestEventService attaches an event service to the context.
// Adding an existing module will reset the events.
func NewTestEventService(ctx context.Context, moduleName string) TestEventService {
unwrap(ctx).events[moduleName] = nil
unwrap(ctx).protoEvents[moduleName] = nil
return TestEventService{moduleName: moduleName}
}
func (e TestEventService) EventManager(ctx context.Context) event.Manager {
return eventManager{moduleName: e.moduleName, ctx: unwrap(ctx)}
}
func (e MemEventsService) GetEvents(ctx context.Context) []event.Event {
func (e TestEventService) GetEvents(ctx context.Context) []event.Event {
return unwrap(ctx).events[e.moduleName]
}
func (e MemEventsService) GetProtoEvents(ctx context.Context) []transaction.Msg {
func (e TestEventService) GetProtoEvents(ctx context.Context) []transaction.Msg {
return unwrap(ctx).protoEvents[e.moduleName]
}

23
core/testing/gas.go Normal file
View File

@ -0,0 +1,23 @@
package coretesting
import (
"context"
"cosmossdk.io/core/gas"
)
var _ gas.Service = &TestGasService{}
type TestGasService struct{}
func (m TestGasService) GasMeter(ctx context.Context) gas.Meter {
dummy := unwrap(ctx)
return dummy.gasMeter
}
func (m TestGasService) GasConfig(ctx context.Context) gas.GasConfig {
dummy := unwrap(ctx)
return dummy.gasConfig
}

15
core/testing/header.go Normal file
View File

@ -0,0 +1,15 @@
package coretesting
import (
"context"
"cosmossdk.io/core/header"
)
var _ header.Service = &TestHeaderService{}
type TestHeaderService struct{}
func (e TestHeaderService) HeaderInfo(ctx context.Context) header.Info {
return unwrap(ctx).header
}

View File

@ -6,8 +6,14 @@ import (
)
func TestKVStoreService(t *testing.T) {
ctx := Context()
svc1 := KVStoreService(ctx, "bank")
cfg := TestEnvironmentConfig{
ModuleName: "bank",
Logger: nil,
MsgRouter: nil,
QueryRouter: nil,
}
ctx, env := NewTestEnvironment(cfg)
svc1 := env.KVStoreService()
// must panic
t.Run("must panic on invalid ctx", func(t *testing.T) {

View File

@ -0,0 +1,17 @@
package coretesting
import (
"context"
"cosmossdk.io/core/transaction"
)
var _ transaction.Service = &TestTransactionService{}
type TestTransactionService struct{}
func (m TestTransactionService) ExecMode(ctx context.Context) transaction.ExecMode {
dummy := unwrap(ctx)
return dummy.execMode
}

View File

@ -139,7 +139,7 @@ func ListenerMux(listeners ...Listener) Listener {
mux.onBatch = func(batch PacketBatch) error {
for _, listener := range listeners {
err := batch.apply(&listener) //nolint:gosec // aliasing is safe here
err := batch.apply(&listener)
if err != nil {
return err
}

View File

@ -0,0 +1,131 @@
package queryclient
import (
"context"
"fmt"
abci "github.com/cometbft/cometbft/api/cometbft/abci/v1"
gogogrpc "github.com/cosmos/gogoproto/grpc"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"
"github.com/cosmos/cosmos-sdk/client/grpc/reflection"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
)
var (
_ gogogrpc.ClientConn = &QueryHelper{}
_ gogogrpc.Server = &QueryHelper{}
)
// QueryHelper is a test utility for building a query client from a proto interface registry.
type QueryHelper struct {
cdc encoding.Codec
routes map[string]GRPCQueryHandler
}
func NewQueryHelper(interfaceRegistry codectypes.InterfaceRegistry) *QueryHelper {
// instantiate the codec
cdc := codec.NewProtoCodec(interfaceRegistry).GRPCCodec()
// Once we have an interface registry, we can register the interface
// registry reflection gRPC service.
qh := &QueryHelper{
cdc: cdc,
routes: map[string]GRPCQueryHandler{},
}
reflection.RegisterReflectionServiceServer(qh, reflection.NewReflectionServiceServer(interfaceRegistry))
return qh
}
// Invoke implements the grpc ClientConn.Invoke method
func (q *QueryHelper) Invoke(ctx context.Context, method string, args, reply interface{}, _ ...grpc.CallOption) error {
querier := q.Route(method)
if querier == nil {
return fmt.Errorf("handler not found for %s", method)
}
reqBz, err := q.cdc.Marshal(args)
if err != nil {
return err
}
res, err := querier(ctx, &abci.QueryRequest{Data: reqBz})
if err != nil {
return err
}
err = q.cdc.Unmarshal(res.Value, reply)
if err != nil {
return err
}
return nil
}
// NewStream implements the grpc ClientConn.NewStream method
func (q *QueryHelper) NewStream(context.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
panic("not implemented")
}
// GRPCQueryHandler defines a function type which handles ABCI Query requests
// using gRPC
type GRPCQueryHandler = func(ctx context.Context, req *abci.QueryRequest) (*abci.QueryResponse, error)
// Route returns the GRPCQueryHandler for a given query route path or nil
// if not found
func (q *QueryHelper) Route(path string) GRPCQueryHandler {
handler, found := q.routes[path]
if !found {
return nil
}
return handler
}
// RegisterService implements the gRPC Server.RegisterService method. sd is a gRPC
// service description, handler is an object which implements that gRPC service/
//
// This functions PANICS:
// - if a protobuf service is registered twice.
func (q *QueryHelper) RegisterService(sd *grpc.ServiceDesc, handler interface{}) {
// adds a top-level query handler based on the gRPC service name
for _, method := range sd.Methods {
q.registerABCIQueryHandler(sd, method, handler)
}
}
func (q *QueryHelper) registerABCIQueryHandler(sd *grpc.ServiceDesc, method grpc.MethodDesc, handler interface{}) {
fqName := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName)
methodHandler := method.Handler
_, found := q.routes[fqName]
if found {
panic(fmt.Sprintf("handler for %s already registered", fqName))
}
q.routes[fqName] = func(ctx context.Context, req *abci.QueryRequest) (*abci.QueryResponse, error) {
// call the method handler from the service description with the handler object,
// a wrapped sdk.Context with proto-unmarshaled data from the ABCI request data
res, err := methodHandler(handler, ctx, func(i interface{}) error {
return q.cdc.Unmarshal(req.Data, i)
}, nil)
if err != nil {
return nil, err
}
// proto marshal the result bytes
var resBytes []byte
resBytes, err = q.cdc.Marshal(res)
if err != nil {
return nil, err
}
// return the result bytes as the response value
return &abci.QueryResponse{
Height: req.Height,
Value: resBytes,
}, nil
}
}

View File

@ -8,7 +8,7 @@ require (
cosmossdk.io/core v1.0.0
cosmossdk.io/depinject v1.1.0
cosmossdk.io/errors v1.0.1
cosmossdk.io/log v1.5.0 // indirect
cosmossdk.io/log v1.5.0
cosmossdk.io/math v1.5.0
cosmossdk.io/store v1.10.0-rc.1
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
@ -165,7 +165,10 @@ require (
golang.org/x/arch v0.12.0 // indirect
)
replace github.com/cosmos/cosmos-sdk => ../../.
replace (
cosmossdk.io/core/testing => ../../core/testing
github.com/cosmos/cosmos-sdk => ../../.
)
// TODO remove post spinning out all modules
replace cosmossdk.io/x/staking => ../staking

View File

@ -10,8 +10,6 @@ cosmossdk.io/collections v1.0.0 h1:YCYIe/pIMtc1iLDD0OrVdfWCnIkpwdy7k9NSQpaR5mg=
cosmossdk.io/collections v1.0.0/go.mod h1:mFfLxnYT1fV+B3Lx9GLap1qxmffIPqQCND4xBExerps=
cosmossdk.io/core v1.0.0 h1:e7XBbISOytLBOXMVwpRPixThXqEkeLGlg8no/qpgS8U=
cosmossdk.io/core v1.0.0/go.mod h1:mKIp3RkoEmtqdEdFHxHwWAULRe+79gfdOvmArrLDbDc=
cosmossdk.io/core/testing v0.0.1 h1:gYCTaftcRrz+HoNXmK7r9KgbG1jgBJ8pNzm/Pa/erFQ=
cosmossdk.io/core/testing v0.0.1/go.mod h1:2VDNz/25qtxgPa0+j8LW5e8Ev/xObqoJA7QuJS9/wIQ=
cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E=
cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI=
cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0=

View File

@ -1,7 +1,6 @@
package keeper_test
import (
gocontext "context"
"fmt"
"time"
@ -84,7 +83,7 @@ func (suite *KeeperTestSuite) TestQueryBalance() {
for _, tc := range testCases {
suite.Run(tc.name, func() {
res, err := queryClient.Balance(gocontext.Background(), tc.req)
res, err := queryClient.Balance(ctx, tc.req)
if tc.expectErrMsg == "" {
suite.Require().NoError(err)
suite.Require().NotNil(res)
@ -102,7 +101,7 @@ func (suite *KeeperTestSuite) TestQueryBalance() {
func (suite *KeeperTestSuite) TestQueryAllBalances() {
ctx, queryClient := suite.ctx, suite.queryClient
_, _, addr := testdata.KeyTestPubAddr()
_, err := queryClient.AllBalances(gocontext.Background(), &types.QueryAllBalancesRequest{})
_, err := queryClient.AllBalances(ctx, &types.QueryAllBalancesRequest{})
suite.Require().Error(err)
addrStr, err := suite.addrCdc.BytesToString(addr)
@ -114,7 +113,7 @@ func (suite *KeeperTestSuite) TestQueryAllBalances() {
CountTotal: false,
}
req := types.NewQueryAllBalancesRequest(addrStr, pageReq, false)
res, err := queryClient.AllBalances(gocontext.Background(), req)
res, err := queryClient.AllBalances(ctx, req)
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.True(res.Balances.IsZero())
@ -132,7 +131,7 @@ func (suite *KeeperTestSuite) TestQueryAllBalances() {
addIBCMetadata(ctx, suite.bankKeeper)
res, err = queryClient.AllBalances(gocontext.Background(), req)
res, err = queryClient.AllBalances(ctx, req)
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Equal(1, res.Balances.Len())
@ -148,7 +147,7 @@ func (suite *KeeperTestSuite) TestQueryAllBalances() {
}
req = types.NewQueryAllBalancesRequest(addrStr, pageReq, true)
testFunc := func() {
res, err = queryClient.AllBalances(gocontext.Background(), req)
res, err = queryClient.AllBalances(ctx, req)
}
suite.Require().NotPanics(testFunc, "AllBalances with resolve denom + incomplete metadata")
suite.Require().NoError(err)
@ -163,7 +162,7 @@ func (suite *KeeperTestSuite) TestQueryAllBalances() {
CountTotal: true,
}
req = types.NewQueryAllBalancesRequest(addrStr, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), req)
res, err = queryClient.AllBalances(ctx, req)
suite.Require().NoError(err)
suite.Equal(1, res.Balances.Len())
suite.Equal(fooCoins.Denom, res.Balances[0].Denom)
@ -178,7 +177,7 @@ func (suite *KeeperTestSuite) TestQueryAllBalances() {
CountTotal: true,
}
req = types.NewQueryAllBalancesRequest(addrStr, pageReq, false)
res, err = queryClient.AllBalances(gocontext.Background(), req)
res, err = queryClient.AllBalances(ctx, req)
suite.Require().NoError(err)
suite.Equal(1, res.Balances.Len())
suite.Equal(ibcCoins.Denom, res.Balances[0].Denom)
@ -190,7 +189,7 @@ func (suite *KeeperTestSuite) TestQueryAllBalances() {
CountTotal: true,
}
req = types.NewQueryAllBalancesRequest(addrStr, pageReq, true)
res, err = queryClient.AllBalances(gocontext.Background(), req)
res, err = queryClient.AllBalances(ctx, req)
suite.Require().NoError(err)
suite.Equal(1, res.Balances.Len())
suite.Equal(ibcPath+"/"+ibcBaseDenom, res.Balances[0].Denom)
@ -202,9 +201,9 @@ func (suite *KeeperTestSuite) TestSpendableBalances() {
addrStr, err := suite.addrCdc.BytesToString(addr)
suite.Require().NoError(err)
ctx := sdk.UnwrapSDKContext(suite.ctx)
ctx := suite.ctx
ctx = ctx.WithHeaderInfo(header.Info{Time: time.Now()})
queryClient := suite.mockQueryClient(ctx)
queryClient := suite.queryClient
_, err = queryClient.SpendableBalances(ctx, &types.QuerySpendableBalancesRequest{})
suite.Require().Error(err)
@ -226,12 +225,14 @@ func (suite *KeeperTestSuite) TestSpendableBalances() {
fooCoins := newFooCoin(50)
barCoins := newBarCoin(30)
currentBlockTime := suite.env.HeaderService().HeaderInfo(ctx).Time
origCoins := sdk.NewCoins(fooCoins, barCoins)
vacc, err := vestingtypes.NewContinuousVestingAccount(
acc,
sdk.NewCoins(fooCoins),
ctx.HeaderInfo().Time.Unix(),
ctx.HeaderInfo().Time.Add(time.Hour).Unix(),
currentBlockTime.Unix(),
currentBlockTime.Add(time.Hour).Unix(),
)
suite.Require().NoError(err)
@ -239,8 +240,8 @@ func (suite *KeeperTestSuite) TestSpendableBalances() {
suite.Require().NoError(testutil.FundAccount(suite.ctx, suite.bankKeeper, addr, origCoins))
// move time forward for some tokens to vest
ctx = ctx.WithHeaderInfo(header.Info{Time: ctx.HeaderInfo().Time.Add(30 * time.Minute)})
queryClient = suite.mockQueryClient(ctx)
ctx = ctx.WithHeaderInfo(header.Info{Time: currentBlockTime.Add(30 * time.Minute)})
queryClient = suite.queryClient
suite.mockSpendableCoins(ctx, vacc)
res, err = queryClient.SpendableBalances(ctx, req)
@ -254,10 +255,10 @@ func (suite *KeeperTestSuite) TestSpendableBalances() {
func (suite *KeeperTestSuite) TestSpendableBalanceByDenom() {
_, _, addr := testdata.KeyTestPubAddr()
ctx := suite.ctx
ctx := sdk.UnwrapSDKContext(suite.ctx)
ctx = ctx.WithHeaderInfo(header.Info{Time: time.Now()})
queryClient := suite.mockQueryClient(ctx)
queryClient := suite.queryClient
_, err := queryClient.SpendableBalanceByDenom(ctx, &types.QuerySpendableBalanceByDenomRequest{})
suite.Require().Error(err)
@ -277,12 +278,14 @@ func (suite *KeeperTestSuite) TestSpendableBalanceByDenom() {
fooCoins := newFooCoin(100)
barCoins := newBarCoin(30)
currentBlockTime := suite.env.HeaderService().HeaderInfo(ctx).Time
origCoins := sdk.NewCoins(fooCoins, barCoins)
vacc, err := vestingtypes.NewContinuousVestingAccount(
acc,
sdk.NewCoins(fooCoins),
ctx.HeaderInfo().Time.Unix(),
ctx.HeaderInfo().Time.Add(time.Hour).Unix(),
currentBlockTime.Unix(),
currentBlockTime.Add(time.Hour).Unix(),
)
suite.Require().NoError(err)
@ -290,8 +293,8 @@ func (suite *KeeperTestSuite) TestSpendableBalanceByDenom() {
suite.Require().NoError(testutil.FundAccount(suite.ctx, suite.bankKeeper, addr, origCoins))
// move time forward for half of the tokens to vest
ctx = ctx.WithHeaderInfo(header.Info{Time: ctx.HeaderInfo().Time.Add(30 * time.Minute)})
queryClient = suite.mockQueryClient(ctx)
ctx = ctx.WithHeaderInfo(header.Info{Time: currentBlockTime.Add(30 * time.Minute)})
queryClient = suite.queryClient
// check fooCoins first, it has some vested and some vesting
suite.mockSpendableCoins(ctx, vacc)
@ -311,7 +314,7 @@ func (suite *KeeperTestSuite) TestSpendableBalanceByDenom() {
func (suite *KeeperTestSuite) TestQueryTotalSupply() {
ctx, queryClient := suite.ctx, suite.queryClient
res, err := queryClient.TotalSupply(gocontext.Background(), &types.QueryTotalSupplyRequest{})
res, err := queryClient.TotalSupply(ctx, &types.QueryTotalSupplyRequest{})
suite.Require().NoError(err)
suite.Require().NotNil(res)
genesisSupply := res.Supply
@ -320,7 +323,7 @@ func (suite *KeeperTestSuite) TestQueryTotalSupply() {
suite.mockMintCoins(mintAcc)
suite.Require().NoError(suite.bankKeeper.MintCoins(ctx, types.MintModuleName, testCoins))
res, err = queryClient.TotalSupply(gocontext.Background(), &types.QueryTotalSupplyRequest{})
res, err = queryClient.TotalSupply(ctx, &types.QueryTotalSupplyRequest{})
suite.Require().NoError(err)
suite.Require().NotNil(res)
@ -339,32 +342,33 @@ func (suite *KeeperTestSuite) TestQueryTotalSupplyOf() {
suite.mockMintCoins(mintAcc)
suite.Require().NoError(suite.bankKeeper.MintCoins(ctx, types.MintModuleName, expectedTotalSupply))
_, err := queryClient.SupplyOf(gocontext.Background(), &types.QuerySupplyOfRequest{})
_, err := queryClient.SupplyOf(ctx, &types.QuerySupplyOfRequest{})
suite.Require().Error(err)
res, err := queryClient.SupplyOf(gocontext.Background(), &types.QuerySupplyOfRequest{Denom: test1Supply.Denom})
res, err := queryClient.SupplyOf(ctx, &types.QuerySupplyOfRequest{Denom: test1Supply.Denom})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(test1Supply, res.Amount)
// total supply bogus denom
res, err = queryClient.SupplyOf(gocontext.Background(), &types.QuerySupplyOfRequest{Denom: "bogus"})
res, err = queryClient.SupplyOf(ctx, &types.QuerySupplyOfRequest{Denom: "bogus"})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(sdk.NewInt64Coin("bogus", 0), res.Amount)
}
func (suite *KeeperTestSuite) TestQueryParams() {
res, err := suite.queryClient.Params(gocontext.Background(), &types.QueryParamsRequest{})
ctx, queryClient := suite.ctx, suite.queryClient
res, err := queryClient.Params(ctx, &types.QueryParamsRequest{})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(suite.bankKeeper.GetParams(suite.ctx), res.GetParams())
suite.Require().Equal(suite.bankKeeper.GetParams(ctx), res.GetParams())
}
func (suite *KeeperTestSuite) TestQueryDenomsMetadata() {
var (
req *types.QueryDenomsMetadataRequest
expMetadata = []types.Metadata(nil)
expMetadata []types.Metadata
)
testCases := []struct {
@ -781,7 +785,7 @@ func (suite *KeeperTestSuite) TestGRPCDenomOwners() {
for name, tc := range testCases {
suite.Run(name, func() {
resp, err := suite.queryClient.DenomOwners(gocontext.Background(), tc.req)
resp, err := suite.queryClient.DenomOwners(suite.ctx, tc.req)
if tc.expPass {
suite.NoError(err)
suite.NotNil(resp)
@ -913,7 +917,7 @@ func (suite *KeeperTestSuite) TestQuerySendEnabled() {
for _, tc := range tests {
suite.Run(tc.name, func() {
resp, err := suite.queryClient.SendEnabled(gocontext.Background(), tc.req)
resp, err := suite.queryClient.SendEnabled(ctx, tc.req)
suite.Require().NoError(err)
if !suite.Assert().Equal(tc.exp, resp) {
if !suite.Assert().Len(resp.SendEnabled, len(tc.exp.SendEnabled)) {
@ -1019,7 +1023,7 @@ func (suite *KeeperTestSuite) TestGRPCDenomOwnersByQuery() {
for name, tc := range testCases {
suite.Run(name, func() {
resp, err := suite.queryClient.DenomOwnersByQuery(gocontext.Background(), tc.req)
resp, err := suite.queryClient.DenomOwnersByQuery(suite.ctx, tc.req)
if tc.expPass {
suite.NoError(err)
suite.NotNil(resp)

View File

@ -18,16 +18,16 @@ import (
"cosmossdk.io/core/header"
coretesting "cosmossdk.io/core/testing"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/bank/keeper"
banktestutil "cosmossdk.io/x/bank/testutil"
banktypes "cosmossdk.io/x/bank/types"
"github.com/cosmos/cosmos-sdk/baseapp"
codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/testutil/queryclient"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
@ -133,7 +133,8 @@ func addIncompleteMetadata(ctx context.Context, k keeper.BaseKeeper) {
type KeeperTestSuite struct {
suite.Suite
ctx context.Context
ctx coretesting.TestContext
env coretesting.TestEnvironment
bankKeeper keeper.BaseKeeper
addrCdc address.Codec
authKeeper *banktestutil.MockAccountKeeper
@ -149,12 +150,16 @@ func TestKeeperTestSuite(t *testing.T) {
}
func (suite *KeeperTestSuite) SetupTest() {
key := storetypes.NewKVStoreKey(banktypes.StoreKey)
testCtx := testutil.DefaultContextWithDB(suite.T(), key, storetypes.NewTransientStoreKey("transient_test"))
ctx := testCtx.Ctx.WithHeaderInfo(header.Info{Time: time.Now()})
encCfg := moduletestutil.MakeTestEncodingConfig(codectestutil.CodecOptions{})
testEnironmentConfig := coretesting.TestEnvironmentConfig{
ModuleName: banktypes.ModuleName,
Logger: log.NewNopLogger(),
MsgRouter: nil,
QueryRouter: nil,
}
env := runtime.NewEnvironment(runtime.NewKVStoreService(key), coretesting.NewNopLogger())
ctx, env := coretesting.NewTestEnvironment(testEnironmentConfig)
ctx = ctx.WithHeaderInfo(header.Info{Time: time.Now()})
ac := codectestutil.CodecOptions{}.GetAddressCodec()
addr, err := ac.BytesToString(accAddrs[4])
@ -170,7 +175,7 @@ func (suite *KeeperTestSuite) SetupTest() {
suite.authKeeper = authKeeper
suite.addrCdc = ac
suite.bankKeeper = keeper.NewBaseKeeper(
env,
env.Environment,
encCfg.Codec,
suite.authKeeper,
map[string]bool{addr: true},
@ -181,21 +186,14 @@ func (suite *KeeperTestSuite) SetupTest() {
DefaultSendEnabled: banktypes.DefaultDefaultSendEnabled,
}))
queryHelper := queryclient.NewQueryHelper(encCfg.InterfaceRegistry)
banktypes.RegisterQueryServer(queryHelper, suite.bankKeeper)
banktypes.RegisterInterfaces(encCfg.InterfaceRegistry)
queryHelper := baseapp.NewQueryServerTestHelper(ctx, encCfg.InterfaceRegistry)
banktypes.RegisterQueryServer(queryHelper, suite.bankKeeper)
queryClient := banktypes.NewQueryClient(queryHelper)
suite.queryClient = queryClient
suite.queryClient = banktypes.NewQueryClient(queryHelper)
suite.msgServer = keeper.NewMsgServerImpl(suite.bankKeeper)
suite.encCfg = encCfg
}
func (suite *KeeperTestSuite) mockQueryClient(ctx sdk.Context) banktypes.QueryClient {
queryHelper := baseapp.NewQueryServerTestHelper(ctx, suite.encCfg.InterfaceRegistry)
banktypes.RegisterQueryServer(queryHelper, suite.bankKeeper)
return banktypes.NewQueryClient(queryHelper)
suite.env = env
}
func (suite *KeeperTestSuite) mockMintCoins(moduleAcc *authtypes.ModuleAccount) {
@ -241,7 +239,7 @@ func (suite *KeeperTestSuite) mockValidateBalance(acc sdk.AccountI) {
suite.authKeeper.EXPECT().GetAccount(suite.ctx, acc.GetAddress()).Return(acc)
}
func (suite *KeeperTestSuite) mockSpendableCoins(ctx sdk.Context, acc sdk.AccountI) {
func (suite *KeeperTestSuite) mockSpendableCoins(ctx context.Context, acc sdk.AccountI) {
suite.authKeeper.EXPECT().GetAccount(ctx, acc.GetAddress()).Return(acc)
}
@ -1410,21 +1408,24 @@ func (suite *KeeperTestSuite) TestMsgSendEvents() {
},
}
ctx := sdk.UnwrapSDKContext(suite.ctx)
events := suite.env.EventService().GetEvents(suite.ctx)
// events are shifted due to the funding account events
events := ctx.EventManager().Events()
require.Equal(8, len(events))
require.Equal(event1.Type, events[7].Type)
attrs, err := event1.Attributes()
attrs1, err := event1.Attributes()
require.NoError(err)
for i := range attrs {
require.Equal(attrs[i].Key, events[7].Attributes[i].Key)
require.Equal(attrs[i].Value, events[7].Attributes[i].Value)
attrs, err := events[7].Attributes()
require.NoError(err)
for i := range attrs1 {
require.Equal(attrs1[i].Key, attrs[i].Key)
require.Equal(attrs1[i].Value, attrs[i].Value)
}
}
func (suite *KeeperTestSuite) TestMsgMultiSendEvents() {
ctx := sdk.UnwrapSDKContext(suite.ctx)
ctx := suite.ctx
require := suite.Require()
acc0 := authtypes.NewBaseAccountWithAddress(accAddrs[0])
@ -1452,7 +1453,7 @@ func (suite *KeeperTestSuite) TestMsgMultiSendEvents() {
suite.authKeeper.EXPECT().GetAccount(suite.ctx, accAddrs[0]).Return(acc0)
require.Error(suite.bankKeeper.InputOutputCoins(ctx, input, outputs))
events := ctx.EventManager().ABCIEvents()
events := suite.env.EventService().GetEvents(suite.ctx)
require.Equal(0, len(events))
// Set addr's coins but not accAddrs[1]'s coins
@ -1462,7 +1463,7 @@ func (suite *KeeperTestSuite) TestMsgMultiSendEvents() {
suite.mockInputOutputCoins([]sdk.AccountI{acc0}, accAddrs[2:4])
require.NoError(suite.bankKeeper.InputOutputCoins(ctx, input, outputs))
events = ctx.EventManager().ABCIEvents()
events = suite.env.EventService().GetEvents(suite.ctx)
require.Equal(10, len(events)) // 10 events because account funding causes extra minting + coin_spent + coin_recv events
// Set addr's coins and accAddrs[1]'s coins
@ -1477,7 +1478,7 @@ func (suite *KeeperTestSuite) TestMsgMultiSendEvents() {
suite.mockInputOutputCoins([]sdk.AccountI{acc0}, accAddrs[2:4])
require.NoError(suite.bankKeeper.InputOutputCoins(ctx, input, outputs))
events = ctx.EventManager().ABCIEvents()
events = suite.env.EventService().GetEvents(suite.ctx)
require.Equal(25, len(events)) // 25 due to account funding + coin_spent + coin_recv events
event1 := coreevent.Event{
@ -1506,21 +1507,29 @@ func (suite *KeeperTestSuite) TestMsgMultiSendEvents() {
require.Equal(event1.Type, events[22].Type)
attrs1, err := event1.Attributes()
require.NoError(err)
attrs, err := events[22].Attributes()
require.NoError(err)
for i := range attrs1 {
require.Equal(attrs1[i].Key, events[22].Attributes[i].Key)
require.Equal(attrs1[i].Value, events[22].Attributes[i].Value)
require.Equal(attrs1[i].Key, attrs[i].Key)
require.Equal(attrs1[i].Value, attrs[i].Value)
}
require.Equal(event2.Type, events[24].Type)
attrs2, err := event2.Attributes()
require.NoError(err)
attrs, err = events[24].Attributes()
require.NoError(err)
for i := range attrs2 {
require.Equal(attrs2[i].Key, events[24].Attributes[i].Key)
require.Equal(attrs2[i].Value, events[24].Attributes[i].Value)
require.Equal(attrs2[i].Key, attrs[i].Key)
require.Equal(attrs2[i].Value, attrs[i].Value)
}
}
func (suite *KeeperTestSuite) TestSpendableCoins() {
ctx := sdk.UnwrapSDKContext(suite.ctx)
ctx := suite.ctx
require := suite.Require()
now := time.Now()
endTime := now.Add(24 * time.Hour)
@ -1560,7 +1569,7 @@ func (suite *KeeperTestSuite) TestSpendableCoins() {
suite.Require().NoError(err)
// Go back to the suite's context since mockFundAccount uses that; FundAccount would fail for bad mocking otherwise.
ctx = sdk.UnwrapSDKContext(suite.ctx)
ctx = suite.ctx
suite.mockFundAccount(accAddrs[2])
require.NoError(banktestutil.FundAccount(ctx, suite.bankKeeper, accAddrs[2], balanceCoins2))
suite.mockSpendableCoins(ctx, vacc2)
@ -1576,7 +1585,7 @@ func (suite *KeeperTestSuite) TestSpendableCoins() {
}
func (suite *KeeperTestSuite) TestVestingAccountSend() {
ctx := sdk.UnwrapSDKContext(suite.ctx)
ctx := suite.ctx
require := suite.Require()
now := time.Now()
endTime := now.Add(24 * time.Hour)
@ -1606,7 +1615,7 @@ func (suite *KeeperTestSuite) TestVestingAccountSend() {
}
func (suite *KeeperTestSuite) TestPeriodicVestingAccountSend() {
ctx := sdk.UnwrapSDKContext(suite.ctx)
ctx := suite.ctx
require := suite.Require()
now := time.Now()
origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100))
@ -1641,7 +1650,7 @@ func (suite *KeeperTestSuite) TestPeriodicVestingAccountSend() {
}
func (suite *KeeperTestSuite) TestVestingAccountReceive() {
ctx := sdk.UnwrapSDKContext(suite.ctx)
ctx := suite.ctx
require := suite.Require()
now := time.Now()
endTime := now.Add(24 * time.Hour)
@ -1674,7 +1683,7 @@ func (suite *KeeperTestSuite) TestVestingAccountReceive() {
}
func (suite *KeeperTestSuite) TestPeriodicVestingAccountReceive() {
ctx := sdk.UnwrapSDKContext(suite.ctx)
ctx := suite.ctx
require := suite.Require()
now := time.Now()
@ -1712,7 +1721,7 @@ func (suite *KeeperTestSuite) TestPeriodicVestingAccountReceive() {
}
func (suite *KeeperTestSuite) TestDelegateCoins() {
ctx := sdk.UnwrapSDKContext(suite.ctx)
ctx := suite.ctx
require := suite.Require()
now := time.Now()
endTime := now.Add(24 * time.Hour)
@ -1722,7 +1731,7 @@ func (suite *KeeperTestSuite) TestDelegateCoins() {
acc0 := authtypes.NewBaseAccountWithAddress(accAddrs[0])
acc1 := authtypes.NewBaseAccountWithAddress(accAddrs[1])
vacc, err := vesting.NewContinuousVestingAccount(acc0, origCoins, ctx.HeaderInfo().Time.Unix(), endTime.Unix())
vacc, err := vesting.NewContinuousVestingAccount(acc0, origCoins, suite.env.HeaderService().HeaderInfo(suite.ctx).Time.Unix(), endTime.Unix())
suite.Require().NoError(err)
suite.mockFundAccount(accAddrs[0])
@ -1770,7 +1779,7 @@ func (suite *KeeperTestSuite) TestDelegateCoins_Invalid() {
}
func (suite *KeeperTestSuite) TestUndelegateCoins() {
ctx := sdk.UnwrapSDKContext(suite.ctx)
ctx := suite.ctx
require := suite.Require()
now := time.Now()
endTime := now.Add(24 * time.Hour)
@ -1780,7 +1789,7 @@ func (suite *KeeperTestSuite) TestUndelegateCoins() {
acc0 := authtypes.NewBaseAccountWithAddress(accAddrs[0])
acc1 := authtypes.NewBaseAccountWithAddress(accAddrs[1])
vacc, err := vesting.NewContinuousVestingAccount(acc0, origCoins, ctx.BlockHeader().Time.Unix(), endTime.Unix())
vacc, err := vesting.NewContinuousVestingAccount(acc0, origCoins, suite.env.HeaderService().HeaderInfo(ctx).Time.Unix(), endTime.Unix())
suite.Require().NoError(err)
suite.mockFundAccount(accAddrs[0])
@ -1938,34 +1947,36 @@ func (suite *KeeperTestSuite) TestBalanceTrackingEvents() {
balances := make(map[string]sdk.Coins)
ctx := sdk.UnwrapSDKContext(suite.ctx)
events := suite.env.EventService().GetEvents(suite.ctx)
for _, e := range ctx.EventManager().ABCIEvents() {
for _, e := range events {
attributes, err := e.Attributes()
suite.Require().NoError(err)
switch e.Type {
case banktypes.EventTypeCoinBurn:
burnedCoins, err := sdk.ParseCoinsNormalized(e.Attributes[1].Value)
burnedCoins, err := sdk.ParseCoinsNormalized(attributes[1].Value)
require.NoError(err)
supply = supply.Sub(burnedCoins...)
case banktypes.EventTypeCoinMint:
mintedCoins, err := sdk.ParseCoinsNormalized(e.Attributes[1].Value)
mintedCoins, err := sdk.ParseCoinsNormalized(attributes[1].Value)
require.NoError(err)
supply = supply.Add(mintedCoins...)
case banktypes.EventTypeCoinSpent:
coinsSpent, err := sdk.ParseCoinsNormalized(e.Attributes[1].Value)
coinsSpent, err := sdk.ParseCoinsNormalized(attributes[1].Value)
require.NoError(err)
_, err = suite.addrCdc.StringToBytes(e.Attributes[0].Value)
_, err = suite.addrCdc.StringToBytes(attributes[0].Value)
require.NoError(err)
balances[e.Attributes[0].Value] = balances[e.Attributes[0].Value].Sub(coinsSpent...)
balances[attributes[0].Value] = balances[attributes[0].Value].Sub(coinsSpent...)
case banktypes.EventTypeCoinReceived:
coinsRecv, err := sdk.ParseCoinsNormalized(e.Attributes[1].Value)
coinsRecv, err := sdk.ParseCoinsNormalized(attributes[1].Value)
require.NoError(err)
_, err = suite.addrCdc.StringToBytes(e.Attributes[0].Value)
_, err = suite.addrCdc.StringToBytes(attributes[0].Value)
require.NoError(err)
balances[e.Attributes[0].Value] = balances[e.Attributes[0].Value].Add(coinsRecv...)
balances[attributes[0].Value] = balances[attributes[0].Value].Add(coinsRecv...)
}
}

View File

@ -83,7 +83,7 @@ func (s *MintingRestrictionTestHelper) TestActual(t *testing.T, tp *MintingRestr
} else {
require.NotNil(t, actual, "resulting MintingRestrictionFn")
s.Calls = s.Calls[:0]
err := actual(sdk.Context{}, tp.Coins)
err := actual(context.Background(), tp.Coins)
if len(tp.ExpErr) != 0 {
assert.EqualError(t, err, tp.ExpErr, "composite MintingRestrictionFn output error")
} else {
@ -389,7 +389,7 @@ func TestComposeMintingRestrictions(t *testing.T) {
func TestNoOpMintingRestrictionFn(t *testing.T) {
var err error
testFunc := func() {
err = types.NoOpMintingRestrictionFn(sdk.Context{}, sdk.Coins{})
err = types.NoOpMintingRestrictionFn(context.Background(), sdk.Coins{})
}
require.NotPanics(t, testFunc, "NoOpMintingRestrictionFn")
assert.NoError(t, err, "NoOpSendRestrictionFn error")
@ -483,7 +483,7 @@ func (s *SendRestrictionTestHelper) TestActual(t *testing.T, tp *SendRestriction
} else {
require.NotNil(t, actual, "resulting SendRestrictionFn")
s.Calls = s.Calls[:0]
addr, err := actual(sdk.Context{}, tp.FromAddr, tp.ToAddr, tp.Coins)
addr, err := actual(context.Background(), tp.FromAddr, tp.ToAddr, tp.Coins)
if len(tp.ExpErr) != 0 {
assert.EqualError(t, err, tp.ExpErr, "composite SendRestrictionFn output error")
} else {
@ -912,7 +912,7 @@ func TestNoOpSendRestrictionFn(t *testing.T) {
var addr sdk.AccAddress
var err error
testFunc := func() {
addr, err = types.NoOpSendRestrictionFn(sdk.Context{}, sdk.AccAddress("first_addr"), expAddr, sdk.Coins{})
addr, err = types.NoOpSendRestrictionFn(context.Background(), sdk.AccAddress("first_addr"), expAddr, sdk.Coins{})
}
require.NotPanics(t, testFunc, "NoOpSendRestrictionFn")
assert.NoError(t, err, "NoOpSendRestrictionFn error")

View File

@ -1,22 +1,18 @@
package types_test
import (
"context"
"fmt"
"testing"
"github.com/stretchr/testify/require"
appmodulev2 "cosmossdk.io/core/appmodule/v2"
corecontext "cosmossdk.io/core/context"
coregas "cosmossdk.io/core/gas"
coreheader "cosmossdk.io/core/header"
coretesting "cosmossdk.io/core/testing"
"cosmossdk.io/log"
sdkmath "cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/bank/types"
codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -29,35 +25,23 @@ var (
unknownAddrStr = "cosmos1ta047h6lw4hxkmn0wah97h6lta0sml880l"
)
type headerService struct{}
func (h headerService) HeaderInfo(ctx context.Context) coreheader.Info {
return sdk.UnwrapSDKContext(ctx).HeaderInfo()
}
type mockGasService struct {
coregas.Service
}
func (m mockGasService) GasMeter(ctx context.Context) coregas.Meter {
return mockGasMeter{}
}
type mockGasMeter struct {
coregas.Meter
}
func (m mockGasMeter) Consume(amount coregas.Gas, descriptor string) error {
func (m mockGasMeter) Consume(_ coregas.Gas, _ string) error {
return nil
}
func TestSendAuthorization(t *testing.T) {
ac := codectestutil.CodecOptions{}.GetAddressCodec()
sdkCtx := testutil.DefaultContextWithDB(t, storetypes.NewKVStoreKey(types.StoreKey), storetypes.NewTransientStoreKey("transient_test")).Ctx.WithHeaderInfo(coreheader.Info{})
ctx := context.WithValue(sdkCtx.Context(), corecontext.EnvironmentContextKey, appmodulev2.Environment{
HeaderService: headerService{},
GasService: mockGasService{},
})
cfg := coretesting.TestEnvironmentConfig{
ModuleName: "bank",
Logger: log.NewNopLogger(),
}
ctx, _ := coretesting.NewTestEnvironment(cfg)
ctx = ctx.WithGas(coregas.GasConfig{}, mockGasMeter{})
allowList := make([]sdk.AccAddress, 1)
allowList[0] = toAddr

View File

@ -9,6 +9,7 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
appmodulev2 "cosmossdk.io/core/appmodule/v2"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/x/bank/v2/types"
@ -25,7 +26,20 @@ func NewHandlers(k *Keeper) handlers {
return handlers{k}
}
// UpdateParams updates the parameters of the bank/v2 module.
// RegisterMsgHandlers registers the message handlers to the router.
func (h handlers) RegisterMsgHandlers(router appmodulev2.MsgRouter) {
appmodulev2.RegisterMsgHandler(router, h.MsgUpdateParams)
appmodulev2.RegisterMsgHandler(router, h.MsgSend)
appmodulev2.RegisterMsgHandler(router, h.MsgMint)
}
// RegisterQueryHandlers registers the query handlers to the router.
func (h handlers) RegisterQueryHandlers(router appmodulev2.QueryRouter) {
appmodulev2.RegisterMsgHandler(router, h.QueryParams)
appmodulev2.RegisterMsgHandler(router, h.QueryBalance)
}
// MsgUpdateParams updates the parameters of the bank/v2 module.
func (h handlers) MsgUpdateParams(ctx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
authorityBytes, err := h.addressCodec.StringToBytes(msg.Authority)
if err != nil {

View File

@ -97,18 +97,13 @@ func (am AppModule) ExportGenesis(ctx context.Context) (json.RawMessage, error)
// RegisterMsgHandlers registers the message handlers for the bank module.
func (am AppModule) RegisterMsgHandlers(router appmodulev2.MsgRouter) {
handlers := keeper.NewHandlers(am.keeper)
appmodulev2.RegisterMsgHandler(router, handlers.MsgUpdateParams)
appmodulev2.RegisterMsgHandler(router, handlers.MsgSend)
appmodulev2.RegisterMsgHandler(router, handlers.MsgMint)
handlers.RegisterMsgHandlers(router)
}
// RegisterQueryHandlers registers the query handlers for the bank module.
func (am AppModule) RegisterQueryHandlers(router appmodulev2.QueryRouter) {
handlers := keeper.NewHandlers(am.keeper)
appmodulev2.RegisterMsgHandler(router, handlers.QueryParams)
appmodulev2.RegisterMsgHandler(router, handlers.QueryBalance)
handlers.RegisterQueryHandlers(router)
}
// GetTxCmd returns the root tx command for the bank/v2 module.