Skip to content

Commit

Permalink
use mocked query client
Browse files Browse the repository at this point in the history
  • Loading branch information
aljo242 committed Jan 6, 2025
1 parent d42ac1f commit a6ffa04
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 6 deletions.
1 change: 1 addition & 0 deletions core/testing/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package coretesting

import (
"context"

"cosmossdk.io/core/event"
"cosmossdk.io/core/gas"
"cosmossdk.io/core/header"
Expand Down
2 changes: 1 addition & 1 deletion schema/appdata/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func ListenerMux(listeners ...Listener) Listener {

mux.onBatch = func(batch PacketBatch) error {
for _, listener := range listeners {
err := batch.apply(&listener) //nolint:gosec // aliasing is safe here
err := batch.apply(&listener)
if err != nil {
return err
}
Expand Down
131 changes: 131 additions & 0 deletions testutil/queryclient/queryclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package queryclient

import (
"context"
"fmt"

abci "github.com/cometbft/cometbft/api/cometbft/abci/v1"
gogogrpc "github.com/cosmos/gogoproto/grpc"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"

"github.com/cosmos/cosmos-sdk/client/grpc/reflection"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
)

var (
_ gogogrpc.ClientConn = &QueryHelper{}
_ gogogrpc.ClientConn = &QueryHelper{}
)

// QueryHelper is a test utility for building a query client from a proto interface registry.
type QueryHelper struct {
cdc encoding.Codec
routes map[string]GRPCQueryHandler
}

func NewQueryHelper(interfaceRegistry codectypes.InterfaceRegistry) *QueryHelper {
// instantiate the codec
cdc := codec.NewProtoCodec(interfaceRegistry).GRPCCodec()
// Once we have an interface registry, we can register the interface
// registry reflection gRPC service.

qH := &QueryHelper{
cdc: cdc,
routes: map[string]GRPCQueryHandler{},
}

reflection.RegisterReflectionServiceServer(qH, reflection.NewReflectionServiceServer(interfaceRegistry))

return qH
}

// Invoke implements the grpc ClientConn.Invoke method
func (q *QueryHelper) Invoke(ctx context.Context, method string, args, reply interface{}, _ ...grpc.CallOption) error {
querier := q.Route(method)
if querier == nil {
return fmt.Errorf("handler not found for %s", method)
}
reqBz, err := q.cdc.Marshal(args)
if err != nil {
return err
}

res, err := querier(ctx, &abci.QueryRequest{Data: reqBz})
if err != nil {
return err
}

err = q.cdc.Unmarshal(res.Value, reply)
if err != nil {
return err
}

return nil
}

// NewStream implements the grpc ClientConn.NewStream method
func (q *QueryHelper) NewStream(context.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
panic("not implemented")
}

// GRPCQueryHandler defines a function type which handles ABCI Query requests
// using gRPC
type GRPCQueryHandler = func(ctx context.Context, req *abci.QueryRequest) (*abci.QueryResponse, error)

// Route returns the GRPCQueryHandler for a given query route path or nil
// if not found
func (qrt *QueryHelper) Route(path string) GRPCQueryHandler {
handler, found := qrt.routes[path]
if !found {
return nil
}
return handler
}

// RegisterService implements the gRPC Server.RegisterService method. sd is a gRPC
// service description, handler is an object which implements that gRPC service/
//
// This functions PANICS:
// - if a protobuf service is registered twice.
func (qrt *QueryHelper) RegisterService(sd *grpc.ServiceDesc, handler interface{}) {
// adds a top-level query handler based on the gRPC service name
for _, method := range sd.Methods {
qrt.registerABCIQueryHandler(sd, method, handler)
}
}

func (qrt *QueryHelper) registerABCIQueryHandler(sd *grpc.ServiceDesc, method grpc.MethodDesc, handler interface{}) {
fqName := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName)
methodHandler := method.Handler

_, found := qrt.routes[fqName]
if found {
panic(fmt.Sprintf("handler for %s already registered", fqName))
}

qrt.routes[fqName] = func(ctx context.Context, req *abci.QueryRequest) (*abci.QueryResponse, error) {
// call the method handler from the service description with the handler object,
// a wrapped sdk.Context with proto-unmarshaled data from the ABCI request data
res, err := methodHandler(handler, ctx, func(i interface{}) error {
return qrt.cdc.Unmarshal(req.Data, i)
}, nil)
if err != nil {
return nil, err
}

// proto marshal the result bytes
var resBytes []byte
resBytes, err = qrt.cdc.Marshal(res)
if err != nil {
return nil, err
}

// return the result bytes as the response value
return &abci.QueryResponse{
Height: req.Height,
Value: resBytes,
}, nil
}
}
2 changes: 1 addition & 1 deletion x/bank/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ func (suite *KeeperTestSuite) TestQueryParams() {
func (suite *KeeperTestSuite) TestQueryDenomsMetadata() {
var (
req *types.QueryDenomsMetadataRequest
expMetadata = []types.Metadata{}
expMetadata []types.Metadata
)

testCases := []struct {
Expand Down
9 changes: 5 additions & 4 deletions x/bank/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil/queryclient"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
Expand Down Expand Up @@ -117,7 +118,7 @@ type KeeperTestSuite struct {
addrCdc address.Codec
authKeeper *banktestutil.MockAccountKeeper

queryClient banktypes.QueryServer
queryClient banktypes.QueryClient
msgServer banktypes.MsgServer

encCfg moduletestutil.TestEncodingConfig
Expand Down Expand Up @@ -164,11 +165,11 @@ func (suite *KeeperTestSuite) SetupTest() {
DefaultSendEnabled: banktypes.DefaultDefaultSendEnabled,
}))

queryHelper := queryclient.NewQueryHelper(encCfg.InterfaceRegistry)
banktypes.RegisterQueryServer(queryHelper, suite.bankKeeper)
banktypes.RegisterInterfaces(encCfg.InterfaceRegistry)

queryServer := keeper.NewQuerier(&suite.bankKeeper)

suite.queryClient = queryServer
suite.queryClient = banktypes.NewQueryClient(queryHelper)
suite.msgServer = keeper.NewMsgServerImpl(suite.bankKeeper)
suite.encCfg = encCfg
suite.env = env
Expand Down

0 comments on commit a6ffa04

Please sign in to comment.