cosmos-sdk/server/v2/api/grpc/service_test.go
2024-11-06 17:14:14 +00:00

225 lines
5.9 KiB
Go

package grpc
import (
"context"
"fmt"
"testing"
"github.com/cosmos/gogoproto/proto"
gogoproto "github.com/cosmos/gogoproto/types/any"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
appmodulev2 "cosmossdk.io/core/appmodule/v2"
"cosmossdk.io/core/transaction"
)
type MockRequestMessage struct {
Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
}
func (m *MockRequestMessage) XXX_MessageName() string {
return "MockRequestMessage"
}
func (m *MockRequestMessage) Reset() {}
func (m *MockRequestMessage) String() string { return "" }
func (m *MockRequestMessage) ProtoMessage() {}
func (m *MockRequestMessage) ValidateBasic() error {
return nil
}
type MockResponseMessage struct {
Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
}
func (m *MockResponseMessage) Reset() {}
func (m *MockResponseMessage) String() string { return "" }
func (m *MockResponseMessage) ProtoMessage() {}
func (m *MockResponseMessage) ValidateBasic() error {
return nil
}
type mockApp[T transaction.Tx] struct {
mock.Mock
}
func (m *mockApp[T]) QueryHandlers() map[string]appmodulev2.Handler {
args := m.Called()
return args.Get(0).(map[string]appmodulev2.Handler)
}
func (m *mockApp[T]) Query(ctx context.Context, height uint64, msg transaction.Msg) (transaction.Msg, error) {
args := m.Called(ctx, height, msg)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(transaction.Msg), args.Error(1)
}
func TestQuery(t *testing.T) {
tests := []struct {
name string
setupMock func(app *mockApp[transaction.Tx])
request *QueryRequest
expectError bool
expectedError string
}{
{
name: "successful query",
setupMock: func(app *mockApp[transaction.Tx]) {
reqMsg := &MockRequestMessage{Data: "request"}
respMsg := &MockResponseMessage{Data: "response"}
handlers := map[string]appmodulev2.Handler{
"/" + proto.MessageName(&MockRequestMessage{}): {
Func: func(ctx context.Context, msg transaction.Msg) (transaction.Msg, error) {
return respMsg, nil
},
MakeMsg: func() transaction.Msg {
return reqMsg
},
MakeMsgResp: func() transaction.Msg {
return respMsg
},
},
}
app.On("QueryHandlers").Return(handlers)
app.On("Query", mock.Anything, uint64(0), reqMsg).Return(respMsg, nil)
},
request: createTestRequest(t),
expectError: false,
},
{
name: "handler not found",
setupMock: func(app *mockApp[transaction.Tx]) {
handlers := map[string]appmodulev2.Handler{}
app.On("QueryHandlers").Return(handlers)
},
request: createTestRequest(t),
expectError: true,
expectedError: "rpc error: code = NotFound desc = handler not found for /MockRequestMessage",
},
{
name: "query error",
setupMock: func(app *mockApp[transaction.Tx]) {
reqMsg := &MockRequestMessage{Data: "request"}
respMsg := &MockRequestMessage{Data: "response"}
handlers := map[string]appmodulev2.Handler{
"/" + proto.MessageName(&MockRequestMessage{}): {
Func: func(ctx context.Context, msg transaction.Msg) (transaction.Msg, error) {
return respMsg, nil
},
MakeMsg: func() transaction.Msg {
return reqMsg
},
MakeMsgResp: func() transaction.Msg {
return respMsg
},
},
}
app.On("QueryHandlers").Return(handlers)
app.On("Query", mock.Anything, uint64(0), reqMsg).Return(nil, assert.AnError)
},
request: createTestRequest(t),
expectError: true,
expectedError: fmt.Sprintf("rpc error: code = Internal desc = query failed: %s", assert.AnError.Error()),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockApp := &mockApp[transaction.Tx]{}
if tt.setupMock != nil {
tt.setupMock(mockApp)
}
service := &v2Service{mockApp.QueryHandlers(), mockApp.Query}
resp, err := service.Query(context.Background(), tt.request)
if tt.expectError {
assert.Error(t, err)
if tt.expectedError != "" {
assert.Equal(t, tt.expectedError, err.Error())
}
} else {
assert.NoError(t, err)
assert.NotNil(t, resp)
assert.NotNil(t, resp.Response)
}
mockApp.AssertExpectations(t)
})
}
}
func TestV2Service_ListQueryHandlers(t *testing.T) {
tests := []struct {
name string
setupMock func(app *mockApp[transaction.Tx])
}{
{
name: "successful list query handlers",
setupMock: func(app *mockApp[transaction.Tx]) {
reqMsg := &MockRequestMessage{Data: "request"}
respMsg := &MockResponseMessage{Data: "response"}
handlers := map[string]appmodulev2.Handler{
"/test.Query": {
Func: func(ctx context.Context, msg transaction.Msg) (transaction.Msg, error) {
return respMsg, nil
},
MakeMsg: func() transaction.Msg {
return reqMsg
},
MakeMsgResp: func() transaction.Msg {
return respMsg
},
},
}
app.On("QueryHandlers").Return(handlers)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockApp := &mockApp[transaction.Tx]{}
if tt.setupMock != nil {
tt.setupMock(mockApp)
}
service := &v2Service{mockApp.QueryHandlers(), mockApp.Query}
resp, err := service.ListQueryHandlers(context.Background(), &ListQueryHandlersRequest{})
assert.NoError(t, err)
assert.NotNil(t, resp)
assert.Len(t, resp.Handlers, 1)
resp.Handlers[0].RequestName = "/MockRequestMessage"
resp.Handlers[0].ResponseName = "/MockResponseMessage"
mockApp.AssertExpectations(t)
})
}
}
func createTestRequest(t *testing.T) *QueryRequest {
t.Helper()
reqMsg := &MockRequestMessage{Data: "request"}
reqBytes, err := proto.Marshal(reqMsg)
if err != nil {
t.Fatalf("failed to marshal request: %v", err)
}
return &QueryRequest{
Request: &gogoproto.Any{
TypeUrl: "/" + proto.MessageName(reqMsg),
Value: reqBytes,
},
}
}