From 3999ce81dc3870bb80afd516d6c354ed512ffa22 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 1 Mar 2023 23:30:00 +0100 Subject: [PATCH 01/14] feat: integration test framework --- docs/docs/building-modules/11-structure.md | 3 + testutil/integration/doc.go | 2 + testutil/integration/example_test.go | 66 ++++++++++++++++ testutil/integration/router.go | 89 ++++++++++++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 testutil/integration/doc.go create mode 100644 testutil/integration/example_test.go create mode 100644 testutil/integration/router.go diff --git a/docs/docs/building-modules/11-structure.md b/docs/docs/building-modules/11-structure.md index 4e7b15398fed..5fc584c990ce 100644 --- a/docs/docs/building-modules/11-structure.md +++ b/docs/docs/building-modules/11-structure.md @@ -53,6 +53,8 @@ x/{module_name} ├── module │   └── module.go │   └── abci.go +├── integration +│   └── integration_test.go ├── simulation │   ├── decoder.go │   ├── genesis.go @@ -81,6 +83,7 @@ x/{module_name} * `module/`: The module's `AppModule` and `AppModuleBasic` implementation. * `abci.go`: The module's `BeginBlocker` and `EndBlocker` implementations (this file is only required if `BeginBlocker` and/or `EndBlocker` need to be defined). * `simulation/`: The module's [simulation](./14-simulator.md) package defines functions used by the blockchain simulator application (`simapp`). +* `integration/`: The module's integration tests. These tests are built using the integration testing suite defined in the `testutil/integration` package of the SDK. * `REAMDE.md`: The module's specification documents outlining important concepts, state storage structure, and message and event type definitions. Learn more how to write module specs in the [spec guidelines](../spec/SPEC-SPEC.md). * The root directory includes type definitions for messages, events, and genesis state, including the type definitions generated by Protocol Buffers. * `autocli.go`: The module [autocli](./10-autocli.md) options. diff --git a/testutil/integration/doc.go b/testutil/integration/doc.go new file mode 100644 index 000000000000..d4a8371c2f98 --- /dev/null +++ b/testutil/integration/doc.go @@ -0,0 +1,2 @@ +// Integration contains the integration test suite used for SDK modules. +package integration diff --git a/testutil/integration/example_test.go b/testutil/integration/example_test.go new file mode 100644 index 000000000000..b5defad5eb77 --- /dev/null +++ b/testutil/integration/example_test.go @@ -0,0 +1,66 @@ +package integration_test + +import ( + "testing" + + "gotest.tools/v3/assert" + + storetypes "cosmossdk.io/store/types" + "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/cosmos/cosmos-sdk/x/params" +) + +func TestIntegrationTestExample(t *testing.T) { + // in this example we are testing the integration of the following modules: + // - mint, which directly depends on auth, bank and staking + + cdc := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, mint.AppModuleBasic{}) + keys := storetypes.NewKVStoreKeys(authtypes.StoreKey, minttypes.StoreKey) + authority := authtypes.NewModuleAddress("gov").String() + + accountKeeper := authkeeper.NewAccountKeeper( + cdc.Codec, + 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(cdc.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(cdc.Codec, keys[minttypes.StoreKey], nil, accountKeeper, nil, authtypes.FeeCollectorName, authority) + mintModule := mint.NewAppModule(cdc.Codec, mintKeeper, accountKeeper, nil, nil) + + integrationApp := integration.NewIntegrationApp(t, keys, authModule, mintModule) + + // register the message servers + authtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), authkeeper.NewMsgServerImpl(accountKeeper)) + minttypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), mintkeeper.NewMsgServerImpl(mintKeeper)) + + // now we can use the application to test an mint message + result, err := integrationApp.RunMsg(&minttypes.MsgUpdateParams{ + Authority: authority, + Params: minttypes.DefaultParams(), + }) + assert.NilError(t, err) + assert.Assert(t, result != nil) + + // we now check the result + resp := minttypes.MsgUpdateParamsResponse{} + err = cdc.Codec.Unmarshal(result.Value, &resp) + assert.NilError(t, err) +} diff --git a/testutil/integration/router.go b/testutil/integration/router.go new file mode 100644 index 000000000000..dbcfd3686ab5 --- /dev/null +++ b/testutil/integration/router.go @@ -0,0 +1,89 @@ +package integration + +import ( + "fmt" + "testing" + + "github.com/cometbft/cometbft/abci/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + "gotest.tools/v3/assert" + + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + dbm "github.com/cosmos/cosmos-db" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" +) + +type IntegrationApp struct { + *baseapp.BaseApp + + t *testing.T + context sdk.Context + logger log.Logger +} + +func NewIntegrationApp(t *testing.T, keys map[string]*storetypes.KVStoreKey, modules ...module.AppModuleBasic) *IntegrationApp { + logger := log.NewTestLogger(t) + db := dbm.NewMemDB() + + interfaceRegistry := codectypes.NewInterfaceRegistry() + for _, module := range modules { + module.RegisterInterfaces(interfaceRegistry) + } + + txConfig := authtx.NewTxConfig(codec.NewProtoCodec(interfaceRegistry), authtx.DefaultSignModes) + + bApp := baseapp.NewBaseApp(t.Name(), logger, db, txConfig.TxDecoder()) + bApp.MountKVStores(keys) + bApp.SetInitChainer(func(ctx sdk.Context, req types.RequestInitChain) (types.ResponseInitChain, error) { + return types.ResponseInitChain{}, nil + }) + + router := baseapp.NewMsgServiceRouter() + router.SetInterfaceRegistry(interfaceRegistry) + bApp.SetMsgServiceRouter(router) + + assert.NilError(t, bApp.LoadLatestVersion()) + + ctx := bApp.NewContext(true, cmtproto.Header{}) + + return &IntegrationApp{ + BaseApp: bApp, + + t: t, + logger: logger, + context: ctx, + } +} + +func (app *IntegrationApp) RunMsg(msg sdk.Msg) (*codectypes.Any, error) { + app.logger.Info("Running msg", "msg", msg.String()) + + handler := app.MsgServiceRouter().Handler(msg) + if handler == nil { + return nil, fmt.Errorf("can't route message %+v", msg) + } + + msgResult, err := handler(app.context, msg) + if err != nil { + return nil, fmt.Errorf("failed to execute message: %w", err) + } + + var response *codectypes.Any + if len(msgResult.MsgResponses) > 0 { + msgResponse := msgResult.MsgResponses[0] + if msgResponse == nil { + return nil, fmt.Errorf("got nil msg response %s", sdk.MsgTypeURL(msg)) + } + + response = msgResponse + } + + return response, nil +} From d8ec0e9c9888ae4e2ffefed03b4b17ff41465c1b Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Mon, 27 Mar 2023 15:16:09 +0200 Subject: [PATCH 02/14] updates --- docs/architecture/adr-059-test-scopes.md | 30 ++++++++-------- docs/docs/building-modules/16-testing.md | 44 +++++++----------------- 2 files changed, 29 insertions(+), 45 deletions(-) diff --git a/docs/architecture/adr-059-test-scopes.md b/docs/architecture/adr-059-test-scopes.md index 6180df5928c0..060344593e95 100644 --- a/docs/architecture/adr-059-test-scopes.md +++ b/docs/architecture/adr-059-test-scopes.md @@ -3,6 +3,8 @@ ## Changelog * 2022-08-02: Initial Draft +* 2023-03-02: Add precision for integration tests +* 2023-03-23: Add precision for E2E tests ## Status @@ -55,13 +57,11 @@ These are almost like integration tests in that they exercise many things togeth use mocks. Example 1 journey vs illustrative tests - [depinject's BDD style tests](https://github.com/cosmos/cosmos-sdk/blob/main/depinject/features/bindings.feature), show how we can -rapidly build up many illustrative cases demonstrating behavioral rules without [very much -code](https://github.com/cosmos/cosmos-sdk/blob/main/depinject/binding_test.go) while maintaining high level readability. +rapidly build up many illustrative cases demonstrating behavioral rules without [very much code](https://github.com/cosmos/cosmos-sdk/blob/main/depinject/binding_test.go) while maintaining high level readability. Example 2 [depinject table driven tests](https://github.com/cosmos/cosmos-sdk/blob/main/depinject/provider_desc_test.go) -Example 3 [Bank keeper tests](https://github.com/cosmos/cosmos-sdk/blob/2bec9d2021918650d3938c3ab242f84289daef80/x/bank/keeper/keeper_test.go#L94-L105) - A mock implementation of `AccountKeeper` is -supplied to the keeper constructor. +Example 3 [Bank keeper tests](https://github.com/cosmos/cosmos-sdk/blob/2bec9d2021918650d3938c3ab242f84289daef80/x/bank/keeper/keeper_test.go#L94-L105) - A mock implementation of `AccountKeeper` is supplied to the keeper constructor. #### Limitations @@ -147,6 +147,9 @@ End to end tests exercise the entire system as we understand it in as close an a to a production environment as is practical. Presently these tests are located at [tests/e2e](https://github.com/cosmos/cosmos-sdk/tree/main/tests/e2e) and rely on [testutil/network](https://github.com/cosmos/cosmos-sdk/tree/main/testutil/network) to start up an in-process Tendermint node. +An application should be built as minimally as possible to exercise the desired functionality. +The SDK uses an application will only the required modules for the tests. The application developer is adviced to use its own application for e2e tests. + #### Limitations In general the limitations of end to end tests are orchestration and compute cost. @@ -162,12 +165,14 @@ The scope of e2e tests has been complected with command line interface testing. We accept these test scopes and identify the following decisions points for each. -| Scope | App Fixture | Mocks? | -| ----------- | ----------- | ------ | -| Unit | None | Yes | -| Integration | depinject | Some | -| Simulation | depinject | No | -| E2E | simapp | No | +| Scope | App Type | Mocks? | +| ----------- | ------------------- | ------ | +| Unit | None | Yes | +| Integration | integration helpers | Some | +| Simulation | minimal app | No | +| E2E | minimal app | No | + +The decision above is valid for the SDK. An application developer should test their application with their full application instead of the minimal app. ### Unit Tests @@ -175,8 +180,6 @@ All modules must have mocked unit test coverage. Illustrative tests should outnumber journeys in unit tests. -~BDD feature tests are recommended when building up illustrative and journey scenarios.~ - Unit tests should outnumber integration tests. Unit tests must not introduce additional dependencies beyond those already present in @@ -200,7 +203,7 @@ Integration tests should outnumber e2e tests. ### Simulations -Simulations shall use `depinject`. They are located under `/x/{moduleName}/simulation`. +Simulations shall use a minimal application (usually via app wiring). They are located under `/x/{moduleName}/simulation`. ### E2E Tests @@ -233,7 +236,6 @@ demonstrated in [PR#12706](https://github.com/cosmos/cosmos-sdk/pull/12706). ### Neutral -* learning curve for BDD style tests * some discovery required for e2e transition to dockertest ## Further Discussions diff --git a/docs/docs/building-modules/16-testing.md b/docs/docs/building-modules/16-testing.md index 7de77ec8993d..0c398df03960 100644 --- a/docs/docs/building-modules/16-testing.md +++ b/docs/docs/building-modules/16-testing.md @@ -57,35 +57,17 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/x/gov/keeper/keeper_test.g Integration tests are at the second level of the [test pyramid](https://martinfowler.com/articles/practical-test-pyramid.html). In the SDK, we locate our integration tests under [`/tests/integrations`](https://github.com/cosmos/cosmos-sdk/tree/main/tests/integration). -The goal of these integration tests is to test a component with a minimal application (i.e. not `simapp`). The minimal application is defined with the help of [`depinject`](../tooling/02-depinject.md) – the SDK dependency injection framework, and includes all necessary modules to test the component. With the helps of the SDK testing package, we can easily create a minimal application and start the application with a set of genesis transactions: . +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. -### Example - -Here, we will walkthrough the integration tests of the `x/distribution` module. The `x/distribution` module has, in addition to keeper unit tests, integration tests that test the `x/distribution` module with a minimal application. This is expected as you may want to test the `x/distribution` module with actual application logic, instead of only mocked dependencies. - -For creating a minimal application, we use [`simtestutil.Setup`](https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/testutil/sims/app_helpers.go#L95-L99) and an [`AppConfig`](../tooling/02-depinject.md) of the `x/distribution` minimal dependencies. - -For instance, the `AppConfig` of `x/distribution` is defined as: - -* https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/x/distribution/testutil/app_config.go +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. -This is a stripped down version of the `simapp` `AppConfig`: - -* https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/simapp/app_config.go - -:::note -You can as well use the `AppConfig` `configurator` for creating an `AppConfig` [inline](https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/x/slashing/app_test.go#L54-L62). There no difference between those two ways, use whichever you prefer. -::: +The SDK provides small helpers for quickly setting up an integration tests. These helpers can be found at . -```go reference -https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/tests/integration/distribution/keeper/keeper_test.go#L28-L33 -``` +### Example -Now the types are injected and we can use them for our tests: +Here, we will walkthrough the integration tests of the `x/evidence` module. -```go reference -https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/tests/integration/distribution/keeper/keeper_test.go#L21-L53 -``` + ## Deterministic and Regression tests @@ -106,6 +88,10 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/tests/integration/bank/kee Simulations uses as well a minimal application, built with [`depinject`](../tooling/02-depinject.md): +:::note +You can as well use the `AppConfig` `configurator` for creating an `AppConfig` [inline](https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/x/slashing/app_test.go#L54-L62). There no difference between those two ways, use whichever you prefer. +::: + Following is an example for `x/gov/` simulations: ```go reference @@ -121,6 +107,7 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/x/gov/simulation/operation End-to-end tests are at the top of the [test pyramid](https://martinfowler.com/articles/practical-test-pyramid.html). They must test the whole application flow, from the user perspective (for instance, CLI tests). They are located under [`/tests/e2e`](https://github.com/cosmos/cosmos-sdk/tree/main/tests/e2e). + For that, the SDK is using `simapp` but you should use your own application (`appd`). Here are some examples: @@ -132,11 +119,6 @@ Here are some examples: The SDK is in the process of creating its E2E tests, as defined in [ADR-59](https://docs.cosmos.network/main/architecture/adr-059-test-scopes.html). This page will eventually be updated with better examples. ::: -## Summary +## Learn More -| Scope | App Fixture | Mocks? | -| ----------- | ----------- | ------ | -| Unit | None | Yes | -| Integration | `depinject` | Some | -| Simulation | `depinject` | No | -| E2E | `appd` | No | +Learn more about testing scope in [ADR-59](https://docs.cosmos.network/main/architecture/adr-059-test-scopes.html). From a03beb6a79adfa8e9b4748219f130ae64a701793 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Mon, 27 Mar 2023 15:46:34 +0200 Subject: [PATCH 03/14] updates --- tests/integration/slashing/integration.go | 7 +++++ .../slashing/keeper/keeper_test.go | 9 ------- testutil/integration/example_test.go | 3 ++- testutil/integration/router.go | 26 ++++++++++++++----- x/staking/keeper/keeper.go | 1 - 5 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 tests/integration/slashing/integration.go diff --git a/tests/integration/slashing/integration.go b/tests/integration/slashing/integration.go new file mode 100644 index 000000000000..928107876615 --- /dev/null +++ b/tests/integration/slashing/integration.go @@ -0,0 +1,7 @@ +package slashing + +import "testing" + +func TestIntegration(t *testing.T) { + t.Skip("TODO") +} diff --git a/tests/integration/slashing/keeper/keeper_test.go b/tests/integration/slashing/keeper/keeper_test.go index ada343cf57fb..3854a569c2b6 100644 --- a/tests/integration/slashing/keeper/keeper_test.go +++ b/tests/integration/slashing/keeper/keeper_test.go @@ -7,7 +7,6 @@ import ( cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" "gotest.tools/v3/assert" - "github.com/cosmos/cosmos-sdk/baseapp" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing/testutil" @@ -35,8 +34,6 @@ type fixture struct { accountKeeper authkeeper.AccountKeeper interfaceRegistry codectypes.InterfaceRegistry addrDels []sdk.AccAddress - queryClient slashingtypes.QueryClient - msgServer slashingtypes.MsgServer } func initFixture(t assert.TestingT) *fixture { @@ -65,14 +62,8 @@ func initFixture(t assert.TestingT) *fixture { f.slashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), info1) f.slashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[1]), info2) - queryHelper := baseapp.NewQueryServerTestHelper(ctx, f.interfaceRegistry) - slashingtypes.RegisterQueryServer(queryHelper, f.slashingKeeper) - queryClient := slashingtypes.NewQueryClient(queryHelper) - f.queryClient = queryClient - f.addrDels = addrDels f.ctx = ctx - f.msgServer = slashingkeeper.NewMsgServerImpl(f.slashingKeeper) return f } diff --git a/testutil/integration/example_test.go b/testutil/integration/example_test.go index b5defad5eb77..820504ba69bd 100644 --- a/testutil/integration/example_test.go +++ b/testutil/integration/example_test.go @@ -47,9 +47,10 @@ func TestIntegrationTestExample(t *testing.T) { integrationApp := integration.NewIntegrationApp(t, keys, authModule, mintModule) - // register the message servers + // 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) // now we can use the application to test an mint message result, err := integrationApp.RunMsg(&minttypes.MsgUpdateParams{ diff --git a/testutil/integration/router.go b/testutil/integration/router.go index dbcfd3686ab5..de4c29fd7dcb 100644 --- a/testutil/integration/router.go +++ b/testutil/integration/router.go @@ -23,9 +23,11 @@ import ( type IntegrationApp struct { *baseapp.BaseApp - t *testing.T - context sdk.Context - logger log.Logger + t *testing.T + ctx sdk.Context + logger log.Logger + + queryHelper *baseapp.QueryServiceTestHelper } func NewIntegrationApp(t *testing.T, keys map[string]*storetypes.KVStoreKey, modules ...module.AppModuleBasic) *IntegrationApp { @@ -56,9 +58,11 @@ func NewIntegrationApp(t *testing.T, keys map[string]*storetypes.KVStoreKey, mod return &IntegrationApp{ BaseApp: bApp, - t: t, - logger: logger, - context: ctx, + t: t, + logger: logger, + ctx: ctx, + + queryHelper: baseapp.NewQueryServerTestHelper(ctx, interfaceRegistry), } } @@ -70,7 +74,7 @@ func (app *IntegrationApp) RunMsg(msg sdk.Msg) (*codectypes.Any, error) { return nil, fmt.Errorf("can't route message %+v", msg) } - msgResult, err := handler(app.context, msg) + msgResult, err := handler(app.ctx, msg) if err != nil { return nil, fmt.Errorf("failed to execute message: %w", err) } @@ -87,3 +91,11 @@ func (app *IntegrationApp) RunMsg(msg sdk.Msg) (*codectypes.Any, error) { return response, nil } + +func (app *IntegrationApp) SDKContext() sdk.Context { + return app.ctx +} + +func (app *IntegrationApp) QueryHelper() *baseapp.QueryServiceTestHelper { + return app.queryHelper +} diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go index 93902cf3d885..7299322e1b66 100644 --- a/x/staking/keeper/keeper.go +++ b/x/staking/keeper/keeper.go @@ -8,7 +8,6 @@ import ( abci "github.com/cometbft/cometbft/abci/types" storetypes "cosmossdk.io/store/types" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking/types" From 463ad91f36e9b3c46d81d2afdc4b48772aaa9442 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Mon, 27 Mar 2023 18:57:58 +0200 Subject: [PATCH 04/14] updates --- .../slashing/keeper/keeper_test.go | 187 ++++++++---------- testutil/integration/example_test.go | 17 +- 2 files changed, 92 insertions(+), 112 deletions(-) diff --git a/tests/integration/slashing/keeper/keeper_test.go b/tests/integration/slashing/keeper/keeper_test.go index 3854a569c2b6..56d2698cb117 100644 --- a/tests/integration/slashing/keeper/keeper_test.go +++ b/tests/integration/slashing/keeper/keeper_test.go @@ -12,13 +12,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/slashing/testutil" "github.com/cosmos/cosmos-sdk/x/staking" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtestutil "github.com/cosmos/cosmos-sdk/x/staking/testutil" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -27,13 +25,11 @@ import ( var InitTokens = sdk.TokensFromConsensusPower(200, sdk.DefaultPowerReduction) type fixture struct { - ctx sdk.Context - slashingKeeper slashingkeeper.Keeper - stakingKeeper *stakingkeeper.Keeper - bankKeeper bankkeeper.Keeper - accountKeeper authkeeper.AccountKeeper - interfaceRegistry codectypes.InterfaceRegistry - addrDels []sdk.AccAddress + ctx sdk.Context + slashingKeeper slashingkeeper.Keeper + stakingKeeper *stakingkeeper.Keeper + bankKeeper bankkeeper.Keeper + addrDels []sdk.AccAddress } func initFixture(t assert.TestingT) *fixture { @@ -41,10 +37,8 @@ func initFixture(t assert.TestingT) *fixture { app, err := simtestutil.Setup( testutil.AppConfig, &f.bankKeeper, - &f.accountKeeper, &f.slashingKeeper, &f.stakingKeeper, - &f.interfaceRegistry, ) assert.NilError(t, err) @@ -52,12 +46,10 @@ func initFixture(t assert.TestingT) *fixture { // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 f.slashingKeeper.SetParams(ctx, testutil.TestParams()) - addrDels := simtestutil.AddTestAddrsIncremental(f.bankKeeper, f.stakingKeeper, ctx, 5, f.stakingKeeper.TokensFromConsensusPower(ctx, 200)) + addrDels := simtestutil.AddTestAddrsIncremental(f.bankKeeper, f.stakingKeeper, ctx, 5, f.stakingKeeper.TokensFromConsensusPower(f.ctx, 200)) - info1 := slashingtypes.NewValidatorSigningInfo(sdk.ConsAddress(addrDels[0]), int64(4), int64(3), - time.Unix(2, 0), false, int64(10)) - info2 := slashingtypes.NewValidatorSigningInfo(sdk.ConsAddress(addrDels[1]), int64(5), int64(4), - time.Unix(2, 0), false, int64(10)) + info1 := slashingtypes.NewValidatorSigningInfo(sdk.ConsAddress(addrDels[0]), int64(4), int64(3), time.Unix(2, 0), false, int64(10)) + info2 := slashingtypes.NewValidatorSigningInfo(sdk.ConsAddress(addrDels[1]), int64(5), int64(4), time.Unix(2, 0), false, int64(10)) f.slashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), info1) f.slashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[1]), info2) @@ -72,16 +64,14 @@ func TestUnJailNotBonded(t *testing.T) { t.Parallel() f := initFixture(t) - ctx := f.ctx - - p := f.stakingKeeper.GetParams(ctx) + p := f.stakingKeeper.GetParams(f.ctx) p.MaxValidators = 5 - f.stakingKeeper.SetParams(ctx, p) + f.stakingKeeper.SetParams(f.ctx, p) - addrDels := simtestutil.AddTestAddrsIncremental(f.bankKeeper, f.stakingKeeper, ctx, 6, f.stakingKeeper.TokensFromConsensusPower(ctx, 200)) + addrDels := simtestutil.AddTestAddrsIncremental(f.bankKeeper, f.stakingKeeper, f.ctx, 6, f.stakingKeeper.TokensFromConsensusPower(f.ctx, 200)) valAddrs := simtestutil.ConvertAddrsToValAddrs(addrDels) pks := simtestutil.CreateTestPubKeys(6) - tstaking := stakingtestutil.NewHelper(t, ctx, f.stakingKeeper) + tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) // create max (5) validators all with the same power for i := uint32(0); i < p.MaxValidators; i++ { @@ -89,46 +79,46 @@ func TestUnJailNotBonded(t *testing.T) { tstaking.CreateValidatorWithValPower(addr, val, 100, true) } - staking.EndBlocker(ctx, f.stakingKeeper) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + staking.EndBlocker(f.ctx, f.stakingKeeper) + f.ctx = f.ctx.WithBlockHeight(f.ctx.BlockHeight() + 1) // create a 6th validator with less power than the cliff validator (won't be bonded) addr, val := valAddrs[5], pks[5] - amt := f.stakingKeeper.TokensFromConsensusPower(ctx, 50) + amt := f.stakingKeeper.TokensFromConsensusPower(f.ctx, 50) msg := tstaking.CreateValidatorMsg(addr, val, amt) msg.MinSelfDelegation = amt - res, err := tstaking.CreateValidatorWithMsg(ctx, msg) + res, err := tstaking.CreateValidatorWithMsg(f.ctx, msg) assert.NilError(t, err) assert.Assert(t, res != nil) - staking.EndBlocker(ctx, f.stakingKeeper) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + staking.EndBlocker(f.ctx, f.stakingKeeper) + f.ctx = f.ctx.WithBlockHeight(f.ctx.BlockHeight() + 1) tstaking.CheckValidator(addr, stakingtypes.Unbonded, false) // unbond below minimum self-delegation assert.Equal(t, p.BondDenom, tstaking.Denom) - tstaking.Undelegate(sdk.AccAddress(addr), addr, f.stakingKeeper.TokensFromConsensusPower(ctx, 1), true) + tstaking.Undelegate(sdk.AccAddress(addr), addr, f.stakingKeeper.TokensFromConsensusPower(f.ctx, 1), true) - staking.EndBlocker(ctx, f.stakingKeeper) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + staking.EndBlocker(f.ctx, f.stakingKeeper) + f.ctx = f.ctx.WithBlockHeight(f.ctx.BlockHeight() + 1) // verify that validator is jailed tstaking.CheckValidator(addr, -1, true) // verify we cannot unjail (yet) - assert.ErrorContains(t, f.slashingKeeper.Unjail(ctx, addr), "cannot be unjailed") + assert.ErrorContains(t, f.slashingKeeper.Unjail(f.ctx, addr), "cannot be unjailed") - staking.EndBlocker(ctx, f.stakingKeeper) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + staking.EndBlocker(f.ctx, f.stakingKeeper) + f.ctx = f.ctx.WithBlockHeight(f.ctx.BlockHeight() + 1) // bond to meet minimum self-delegation tstaking.DelegateWithPower(sdk.AccAddress(addr), addr, 1) - staking.EndBlocker(ctx, f.stakingKeeper) - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + staking.EndBlocker(f.ctx, f.stakingKeeper) + f.ctx = f.ctx.WithBlockHeight(f.ctx.BlockHeight() + 1) // verify we can immediately unjail - assert.NilError(t, f.slashingKeeper.Unjail(ctx, addr)) + assert.NilError(t, f.slashingKeeper.Unjail(f.ctx, addr)) tstaking.CheckValidator(addr, -1, false) } @@ -140,45 +130,43 @@ func TestHandleNewValidator(t *testing.T) { t.Parallel() f := initFixture(t) - ctx := f.ctx - - addrDels := simtestutil.AddTestAddrsIncremental(f.bankKeeper, f.stakingKeeper, ctx, 1, f.stakingKeeper.TokensFromConsensusPower(ctx, 0)) + addrDels := simtestutil.AddTestAddrsIncremental(f.bankKeeper, f.stakingKeeper, f.ctx, 1, f.stakingKeeper.TokensFromConsensusPower(f.ctx, 0)) valAddrs := simtestutil.ConvertAddrsToValAddrs(addrDels) pks := simtestutil.CreateTestPubKeys(1) addr, val := valAddrs[0], pks[0] - tstaking := stakingtestutil.NewHelper(t, ctx, f.stakingKeeper) - ctx = ctx.WithBlockHeight(f.slashingKeeper.SignedBlocksWindow(ctx) + 1) + tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) + f.ctx = f.ctx.WithBlockHeight(f.slashingKeeper.SignedBlocksWindow(f.ctx) + 1) // Validator created amt := tstaking.CreateValidatorWithValPower(addr, val, 100, true) - staking.EndBlocker(ctx, f.stakingKeeper) + staking.EndBlocker(f.ctx, f.stakingKeeper) assert.DeepEqual( - t, f.bankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(f.stakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), + t, f.bankKeeper.GetAllBalances(f.ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(f.stakingKeeper.GetParams(f.ctx).BondDenom, InitTokens.Sub(amt))), ) - assert.DeepEqual(t, amt, f.stakingKeeper.Validator(ctx, addr).GetBondedTokens()) + assert.DeepEqual(t, amt, f.stakingKeeper.Validator(f.ctx, addr).GetBondedTokens()) // Now a validator, for two blocks - f.slashingKeeper.HandleValidatorSignature(ctx, val.Address(), 100, true) - ctx = ctx.WithBlockHeight(f.slashingKeeper.SignedBlocksWindow(ctx) + 2) - f.slashingKeeper.HandleValidatorSignature(ctx, val.Address(), 100, false) + f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), 100, true) + f.ctx = f.ctx.WithBlockHeight(f.slashingKeeper.SignedBlocksWindow(f.ctx) + 2) + f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), 100, false) - info, found := f.slashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + info, found := f.slashingKeeper.GetValidatorSigningInfo(f.ctx, sdk.ConsAddress(val.Address())) assert.Assert(t, found) - assert.Equal(t, f.slashingKeeper.SignedBlocksWindow(ctx)+1, info.StartHeight) + assert.Equal(t, f.slashingKeeper.SignedBlocksWindow(f.ctx)+1, info.StartHeight) assert.Equal(t, int64(2), info.IndexOffset) assert.Equal(t, int64(1), info.MissedBlocksCounter) assert.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) // validator should be bonded still, should not have been jailed or slashed - validator, _ := f.stakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + validator, _ := f.stakingKeeper.GetValidatorByConsAddr(f.ctx, sdk.GetConsAddress(val)) assert.Equal(t, stakingtypes.Bonded, validator.GetStatus()) - bondPool := f.stakingKeeper.GetBondedPool(ctx) - expTokens := f.stakingKeeper.TokensFromConsensusPower(ctx, 100) + bondPool := f.stakingKeeper.GetBondedPool(f.ctx) + expTokens := f.stakingKeeper.TokensFromConsensusPower(f.ctx, 100) // adding genesis validator tokens - expTokens = expTokens.Add(f.stakingKeeper.TokensFromConsensusPower(ctx, 1)) - assert.Assert(t, expTokens.Equal(f.bankKeeper.GetBalance(ctx, bondPool.GetAddress(), f.stakingKeeper.BondDenom(ctx)).Amount)) + expTokens = expTokens.Add(f.stakingKeeper.TokensFromConsensusPower(f.ctx, 1)) + assert.Assert(t, expTokens.Equal(f.bankKeeper.GetBalance(f.ctx, bondPool.GetAddress(), f.stakingKeeper.BondDenom(f.ctx)).Amount)) } // Test a jailed validator being "down" twice @@ -187,50 +175,47 @@ func TestHandleAlreadyJailed(t *testing.T) { t.Parallel() f := initFixture(t) - // initial setup - ctx := f.ctx - - addrDels := simtestutil.AddTestAddrsIncremental(f.bankKeeper, f.stakingKeeper, ctx, 1, f.stakingKeeper.TokensFromConsensusPower(ctx, 200)) + addrDels := simtestutil.AddTestAddrsIncremental(f.bankKeeper, f.stakingKeeper, f.ctx, 1, f.stakingKeeper.TokensFromConsensusPower(f.ctx, 200)) valAddrs := simtestutil.ConvertAddrsToValAddrs(addrDels) pks := simtestutil.CreateTestPubKeys(1) addr, val := valAddrs[0], pks[0] power := int64(100) - tstaking := stakingtestutil.NewHelper(t, ctx, f.stakingKeeper) + tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) amt := tstaking.CreateValidatorWithValPower(addr, val, power, true) - staking.EndBlocker(ctx, f.stakingKeeper) + staking.EndBlocker(f.ctx, f.stakingKeeper) // 1000 first blocks OK height := int64(0) - for ; height < f.slashingKeeper.SignedBlocksWindow(ctx); height++ { - ctx = ctx.WithBlockHeight(height) - f.slashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) + for ; height < f.slashingKeeper.SignedBlocksWindow(f.ctx); height++ { + f.ctx = f.ctx.WithBlockHeight(height) + f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, true) } // 501 blocks missed - for ; height < f.slashingKeeper.SignedBlocksWindow(ctx)+(f.slashingKeeper.SignedBlocksWindow(ctx)-f.slashingKeeper.MinSignedPerWindow(ctx))+1; height++ { - ctx = ctx.WithBlockHeight(height) - f.slashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + for ; height < f.slashingKeeper.SignedBlocksWindow(f.ctx)+(f.slashingKeeper.SignedBlocksWindow(f.ctx)-f.slashingKeeper.MinSignedPerWindow(f.ctx))+1; height++ { + f.ctx = f.ctx.WithBlockHeight(height) + f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, false) } // end block - staking.EndBlocker(ctx, f.stakingKeeper) + staking.EndBlocker(f.ctx, f.stakingKeeper) // validator should have been jailed and slashed - validator, _ := f.stakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + validator, _ := f.stakingKeeper.GetValidatorByConsAddr(f.ctx, sdk.GetConsAddress(val)) assert.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) // validator should have been slashed - resultingTokens := amt.Sub(f.stakingKeeper.TokensFromConsensusPower(ctx, 1)) + resultingTokens := amt.Sub(f.stakingKeeper.TokensFromConsensusPower(f.ctx, 1)) assert.DeepEqual(t, resultingTokens, validator.GetTokens()) // another block missed - ctx = ctx.WithBlockHeight(height) - f.slashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + f.ctx = f.ctx.WithBlockHeight(height) + f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, false) // validator should not have been slashed twice - validator, _ = f.stakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + validator, _ = f.stakingKeeper.GetValidatorByConsAddr(f.ctx, sdk.GetConsAddress(val)) assert.DeepEqual(t, resultingTokens, validator.GetTokens()) } @@ -241,74 +226,72 @@ func TestValidatorDippingInAndOut(t *testing.T) { t.Parallel() f := initFixture(t) - // initial setup - ctx := f.ctx - params := f.stakingKeeper.GetParams(ctx) + params := f.stakingKeeper.GetParams(f.ctx) params.MaxValidators = 1 - f.stakingKeeper.SetParams(ctx, params) + f.stakingKeeper.SetParams(f.ctx, params) power := int64(100) pks := simtestutil.CreateTestPubKeys(3) - simtestutil.AddTestAddrsFromPubKeys(f.bankKeeper, f.stakingKeeper, ctx, pks, f.stakingKeeper.TokensFromConsensusPower(ctx, 200)) + simtestutil.AddTestAddrsFromPubKeys(f.bankKeeper, f.stakingKeeper, f.ctx, pks, f.stakingKeeper.TokensFromConsensusPower(f.ctx, 200)) addr, val := pks[0].Address(), pks[0] consAddr := sdk.ConsAddress(addr) - tstaking := stakingtestutil.NewHelper(t, ctx, f.stakingKeeper) + tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) valAddr := sdk.ValAddress(addr) tstaking.CreateValidatorWithValPower(valAddr, val, power, true) - validatorUpdates := staking.EndBlocker(ctx, f.stakingKeeper) + validatorUpdates := staking.EndBlocker(f.ctx, f.stakingKeeper) assert.Equal(t, 2, len(validatorUpdates)) tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) // 100 first blocks OK height := int64(0) for ; height < int64(100); height++ { - ctx = ctx.WithBlockHeight(height) - f.slashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) + f.ctx = f.ctx.WithBlockHeight(height) + f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, true) } // kick first validator out of validator set tstaking.CreateValidatorWithValPower(sdk.ValAddress(pks[1].Address()), pks[1], power+1, true) - validatorUpdates = staking.EndBlocker(ctx, f.stakingKeeper) + validatorUpdates = staking.EndBlocker(f.ctx, f.stakingKeeper) assert.Equal(t, 2, len(validatorUpdates)) tstaking.CheckValidator(sdk.ValAddress(pks[1].Address()), stakingtypes.Bonded, false) tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, false) // 600 more blocks happened height = height + 600 - ctx = ctx.WithBlockHeight(height) + f.ctx = f.ctx.WithBlockHeight(height) // validator added back in tstaking.DelegateWithPower(sdk.AccAddress(pks[2].Address()), valAddr, 50) - validatorUpdates = staking.EndBlocker(ctx, f.stakingKeeper) + validatorUpdates = staking.EndBlocker(f.ctx, f.stakingKeeper) assert.Equal(t, 2, len(validatorUpdates)) tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) newPower := power + 50 // validator misses a block - f.slashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) + f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), newPower, false) height++ // shouldn't be jailed/kicked yet tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) // validator misses an additional 500 more blocks within the SignedBlockWindow (here 1000 blocks). - latest := f.slashingKeeper.SignedBlocksWindow(ctx) + height + latest := f.slashingKeeper.SignedBlocksWindow(f.ctx) + height // misses 500 blocks + within the signing windows i.e. 700-1700 // validators misses all 1000 block of a SignedBlockWindows for ; height < latest+1; height++ { - ctx = ctx.WithBlockHeight(height) - f.slashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) + f.ctx = f.ctx.WithBlockHeight(height) + f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), newPower, false) } // should now be jailed & kicked - staking.EndBlocker(ctx, f.stakingKeeper) + staking.EndBlocker(f.ctx, f.stakingKeeper) tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) // check all the signing information - signInfo, found := f.slashingKeeper.GetValidatorSigningInfo(ctx, consAddr) + signInfo, found := f.slashingKeeper.GetValidatorSigningInfo(f.ctx, consAddr) assert.Assert(t, found) assert.Equal(t, int64(700), signInfo.StartHeight) assert.Equal(t, int64(0), signInfo.MissedBlocksCounter) @@ -316,30 +299,30 @@ func TestValidatorDippingInAndOut(t *testing.T) { // some blocks pass height = int64(5000) - ctx = ctx.WithBlockHeight(height) + f.ctx = f.ctx.WithBlockHeight(height) // validator rejoins and starts signing again - f.stakingKeeper.Unjail(ctx, consAddr) + f.stakingKeeper.Unjail(f.ctx, consAddr) - f.slashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, true) + f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), newPower, true) // validator should not be kicked since we reset counter/array when it was jailed - staking.EndBlocker(ctx, f.stakingKeeper) + staking.EndBlocker(f.ctx, f.stakingKeeper) tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) // check start height is correctly set - signInfo, found = f.slashingKeeper.GetValidatorSigningInfo(ctx, consAddr) + signInfo, found = f.slashingKeeper.GetValidatorSigningInfo(f.ctx, consAddr) assert.Assert(t, found) assert.Equal(t, height, signInfo.StartHeight) // validator misses 501 blocks after SignedBlockWindow period (1000 blocks) - latest = f.slashingKeeper.SignedBlocksWindow(ctx) + height - for ; height < latest+f.slashingKeeper.MinSignedPerWindow(ctx); height++ { - ctx = ctx.WithBlockHeight(height) - f.slashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) + latest = f.slashingKeeper.SignedBlocksWindow(f.ctx) + height + for ; height < latest+f.slashingKeeper.MinSignedPerWindow(f.ctx); height++ { + f.ctx = f.ctx.WithBlockHeight(height) + f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), newPower, false) } // validator should now be jailed & kicked - staking.EndBlocker(ctx, f.stakingKeeper) + staking.EndBlocker(f.ctx, f.stakingKeeper) tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) } diff --git a/testutil/integration/example_test.go b/testutil/integration/example_test.go index 820504ba69bd..087e1c70ebd2 100644 --- a/testutil/integration/example_test.go +++ b/testutil/integration/example_test.go @@ -15,35 +15,32 @@ import ( "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/cosmos/cosmos-sdk/x/params" ) func TestIntegrationTestExample(t *testing.T) { // in this example we are testing the integration of the following modules: // - mint, which directly depends on auth, bank and staking - cdc := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, mint.AppModuleBasic{}) + encodingCfg := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, mint.AppModuleBasic{}) keys := storetypes.NewKVStoreKeys(authtypes.StoreKey, minttypes.StoreKey) authority := authtypes.NewModuleAddress("gov").String() accountKeeper := authkeeper.NewAccountKeeper( - cdc.Codec, + encodingCfg.Codec, keys[authtypes.StoreKey], authtypes.ProtoBaseAccount, - map[string][]string{ - minttypes.ModuleName: {authtypes.Minter}, - }, + 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(cdc.Codec, accountKeeper, authsims.RandomGenesisAccounts, nil) + 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(cdc.Codec, keys[minttypes.StoreKey], nil, accountKeeper, nil, authtypes.FeeCollectorName, authority) - mintModule := mint.NewAppModule(cdc.Codec, mintKeeper, accountKeeper, nil, nil) + mintKeeper := mintkeeper.NewKeeper(encodingCfg.Codec, keys[minttypes.StoreKey], nil, accountKeeper, nil, authtypes.FeeCollectorName, authority) + mintModule := mint.NewAppModule(encodingCfg.Codec, mintKeeper, accountKeeper, nil, nil) integrationApp := integration.NewIntegrationApp(t, keys, authModule, mintModule) @@ -62,6 +59,6 @@ func TestIntegrationTestExample(t *testing.T) { // we now check the result resp := minttypes.MsgUpdateParamsResponse{} - err = cdc.Codec.Unmarshal(result.Value, &resp) + err = encodingCfg.Codec.Unmarshal(result.Value, &resp) assert.NilError(t, err) } From 929cd32c9bab2b207df95cf05d245c6b0f80d87b Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 28 Mar 2023 16:10:18 +0200 Subject: [PATCH 05/14] fix conflict after update --- testutil/integration/example_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testutil/integration/example_test.go b/testutil/integration/example_test.go index 087e1c70ebd2..40ab012cd503 100644 --- a/testutil/integration/example_test.go +++ b/testutil/integration/example_test.go @@ -6,6 +6,7 @@ import ( "gotest.tools/v3/assert" 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" @@ -27,7 +28,7 @@ func TestIntegrationTestExample(t *testing.T) { accountKeeper := authkeeper.NewAccountKeeper( encodingCfg.Codec, - keys[authtypes.StoreKey], + runtime.NewKVStoreService(keys[authtypes.StoreKey]), authtypes.ProtoBaseAccount, map[string][]string{minttypes.ModuleName: {authtypes.Minter}}, "cosmos", From f64df3f25d81b08cabe6cd2ce81a79515b1af021 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 28 Mar 2023 16:26:54 +0200 Subject: [PATCH 06/14] linting --- testutil/integration/router.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/testutil/integration/router.go b/testutil/integration/router.go index de4c29fd7dcb..5c98fe4e8610 100644 --- a/testutil/integration/router.go +++ b/testutil/integration/router.go @@ -20,7 +20,7 @@ import ( authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" ) -type IntegrationApp struct { +type App struct { *baseapp.BaseApp t *testing.T @@ -30,7 +30,7 @@ type IntegrationApp struct { queryHelper *baseapp.QueryServiceTestHelper } -func NewIntegrationApp(t *testing.T, keys map[string]*storetypes.KVStoreKey, modules ...module.AppModuleBasic) *IntegrationApp { +func NewIntegrationApp(t *testing.T, keys map[string]*storetypes.KVStoreKey, modules ...module.AppModuleBasic) *App { logger := log.NewTestLogger(t) db := dbm.NewMemDB() @@ -55,7 +55,7 @@ func NewIntegrationApp(t *testing.T, keys map[string]*storetypes.KVStoreKey, mod ctx := bApp.NewContext(true, cmtproto.Header{}) - return &IntegrationApp{ + return &App{ BaseApp: bApp, t: t, @@ -66,7 +66,7 @@ func NewIntegrationApp(t *testing.T, keys map[string]*storetypes.KVStoreKey, mod } } -func (app *IntegrationApp) RunMsg(msg sdk.Msg) (*codectypes.Any, error) { +func (app *App) RunMsg(msg sdk.Msg) (*codectypes.Any, error) { app.logger.Info("Running msg", "msg", msg.String()) handler := app.MsgServiceRouter().Handler(msg) @@ -92,10 +92,10 @@ func (app *IntegrationApp) RunMsg(msg sdk.Msg) (*codectypes.Any, error) { return response, nil } -func (app *IntegrationApp) SDKContext() sdk.Context { +func (app *App) SDKContext() sdk.Context { return app.ctx } -func (app *IntegrationApp) QueryHelper() *baseapp.QueryServiceTestHelper { +func (app *App) QueryHelper() *baseapp.QueryServiceTestHelper { return app.queryHelper } From aa411b725a5d48f947ee39bed28d71c99db5286f Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 28 Mar 2023 20:12:14 +0200 Subject: [PATCH 07/14] feedback --- docs/docs/building-modules/11-structure.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/docs/building-modules/11-structure.md b/docs/docs/building-modules/11-structure.md index 5fc584c990ce..5e31c31d944b 100644 --- a/docs/docs/building-modules/11-structure.md +++ b/docs/docs/building-modules/11-structure.md @@ -53,8 +53,6 @@ x/{module_name} ├── module │   └── module.go │   └── abci.go -├── integration -│   └── integration_test.go ├── simulation │   ├── decoder.go │   ├── genesis.go @@ -77,13 +75,12 @@ x/{module_name} └── README.md ``` -* `client/`: The module's CLI client functionality implementation and the module's integration testing suite. +* `client/`: The module's CLI client functionality implementation and the module's CLI testing suite. * `exported/`: The module's exported types - typically interface types. If a module relies on keepers from another module, it is expected to receive the keepers as interface contracts through the `expected_keepers.go` file (see below) in order to avoid a direct dependency on the module implementing the keepers. However, these interface contracts can define methods that operate on and/or return types that are specific to the module that is implementing the keepers and this is where `exported/` comes into play. The interface types that are defined in `exported/` use canonical types, allowing for the module to receive the keepers as interface contracts through the `expected_keepers.go` file. This pattern allows for code to remain DRY and also alleviates import cycle chaos. * `keeper/`: The module's `Keeper` and `MsgServer` implementation. * `module/`: The module's `AppModule` and `AppModuleBasic` implementation. * `abci.go`: The module's `BeginBlocker` and `EndBlocker` implementations (this file is only required if `BeginBlocker` and/or `EndBlocker` need to be defined). * `simulation/`: The module's [simulation](./14-simulator.md) package defines functions used by the blockchain simulator application (`simapp`). -* `integration/`: The module's integration tests. These tests are built using the integration testing suite defined in the `testutil/integration` package of the SDK. * `REAMDE.md`: The module's specification documents outlining important concepts, state storage structure, and message and event type definitions. Learn more how to write module specs in the [spec guidelines](../spec/SPEC-SPEC.md). * The root directory includes type definitions for messages, events, and genesis state, including the type definitions generated by Protocol Buffers. * `autocli.go`: The module [autocli](./10-autocli.md) options. From 7b8ab5adb9233b48ac041170311b8ad3e72604aa Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 28 Mar 2023 20:13:20 +0200 Subject: [PATCH 08/14] update example --- docs/docs/building-modules/16-testing.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/building-modules/16-testing.md b/docs/docs/building-modules/16-testing.md index 0c398df03960..338c92a2ddab 100644 --- a/docs/docs/building-modules/16-testing.md +++ b/docs/docs/building-modules/16-testing.md @@ -65,9 +65,9 @@ The SDK provides small helpers for quickly setting up an integration tests. Thes ### Example -Here, we will walkthrough the integration tests of the `x/evidence` module. - - +```go reference +https://github.com/cosmos/cosmos-sdk/blob/julien/integration/testutil/integration/example_test.go#L21-L65 +``` ## Deterministic and Regression tests From 35deb0ce8656915ee179c513ff42b92d30b85f83 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 30 Mar 2023 23:34:31 +0200 Subject: [PATCH 09/14] feedback --- tests/integration/slashing/integration.go | 7 ------- testutil/integration/example_test.go | 12 +++++++++--- testutil/integration/router.go | 18 +++++++----------- 3 files changed, 16 insertions(+), 21 deletions(-) delete mode 100644 tests/integration/slashing/integration.go diff --git a/tests/integration/slashing/integration.go b/tests/integration/slashing/integration.go deleted file mode 100644 index 928107876615..000000000000 --- a/tests/integration/slashing/integration.go +++ /dev/null @@ -1,7 +0,0 @@ -package slashing - -import "testing" - -func TestIntegration(t *testing.T) { - t.Skip("TODO") -} diff --git a/testutil/integration/example_test.go b/testutil/integration/example_test.go index 40ab012cd503..fdcc5d5eaeed 100644 --- a/testutil/integration/example_test.go +++ b/testutil/integration/example_test.go @@ -5,6 +5,7 @@ import ( "gotest.tools/v3/assert" + "cosmossdk.io/log" storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/runtime" "github.com/cosmos/cosmos-sdk/testutil/integration" @@ -21,7 +22,6 @@ import ( func TestIntegrationTestExample(t *testing.T) { // 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() @@ -43,17 +43,20 @@ func TestIntegrationTestExample(t *testing.T) { mintKeeper := mintkeeper.NewKeeper(encodingCfg.Codec, keys[minttypes.StoreKey], nil, accountKeeper, nil, authtypes.FeeCollectorName, authority) mintModule := mint.NewAppModule(encodingCfg.Codec, mintKeeper, accountKeeper, nil, nil) - integrationApp := integration.NewIntegrationApp(t, keys, authModule, mintModule) + integrationApp := integration.NewIntegrationApp(t.Name(), log.NewTestLogger(t), 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 an mint message result, err := integrationApp.RunMsg(&minttypes.MsgUpdateParams{ Authority: authority, - Params: minttypes.DefaultParams(), + Params: params, }) assert.NilError(t, err) assert.Assert(t, result != nil) @@ -62,4 +65,7 @@ func TestIntegrationTestExample(t *testing.T) { resp := minttypes.MsgUpdateParamsResponse{} err = encodingCfg.Codec.Unmarshal(result.Value, &resp) assert.NilError(t, err) + + // we can also check the state of the application + assert.DeepEqual(t, mintKeeper.GetParams(integrationApp.SDKContext()), params) } diff --git a/testutil/integration/router.go b/testutil/integration/router.go index 5c98fe4e8610..e224fdbce1e2 100644 --- a/testutil/integration/router.go +++ b/testutil/integration/router.go @@ -2,11 +2,9 @@ package integration import ( "fmt" - "testing" "github.com/cometbft/cometbft/abci/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - "gotest.tools/v3/assert" "cosmossdk.io/log" storetypes "cosmossdk.io/store/types" @@ -23,15 +21,13 @@ import ( type App struct { *baseapp.BaseApp - t *testing.T ctx sdk.Context logger log.Logger queryHelper *baseapp.QueryServiceTestHelper } -func NewIntegrationApp(t *testing.T, keys map[string]*storetypes.KVStoreKey, modules ...module.AppModuleBasic) *App { - logger := log.NewTestLogger(t) +func NewIntegrationApp(name string, logger log.Logger, keys map[string]*storetypes.KVStoreKey, modules ...module.AppModuleBasic) *App { db := dbm.NewMemDB() interfaceRegistry := codectypes.NewInterfaceRegistry() @@ -41,7 +37,7 @@ func NewIntegrationApp(t *testing.T, keys map[string]*storetypes.KVStoreKey, mod txConfig := authtx.NewTxConfig(codec.NewProtoCodec(interfaceRegistry), authtx.DefaultSignModes) - bApp := baseapp.NewBaseApp(t.Name(), logger, db, txConfig.TxDecoder()) + bApp := baseapp.NewBaseApp(fmt.Sprintf("integration-app-%s", name), logger, db, txConfig.TxDecoder()) bApp.MountKVStores(keys) bApp.SetInitChainer(func(ctx sdk.Context, req types.RequestInitChain) (types.ResponseInitChain, error) { return types.ResponseInitChain{}, nil @@ -51,17 +47,17 @@ func NewIntegrationApp(t *testing.T, keys map[string]*storetypes.KVStoreKey, mod router.SetInterfaceRegistry(interfaceRegistry) bApp.SetMsgServiceRouter(router) - assert.NilError(t, bApp.LoadLatestVersion()) + if err := bApp.LoadLatestVersion(); err != nil { + panic(err) + } ctx := bApp.NewContext(true, cmtproto.Header{}) return &App{ BaseApp: bApp, - t: t, - logger: logger, - ctx: ctx, - + logger: logger, + ctx: ctx, queryHelper: baseapp.NewQueryServerTestHelper(ctx, interfaceRegistry), } } From 1ed278563867cd9de024ed24904742c491f60700 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 30 Mar 2023 23:36:17 +0200 Subject: [PATCH 10/14] update docs --- docs/docs/building-modules/16-testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/building-modules/16-testing.md b/docs/docs/building-modules/16-testing.md index 338c92a2ddab..26167186a5d1 100644 --- a/docs/docs/building-modules/16-testing.md +++ b/docs/docs/building-modules/16-testing.md @@ -66,7 +66,7 @@ The SDK provides small helpers for quickly setting up an integration tests. Thes ### Example ```go reference -https://github.com/cosmos/cosmos-sdk/blob/julien/integration/testutil/integration/example_test.go#L21-L65 +https://github.com/cosmos/cosmos-sdk/blob/35deb0ce8656915ee179c513ff42b92d30b85f83/testutil/integration/example_test.go#L22-L71 ``` ## Deterministic and Regression tests From a4c6e2e7d054833dce65c8682cc39ba960fa851e Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 31 Mar 2023 16:54:10 +0200 Subject: [PATCH 11/14] feedback (1/2) - go docs and examples --- testutil/integration/example_test.go | 4 +++- testutil/integration/router.go | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/testutil/integration/example_test.go b/testutil/integration/example_test.go index fdcc5d5eaeed..77cd59de02a9 100644 --- a/testutil/integration/example_test.go +++ b/testutil/integration/example_test.go @@ -53,12 +53,14 @@ func TestIntegrationTestExample(t *testing.T) { params := minttypes.DefaultParams() params.BlocksPerYear = 10000 - // now we can use the application to test an mint message + // now we can use the application to test a mint message result, err := integrationApp.RunMsg(&minttypes.MsgUpdateParams{ Authority: authority, Params: params, }) assert.NilError(t, err) + // in this example the resut is an empty response, a nil check is enough + // in other cases, it is recommanded to check the result value. assert.Assert(t, result != nil) // we now check the result diff --git a/testutil/integration/router.go b/testutil/integration/router.go index e224fdbce1e2..348eda76e5f7 100644 --- a/testutil/integration/router.go +++ b/testutil/integration/router.go @@ -18,6 +18,7 @@ import ( authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" ) +// App is a test application that can be used to test the integration of modules. type App struct { *baseapp.BaseApp @@ -27,7 +28,8 @@ type App struct { queryHelper *baseapp.QueryServiceTestHelper } -func NewIntegrationApp(name string, logger log.Logger, keys map[string]*storetypes.KVStoreKey, modules ...module.AppModuleBasic) *App { +// NewIntegrationApp creates a application for testing purposes. This application is able to route messages to the respectives handlers. +func NewIntegrationApp(nameSuffix string, logger log.Logger, keys map[string]*storetypes.KVStoreKey, modules ...module.AppModuleBasic) *App { db := dbm.NewMemDB() interfaceRegistry := codectypes.NewInterfaceRegistry() @@ -37,7 +39,7 @@ func NewIntegrationApp(name string, logger log.Logger, keys map[string]*storetyp txConfig := authtx.NewTxConfig(codec.NewProtoCodec(interfaceRegistry), authtx.DefaultSignModes) - bApp := baseapp.NewBaseApp(fmt.Sprintf("integration-app-%s", name), logger, db, txConfig.TxDecoder()) + bApp := baseapp.NewBaseApp(fmt.Sprintf("integration-app-%s", nameSuffix), logger, db, txConfig.TxDecoder()) bApp.MountKVStores(keys) bApp.SetInitChainer(func(ctx sdk.Context, req types.RequestInitChain) (types.ResponseInitChain, error) { return types.ResponseInitChain{}, nil @@ -48,7 +50,7 @@ func NewIntegrationApp(name string, logger log.Logger, keys map[string]*storetyp bApp.SetMsgServiceRouter(router) if err := bApp.LoadLatestVersion(); err != nil { - panic(err) + panic(fmt.Errorf("failed to load application version from store:%w", err)) } ctx := bApp.NewContext(true, cmtproto.Header{}) @@ -62,24 +64,30 @@ func NewIntegrationApp(name string, logger log.Logger, keys map[string]*storetyp } } +// RunMsg allows to run a message and return the response. +// In order to run a message, the application must have a handler for it. +// These handlers are registered on the application message service router. +// The result of the message execution is returned as a Any type. +// That any type can be unmarshaled to the expected response type. +// If the message execution fails, an error is returned. func (app *App) RunMsg(msg sdk.Msg) (*codectypes.Any, error) { app.logger.Info("Running msg", "msg", msg.String()) handler := app.MsgServiceRouter().Handler(msg) if handler == nil { - return nil, fmt.Errorf("can't route message %+v", msg) + return nil, fmt.Errorf("handler is nil, can't route message %s: %+v", sdk.MsgTypeURL(msg), msg) } msgResult, err := handler(app.ctx, msg) if err != nil { - return nil, fmt.Errorf("failed to execute message: %w", err) + return nil, fmt.Errorf("failed to execute message %s: %w", sdk.MsgTypeURL(msg), err) } var response *codectypes.Any if len(msgResult.MsgResponses) > 0 { msgResponse := msgResult.MsgResponses[0] if msgResponse == nil { - return nil, fmt.Errorf("got nil msg response %s", sdk.MsgTypeURL(msg)) + return nil, fmt.Errorf("got nil msg response %s in message result: %s", sdk.MsgTypeURL(msg), msgResult.String()) } response = msgResponse From 90ae0ed2e060b5e010325d9dc9fb79793dd30e39 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 31 Mar 2023 17:11:15 +0200 Subject: [PATCH 12/14] feedback (2/2) - convert as example --- testutil/integration/example_test.go | 97 +++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 10 deletions(-) diff --git a/testutil/integration/example_test.go b/testutil/integration/example_test.go index 77cd59de02a9..3fe5c3460572 100644 --- a/testutil/integration/example_test.go +++ b/testutil/integration/example_test.go @@ -1,9 +1,8 @@ package integration_test import ( - "testing" - - "gotest.tools/v3/assert" + "fmt" + "io" "cosmossdk.io/log" storetypes "cosmossdk.io/store/types" @@ -17,9 +16,12 @@ import ( "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" ) -func TestIntegrationTestExample(t *testing.T) { +// 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{}) @@ -43,7 +45,9 @@ func TestIntegrationTestExample(t *testing.T) { mintKeeper := mintkeeper.NewKeeper(encodingCfg.Codec, keys[minttypes.StoreKey], nil, accountKeeper, nil, authtypes.FeeCollectorName, authority) mintModule := mint.NewAppModule(encodingCfg.Codec, mintKeeper, accountKeeper, nil, nil) - integrationApp := integration.NewIntegrationApp(t.Name(), log.NewTestLogger(t), keys, authModule, mintModule) + // 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)) @@ -58,16 +62,89 @@ func TestIntegrationTestExample(t *testing.T) { Authority: authority, Params: params, }) - assert.NilError(t, err) + if err != nil { + panic(err) + } + // in this example the resut is an empty response, a nil check is enough // in other cases, it is recommanded to check the result value. - assert.Assert(t, result != nil) + if result == nil { + panic(fmt.Errorf("unexpected nil result")) + } // we now check the result resp := minttypes.MsgUpdateParamsResponse{} err = encodingCfg.Codec.Unmarshal(result.Value, &resp) - assert.NilError(t, err) + 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 ExampleOneModule() { + // 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 resut is an empty response, a nil check is enough + // in other cases, it is recommanded 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 can also check the state of the application - assert.DeepEqual(t, mintKeeper.GetParams(integrationApp.SDKContext()), params) + // 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 } From 18d6547fcb41a8f47c5a32f055b8f2aa630fd90f Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 31 Mar 2023 17:43:50 +0200 Subject: [PATCH 13/14] typos and linting --- testutil/integration/example_test.go | 10 +++++----- testutil/integration/router.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/testutil/integration/example_test.go b/testutil/integration/example_test.go index 3fe5c3460572..9607ecb42605 100644 --- a/testutil/integration/example_test.go +++ b/testutil/integration/example_test.go @@ -66,8 +66,8 @@ func Example() { panic(err) } - // in this example the resut is an empty response, a nil check is enough - // in other cases, it is recommanded to check the result value. + // 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")) } @@ -90,7 +90,7 @@ func Example() { // 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 ExampleOneModule() { +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) @@ -127,8 +127,8 @@ func ExampleOneModule() { panic(err) } - // in this example the resut is an empty response, a nil check is enough - // in other cases, it is recommanded to check the result value. + // 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")) } diff --git a/testutil/integration/router.go b/testutil/integration/router.go index 348eda76e5f7..1a62a02ad032 100644 --- a/testutil/integration/router.go +++ b/testutil/integration/router.go @@ -28,7 +28,7 @@ type App struct { queryHelper *baseapp.QueryServiceTestHelper } -// NewIntegrationApp creates a application for testing purposes. This application is able to route messages to the respectives handlers. +// NewIntegrationApp creates a application for testing purposes. This application is able to route messages to their respective handlers. func NewIntegrationApp(nameSuffix string, logger log.Logger, keys map[string]*storetypes.KVStoreKey, modules ...module.AppModuleBasic) *App { db := dbm.NewMemDB() @@ -50,7 +50,7 @@ func NewIntegrationApp(nameSuffix string, logger log.Logger, keys map[string]*st bApp.SetMsgServiceRouter(router) if err := bApp.LoadLatestVersion(); err != nil { - panic(fmt.Errorf("failed to load application version from store:%w", err)) + panic(fmt.Errorf("failed to load application version from store: %w", err)) } ctx := bApp.NewContext(true, cmtproto.Header{}) From b1d47feb12d3c83822651bb83eae513ef8c7db57 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 31 Mar 2023 19:17:09 +0200 Subject: [PATCH 14/14] feedback @likhita-809 --- docs/docs/building-modules/16-testing.md | 4 ++-- testutil/integration/doc.go | 2 +- testutil/integration/example_test.go | 2 +- testutil/integration/router.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/docs/building-modules/16-testing.md b/docs/docs/building-modules/16-testing.md index 26167186a5d1..ad8089e05c9d 100644 --- a/docs/docs/building-modules/16-testing.md +++ b/docs/docs/building-modules/16-testing.md @@ -66,7 +66,7 @@ The SDK provides small helpers for quickly setting up an integration tests. Thes ### Example ```go reference -https://github.com/cosmos/cosmos-sdk/blob/35deb0ce8656915ee179c513ff42b92d30b85f83/testutil/integration/example_test.go#L22-L71 +https://github.com/cosmos/cosmos-sdk/blob/29e22b3bdb05353555c8e0b269311bbff7b8deca/testutil/integration/example_test.go#L22-L89 ``` ## Deterministic and Regression tests @@ -89,7 +89,7 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/tests/integration/bank/kee Simulations uses as well a minimal application, built with [`depinject`](../tooling/02-depinject.md): :::note -You can as well use the `AppConfig` `configurator` for creating an `AppConfig` [inline](https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/x/slashing/app_test.go#L54-L62). There no difference between those two ways, use whichever you prefer. +You can as well use the `AppConfig` `configurator` for creating an `AppConfig` [inline](https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/x/slashing/app_test.go#L54-L62). There is no difference between those two ways, use whichever you prefer. ::: Following is an example for `x/gov/` simulations: diff --git a/testutil/integration/doc.go b/testutil/integration/doc.go index d4a8371c2f98..9bb3feef40f2 100644 --- a/testutil/integration/doc.go +++ b/testutil/integration/doc.go @@ -1,2 +1,2 @@ -// Integration contains the integration test suite used for SDK modules. +// Integration contains the integration test setup used for SDK modules. package integration diff --git a/testutil/integration/example_test.go b/testutil/integration/example_test.go index 9607ecb42605..e15c3801e78c 100644 --- a/testutil/integration/example_test.go +++ b/testutil/integration/example_test.go @@ -19,7 +19,7 @@ import ( "github.com/google/go-cmp/cmp" ) -// Example shows how to use the integration test framework to test the integration of a modules. +// Example shows how to use the integration test framework to test the integration of SDK 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: diff --git a/testutil/integration/router.go b/testutil/integration/router.go index 1a62a02ad032..096614759a22 100644 --- a/testutil/integration/router.go +++ b/testutil/integration/router.go @@ -28,7 +28,7 @@ type App struct { queryHelper *baseapp.QueryServiceTestHelper } -// NewIntegrationApp creates a application for testing purposes. This application is able to route messages to their respective handlers. +// NewIntegrationApp creates an application for testing purposes. This application is able to route messages to their respective handlers. func NewIntegrationApp(nameSuffix string, logger log.Logger, keys map[string]*storetypes.KVStoreKey, modules ...module.AppModuleBasic) *App { db := dbm.NewMemDB()