diff --git a/cluster/mocks/rpc.go b/cluster/mocks/rpc.go new file mode 100644 index 00000000..fabdcccb --- /dev/null +++ b/cluster/mocks/rpc.go @@ -0,0 +1,198 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: cluster/rpc_interfaces.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + gomock "github.com/golang/mock/gomock" + cluster "github.com/topfreegames/pitaya/cluster" + message "github.com/topfreegames/pitaya/internal/message" + protos "github.com/topfreegames/pitaya/protos" + route "github.com/topfreegames/pitaya/route" + session "github.com/topfreegames/pitaya/session" + reflect "reflect" +) + +// MockRPCServer is a mock of RPCServer interface +type MockRPCServer struct { + ctrl *gomock.Controller + recorder *MockRPCServerMockRecorder +} + +// MockRPCServerMockRecorder is the mock recorder for MockRPCServer +type MockRPCServerMockRecorder struct { + mock *MockRPCServer +} + +// NewMockRPCServer creates a new mock instance +func NewMockRPCServer(ctrl *gomock.Controller) *MockRPCServer { + mock := &MockRPCServer{ctrl: ctrl} + mock.recorder = &MockRPCServerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockRPCServer) EXPECT() *MockRPCServerMockRecorder { + return m.recorder +} + +// GetUnhandledRequestsChannel mocks base method +func (m *MockRPCServer) GetUnhandledRequestsChannel() chan *protos.Request { + ret := m.ctrl.Call(m, "GetUnhandledRequestsChannel") + ret0, _ := ret[0].(chan *protos.Request) + return ret0 +} + +// GetUnhandledRequestsChannel indicates an expected call of GetUnhandledRequestsChannel +func (mr *MockRPCServerMockRecorder) GetUnhandledRequestsChannel() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnhandledRequestsChannel", reflect.TypeOf((*MockRPCServer)(nil).GetUnhandledRequestsChannel)) +} + +// GetUserPushChannel mocks base method +func (m *MockRPCServer) GetUserPushChannel() chan *protos.Push { + ret := m.ctrl.Call(m, "GetUserPushChannel") + ret0, _ := ret[0].(chan *protos.Push) + return ret0 +} + +// GetUserPushChannel indicates an expected call of GetUserPushChannel +func (mr *MockRPCServerMockRecorder) GetUserPushChannel() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserPushChannel", reflect.TypeOf((*MockRPCServer)(nil).GetUserPushChannel)) +} + +// Init mocks base method +func (m *MockRPCServer) Init() error { + ret := m.ctrl.Call(m, "Init") + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init +func (mr *MockRPCServerMockRecorder) Init() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockRPCServer)(nil).Init)) +} + +// AfterInit mocks base method +func (m *MockRPCServer) AfterInit() { + m.ctrl.Call(m, "AfterInit") +} + +// AfterInit indicates an expected call of AfterInit +func (mr *MockRPCServerMockRecorder) AfterInit() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterInit", reflect.TypeOf((*MockRPCServer)(nil).AfterInit)) +} + +// BeforeShutdown mocks base method +func (m *MockRPCServer) BeforeShutdown() { + m.ctrl.Call(m, "BeforeShutdown") +} + +// BeforeShutdown indicates an expected call of BeforeShutdown +func (mr *MockRPCServerMockRecorder) BeforeShutdown() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeforeShutdown", reflect.TypeOf((*MockRPCServer)(nil).BeforeShutdown)) +} + +// Shutdown mocks base method +func (m *MockRPCServer) Shutdown() error { + ret := m.ctrl.Call(m, "Shutdown") + ret0, _ := ret[0].(error) + return ret0 +} + +// Shutdown indicates an expected call of Shutdown +func (mr *MockRPCServerMockRecorder) Shutdown() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockRPCServer)(nil).Shutdown)) +} + +// MockRPCClient is a mock of RPCClient interface +type MockRPCClient struct { + ctrl *gomock.Controller + recorder *MockRPCClientMockRecorder +} + +// MockRPCClientMockRecorder is the mock recorder for MockRPCClient +type MockRPCClientMockRecorder struct { + mock *MockRPCClient +} + +// NewMockRPCClient creates a new mock instance +func NewMockRPCClient(ctrl *gomock.Controller) *MockRPCClient { + mock := &MockRPCClient{ctrl: ctrl} + mock.recorder = &MockRPCClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockRPCClient) EXPECT() *MockRPCClientMockRecorder { + return m.recorder +} + +// Send mocks base method +func (m *MockRPCClient) Send(route string, data []byte) error { + ret := m.ctrl.Call(m, "Send", route, data) + ret0, _ := ret[0].(error) + return ret0 +} + +// Send indicates an expected call of Send +func (mr *MockRPCClientMockRecorder) Send(route, data interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockRPCClient)(nil).Send), route, data) +} + +// Call mocks base method +func (m *MockRPCClient) Call(rpcType protos.RPCType, route *route.Route, session *session.Session, msg *message.Message, server *cluster.Server) (*protos.Response, error) { + ret := m.ctrl.Call(m, "Call", rpcType, route, session, msg, server) + ret0, _ := ret[0].(*protos.Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Call indicates an expected call of Call +func (mr *MockRPCClientMockRecorder) Call(rpcType, route, session, msg, server interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Call", reflect.TypeOf((*MockRPCClient)(nil).Call), rpcType, route, session, msg, server) +} + +// Init mocks base method +func (m *MockRPCClient) Init() error { + ret := m.ctrl.Call(m, "Init") + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init +func (mr *MockRPCClientMockRecorder) Init() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockRPCClient)(nil).Init)) +} + +// AfterInit mocks base method +func (m *MockRPCClient) AfterInit() { + m.ctrl.Call(m, "AfterInit") +} + +// AfterInit indicates an expected call of AfterInit +func (mr *MockRPCClientMockRecorder) AfterInit() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterInit", reflect.TypeOf((*MockRPCClient)(nil).AfterInit)) +} + +// BeforeShutdown mocks base method +func (m *MockRPCClient) BeforeShutdown() { + m.ctrl.Call(m, "BeforeShutdown") +} + +// BeforeShutdown indicates an expected call of BeforeShutdown +func (mr *MockRPCClientMockRecorder) BeforeShutdown() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeforeShutdown", reflect.TypeOf((*MockRPCClient)(nil).BeforeShutdown)) +} + +// Shutdown mocks base method +func (m *MockRPCClient) Shutdown() error { + ret := m.ctrl.Call(m, "Shutdown") + ret0, _ := ret[0].(error) + return ret0 +} + +// Shutdown indicates an expected call of Shutdown +func (mr *MockRPCClientMockRecorder) Shutdown() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockRPCClient)(nil).Shutdown)) +} diff --git a/service/handler_test.go b/service/handler_test.go index d4e57e4a..1f809e33 100644 --- a/service/handler_test.go +++ b/service/handler_test.go @@ -45,12 +45,12 @@ func (m *MyComp) Handler2(ss *session.Session, b []byte) ([]byte, error) { return nil, nil } -type NoHandlerComp struct { +type NoHandlerRemoteComp struct { component.Base } -func (m *NoHandlerComp) Init() {} -func (m *NoHandlerComp) Shutdown() {} +func (m *NoHandlerRemoteComp) Init() {} +func (m *NoHandlerRemoteComp) Shutdown() {} func TestNewHandlerService(t *testing.T) { dieChan := make(chan bool) @@ -61,10 +61,6 @@ func TestNewHandlerService(t *testing.T) { mockSerializer := mocks.NewMockSerializer(ctrl) heartbeatTimeout := 1 * time.Second sv := &cluster.Server{} - // ID: "id1", - // Type: "type1", - // Frontend: true, - // } remoteSvc := &RemoteService{} svc := NewHandlerService( dieChan, @@ -84,7 +80,6 @@ func TestNewHandlerService(t *testing.T) { assert.Equal(t, mockSerializer, svc.serializer) assert.Equal(t, heartbeatTimeout, svc.heartbeatTimeout) assert.Equal(t, 10, svc.messagesBufferSize) - assert.Equal(t, mockSerializer, svc.serializer) assert.Equal(t, sv, svc.server) assert.Equal(t, remoteSvc, svc.remoteService) assert.NotNil(t, svc.chLocalProcess) @@ -119,8 +114,8 @@ func TestHandlerServiceRegisterFailsIfRegisterTwice(t *testing.T) { func TestHandlerServiceRegisterFailsIfNoHandlerMethods(t *testing.T) { svc := NewHandlerService(nil, nil, nil, nil, 0, 0, 0, 0, nil, nil) - err := svc.Register(&NoHandlerComp{}, []component.Option{}) - assert.Equal(t, errors.New("type NoHandlerComp has no exported methods of suitable type"), err) + err := svc.Register(&NoHandlerRemoteComp{}, []component.Option{}) + assert.Equal(t, errors.New("type NoHandlerRemoteComp has no exported methods of suitable type"), err) } func TestHandlerServiceProcessMessage(t *testing.T) { diff --git a/service/remote_test.go b/service/remote_test.go new file mode 100644 index 00000000..d3697bf8 --- /dev/null +++ b/service/remote_test.go @@ -0,0 +1,202 @@ +// Copyright (c) TFG Co. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package service + +import ( + "errors" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/golang/mock/gomock" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/topfreegames/pitaya/cluster" + clustermocks "github.com/topfreegames/pitaya/cluster/mocks" + "github.com/topfreegames/pitaya/component" + "github.com/topfreegames/pitaya/constants" + "github.com/topfreegames/pitaya/helpers" + "github.com/topfreegames/pitaya/internal/codec" + "github.com/topfreegames/pitaya/internal/message" + "github.com/topfreegames/pitaya/protos" + "github.com/topfreegames/pitaya/route" + "github.com/topfreegames/pitaya/router" + serializemocks "github.com/topfreegames/pitaya/serialize/mocks" + "github.com/topfreegames/pitaya/session" + "github.com/topfreegames/pitaya/session/mocks" +) + +func (m *MyComp) Remote1() ([]byte, error) { + return nil, nil +} +func (m *MyComp) Remote2() (*TestType, error) { + return nil, nil +} + +func TestNewRemoteService(t *testing.T) { + packetEncoder := codec.NewPomeloPacketEncoder() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockSerializer := serializemocks.NewMockSerializer(ctrl) + mockSD := clustermocks.NewMockServiceDiscovery(ctrl) + mockRPCClient := clustermocks.NewMockRPCClient(ctrl) + mockRPCServer := clustermocks.NewMockRPCServer(ctrl) + router := router.New() + svc := NewRemoteService(mockRPCClient, mockRPCServer, mockSD, packetEncoder, mockSerializer, router) + + assert.NotNil(t, svc) + assert.Empty(t, svc.services) + assert.Equal(t, mockRPCClient, svc.rpcClient) + assert.Equal(t, mockRPCServer, svc.rpcServer) + assert.Equal(t, packetEncoder, svc.encoder) + assert.Equal(t, mockSD, svc.serviceDiscovery) + assert.Equal(t, mockSerializer, svc.serializer) + assert.Equal(t, router, svc.router) +} + +func TestRemoteServiceRegister(t *testing.T) { + svc := NewRemoteService(nil, nil, nil, nil, nil, nil) + err := svc.Register(&MyComp{}, []component.Option{}) + assert.NoError(t, err) + defer func() { remotes = make(map[string]*component.Remote, 0) }() + assert.Len(t, svc.services, 1) + val, ok := svc.services["MyComp"] + assert.True(t, ok) + assert.NotNil(t, val) + assert.Len(t, remotes, 2) + val2, ok := remotes["MyComp.Remote1"] + assert.True(t, ok) + assert.NotNil(t, val2) + val2, ok = remotes["MyComp.Remote2"] + assert.True(t, ok) + assert.NotNil(t, val2) +} + +func TestRemoteServiceRegisterFailsIfRegisterTwice(t *testing.T) { + svc := NewRemoteService(nil, nil, nil, nil, nil, nil) + err := svc.Register(&MyComp{}, []component.Option{}) + assert.NoError(t, err) + err = svc.Register(&MyComp{}, []component.Option{}) + assert.Contains(t, err.Error(), "remote: service already defined") +} + +func TestRemoteServiceRegisterFailsIfNoRemoteMethods(t *testing.T) { + svc := NewRemoteService(nil, nil, nil, nil, nil, nil) + err := svc.Register(&NoHandlerRemoteComp{}, []component.Option{}) + assert.Equal(t, errors.New("type NoHandlerRemoteComp has no exported methods of suitable type"), err) +} + +func TestRemoteServiceProcessUserPush(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockRPCServer := clustermocks.NewMockRPCServer(ctrl) + mockEntity := mocks.NewMockNetworkEntity(ctrl) + + uid := uuid.New().String() + push := &protos.Push{Route: "bla.bla", Uid: uid, Data: []byte("data")} + userPushCh := make(chan *protos.Push) + + ss := session.New(mockEntity, true) + err := ss.Bind(uid) + assert.NoError(t, err) + + svc := NewRemoteService(nil, mockRPCServer, nil, nil, nil, nil) + assert.NotNil(t, svc) + mockRPCServer.EXPECT().GetUserPushChannel().Return(userPushCh) + + called := false + mockEntity.EXPECT().Push(push.Route, push.Data).Do(func(route string, data []byte) { + called = true + }) + go svc.ProcessUserPush() + userPushCh <- push + helpers.ShouldEventuallyReturn(t, func() bool { return called == true }, true) +} + +func TestRemoteServiceSendReply(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockRPCClient := clustermocks.NewMockRPCClient(ctrl) + svc := NewRemoteService(mockRPCClient, nil, nil, nil, nil, nil) + assert.NotNil(t, svc) + + reply := uuid.New().String() + resp := &protos.Response{Data: []byte(uuid.New().String()), Error: uuid.New().String()} + p, _ := proto.Marshal(resp) + mockRPCClient.EXPECT().Send(reply, p) + svc.sendReply(reply, resp) +} + +func TestRemoteServiceRemoteCall(t *testing.T) { + ss := session.New(nil, true) + rt := route.NewRoute("sv", "svc", "method") + sv := &cluster.Server{} + tables := []struct { + name string + server *cluster.Server + res *protos.Response + err error + }{ + {"no_target_route_error", nil, nil, constants.ErrServiceDiscoveryNotInitialized}, + {"error", sv, nil, errors.New("ble")}, + {"success", sv, &protos.Response{Data: []byte("ok")}, nil}, + } + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + for _, table := range tables { + t.Run(table.name, func(t *testing.T) { + mockRPCClient := clustermocks.NewMockRPCClient(ctrl) + svc := NewRemoteService(mockRPCClient, nil, nil, nil, nil, nil) + assert.NotNil(t, svc) + + msg := &message.Message{} + if sv != nil { + mockRPCClient.EXPECT().Call(protos.RPCType_Sys, rt, ss, msg, sv).Return(table.res, table.err) + } + res, err := svc.remoteCall(sv, protos.RPCType_Sys, rt, ss, msg) + assert.Equal(t, table.err, err) + assert.Equal(t, table.res, res) + }) + } +} + +func TestRemoteServiceProcessRemoteMessages(t *testing.T) { + // TODO +} + +func TestRemoteServiceHandleRPCUser(t *testing.T) { + // TODO +} + +func TestRemoteServiceHandleRPCSys(t *testing.T) { + // TODO +} + +func TestRemoteServiceRemoteProcess(t *testing.T) { + // TODO +} + +func TestRemoteServiceRPC(t *testing.T) { + // TODO +}