github.com/cosmos/cosmos-sdk@v0.50.10/server/mock/app.go (about)

     1  package mock
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"path/filepath"
     9  
    10  	abci "github.com/cometbft/cometbft/abci/types"
    11  	db "github.com/cosmos/cosmos-db"
    12  	"google.golang.org/grpc"
    13  	"google.golang.org/protobuf/proto"
    14  	"google.golang.org/protobuf/reflect/protodesc"
    15  	"google.golang.org/protobuf/reflect/protoregistry"
    16  	"google.golang.org/protobuf/types/descriptorpb"
    17  
    18  	"cosmossdk.io/log"
    19  	storetypes "cosmossdk.io/store/types"
    20  
    21  	bam "github.com/cosmos/cosmos-sdk/baseapp"
    22  	"github.com/cosmos/cosmos-sdk/codec"
    23  	"github.com/cosmos/cosmos-sdk/codec/testutil"
    24  	servertypes "github.com/cosmos/cosmos-sdk/server/types"
    25  	sdk "github.com/cosmos/cosmos-sdk/types"
    26  	genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
    27  )
    28  
    29  // NewApp creates a simple mock kvstore app for testing. It should work
    30  // similar to a real app. Make sure rootDir is empty before running the test,
    31  // in order to guarantee consistent results.
    32  func NewApp(rootDir string, logger log.Logger) (servertypes.ABCI, error) {
    33  	db, err := db.NewGoLevelDB("mock", filepath.Join(rootDir, "data"), nil)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	capKeyMainStore := storetypes.NewKVStoreKey("main")
    39  
    40  	baseApp := bam.NewBaseApp("kvstore", logger, db, decodeTx)
    41  	baseApp.MountStores(capKeyMainStore)
    42  	baseApp.SetInitChainer(InitChainer(capKeyMainStore))
    43  
    44  	interfaceRegistry := testutil.CodecOptions{}.NewInterfaceRegistry()
    45  	interfaceRegistry.RegisterImplementations((*sdk.Msg)(nil), &KVStoreTx{})
    46  	baseApp.SetInterfaceRegistry(interfaceRegistry)
    47  
    48  	router := bam.NewMsgServiceRouter()
    49  	router.SetInterfaceRegistry(interfaceRegistry)
    50  
    51  	newDesc := &grpc.ServiceDesc{
    52  		ServiceName: "Test",
    53  		Methods: []grpc.MethodDesc{
    54  			{
    55  				MethodName: "Test",
    56  				Handler:    MsgTestHandler,
    57  			},
    58  		},
    59  	}
    60  
    61  	router.RegisterService(newDesc, &MsgServerImpl{capKeyMainStore})
    62  	baseApp.SetMsgServiceRouter(router)
    63  
    64  	if err := baseApp.LoadLatestVersion(); err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	return baseApp, nil
    69  }
    70  
    71  // KVStoreHandler is a simple handler that takes KVStoreTx and writes
    72  // them to the db.
    73  func KVStoreHandler(storeKey storetypes.StoreKey) bam.MsgServiceHandler {
    74  	return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
    75  		dTx, ok := msg.(*KVStoreTx)
    76  		if !ok {
    77  			return nil, errors.New("KVStoreHandler should only receive KVStoreTx")
    78  		}
    79  
    80  		key := dTx.key
    81  		value := dTx.value
    82  
    83  		store := ctx.KVStore(storeKey)
    84  		store.Set(key, value)
    85  
    86  		return &sdk.Result{
    87  			Log: fmt.Sprintf("set %s=%s", key, value),
    88  		}, nil
    89  	}
    90  }
    91  
    92  // basic KV structure
    93  type KV struct {
    94  	Key   string `json:"key"`
    95  	Value string `json:"value"`
    96  }
    97  
    98  // What Genesis JSON is formatted as
    99  type GenesisJSON struct {
   100  	Values []KV `json:"values"`
   101  }
   102  
   103  // InitChainer returns a function that can initialize the chain
   104  // with key/value pairs
   105  func InitChainer(key storetypes.StoreKey) func(sdk.Context, *abci.RequestInitChain) (*abci.ResponseInitChain, error) {
   106  	return func(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) {
   107  		stateJSON := req.AppStateBytes
   108  
   109  		genesisState := new(GenesisJSON)
   110  		err := json.Unmarshal(stateJSON, genesisState)
   111  		if err != nil {
   112  			return &abci.ResponseInitChain{}, err
   113  		}
   114  
   115  		for _, val := range genesisState.Values {
   116  			store := ctx.KVStore(key)
   117  			store.Set([]byte(val.Key), []byte(val.Value))
   118  		}
   119  		return &abci.ResponseInitChain{}, nil
   120  	}
   121  }
   122  
   123  // AppGenState can be passed into InitCmd, returns a static string of a few
   124  // key-values that can be parsed by InitChainer
   125  func AppGenState(_ *codec.LegacyAmino, _ genutiltypes.AppGenesis, _ []json.RawMessage) (appState json.RawMessage, err error) {
   126  	appState = json.RawMessage(`{
   127    "values": [
   128      {
   129          "key": "hello",
   130          "value": "goodbye"
   131      },
   132      {
   133          "key": "foo",
   134          "value": "bar"
   135      }
   136    ]
   137  }`)
   138  	return
   139  }
   140  
   141  // AppGenStateEmpty returns an empty transaction state for mocking.
   142  func AppGenStateEmpty(_ *codec.LegacyAmino, _ genutiltypes.AppGenesis, _ []json.RawMessage) (appState json.RawMessage, err error) {
   143  	appState = json.RawMessage(``)
   144  	return
   145  }
   146  
   147  // Manually write the handlers for this custom message
   148  type MsgServer interface {
   149  	Test(ctx context.Context, msg *KVStoreTx) (*sdk.Result, error)
   150  }
   151  
   152  type MsgServerImpl struct {
   153  	capKeyMainStore *storetypes.KVStoreKey
   154  }
   155  
   156  func MsgTestHandler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { // nolint: revive // refactor this in a followup pr
   157  	in := new(KVStoreTx)
   158  	if err := dec(in); err != nil {
   159  		return nil, err
   160  	}
   161  	if interceptor == nil {
   162  		return srv.(MsgServer).Test(ctx, in)
   163  	}
   164  	info := &grpc.UnaryServerInfo{
   165  		Server:     srv,
   166  		FullMethod: "/KVStoreTx",
   167  	}
   168  	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
   169  		return srv.(MsgServer).Test(ctx, req.(*KVStoreTx))
   170  	}
   171  	return interceptor(ctx, in, info, handler)
   172  }
   173  
   174  func (m MsgServerImpl) Test(ctx context.Context, msg *KVStoreTx) (*sdk.Result, error) {
   175  	return KVStoreHandler(m.capKeyMainStore)(sdk.UnwrapSDKContext(ctx), msg)
   176  }
   177  
   178  func init() {
   179  	err := registerFauxDescriptor()
   180  	if err != nil {
   181  		panic(err)
   182  	}
   183  }
   184  
   185  func registerFauxDescriptor() error {
   186  	fauxDescriptor, err := protodesc.NewFile(&descriptorpb.FileDescriptorProto{
   187  		Name:             proto.String("faux_proto/test.proto"),
   188  		Dependency:       nil,
   189  		PublicDependency: nil,
   190  		WeakDependency:   nil,
   191  		MessageType: []*descriptorpb.DescriptorProto{
   192  			{
   193  				Name: proto.String("KVStoreTx"),
   194  			},
   195  		},
   196  		EnumType: nil,
   197  		Service: []*descriptorpb.ServiceDescriptorProto{
   198  			{
   199  				Name: proto.String("Test"),
   200  				Method: []*descriptorpb.MethodDescriptorProto{
   201  					{
   202  						Name:       proto.String("Test"),
   203  						InputType:  proto.String("KVStoreTx"),
   204  						OutputType: proto.String("KVStoreTx"),
   205  					},
   206  				},
   207  			},
   208  		},
   209  		Extension:      nil,
   210  		Options:        nil,
   211  		SourceCodeInfo: nil,
   212  		Syntax:         nil,
   213  		Edition:        nil,
   214  	}, nil)
   215  	if err != nil {
   216  		return err
   217  	}
   218  
   219  	return protoregistry.GlobalFiles.RegisterFile(fauxDescriptor)
   220  }