github.com/cosmos/cosmos-sdk@v0.50.10/testutil/integration/router.go (about) 1 package integration 2 3 import ( 4 "context" 5 "fmt" 6 7 cmtabcitypes "github.com/cometbft/cometbft/abci/types" 8 cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" 9 dbm "github.com/cosmos/cosmos-db" 10 11 "cosmossdk.io/core/appmodule" 12 "cosmossdk.io/log" 13 "cosmossdk.io/store" 14 "cosmossdk.io/store/metrics" 15 storetypes "cosmossdk.io/store/types" 16 17 "github.com/cosmos/cosmos-sdk/baseapp" 18 "github.com/cosmos/cosmos-sdk/codec" 19 codectypes "github.com/cosmos/cosmos-sdk/codec/types" 20 "github.com/cosmos/cosmos-sdk/runtime" 21 simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" 22 sdk "github.com/cosmos/cosmos-sdk/types" 23 "github.com/cosmos/cosmos-sdk/types/module" 24 authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" 25 authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 26 consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" 27 consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" 28 ) 29 30 const appName = "integration-app" 31 32 // App is a test application that can be used to test the integration of modules. 33 type App struct { 34 *baseapp.BaseApp 35 36 ctx sdk.Context 37 logger log.Logger 38 moduleManager module.Manager 39 queryHelper *baseapp.QueryServiceTestHelper 40 } 41 42 // NewIntegrationApp creates an application for testing purposes. This application 43 // is able to route messages to their respective handlers. 44 func NewIntegrationApp( 45 sdkCtx sdk.Context, 46 logger log.Logger, 47 keys map[string]*storetypes.KVStoreKey, 48 appCodec codec.Codec, 49 modules map[string]appmodule.AppModule, 50 baseAppOptions ...func(*baseapp.BaseApp), 51 ) *App { 52 db := dbm.NewMemDB() 53 54 interfaceRegistry := codectypes.NewInterfaceRegistry() 55 moduleManager := module.NewManagerFromMap(modules) 56 basicModuleManager := module.NewBasicManagerFromManager(moduleManager, nil) 57 basicModuleManager.RegisterInterfaces(interfaceRegistry) 58 59 txConfig := authtx.NewTxConfig(codec.NewProtoCodec(interfaceRegistry), authtx.DefaultSignModes) 60 bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), append(baseAppOptions, baseapp.SetChainID(appName))...) 61 bApp.MountKVStores(keys) 62 63 bApp.SetInitChainer(func(_ sdk.Context, _ *cmtabcitypes.RequestInitChain) (*cmtabcitypes.ResponseInitChain, error) { 64 for _, mod := range modules { 65 if m, ok := mod.(module.HasGenesis); ok { 66 m.InitGenesis(sdkCtx, appCodec, m.DefaultGenesis(appCodec)) 67 } 68 } 69 70 return &cmtabcitypes.ResponseInitChain{}, nil 71 }) 72 73 bApp.SetBeginBlocker(func(_ sdk.Context) (sdk.BeginBlock, error) { 74 return moduleManager.BeginBlock(sdkCtx) 75 }) 76 bApp.SetEndBlocker(func(_ sdk.Context) (sdk.EndBlock, error) { 77 return moduleManager.EndBlock(sdkCtx) 78 }) 79 80 router := baseapp.NewMsgServiceRouter() 81 router.SetInterfaceRegistry(interfaceRegistry) 82 bApp.SetMsgServiceRouter(router) 83 84 if keys[consensusparamtypes.StoreKey] != nil { 85 // set baseApp param store 86 consensusParamsKeeper := consensusparamkeeper.NewKeeper(appCodec, runtime.NewKVStoreService(keys[consensusparamtypes.StoreKey]), authtypes.NewModuleAddress("gov").String(), runtime.EventService{}) 87 bApp.SetParamStore(consensusParamsKeeper.ParamsStore) 88 89 if err := bApp.LoadLatestVersion(); err != nil { 90 panic(fmt.Errorf("failed to load application version from store: %w", err)) 91 } 92 93 if _, err := bApp.InitChain(&cmtabcitypes.RequestInitChain{ChainId: appName, ConsensusParams: simtestutil.DefaultConsensusParams}); err != nil { 94 panic(fmt.Errorf("failed to initialize application: %w", err)) 95 } 96 } else { 97 if err := bApp.LoadLatestVersion(); err != nil { 98 panic(fmt.Errorf("failed to load application version from store: %w", err)) 99 } 100 101 if _, err := bApp.InitChain(&cmtabcitypes.RequestInitChain{ChainId: appName}); err != nil { 102 panic(fmt.Errorf("failed to initialize application: %w", err)) 103 } 104 } 105 106 bApp.Commit() 107 108 ctx := sdkCtx.WithBlockHeader(cmtproto.Header{ChainID: appName}).WithIsCheckTx(true) 109 110 return &App{ 111 BaseApp: bApp, 112 logger: logger, 113 ctx: ctx, 114 moduleManager: *moduleManager, 115 queryHelper: baseapp.NewQueryServerTestHelper(ctx, interfaceRegistry), 116 } 117 } 118 119 // RunMsg provides the ability to run a message and return the response. 120 // In order to run a message, the application must have a handler for it. 121 // These handlers are registered on the application message service router. 122 // The result of the message execution is returned as an Any type. 123 // That any type can be unmarshaled to the expected response type. 124 // If the message execution fails, an error is returned. 125 func (app *App) RunMsg(msg sdk.Msg, option ...Option) (*codectypes.Any, error) { 126 // set options 127 cfg := &Config{} 128 for _, opt := range option { 129 opt(cfg) 130 } 131 132 if cfg.AutomaticCommit { 133 defer app.Commit() 134 } 135 136 if cfg.AutomaticFinalizeBlock { 137 height := app.LastBlockHeight() + 1 138 if _, err := app.FinalizeBlock(&cmtabcitypes.RequestFinalizeBlock{Height: height}); err != nil { 139 return nil, fmt.Errorf("failed to run finalize block: %w", err) 140 } 141 } 142 143 app.logger.Info("Running msg", "msg", msg.String()) 144 145 handler := app.MsgServiceRouter().Handler(msg) 146 if handler == nil { 147 return nil, fmt.Errorf("handler is nil, can't route message %s: %+v", sdk.MsgTypeURL(msg), msg) 148 } 149 150 msgResult, err := handler(app.ctx, msg) 151 if err != nil { 152 return nil, fmt.Errorf("failed to execute message %s: %w", sdk.MsgTypeURL(msg), err) 153 } 154 155 var response *codectypes.Any 156 if len(msgResult.MsgResponses) > 0 { 157 msgResponse := msgResult.MsgResponses[0] 158 if msgResponse == nil { 159 return nil, fmt.Errorf("got nil msg response %s in message result: %s", sdk.MsgTypeURL(msg), msgResult.String()) 160 } 161 162 response = msgResponse 163 } 164 165 return response, nil 166 } 167 168 // Context returns the application context. It can be unwrapped to a sdk.Context, 169 // with the sdk.UnwrapSDKContext function. 170 func (app *App) Context() context.Context { 171 return app.ctx 172 } 173 174 // QueryHelper returns the application query helper. 175 // It can be used when registering query services. 176 func (app *App) QueryHelper() *baseapp.QueryServiceTestHelper { 177 return app.queryHelper 178 } 179 180 // CreateMultiStore is a helper for setting up multiple stores for provided modules. 181 func CreateMultiStore(keys map[string]*storetypes.KVStoreKey, logger log.Logger) storetypes.CommitMultiStore { 182 db := dbm.NewMemDB() 183 cms := store.NewCommitMultiStore(db, logger, metrics.NewNoOpMetrics()) 184 185 for key := range keys { 186 cms.MountStoreWithDB(keys[key], storetypes.StoreTypeIAVL, db) 187 } 188 189 _ = cms.LoadLatestVersion() 190 return cms 191 }