cosmos-sdk/core/testing/queryclient/queryclient.go
Alex | Interchain Labs 952db2b320
chore: remove baseapp from x/accounts (#23355)
Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com>
2025-01-16 18:52:36 +00:00

132 lines
3.4 KiB
Go

package queryclient
import (
"context"
"fmt"
gogogrpc "github.com/cosmos/gogoproto/grpc"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"
)
var (
_ gogogrpc.ClientConn = &QueryHelper{}
_ gogogrpc.Server = &QueryHelper{}
)
// GRPCQueryHandler defines a function type which handles mocked ABCI Query requests
// using gRPC
type GRPCQueryHandler = func(ctx context.Context, req *QueryRequest) (*QueryResponse, error)
// QueryRequest is a light mock of cometbft abci.QueryRequest.
type QueryRequest struct {
Data []byte
Height int64
}
// QueryResponse is a light mock of cometbft abci.QueryResponse.
type QueryResponse struct {
Value []byte
Height int64
}
// 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(cdc encoding.Codec) *QueryHelper {
qh := &QueryHelper{
cdc: cdc,
routes: map[string]GRPCQueryHandler{},
}
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, &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")
}
// Route returns the GRPCQueryHandler for a given query route path or nil
// if not found
func (q *QueryHelper) Route(path string) GRPCQueryHandler {
handler, found := q.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 (q *QueryHelper) RegisterService(sd *grpc.ServiceDesc, handler interface{}) {
// adds a top-level query handler based on the gRPC service name
for _, method := range sd.Methods {
q.registerABCIQueryHandler(sd, method, handler)
}
}
func (q *QueryHelper) registerABCIQueryHandler(sd *grpc.ServiceDesc, method grpc.MethodDesc, handler interface{}) {
fqName := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName)
methodHandler := method.Handler
_, found := q.routes[fqName]
if found {
panic(fmt.Sprintf("handler for %s already registered", fqName))
}
q.routes[fqName] = func(ctx context.Context, req *QueryRequest) (*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 q.cdc.Unmarshal(req.Data, i)
}, nil)
if err != nil {
return nil, err
}
// proto marshal the result bytes
var resBytes []byte
resBytes, err = q.cdc.Marshal(res)
if err != nil {
return nil, err
}
// return the result bytes as the response value
return &QueryResponse{
Height: req.Height,
Value: resBytes,
}, nil
}
}