From 7d81bd20f6d08ebed9c6baee6e667de90d96f38b Mon Sep 17 00:00:00 2001 From: Fiachra Corcoran Date: Mon, 12 Feb 2024 14:43:46 +0000 Subject: [PATCH] Add mockery test case to token reconciler (#446) Add mock nephio gitea client. Add example test case fro deketeToken function. Anti pattern arises in relation to the gitea client wrapper as it's also being used by the repo reconciler. --------- Signed-off-by: efiacor Co-authored-by: Liam Fallon <35595825+liamfallon@users.noreply.github.com> --- controllers/pkg/.mockery.yaml | 6 ++ controllers/pkg/giteaclient/giteaclient.go | 5 + .../pkg/giteaclient/mock_GiteaClient.go | 55 +++++++++++ .../reconcilers/repository/reconciler_test.go | 1 + .../pkg/reconcilers/token/reconciler.go | 4 +- .../pkg/reconcilers/token/reconciler_test.go | 98 +++++++++++++++++++ default-go-test.mk | 19 ++++ 7 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 controllers/pkg/.mockery.yaml create mode 100644 controllers/pkg/reconcilers/token/reconciler_test.go diff --git a/controllers/pkg/.mockery.yaml b/controllers/pkg/.mockery.yaml new file mode 100644 index 00000000..7baec8e4 --- /dev/null +++ b/controllers/pkg/.mockery.yaml @@ -0,0 +1,6 @@ +packages: + github.com/nephio-project/nephio/controllers/pkg/giteaclient: + interfaces: + GiteaClient: + config: + dir: "{{.InterfaceDir}}" \ No newline at end of file diff --git a/controllers/pkg/giteaclient/giteaclient.go b/controllers/pkg/giteaclient/giteaclient.go index e812c66a..f2b9c63e 100644 --- a/controllers/pkg/giteaclient/giteaclient.go +++ b/controllers/pkg/giteaclient/giteaclient.go @@ -40,6 +40,7 @@ type GiteaClient interface { GetRepo(userName string, repoCRName string) (*gitea.Repository, *gitea.Response, error) CreateRepo(createRepoOption gitea.CreateRepoOption) (*gitea.Repository, *gitea.Response, error) EditRepo(userName string, repoCRName string, editRepoOption gitea.EditRepoOption) (*gitea.Repository, *gitea.Response, error) + DeleteAccessToken(value interface{}) (*gitea.Response, error) } var lock = &sync.Mutex{} @@ -166,3 +167,7 @@ func (r *gc) CreateRepo(createRepoOption gitea.CreateRepoOption) (*gitea.Reposit func (r *gc) EditRepo(userName string, repoCRName string, editRepoOption gitea.EditRepoOption) (*gitea.Repository, *gitea.Response, error) { return r.giteaClient.EditRepo(userName, repoCRName, editRepoOption) } + +func (r *gc) DeleteAccessToken(value interface{}) (*gitea.Response, error) { + return r.giteaClient.DeleteAccessToken(value) +} diff --git a/controllers/pkg/giteaclient/mock_GiteaClient.go b/controllers/pkg/giteaclient/mock_GiteaClient.go index 20aacf2d..c1ad79d6 100644 --- a/controllers/pkg/giteaclient/mock_GiteaClient.go +++ b/controllers/pkg/giteaclient/mock_GiteaClient.go @@ -86,6 +86,61 @@ func (_c *MockGiteaClient_CreateRepo_Call) RunAndReturn(run func(gitea.CreateRep return _c } + +// DeleteAccessToken provides a mock function with given fields: value +func (_m *MockGiteaClient) DeleteAccessToken(value interface{}) (*gitea.Response, error) { + ret := _m.Called(value) + + var r0 *gitea.Response + var r1 error + if rf, ok := ret.Get(0).(func(interface{}) (*gitea.Response, error)); ok { + return rf(value) + } + if rf, ok := ret.Get(0).(func(interface{}) *gitea.Response); ok { + r0 = rf(value) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*gitea.Response) + } + } + + if rf, ok := ret.Get(1).(func(interface{}) error); ok { + r1 = rf(value) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockGiteaClient_DeleteAccessToken_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteAccessToken' +type MockGiteaClient_DeleteAccessToken_Call struct { + *mock.Call +} + +// DeleteAccessToken is a helper method to define mock.On call +// - value interface{} +func (_e *MockGiteaClient_Expecter) DeleteAccessToken(value interface{}) *MockGiteaClient_DeleteAccessToken_Call { + return &MockGiteaClient_DeleteAccessToken_Call{Call: _e.mock.On("DeleteAccessToken", value)} +} + +func (_c *MockGiteaClient_DeleteAccessToken_Call) Run(run func(value interface{})) *MockGiteaClient_DeleteAccessToken_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *MockGiteaClient_DeleteAccessToken_Call) Return(_a0 *gitea.Response, _a1 error) *MockGiteaClient_DeleteAccessToken_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockGiteaClient_DeleteAccessToken_Call) RunAndReturn(run func(interface{}) (*gitea.Response, error)) *MockGiteaClient_DeleteAccessToken_Call { + _c.Call.Return(run) + return _c +} + // DeleteRepo provides a mock function with given fields: owner, repo func (_m *MockGiteaClient) DeleteRepo(owner string, repo string) (*gitea.Response, error) { ret := _m.Called(owner, repo) diff --git a/controllers/pkg/reconcilers/repository/reconciler_test.go b/controllers/pkg/reconcilers/repository/reconciler_test.go index 366ce613..770b6a60 100644 --- a/controllers/pkg/reconcilers/repository/reconciler_test.go +++ b/controllers/pkg/reconcilers/repository/reconciler_test.go @@ -49,6 +49,7 @@ type repoTest struct { wantErr bool } + func TestUpsertRepo(t *testing.T) { dummyString := "Dummy String" dummyBool := true diff --git a/controllers/pkg/reconcilers/token/reconciler.go b/controllers/pkg/reconcilers/token/reconciler.go index c3c325bf..fd050537 100644 --- a/controllers/pkg/reconcilers/token/reconciler.go +++ b/controllers/pkg/reconcilers/token/reconciler.go @@ -118,7 +118,7 @@ func (r *reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu // Delete the token from the git server // when successful remove the finalizer if cr.Spec.Lifecycle.DeletionPolicy == commonv1alpha1.DeletionDelete { - if err := r.deleteToken(ctx, giteaClient, cr); err != nil { + if err := r.deleteToken(ctx, r.giteaClient, cr); err != nil { return ctrl.Result{Requeue: true}, errors.Wrap(r.Status().Update(ctx, cr), errUpdateStatus) } } @@ -219,7 +219,7 @@ func (r *reconciler) createToken(ctx context.Context, giteaClient *gitea.Client, return nil } -func (r *reconciler) deleteToken(ctx context.Context, giteaClient *gitea.Client, cr *infrav1alpha1.Token) error { +func (r *reconciler) deleteToken(ctx context.Context, giteaClient giteaclient.GiteaClient, cr *infrav1alpha1.Token) error { _, err := giteaClient.DeleteAccessToken(cr.GetTokenName()) if err != nil { log.FromContext(ctx).Error(err, "cannot delete token") diff --git a/controllers/pkg/reconcilers/token/reconciler_test.go b/controllers/pkg/reconcilers/token/reconciler_test.go new file mode 100644 index 00000000..b2967838 --- /dev/null +++ b/controllers/pkg/reconcilers/token/reconciler_test.go @@ -0,0 +1,98 @@ +// Copyright 2023 The Nephio Authors +// +// 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 token + +import ( + "context" + "fmt" + "github.com/go-logr/logr" + "github.com/nephio-project/nephio/controllers/pkg/giteaclient" + "github.com/nephio-project/nephio/controllers/pkg/resource" + "github.com/stretchr/testify/mock" + "sigs.k8s.io/controller-runtime/pkg/log" + "testing" + infrav1alpha1 "github.com/nephio-project/api/infra/v1alpha1" +) + +func TestDeleteToken(t *testing.T) { + type mockHelper struct { + methodName string + argType []string + retArgList []interface{} + } + type fields struct { + APIPatchingApplicator resource.APIPatchingApplicator + giteaClient giteaclient.GiteaClient + finalizer *resource.APIFinalizer + l logr.Logger + } + type args struct { + ctx context.Context + giteaClient giteaclient.GiteaClient + cr *infrav1alpha1.Token + } + tests := []struct { + name string + fields fields + args args + mocks []mockHelper + wantErr bool + }{ + { + name: "Delete Access token reports error", + fields: fields{resource.NewAPIPatchingApplicator(nil), nil, nil, log.FromContext(nil)}, + args: args{nil, nil, &infrav1alpha1.Token{}}, + mocks: []mockHelper{ + {"DeleteAccessToken", []string{"string"}, []interface{}{nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")}}, + }, + wantErr: true, + }, + { + name: "Delete Access token success", + fields: fields{resource.NewAPIPatchingApplicator(nil), nil, nil, log.FromContext(nil)}, + args: args{nil, nil, &infrav1alpha1.Token{}}, + mocks: []mockHelper{ + {"DeleteAccessToken", []string{"string"}, []interface{}{nil, nil}}, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &reconciler{ + APIPatchingApplicator: tt.fields.APIPatchingApplicator, + giteaClient: tt.fields.giteaClient, + finalizer: tt.fields.finalizer, + } + // The below block being setup and processing of mocks before invoking the function to be tested + mockGClient := new(giteaclient.MockGiteaClient) + tt.args.giteaClient = mockGClient + tt.fields.giteaClient = mockGClient + for counter := range tt.mocks { + call := mockGClient.Mock.On(tt.mocks[counter].methodName) + for _, arg := range tt.mocks[counter].argType { + call.Arguments = append(call.Arguments, mock.AnythingOfType(arg)) + } + for _, ret := range tt.mocks[counter].retArgList { + call.ReturnArguments = append(call.ReturnArguments, ret) + } + } + + if err := r.deleteToken(tt.args.ctx, tt.args.giteaClient, tt.args.cr); (err != nil) != tt.wantErr { + t.Errorf("deleteToken() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/default-go-test.mk b/default-go-test.mk index 51aef3d9..e4157197 100644 --- a/default-go-test.mk +++ b/default-go-test.mk @@ -14,10 +14,13 @@ GO_VERSION ?= 1.20.2 +MOCKERY_VERSION=2.37.1 TEST_COVERAGE_FILE=lcov.info TEST_COVERAGE_HTML_FILE=coverage_unit.html TEST_COVERAGE_FUNC_FILE=func_coverage.out GIT_ROOT_DIR ?= $(dir $(lastword $(MAKEFILE_LIST))) +OS_ARCH ?= $(shell uname -m) +OS ?= $(shell uname) include $(GIT_ROOT_DIR)/detect-container-runtime.mk .PHONY: unit @@ -36,6 +39,22 @@ else go tool cover -func=${TEST_COVERAGE_FILE} -o ${TEST_COVERAGE_FUNC_FILE} endif +.PHONY: install-mockery +install-mockery: ## install mockery +ifeq ($(CONTAINER_RUNNABLE), 0) + $(CONTAINER_RUNTIME) pull docker.io/vektra/mockery:v${MOCKERY_VERSION} +else + wget -qO- https://github.com/vektra/mockery/releases/download/v${MOCKERY_VERSION}/mockery_${MOCKERY_VERSION}_${OS}_${OS_ARCH}.tar.gz | sudo tar -xvzf - -C /usr/local/bin +endif + +.PHONY: generate-mocks +generate-mocks: +ifeq ($(CONTAINER_RUNNABLE), 0) + $(CONTAINER_RUNTIME) run --security-opt label=disable -v ${PWD}:/src -w /src docker.io/vektra/mockery:v${MOCKERY_VERSION} +else + mockery +endif + .PHONY: unit-clean unit-clean: ## Clean up the artifacts created by the unit tests ifeq ($(CONTAINER_RUNNABLE), 0)