225 lines
5.9 KiB
Go
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,
|
|
},
|
|
}
|
|
}
|