From 893451b1cb040e7a4a22f3fe03a179ab832f58bf Mon Sep 17 00:00:00 2001 From: Lukasz Wojciechowski Date: Fri, 14 Sep 2018 18:28:56 +0200 Subject: [PATCH] Add tests for rpc/dryad package Tests of rpc/dryad package verify both service and client sides of RPC communication connected together. On service side instead of true operations on Dryad mockup is used. The mockup of Dryad interface is generated by go:generate mechanism embedded in boruta.go file and is stored in mocks/mock_dryad.go file. Change-Id: Id7ceaab9a70cc1404b1df9a66978ba0eb551f8bb Signed-off-by: Lukasz Wojciechowski --- boruta.go | 1 + mocks/mock_dryad.go | 70 +++++++++++++ rpc/dryad/dryad_suite_test.go | 29 ++++++ rpc/dryad/dryad_test.go | 225 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 325 insertions(+) create mode 100644 mocks/mock_dryad.go create mode 100644 rpc/dryad/dryad_suite_test.go create mode 100644 rpc/dryad/dryad_test.go diff --git a/boruta.go b/boruta.go index cde8065..6689693 100644 --- a/boruta.go +++ b/boruta.go @@ -23,6 +23,7 @@ package boruta //go:generate mockgen -destination=mocks/mock_requests.go -package=mocks git.tizen.org/tools/boruta Requests //go:generate mockgen -destination=mocks/mock_workers.go -package=mocks git.tizen.org/tools/boruta Workers,Superviser +//go:generate mockgen -destination=mocks/mock_dryad.go -package=mocks git.tizen.org/tools/boruta Dryad import ( "crypto/rsa" diff --git a/mocks/mock_dryad.go b/mocks/mock_dryad.go new file mode 100644 index 0000000..9a63525 --- /dev/null +++ b/mocks/mock_dryad.go @@ -0,0 +1,70 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: git.tizen.org/tools/boruta (interfaces: Dryad) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + gomock "github.com/golang/mock/gomock" + ssh "golang.org/x/crypto/ssh" + reflect "reflect" +) + +// MockDryad is a mock of Dryad interface +type MockDryad struct { + ctrl *gomock.Controller + recorder *MockDryadMockRecorder +} + +// MockDryadMockRecorder is the mock recorder for MockDryad +type MockDryadMockRecorder struct { + mock *MockDryad +} + +// NewMockDryad creates a new mock instance +func NewMockDryad(ctrl *gomock.Controller) *MockDryad { + mock := &MockDryad{ctrl: ctrl} + mock.recorder = &MockDryadMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockDryad) EXPECT() *MockDryadMockRecorder { + return m.recorder +} + +// Healthcheck mocks base method +func (m *MockDryad) Healthcheck() error { + ret := m.ctrl.Call(m, "Healthcheck") + ret0, _ := ret[0].(error) + return ret0 +} + +// Healthcheck indicates an expected call of Healthcheck +func (mr *MockDryadMockRecorder) Healthcheck() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Healthcheck", reflect.TypeOf((*MockDryad)(nil).Healthcheck)) +} + +// Prepare mocks base method +func (m *MockDryad) Prepare(arg0 *ssh.PublicKey) error { + ret := m.ctrl.Call(m, "Prepare", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Prepare indicates an expected call of Prepare +func (mr *MockDryadMockRecorder) Prepare(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prepare", reflect.TypeOf((*MockDryad)(nil).Prepare), arg0) +} + +// PutInMaintenance mocks base method +func (m *MockDryad) PutInMaintenance(arg0 string) error { + ret := m.ctrl.Call(m, "PutInMaintenance", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// PutInMaintenance indicates an expected call of PutInMaintenance +func (mr *MockDryadMockRecorder) PutInMaintenance(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutInMaintenance", reflect.TypeOf((*MockDryad)(nil).PutInMaintenance), arg0) +} diff --git a/rpc/dryad/dryad_suite_test.go b/rpc/dryad/dryad_suite_test.go new file mode 100644 index 0000000..adc5b0d --- /dev/null +++ b/rpc/dryad/dryad_suite_test.go @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package dryad + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestSuperviser(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Dryad Suite") +} diff --git a/rpc/dryad/dryad_test.go b/rpc/dryad/dryad_test.go new file mode 100644 index 0000000..3a48489 --- /dev/null +++ b/rpc/dryad/dryad_test.go @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package dryad + +import ( + "crypto/rand" + "crypto/rsa" + "errors" + "net" + "net/rpc" + + "git.tizen.org/tools/boruta/mocks" + "github.com/golang/mock/gomock" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "golang.org/x/crypto/ssh" +) + +var _ = Describe("Dryad", func() { + var ctrl *gomock.Controller + var md *mocks.MockDryad + + const badAddr = "1500.100.900.300:400" + const testMessage = "This is a test message. Do not panic!" + var testError = errors.New("test error") + + BeforeEach(func() { + ctrl = gomock.NewController(GinkgoT()) + md = mocks.NewMockDryad(ctrl) + }) + AfterEach(func() { + ctrl.Finish() + }) + + Describe("DryadService creation", func() { + It("NewDryadService should create new service", func() { + service := NewDryadService(md) + Expect(service).NotTo(BeNil()) + Expect(service.impl).To(Equal(md)) + }) + It("RegisterDryadService should create and register new service", func() { + rpcServer := rpc.NewServer() + + err := RegisterDryadService(rpcServer, md) + Expect(err).NotTo(HaveOccurred()) + }) + }) + Describe("with DryadService listening", func() { + var listener net.Listener + + BeforeEach(func() { + var err error + listener, err = net.Listen("tcp", "") + Expect(err).NotTo(HaveOccurred()) + rpcServer := rpc.NewServer() + err = RegisterDryadService(rpcServer, md) + Expect(err).NotTo(HaveOccurred()) + go rpcServer.Accept(listener) + }) + AfterEach(func() { + err := listener.Close() + Expect(err).NotTo(HaveOccurred()) + }) + + Describe("Client connection", func() { + It("DialDryadClient should connect to the service", func() { + client, err := DialDryadClient(listener.Addr().String()) + Expect(err).NotTo(HaveOccurred()) + Expect(client).NotTo(BeNil()) + Expect(client.client).NotTo(BeNil()) + }) + It("DialDryadClient should not connect to not existing address", func() { + client, err := DialDryadClient(badAddr) + Expect(err).To(HaveOccurred()) + Expect(client).NotTo(BeNil()) + Expect(client.client).To(BeNil()) + }) + It("NewDryadClient should create new client", func() { + rpcClient, err := rpc.Dial("tcp", listener.Addr().String()) + Expect(err).NotTo(HaveOccurred()) + + client := NewDryadClient(rpcClient) + Expect(client).NotTo(BeNil()) + Expect(client.client).To(Equal(rpcClient)) + }) + It("Close should close connection created with DialDryadClient", func() { + client, err := DialDryadClient(listener.Addr().String()) + Expect(err).NotTo(HaveOccurred()) + Expect(client).NotTo(BeNil()) + Expect(client.client).NotTo(BeNil()) + + err = client.Close() + Expect(err).NotTo(HaveOccurred()) + }) + It("Create should connect to the service", func() { + client := new(DryadClient) + addr, err := net.ResolveTCPAddr("tcp", listener.Addr().String()) + Expect(err).NotTo(HaveOccurred()) + + err = client.Create(addr) + Expect(err).NotTo(HaveOccurred()) + Expect(client).NotTo(BeNil()) + Expect(client.client).NotTo(BeNil()) + }) + It("Create should fail to connect to not existing address", func() { + client := new(DryadClient) + badTCPAddr := &net.TCPAddr{ + IP: net.IPv6unspecified, + Port: -1000, + } + + err := client.Create(badTCPAddr) + Expect(err).To(HaveOccurred()) + Expect(client).NotTo(BeNil()) + Expect(client.client).To(BeNil()) + }) + It("Close should close connection created with DialDryadClient", func() { + client := new(DryadClient) + addr, err := net.ResolveTCPAddr("tcp", listener.Addr().String()) + Expect(err).NotTo(HaveOccurred()) + err = client.Create(addr) + Expect(err).NotTo(HaveOccurred()) + Expect(client).NotTo(BeNil()) + Expect(client.client).NotTo(BeNil()) + + err = client.Close() + Expect(err).NotTo(HaveOccurred()) + }) + }) + Describe("with DryadClient connected", func() { + var client *DryadClient + + BeforeEach(func() { + client = new(DryadClient) + addr, err := net.ResolveTCPAddr("tcp", listener.Addr().String()) + Expect(err).NotTo(HaveOccurred()) + err = client.Create(addr) + Expect(err).NotTo(HaveOccurred()) + Expect(client).NotTo(BeNil()) + Expect(client.client).NotTo(BeNil()) + }) + AfterEach(func() { + err := client.Close() + Expect(err).NotTo(HaveOccurred()) + }) + + Describe("Healthcheck", func() { + It("should run Healthcheck on server", func() { + md.EXPECT().Healthcheck() + + err := client.Healthcheck() + Expect(err).NotTo(HaveOccurred()) + }) + It("should run Healthcheck on server and pass an error", func() { + md.EXPECT().Healthcheck().Return(testError) + + err := client.Healthcheck() + Expect(err).To(Equal(rpc.ServerError(testError.Error()))) + }) + }) + Describe("PutInMaintenance", func() { + It("should run PutInMaintenance on server", func() { + md.EXPECT().PutInMaintenance(testMessage) + + err := client.PutInMaintenance(testMessage) + Expect(err).NotTo(HaveOccurred()) + }) + It("should run PutInMaintenance on server and pass an error", func() { + md.EXPECT().PutInMaintenance(testMessage).Return(testError) + + err := client.PutInMaintenance(testMessage) + Expect(err).To(Equal(rpc.ServerError(testError.Error()))) + }) + }) + Describe("Prepare", func() { + const bits = 32 + + It("should run Prepare on server", func() { + privateKey, err := rsa.GenerateKey(rand.Reader, bits) + Expect(err).NotTo(HaveOccurred()) + publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey) + Expect(err).NotTo(HaveOccurred()) + md.EXPECT().Prepare(gomock.Any()).DoAndReturn(func(key *ssh.PublicKey) error { + received := (*key).Marshal() + expected := publicKey.Marshal() + Expect(received).To(Equal(expected)) + return nil + }) + + err = client.Prepare(&publicKey) + Expect(err).NotTo(HaveOccurred()) + }) + It("should run Prepare on server and pass an error", func() { + privateKey, err := rsa.GenerateKey(rand.Reader, bits) + Expect(err).NotTo(HaveOccurred()) + publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey) + Expect(err).NotTo(HaveOccurred()) + md.EXPECT().Prepare(gomock.Any()).DoAndReturn(func(key *ssh.PublicKey) error { + received := (*key).Marshal() + expected := publicKey.Marshal() + Expect(received).To(Equal(expected)) + return testError + }) + + err = client.Prepare(&publicKey) + Expect(err).To(Equal(rpc.ServerError(testError.Error()))) + }) + }) + }) + }) +}) -- 2.7.4