integration

package
v0.50.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 4, 2024 License: Apache-2.0 Imports: 21 Imported by: 2

Documentation

Overview

Integration contains the integration test setup used for SDK modules.

Example

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.

package main

import (
	"fmt"

	cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
	"github.com/google/go-cmp/cmp"

	"cosmossdk.io/core/appmodule"
	"cosmossdk.io/log"

	storetypes "cosmossdk.io/store/types"

	addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
	"github.com/cosmos/cosmos-sdk/runtime"
	"github.com/cosmos/cosmos-sdk/testutil/integration"

	sdk "github.com/cosmos/cosmos-sdk/types"

	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"
)

func main() {
	// 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()

	// replace the logger by testing values in a real test case (e.g. log.NewTestLogger(t))
	logger := log.NewNopLogger()

	cms := integration.CreateMultiStore(keys, logger)
	newCtx := sdk.NewContext(cms, cmtproto.Header{}, true, logger)

	accountKeeper := authkeeper.NewAccountKeeper(
		encodingCfg.Codec,
		runtime.NewKVStoreService(keys[authtypes.StoreKey]),
		authtypes.ProtoBaseAccount,
		map[string][]string{minttypes.ModuleName: {authtypes.Minter}},
		addresscodec.NewBech32Codec("cosmos"),
		"cosmos",
		authority,
	)

	// subspace is nil because we don't test params (which is legacy anyway)
	authModule := auth.NewAppModule(encodingCfg.Codec, accountKeeper, authsims.RandomGenesisAccounts, nil)

	// here bankkeeper and staking keeper is nil because we are not testing them
	// subspace is nil because we don't test params (which is legacy anyway)
	mintKeeper := mintkeeper.NewKeeper(encodingCfg.Codec, runtime.NewKVStoreService(keys[minttypes.StoreKey]), nil, accountKeeper, nil, authtypes.FeeCollectorName, authority)
	mintModule := mint.NewAppModule(encodingCfg.Codec, mintKeeper, accountKeeper, nil, nil)

	// create the application and register all the modules from the previous step
	integrationApp := integration.NewIntegrationApp(
		newCtx,
		logger,
		keys,
		encodingCfg.Codec,
		map[string]appmodule.AppModule{
			authtypes.ModuleName: authModule,
			minttypes.ModuleName: 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.NewQueryServerImpl(mintKeeper))

	params := minttypes.DefaultParams()
	params.BlocksPerYear = 10000

	// now we can use the application to test a mint message
	result, err := integrationApp.RunMsg(&minttypes.MsgUpdateParams{
		Authority: authority,
		Params:    params,
	})
	if err != nil {
		panic(err)
	}

	// in this example the result is an empty response, a nil check is enough
	// in other cases, it is recommended to check the result value.
	if result == nil {
		panic(fmt.Errorf("unexpected nil result"))
	}

	// we now check the result
	resp := minttypes.MsgUpdateParamsResponse{}
	err = encodingCfg.Codec.Unmarshal(result.Value, &resp)
	if err != nil {
		panic(err)
	}

	sdkCtx := sdk.UnwrapSDKContext(integrationApp.Context())

	// we should also check the state of the application
	got, err := mintKeeper.Params.Get(sdkCtx)
	if err != nil {
		panic(err)
	}

	if diff := cmp.Diff(got, params); diff != "" {
		panic(diff)
	}
	fmt.Println(got.BlocksPerYear)
}
Output:

10000
Example (OneModule)

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.

package main

import (
	"fmt"
	"io"

	cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
	"github.com/google/go-cmp/cmp"

	"cosmossdk.io/core/appmodule"
	"cosmossdk.io/log"

	storetypes "cosmossdk.io/store/types"

	addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
	"github.com/cosmos/cosmos-sdk/runtime"
	"github.com/cosmos/cosmos-sdk/testutil/integration"

	sdk "github.com/cosmos/cosmos-sdk/types"

	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"
)

func main() {
	// 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()

	// replace the logger by testing values in a real test case (e.g. log.NewTestLogger(t))
	logger := log.NewLogger(io.Discard)

	cms := integration.CreateMultiStore(keys, logger)
	newCtx := sdk.NewContext(cms, cmtproto.Header{}, true, logger)

	accountKeeper := authkeeper.NewAccountKeeper(
		encodingCfg.Codec,
		runtime.NewKVStoreService(keys[authtypes.StoreKey]),
		authtypes.ProtoBaseAccount,
		map[string][]string{minttypes.ModuleName: {authtypes.Minter}},
		addresscodec.NewBech32Codec("cosmos"),
		"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
	integrationApp := integration.NewIntegrationApp(
		newCtx,
		logger,
		keys,
		encodingCfg.Codec,
		map[string]appmodule.AppModule{
			authtypes.ModuleName: 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,
	},
		// this allows to the begin and end blocker of the module before and after the message
		integration.WithAutomaticFinalizeBlock(),
		// this allows to commit the state after the message
		integration.WithAutomaticCommit(),
	)
	if err != nil {
		panic(err)
	}

	// verify that the begin and end blocker were called
	// NOTE: in this example, we are testing auth, which doesn't have any begin or end blocker
	// so verifying the block height is enough
	if integrationApp.LastBlockHeight() != 2 {
		panic(fmt.Errorf("expected block height to be 2, got %d", integrationApp.LastBlockHeight()))
	}

	// in this example the result is an empty response, a nil check is enough
	// in other cases, it is recommended to check the result value.
	if result == nil {
		panic(fmt.Errorf("unexpected nil result"))
	}

	// we now check the result
	resp := authtypes.MsgUpdateParamsResponse{}
	err = encodingCfg.Codec.Unmarshal(result.Value, &resp)
	if err != nil {
		panic(err)
	}

	sdkCtx := sdk.UnwrapSDKContext(integrationApp.Context())

	// we should also check the state of the application
	got := accountKeeper.GetParams(sdkCtx)
	if diff := cmp.Diff(got, params); diff != "" {
		panic(diff)
	}
	fmt.Println(got.MaxMemoCharacters)
}
Output:

1000

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CreateMultiStore

func CreateMultiStore(keys map[string]*storetypes.KVStoreKey, logger log.Logger) storetypes.CommitMultiStore

CreateMultiStore is a helper for setting up multiple stores for provided modules.

Types

type App

type App struct {
	*baseapp.BaseApp
	// contains filtered or unexported fields
}

App is a test application that can be used to test the integration of modules.

func NewIntegrationApp

func NewIntegrationApp(
	sdkCtx sdk.Context,
	logger log.Logger,
	keys map[string]*storetypes.KVStoreKey,
	appCodec codec.Codec,
	modules map[string]appmodule.AppModule,
) *App

NewIntegrationApp creates an application for testing purposes. This application is able to route messages to their respective handlers.

func (*App) Context

func (app *App) Context() context.Context

Context returns the application context. It can be unwrapped to a sdk.Context, with the sdk.UnwrapSDKContext function.

func (*App) QueryHelper

func (app *App) QueryHelper() *baseapp.QueryServiceTestHelper

QueryHelper returns the application query helper. It can be used when registering query services.

func (*App) RunMsg

func (app *App) RunMsg(msg sdk.Msg, option ...Option) (*codectypes.Any, error)

RunMsg provides the ability 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 an Any type. That any type can be unmarshaled to the expected response type. If the message execution fails, an error is returned.

type Config

type Config struct {
	AutomaticFinalizeBlock bool
	AutomaticCommit        bool
}

Config is the configuration for the integration app.

type Option

type Option func(*Config)

Option is a function that can be used to configure the integration app.

func WithAutomaticCommit

func WithAutomaticCommit() Option

WithAutomaticCommit enables automatic commit. This means that the integration app will automatically commit the state after each msgs.

func WithAutomaticFinalizeBlock

func WithAutomaticFinalizeBlock() Option

WithAutomaticFinalizeBlock calls ABCI finalize block.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL