From 74ddac862c1e16587b939ae1ceba18e7f4afb5c9 Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Tue, 8 Oct 2024 16:51:12 +0530 Subject: [PATCH 01/11] [MM-844]: Added server testcase for store/welcome_store.go --- calendar/store/welcome_store_test.go | 189 ++++++++++++++++++ calendar/tracker/mock_tracker/mock_tracker.go | 107 ++++++++++ 2 files changed, 296 insertions(+) create mode 100644 calendar/store/welcome_store_test.go create mode 100644 calendar/tracker/mock_tracker/mock_tracker.go diff --git a/calendar/store/welcome_store_test.go b/calendar/store/welcome_store_test.go new file mode 100644 index 00000000..78968cf2 --- /dev/null +++ b/calendar/store/welcome_store_test.go @@ -0,0 +1,189 @@ +package store + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/mattermost/mattermost/server/public/model" + "github.com/mattermost/mattermost/server/public/plugin" + + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/tracker/mock_tracker" + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/utils/bot/mock_bot" +) + +type MockPluginAPI struct { + plugin.API + mock.Mock +} + +func (m *MockPluginAPI) KVGet(key string) ([]byte, *model.AppError) { + args := m.Called(key) + data, _ := args.Get(0).([]byte) + if err := args.Get(1); err != nil { + return nil, err.(*model.AppError) + } + return data, nil +} + +func (m *MockPluginAPI) KVSet(key string, data []byte) *model.AppError { + args := m.Called(key, data) + if err := args.Get(0); err != nil { + return err.(*model.AppError) + } + return nil +} + +func (m *MockPluginAPI) KVDelete(key string) *model.AppError { + args := m.Called(key) + if err := args.Get(0); err != nil { + return err.(*model.AppError) + } + return nil +} + +func TestLoadUserWelcomePost(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockLogger := mock_bot.NewMockLogger(ctrl) + mockTracker := mock_tracker.NewMockTracker(ctrl) + mockAPI := &MockPluginAPI{} + store := NewPluginStore(mockAPI, mockLogger, mockTracker, false, nil) + + tests := []struct { + name string + setup func(*MockPluginAPI) + assertions func(*testing.T, string, error) + }{ + { + name: "Error loading user welcome post", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVGet", mock.AnythingOfType("string")).Return(nil, &model.AppError{Message: "KVGet failed"}) + }, + assertions: func(t *testing.T, resp string, err error) { + require.Equal(t, resp, "") + require.EqualError(t, err, "failed plugin KVGet: KVGet failed") + }, + }, + { + name: "Success loading user welcome post", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.ExpectedCalls = nil + mockAPI.On("KVGet", mock.AnythingOfType("string")).Return([]byte(`"mockPostID"`), nil) + }, + assertions: func(t *testing.T, resp string, err error) { + require.NoError(t, err) + require.Equal(t, "mockPostID", resp) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.setup(mockAPI) + + resp, err := store.LoadUserWelcomePost("mockMMUserID") + + tt.assertions(t, resp, err) + + mockAPI.AssertExpectations(t) + }) + } +} + +func TestStoreUserWelcomePost(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockLogger := mock_bot.NewMockLogger(ctrl) + mockTracker := mock_tracker.NewMockTracker(ctrl) + mockAPI := &MockPluginAPI{} + store := NewPluginStore(mockAPI, mockLogger, mockTracker, false, nil) + + tests := []struct { + name string + setup func(*MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error storing user welcome post", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVSet", mock.Anything, mock.Anything).Return(&model.AppError{Message: "KVSet failed"}) + }, + assertions: func(t *testing.T, err error) { + require.ErrorContainsf(t, err, "failed plugin KVSet (ttl: 0s)", `"mockMMUserID": KVSet failed`) + }, + }, + { + name: "Success storing user welcome post", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.ExpectedCalls = nil + mockAPI.On("KVSet", mock.Anything, mock.Anything).Return(nil) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.setup(mockAPI) + + err := store.StoreUserWelcomePost("mockMMUserID", "mockPostID") + + tt.assertions(t, err) + + mockAPI.AssertExpectations(t) + }) + } +} + +func TestDeleteUserWelcomePost(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockLogger := mock_bot.NewMockLogger(ctrl) + mockTracker := mock_tracker.NewMockTracker(ctrl) + mockAPI := &MockPluginAPI{} + store := NewPluginStore(mockAPI, mockLogger, mockTracker, false, nil) + + tests := []struct { + name string + setup func(*MockPluginAPI) + assertions func(*testing.T, string, error) + }{ + { + name: "Error deleting user welcome post", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVGet", mock.AnythingOfType("string")).Return([]byte(`"mockPostID"`), nil) + mockAPI.On("KVDelete", mock.Anything, mock.Anything).Return(&model.AppError{Message: "KVDelete failed"}) + }, + assertions: func(t *testing.T, resp string, err error) { + require.Equal(t, "", resp) + require.ErrorContains(t, err, "failed plugin KVdelete", "KVDelete failed") + }, + }, + { + name: "Success deleting user welcome post", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.ExpectedCalls = nil + mockAPI.On("KVGet", mock.AnythingOfType("string")).Return([]byte(`"mockPostID"`), nil) + mockAPI.On("KVDelete", mock.Anything, mock.Anything).Return(nil) + }, + assertions: func(t *testing.T, resp string, err error) { + require.Equal(t, "mockPostID", resp) + require.NoError(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.setup(mockAPI) + + resp, err := store.DeleteUserWelcomePost("mockMMUserID") + + tt.assertions(t, resp, err) + + mockAPI.AssertExpectations(t) + }) + } +} diff --git a/calendar/tracker/mock_tracker/mock_tracker.go b/calendar/tracker/mock_tracker/mock_tracker.go new file mode 100644 index 00000000..fe1ddec7 --- /dev/null +++ b/calendar/tracker/mock_tracker/mock_tracker.go @@ -0,0 +1,107 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: tracker.go + +// Package mock_tracker is a generated GoMock package. +package mock_tracker + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + model "github.com/mattermost/mattermost/server/public/model" +) + +// MockTracker is a mock of Tracker interface. +type MockTracker struct { + ctrl *gomock.Controller + recorder *MockTrackerMockRecorder +} + +// MockTrackerMockRecorder is the mock recorder for MockTracker. +type MockTrackerMockRecorder struct { + mock *MockTracker +} + +// NewMockTracker creates a new mock instance. +func NewMockTracker(ctrl *gomock.Controller) *MockTracker { + mock := &MockTracker{ctrl: ctrl} + mock.recorder = &MockTrackerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockTracker) EXPECT() *MockTrackerMockRecorder { + return m.recorder +} + +// ReloadConfig mocks base method. +func (m *MockTracker) ReloadConfig(config *model.Config) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReloadConfig", config) +} + +// ReloadConfig indicates an expected call of ReloadConfig. +func (mr *MockTrackerMockRecorder) ReloadConfig(config interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReloadConfig", reflect.TypeOf((*MockTracker)(nil).ReloadConfig), config) +} + +// TrackAutomaticStatusUpdate mocks base method. +func (m *MockTracker) TrackAutomaticStatusUpdate(userID, value, location string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "TrackAutomaticStatusUpdate", userID, value, location) +} + +// TrackAutomaticStatusUpdate indicates an expected call of TrackAutomaticStatusUpdate. +func (mr *MockTrackerMockRecorder) TrackAutomaticStatusUpdate(userID, value, location interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TrackAutomaticStatusUpdate", reflect.TypeOf((*MockTracker)(nil).TrackAutomaticStatusUpdate), userID, value, location) +} + +// TrackDailySummarySent mocks base method. +func (m *MockTracker) TrackDailySummarySent(userID string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "TrackDailySummarySent", userID) +} + +// TrackDailySummarySent indicates an expected call of TrackDailySummarySent. +func (mr *MockTrackerMockRecorder) TrackDailySummarySent(userID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TrackDailySummarySent", reflect.TypeOf((*MockTracker)(nil).TrackDailySummarySent), userID) +} + +// TrackUserAuthenticated mocks base method. +func (m *MockTracker) TrackUserAuthenticated(userID string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "TrackUserAuthenticated", userID) +} + +// TrackUserAuthenticated indicates an expected call of TrackUserAuthenticated. +func (mr *MockTrackerMockRecorder) TrackUserAuthenticated(userID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TrackUserAuthenticated", reflect.TypeOf((*MockTracker)(nil).TrackUserAuthenticated), userID) +} + +// TrackUserDeauthenticated mocks base method. +func (m *MockTracker) TrackUserDeauthenticated(userID string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "TrackUserDeauthenticated", userID) +} + +// TrackUserDeauthenticated indicates an expected call of TrackUserDeauthenticated. +func (mr *MockTrackerMockRecorder) TrackUserDeauthenticated(userID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TrackUserDeauthenticated", reflect.TypeOf((*MockTracker)(nil).TrackUserDeauthenticated), userID) +} + +// TrackWelcomeFlowCompletion mocks base method. +func (m *MockTracker) TrackWelcomeFlowCompletion(userID string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "TrackWelcomeFlowCompletion", userID) +} + +// TrackWelcomeFlowCompletion indicates an expected call of TrackWelcomeFlowCompletion. +func (mr *MockTrackerMockRecorder) TrackWelcomeFlowCompletion(userID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TrackWelcomeFlowCompletion", reflect.TypeOf((*MockTracker)(nil).TrackWelcomeFlowCompletion), userID) +} From a8a34734bf8fa46fab89e835bcb4de866408dc5f Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Tue, 8 Oct 2024 18:31:11 +0530 Subject: [PATCH 02/11] [MM-844]: Updated the go.sum --- go.mod | 1 + go.sum | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/go.mod b/go.mod index 46583399..123599fb 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/segmentio/backo-go v1.0.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/stretchr/objx v0.5.1 // indirect github.com/tidwall/gjson v1.17.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect diff --git a/go.sum b/go.sum index 74bdcfea..e5ec58f6 100644 --- a/go.sum +++ b/go.sum @@ -187,12 +187,17 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= From 1f32a0658a9a61640beb4ad65b1bb2779ea70603 Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Mon, 14 Oct 2024 12:46:09 +0530 Subject: [PATCH 03/11] [MM-844]: Moved clearing of expected mock calls to loop --- calendar/store/welcome_store_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/calendar/store/welcome_store_test.go b/calendar/store/welcome_store_test.go index 78968cf2..b95f52ba 100644 --- a/calendar/store/welcome_store_test.go +++ b/calendar/store/welcome_store_test.go @@ -70,7 +70,6 @@ func TestLoadUserWelcomePost(t *testing.T) { { name: "Success loading user welcome post", setup: func(mockAPI *MockPluginAPI) { - mockAPI.ExpectedCalls = nil mockAPI.On("KVGet", mock.AnythingOfType("string")).Return([]byte(`"mockPostID"`), nil) }, assertions: func(t *testing.T, resp string, err error) { @@ -81,6 +80,7 @@ func TestLoadUserWelcomePost(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + mockAPI.ExpectedCalls = nil tt.setup(mockAPI) resp, err := store.LoadUserWelcomePost("mockMMUserID") @@ -117,7 +117,6 @@ func TestStoreUserWelcomePost(t *testing.T) { { name: "Success storing user welcome post", setup: func(mockAPI *MockPluginAPI) { - mockAPI.ExpectedCalls = nil mockAPI.On("KVSet", mock.Anything, mock.Anything).Return(nil) }, assertions: func(t *testing.T, err error) { @@ -127,6 +126,7 @@ func TestStoreUserWelcomePost(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + mockAPI.ExpectedCalls = nil tt.setup(mockAPI) err := store.StoreUserWelcomePost("mockMMUserID", "mockPostID") @@ -165,7 +165,6 @@ func TestDeleteUserWelcomePost(t *testing.T) { { name: "Success deleting user welcome post", setup: func(mockAPI *MockPluginAPI) { - mockAPI.ExpectedCalls = nil mockAPI.On("KVGet", mock.AnythingOfType("string")).Return([]byte(`"mockPostID"`), nil) mockAPI.On("KVDelete", mock.Anything, mock.Anything).Return(nil) }, @@ -177,6 +176,7 @@ func TestDeleteUserWelcomePost(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + mockAPI.ExpectedCalls = nil tt.setup(mockAPI) resp, err := store.DeleteUserWelcomePost("mockMMUserID") From 3a2b112badfc5bf2087e8ad20068ba06affe12f3 Mon Sep 17 00:00:00 2001 From: kshitij katiyar <90389917+Kshitij-Katiyar@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:43:00 +0530 Subject: [PATCH 04/11] [MM-843]: Added server testcase for kvstore/plugin_store.go file (#18) * [MM-843]: Added server testcase for kvstore/plugin_store.go file * [MM-843]: removed ununsed vars --- calendar/utils/kvstore/plugin_store_test.go | 323 ++++++++++++++++++++ go.mod | 1 + go.sum | 5 + 3 files changed, 329 insertions(+) create mode 100644 calendar/utils/kvstore/plugin_store_test.go diff --git a/calendar/utils/kvstore/plugin_store_test.go b/calendar/utils/kvstore/plugin_store_test.go new file mode 100644 index 00000000..ef18602c --- /dev/null +++ b/calendar/utils/kvstore/plugin_store_test.go @@ -0,0 +1,323 @@ +package kvstore + +import ( + "testing" + "time" + + "github.com/mattermost/mattermost/server/public/model" + "github.com/mattermost/mattermost/server/public/plugin" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +type MockPluginAPI struct { + plugin.API + mock.Mock +} + +func (m *MockPluginAPI) KVGet(key string) ([]byte, *model.AppError) { + args := m.Called(key) + data, _ := args.Get(0).([]byte) + if err := args.Get(1); err != nil { + return nil, err.(*model.AppError) + } + return data, nil +} + +func (m *MockPluginAPI) KVSet(key string, data []byte) *model.AppError { + args := m.Called(key, data) + if err := args.Get(0); err != nil { + return err.(*model.AppError) + } + return nil +} + +func (m *MockPluginAPI) KVSetWithExpiry(key string, data []byte, ttl int64) *model.AppError { + args := m.Called(key, data, ttl) + if err := args.Get(0); err != nil { + return err.(*model.AppError) + } + return nil +} + +func (m *MockPluginAPI) KVSetWithOptions(key string, value []byte, options model.PluginKVSetOptions) (bool, *model.AppError) { + args := m.Called(key, value, options) + success := args.Bool(0) + if err := args.Get(1); err != nil { + return success, err.(*model.AppError) + } + return success, nil +} + +func (m *MockPluginAPI) KVDelete(key string) *model.AppError { + args := m.Called(key) + if err := args.Get(0); err != nil { + return err.(*model.AppError) + } + return nil +} + +func TestLoad(t *testing.T) { + tests := []struct { + name string + key string + setup func(*MockPluginAPI) + assertions func(*testing.T, []byte, error) + }{ + { + name: "Error during KVGet", + key: "error-key", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVGet", "error-key").Return(nil, &model.AppError{Message: "KVGet failed"}) + }, + assertions: func(t *testing.T, data []byte, err error) { + require.Nil(t, data, "expected nil data") + require.EqualError(t, err, "failed plugin KVGet: KVGet failed", "unexpected error message") + }, + }, + { + name: "Key not found", + key: "missing-key", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVGet", "missing-key").Return(nil, nil) + }, + assertions: func(t *testing.T, data []byte, err error) { + require.Nil(t, data, "expected nil data") + require.EqualError(t, err, ErrNotFound.Error(), "unexpected error message") + }, + }, + { + name: "Load successfully", + key: "test-key", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVGet", "test-key").Return([]byte("test-value"), nil) + }, + assertions: func(t *testing.T, data []byte, err error) { + require.Equal(t, []byte("test-value"), data, "unexpected data returned") + require.NoError(t, err, "unexpected error occurred") + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI := &MockPluginAPI{} + store := NewPluginStore(mockAPI) + tt.setup(mockAPI) + + data, err := store.Load(tt.key) + + tt.assertions(t, data, err) + + mockAPI.AssertExpectations(t) + }) + } +} + +func TestStore(t *testing.T) { + tests := []struct { + name string + expiryTime int + setup func(*MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error during KVSet with TTL", + expiryTime: 60, + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVSetWithExpiry", "mockKey", []byte("mockValue"), int64(60)).Return(&model.AppError{Message: "KVSet failed"}) + }, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "failed plugin KVSet (ttl: 60s) \"mockKey\": KVSet failed", "unexpected error message") + }, + }, + { + name: "Error during KVSet without TTL", + expiryTime: 0, + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVSet", "mockKey", []byte("mockValue")).Return(&model.AppError{Message: "KVSet failed"}) + }, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "failed plugin KVSet (ttl: 0s) \"mockKey\": KVSet failed", "unexpected error message") + }, + }, + { + name: "Store with TTL successfully", + expiryTime: 60, + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVSetWithExpiry", "mockKey", []byte("mockValue"), int64(60)).Return(nil) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err, "unexpected error occurred") + }, + }, + { + name: "Store without TTL successfully", + expiryTime: 0, + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVSet", "mockKey", []byte("mockValue")).Return(nil) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err, "unexpected error occurred") + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI := &MockPluginAPI{} + store := NewPluginStoreWithExpiry(mockAPI, time.Duration(tt.expiryTime)*time.Second) + tt.setup(mockAPI) + + err := store.Store("mockKey", []byte("mockValue")) + + tt.assertions(t, err) + + mockAPI.AssertExpectations(t) + }) + } +} + +func TestStoreTTL(t *testing.T) { + tests := []struct { + name string + setup func(*MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error during storing with TTL", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVSetWithExpiry", "mockKey", []byte("mockValue"), int64(60)).Return(&model.AppError{Message: "KVSet failed"}) + }, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "failed plugin KVSet (ttl: 60s) \"mockKey\": KVSet failed", "unexpected error message") + }, + }, + { + name: "Store with TTL successfully", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVSetWithExpiry", "mockKey", []byte("mockValue"), int64(60)).Return(nil) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err, "unexpected error occurred") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI := &MockPluginAPI{} + store := NewPluginStoreWithExpiry(mockAPI, 60*time.Second) + tt.setup(mockAPI) + + err := store.StoreTTL("mockKey", []byte("mockValue"), 60) + + tt.assertions(t, err) + + mockAPI.AssertExpectations(t) + }) + } +} + +func TestStoreWithOptions(t *testing.T) { + tests := []struct { + name string + expiryTime int64 + opts model.PluginKVSetOptions + setup func(*MockPluginAPI) + assertions func(*testing.T, bool, error) + }{ + { + name: "Error during KVSetWithOptions", + expiryTime: 60, + opts: model.PluginKVSetOptions{ + ExpireInSeconds: 30, + }, + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVSetWithOptions", "mockKey", []byte("mockValue"), model.PluginKVSetOptions{ExpireInSeconds: 30}).Return(false, &model.AppError{Message: "KVSet failed"}) + }, + assertions: func(t *testing.T, success bool, err error) { + require.False(t, success, "expected success to be false") + require.EqualError(t, err, "failed plugin KVSet (ttl: 30s) \"mockKey\": KVSet failed", "unexpected error message") + }, + }, + { + name: "Use default TTL when opts.ExpireInSeconds is 0", + expiryTime: 60, + opts: model.PluginKVSetOptions{}, + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVSetWithOptions", "mockKey", []byte("mockValue"), model.PluginKVSetOptions{ExpireInSeconds: 60}).Return(true, nil) + }, + assertions: func(t *testing.T, success bool, err error) { + require.True(t, success, "expected success to be true") + require.NoError(t, err, "unexpected error occurred") + }, + }, + { + name: "Store with options successfully", + expiryTime: 60, + opts: model.PluginKVSetOptions{ + ExpireInSeconds: 30, + }, + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVSetWithOptions", "mockKey", []byte("mockValue"), model.PluginKVSetOptions{ExpireInSeconds: 30}).Return(true, nil) + }, + assertions: func(t *testing.T, success bool, err error) { + require.True(t, success, "expected success to be true") + require.NoError(t, err, "unexpected error occurred") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI := &MockPluginAPI{} + store := NewPluginStoreWithExpiry(mockAPI, time.Duration(tt.expiryTime)*time.Second) + tt.setup(mockAPI) + + success, err := store.StoreWithOptions("mockKey", []byte("mockValue"), tt.opts) + + tt.assertions(t, success, err) + + mockAPI.AssertExpectations(t) + }) + } +} + +func TestDelete(t *testing.T) { + tests := []struct { + name string + setup func(*MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error during KVDelete", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVDelete", "mockKey").Return(&model.AppError{Message: "KVDelete failed"}) + }, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "failed plugin KVdelete \"mockKey\": KVDelete failed", "unexpected error message") + }, + }, + { + name: "Delete successfully", + setup: func(mockAPI *MockPluginAPI) { + mockAPI.On("KVDelete", "mockKey").Return(nil) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err, "unexpected error occurred") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI := &MockPluginAPI{} + store := NewPluginStore(mockAPI) + tt.setup(mockAPI) + + err := store.Delete("mockKey") + + tt.assertions(t, err) + + mockAPI.AssertExpectations(t) + }) + } +} diff --git a/go.mod b/go.mod index 46583399..123599fb 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/segmentio/backo-go v1.0.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/stretchr/objx v0.5.1 // indirect github.com/tidwall/gjson v1.17.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect diff --git a/go.sum b/go.sum index 74bdcfea..e5ec58f6 100644 --- a/go.sum +++ b/go.sum @@ -187,12 +187,17 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= From 75f6cebd0b5ed8f51d897b48eed6bdcae6e5eda1 Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Mon, 14 Oct 2024 15:50:14 +0530 Subject: [PATCH 05/11] updated go.mod and go.sum entries --- go.mod | 4 ++-- go.sum | 13 ++++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index dbff7959..81d53191 100644 --- a/go.mod +++ b/go.mod @@ -53,8 +53,8 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/segmentio/backo-go v1.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/stretchr/objx v0.5.1 // indirect - github.com/tidwall/gjson v1.17.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/tidwall/gjson v1.17.1 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tinylib/msgp v1.2.0 // indirect diff --git a/go.sum b/go.sum index 0097777a..249376d4 100644 --- a/go.sum +++ b/go.sum @@ -191,19 +191,14 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= -github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= From 65d7b43efb95fb4449a43676be748d4de32142cd Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Mon, 14 Oct 2024 16:19:19 +0530 Subject: [PATCH 06/11] refactored mock plugin setup --- calendar/testutil/mock_setup.go | 55 +++++++++++ calendar/utils/kvstore/plugin_store_test.go | 100 ++++++-------------- 2 files changed, 82 insertions(+), 73 deletions(-) create mode 100644 calendar/testutil/mock_setup.go diff --git a/calendar/testutil/mock_setup.go b/calendar/testutil/mock_setup.go new file mode 100644 index 00000000..d10f8cd3 --- /dev/null +++ b/calendar/testutil/mock_setup.go @@ -0,0 +1,55 @@ +package testutil + +import ( + "github.com/stretchr/testify/mock" + + "github.com/mattermost/mattermost/server/public/model" + "github.com/mattermost/mattermost/server/public/plugin" +) + +type MockPluginAPI struct { + plugin.API + mock.Mock +} + +func (m *MockPluginAPI) KVGet(key string) ([]byte, *model.AppError) { + args := m.Called(key) + data, _ := args.Get(0).([]byte) + if err := args.Get(1); err != nil { + return nil, err.(*model.AppError) + } + return data, nil +} + +func (m *MockPluginAPI) KVSet(key string, data []byte) *model.AppError { + args := m.Called(key, data) + if err := args.Get(0); err != nil { + return err.(*model.AppError) + } + return nil +} + +func (m *MockPluginAPI) KVSetWithExpiry(key string, data []byte, ttl int64) *model.AppError { + args := m.Called(key, data, ttl) + if err := args.Get(0); err != nil { + return err.(*model.AppError) + } + return nil +} + +func (m *MockPluginAPI) KVSetWithOptions(key string, value []byte, options model.PluginKVSetOptions) (bool, *model.AppError) { + args := m.Called(key, value, options) + success := args.Bool(0) + if err := args.Get(1); err != nil { + return success, err.(*model.AppError) + } + return success, nil +} + +func (m *MockPluginAPI) KVDelete(key string) *model.AppError { + args := m.Called(key) + if err := args.Get(0); err != nil { + return err.(*model.AppError) + } + return nil +} diff --git a/calendar/utils/kvstore/plugin_store_test.go b/calendar/utils/kvstore/plugin_store_test.go index ef18602c..53429834 100644 --- a/calendar/utils/kvstore/plugin_store_test.go +++ b/calendar/utils/kvstore/plugin_store_test.go @@ -4,70 +4,24 @@ import ( "testing" "time" - "github.com/mattermost/mattermost/server/public/model" - "github.com/mattermost/mattermost/server/public/plugin" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" -) - -type MockPluginAPI struct { - plugin.API - mock.Mock -} - -func (m *MockPluginAPI) KVGet(key string) ([]byte, *model.AppError) { - args := m.Called(key) - data, _ := args.Get(0).([]byte) - if err := args.Get(1); err != nil { - return nil, err.(*model.AppError) - } - return data, nil -} - -func (m *MockPluginAPI) KVSet(key string, data []byte) *model.AppError { - args := m.Called(key, data) - if err := args.Get(0); err != nil { - return err.(*model.AppError) - } - return nil -} -func (m *MockPluginAPI) KVSetWithExpiry(key string, data []byte, ttl int64) *model.AppError { - args := m.Called(key, data, ttl) - if err := args.Get(0); err != nil { - return err.(*model.AppError) - } - return nil -} - -func (m *MockPluginAPI) KVSetWithOptions(key string, value []byte, options model.PluginKVSetOptions) (bool, *model.AppError) { - args := m.Called(key, value, options) - success := args.Bool(0) - if err := args.Get(1); err != nil { - return success, err.(*model.AppError) - } - return success, nil -} + "github.com/mattermost/mattermost/server/public/model" -func (m *MockPluginAPI) KVDelete(key string) *model.AppError { - args := m.Called(key) - if err := args.Get(0); err != nil { - return err.(*model.AppError) - } - return nil -} + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/testutil" +) func TestLoad(t *testing.T) { tests := []struct { name string key string - setup func(*MockPluginAPI) + setup func(*testutil.MockPluginAPI) assertions func(*testing.T, []byte, error) }{ { name: "Error during KVGet", key: "error-key", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVGet", "error-key").Return(nil, &model.AppError{Message: "KVGet failed"}) }, assertions: func(t *testing.T, data []byte, err error) { @@ -78,7 +32,7 @@ func TestLoad(t *testing.T) { { name: "Key not found", key: "missing-key", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVGet", "missing-key").Return(nil, nil) }, assertions: func(t *testing.T, data []byte, err error) { @@ -89,7 +43,7 @@ func TestLoad(t *testing.T) { { name: "Load successfully", key: "test-key", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVGet", "test-key").Return([]byte("test-value"), nil) }, assertions: func(t *testing.T, data []byte, err error) { @@ -101,7 +55,7 @@ func TestLoad(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockAPI := &MockPluginAPI{} + mockAPI := &testutil.MockPluginAPI{} store := NewPluginStore(mockAPI) tt.setup(mockAPI) @@ -118,13 +72,13 @@ func TestStore(t *testing.T) { tests := []struct { name string expiryTime int - setup func(*MockPluginAPI) + setup func(*testutil.MockPluginAPI) assertions func(*testing.T, error) }{ { name: "Error during KVSet with TTL", expiryTime: 60, - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVSetWithExpiry", "mockKey", []byte("mockValue"), int64(60)).Return(&model.AppError{Message: "KVSet failed"}) }, assertions: func(t *testing.T, err error) { @@ -134,7 +88,7 @@ func TestStore(t *testing.T) { { name: "Error during KVSet without TTL", expiryTime: 0, - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVSet", "mockKey", []byte("mockValue")).Return(&model.AppError{Message: "KVSet failed"}) }, assertions: func(t *testing.T, err error) { @@ -144,7 +98,7 @@ func TestStore(t *testing.T) { { name: "Store with TTL successfully", expiryTime: 60, - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVSetWithExpiry", "mockKey", []byte("mockValue"), int64(60)).Return(nil) }, assertions: func(t *testing.T, err error) { @@ -154,7 +108,7 @@ func TestStore(t *testing.T) { { name: "Store without TTL successfully", expiryTime: 0, - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVSet", "mockKey", []byte("mockValue")).Return(nil) }, assertions: func(t *testing.T, err error) { @@ -165,7 +119,7 @@ func TestStore(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockAPI := &MockPluginAPI{} + mockAPI := &testutil.MockPluginAPI{} store := NewPluginStoreWithExpiry(mockAPI, time.Duration(tt.expiryTime)*time.Second) tt.setup(mockAPI) @@ -181,12 +135,12 @@ func TestStore(t *testing.T) { func TestStoreTTL(t *testing.T) { tests := []struct { name string - setup func(*MockPluginAPI) + setup func(*testutil.MockPluginAPI) assertions func(*testing.T, error) }{ { name: "Error during storing with TTL", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVSetWithExpiry", "mockKey", []byte("mockValue"), int64(60)).Return(&model.AppError{Message: "KVSet failed"}) }, assertions: func(t *testing.T, err error) { @@ -195,7 +149,7 @@ func TestStoreTTL(t *testing.T) { }, { name: "Store with TTL successfully", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVSetWithExpiry", "mockKey", []byte("mockValue"), int64(60)).Return(nil) }, assertions: func(t *testing.T, err error) { @@ -205,7 +159,7 @@ func TestStoreTTL(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockAPI := &MockPluginAPI{} + mockAPI := &testutil.MockPluginAPI{} store := NewPluginStoreWithExpiry(mockAPI, 60*time.Second) tt.setup(mockAPI) @@ -223,7 +177,7 @@ func TestStoreWithOptions(t *testing.T) { name string expiryTime int64 opts model.PluginKVSetOptions - setup func(*MockPluginAPI) + setup func(*testutil.MockPluginAPI) assertions func(*testing.T, bool, error) }{ { @@ -232,7 +186,7 @@ func TestStoreWithOptions(t *testing.T) { opts: model.PluginKVSetOptions{ ExpireInSeconds: 30, }, - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVSetWithOptions", "mockKey", []byte("mockValue"), model.PluginKVSetOptions{ExpireInSeconds: 30}).Return(false, &model.AppError{Message: "KVSet failed"}) }, assertions: func(t *testing.T, success bool, err error) { @@ -244,7 +198,7 @@ func TestStoreWithOptions(t *testing.T) { name: "Use default TTL when opts.ExpireInSeconds is 0", expiryTime: 60, opts: model.PluginKVSetOptions{}, - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVSetWithOptions", "mockKey", []byte("mockValue"), model.PluginKVSetOptions{ExpireInSeconds: 60}).Return(true, nil) }, assertions: func(t *testing.T, success bool, err error) { @@ -258,7 +212,7 @@ func TestStoreWithOptions(t *testing.T) { opts: model.PluginKVSetOptions{ ExpireInSeconds: 30, }, - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVSetWithOptions", "mockKey", []byte("mockValue"), model.PluginKVSetOptions{ExpireInSeconds: 30}).Return(true, nil) }, assertions: func(t *testing.T, success bool, err error) { @@ -269,7 +223,7 @@ func TestStoreWithOptions(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockAPI := &MockPluginAPI{} + mockAPI := &testutil.MockPluginAPI{} store := NewPluginStoreWithExpiry(mockAPI, time.Duration(tt.expiryTime)*time.Second) tt.setup(mockAPI) @@ -285,12 +239,12 @@ func TestStoreWithOptions(t *testing.T) { func TestDelete(t *testing.T) { tests := []struct { name string - setup func(*MockPluginAPI) + setup func(*testutil.MockPluginAPI) assertions func(*testing.T, error) }{ { name: "Error during KVDelete", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVDelete", "mockKey").Return(&model.AppError{Message: "KVDelete failed"}) }, assertions: func(t *testing.T, err error) { @@ -299,7 +253,7 @@ func TestDelete(t *testing.T) { }, { name: "Delete successfully", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVDelete", "mockKey").Return(nil) }, assertions: func(t *testing.T, err error) { @@ -309,7 +263,7 @@ func TestDelete(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockAPI := &MockPluginAPI{} + mockAPI := &testutil.MockPluginAPI{} store := NewPluginStore(mockAPI) tt.setup(mockAPI) From e8015e38b90223c5bcee49979d6365dde24c6f44 Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Mon, 21 Oct 2024 18:40:09 +0530 Subject: [PATCH 07/11] [MM-844]: refactored the code --- calendar/store/test_util.go | 23 +++++++++ calendar/store/welcome_store_test.go | 74 +++++----------------------- 2 files changed, 36 insertions(+), 61 deletions(-) create mode 100644 calendar/store/test_util.go diff --git a/calendar/store/test_util.go b/calendar/store/test_util.go new file mode 100644 index 00000000..e3a7f05c --- /dev/null +++ b/calendar/store/test_util.go @@ -0,0 +1,23 @@ +package store + +import ( + "testing" + + "github.com/golang/mock/gomock" + + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/testutil" + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/tracker/mock_tracker" + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/utils/bot/mock_bot" +) + +func GetMockSetup(t *testing.T) (*testutil.MockPluginAPI, Store, *mock_bot.MockLogger, *mock_bot.MockLogger, *mock_tracker.MockTracker) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockLogger := mock_bot.NewMockLogger(ctrl) + mockLoggerWith := mock_bot.NewMockLogger(ctrl) + mockTracker := mock_tracker.NewMockTracker(ctrl) + mockAPI := &testutil.MockPluginAPI{} + store := NewPluginStore(mockAPI, mockLogger, mockTracker, false, nil) + + return mockAPI, store, mockLogger, mockLoggerWith, mockTracker +} diff --git a/calendar/store/welcome_store_test.go b/calendar/store/welcome_store_test.go index b95f52ba..33b61113 100644 --- a/calendar/store/welcome_store_test.go +++ b/calendar/store/welcome_store_test.go @@ -3,63 +3,25 @@ package store import ( "testing" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/mattermost/mattermost/server/public/model" - "github.com/mattermost/mattermost/server/public/plugin" - "github.com/mattermost/mattermost-plugin-mscalendar/calendar/tracker/mock_tracker" - "github.com/mattermost/mattermost-plugin-mscalendar/calendar/utils/bot/mock_bot" + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/testutil" ) -type MockPluginAPI struct { - plugin.API - mock.Mock -} - -func (m *MockPluginAPI) KVGet(key string) ([]byte, *model.AppError) { - args := m.Called(key) - data, _ := args.Get(0).([]byte) - if err := args.Get(1); err != nil { - return nil, err.(*model.AppError) - } - return data, nil -} - -func (m *MockPluginAPI) KVSet(key string, data []byte) *model.AppError { - args := m.Called(key, data) - if err := args.Get(0); err != nil { - return err.(*model.AppError) - } - return nil -} - -func (m *MockPluginAPI) KVDelete(key string) *model.AppError { - args := m.Called(key) - if err := args.Get(0); err != nil { - return err.(*model.AppError) - } - return nil -} - func TestLoadUserWelcomePost(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - mockLogger := mock_bot.NewMockLogger(ctrl) - mockTracker := mock_tracker.NewMockTracker(ctrl) - mockAPI := &MockPluginAPI{} - store := NewPluginStore(mockAPI, mockLogger, mockTracker, false, nil) + mockAPI, store, _, _, _ := GetMockSetup(t) tests := []struct { name string - setup func(*MockPluginAPI) + setup func(*testutil.MockPluginAPI) assertions func(*testing.T, string, error) }{ { name: "Error loading user welcome post", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVGet", mock.AnythingOfType("string")).Return(nil, &model.AppError{Message: "KVGet failed"}) }, assertions: func(t *testing.T, resp string, err error) { @@ -69,7 +31,7 @@ func TestLoadUserWelcomePost(t *testing.T) { }, { name: "Success loading user welcome post", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVGet", mock.AnythingOfType("string")).Return([]byte(`"mockPostID"`), nil) }, assertions: func(t *testing.T, resp string, err error) { @@ -93,21 +55,16 @@ func TestLoadUserWelcomePost(t *testing.T) { } func TestStoreUserWelcomePost(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - mockLogger := mock_bot.NewMockLogger(ctrl) - mockTracker := mock_tracker.NewMockTracker(ctrl) - mockAPI := &MockPluginAPI{} - store := NewPluginStore(mockAPI, mockLogger, mockTracker, false, nil) + mockAPI, store, _, _, _ := GetMockSetup(t) tests := []struct { name string - setup func(*MockPluginAPI) + setup func(*testutil.MockPluginAPI) assertions func(*testing.T, error) }{ { name: "Error storing user welcome post", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVSet", mock.Anything, mock.Anything).Return(&model.AppError{Message: "KVSet failed"}) }, assertions: func(t *testing.T, err error) { @@ -116,7 +73,7 @@ func TestStoreUserWelcomePost(t *testing.T) { }, { name: "Success storing user welcome post", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVSet", mock.Anything, mock.Anything).Return(nil) }, assertions: func(t *testing.T, err error) { @@ -139,21 +96,16 @@ func TestStoreUserWelcomePost(t *testing.T) { } func TestDeleteUserWelcomePost(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - mockLogger := mock_bot.NewMockLogger(ctrl) - mockTracker := mock_tracker.NewMockTracker(ctrl) - mockAPI := &MockPluginAPI{} - store := NewPluginStore(mockAPI, mockLogger, mockTracker, false, nil) + mockAPI, store, _, _, _ := GetMockSetup(t) tests := []struct { name string - setup func(*MockPluginAPI) + setup func(*testutil.MockPluginAPI) assertions func(*testing.T, string, error) }{ { name: "Error deleting user welcome post", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVGet", mock.AnythingOfType("string")).Return([]byte(`"mockPostID"`), nil) mockAPI.On("KVDelete", mock.Anything, mock.Anything).Return(&model.AppError{Message: "KVDelete failed"}) }, @@ -164,7 +116,7 @@ func TestDeleteUserWelcomePost(t *testing.T) { }, { name: "Success deleting user welcome post", - setup: func(mockAPI *MockPluginAPI) { + setup: func(mockAPI *testutil.MockPluginAPI) { mockAPI.On("KVGet", mock.AnythingOfType("string")).Return([]byte(`"mockPostID"`), nil) mockAPI.On("KVDelete", mock.Anything, mock.Anything).Return(nil) }, From 96d0b6fcda763202f373c213e9732e382f411a2f Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Wed, 6 Nov 2024 12:02:08 +0530 Subject: [PATCH 08/11] [MM-844]: Fixed the go sum --- go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/go.sum b/go.sum index c1f0d70c..249376d4 100644 --- a/go.sum +++ b/go.sum @@ -196,7 +196,6 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= From b489232148d537632499cf136bccb04d5ba508e2 Mon Sep 17 00:00:00 2001 From: kshitij katiyar <90389917+Kshitij-Katiyar@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:57:17 +0530 Subject: [PATCH 09/11] [MM-845]: Added server testcases for store/user_store.go --- calendar/store/test_util.go | 54 ++ calendar/store/user_store_test.go | 874 ++++++++++++++++++++++++++++++ 2 files changed, 928 insertions(+) create mode 100644 calendar/store/user_store_test.go diff --git a/calendar/store/test_util.go b/calendar/store/test_util.go index e3a7f05c..319209af 100644 --- a/calendar/store/test_util.go +++ b/calendar/store/test_util.go @@ -1,15 +1,34 @@ package store import ( + "encoding/json" + "fmt" "testing" "github.com/golang/mock/gomock" + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/remote" "github.com/mattermost/mattermost-plugin-mscalendar/calendar/testutil" "github.com/mattermost/mattermost-plugin-mscalendar/calendar/tracker/mock_tracker" "github.com/mattermost/mattermost-plugin-mscalendar/calendar/utils/bot/mock_bot" ) +const ( + MockMMUsername = "mockMMUsername" + MockMMDisplayName = "mockMMDisplayName" + MockMMUserID = "mockMMUserID" + MockRemoteID = "mockRemoteID" + MockRemoteUserID = "mockRemoteUserID" + MockRemoteMail = "mock@remote.com" + MockEventID = "mockEventID" + MockChannelID = "mockChannelID" + MockUserIndexJSON = `[{"mm_id": "mockMMUserID"}]` + InvalidMockUserIndexJSON = `[{"mm_id": "invalidMockMMUserID"}]` + MockRemoteJSON = `{"remote": {"id": "mockRemoteID"}}` + MockUserJSON = `[{"MattermostUserID":"mockMMUserID","RemoteID":"mockRemoteID"}]` + MockUserDetailsWithEventJSON = `{"mm_id":"mockUserID","active_events": []}` +) + func GetMockSetup(t *testing.T) (*testutil.MockPluginAPI, Store, *mock_bot.MockLogger, *mock_bot.MockLogger, *mock_tracker.MockTracker) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -21,3 +40,38 @@ func GetMockSetup(t *testing.T) (*testutil.MockPluginAPI, Store, *mock_bot.MockL return mockAPI, store, mockLogger, mockLoggerWith, mockTracker } + +func GetRemoteUserJSON(noOfUsers int) string { + type RemoteUser struct { + MMUsername string `json:"mm_username"` + RemoteID string `json:"remote_id"` + MMID string `json:"mm_id"` + Email string `json:"email"` + } + + var users []RemoteUser + for i := 1; i <= noOfUsers; i++ { + user := RemoteUser{ + MMUsername: fmt.Sprintf("user%d", i), + RemoteID: fmt.Sprintf("remote%d", i), + MMID: fmt.Sprintf("user%d", i), + Email: fmt.Sprintf("user%d@example.com", i), + } + users = append(users, user) + } + + result, _ := json.Marshal(users) + return string(result) +} + +func GetMockUser() *User { + return &User{ + MattermostUserID: MockMMUserID, + MattermostUsername: MockMMUsername, + MattermostDisplayName: MockMMDisplayName, + Remote: &remote.User{ + ID: MockRemoteID, + Mail: MockRemoteMail, + }, + } +} diff --git a/calendar/store/user_store_test.go b/calendar/store/user_store_test.go new file mode 100644 index 00000000..5bcdbbf4 --- /dev/null +++ b/calendar/store/user_store_test.go @@ -0,0 +1,874 @@ +package store + +import ( + "testing" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/testutil" + + "github.com/mattermost/mattermost/server/public/model" +) + +func TestLoadUser(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, *User, error) + }{ + { + name: "Error loading user", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return(nil, &model.AppError{Message: "KVGet failed"}) + }, + assertions: func(t *testing.T, user *User, err error) { + require.Nil(t, user) + require.EqualError(t, err, "failed plugin KVGet: KVGet failed") + }, + }, + { + name: "Success loading user", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(`{"isCustomStatusSet": false}`), nil) + }, + assertions: func(t *testing.T, user *User, err error) { + require.NoError(t, err) + require.NotNil(t, user) + require.False(t, user.IsCustomStatusSet) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + user, err := store.LoadUser(MockMMUserID) + + tt.assertions(t, user, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestLoadMattermostUserID(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, string, error) + }{ + { + name: "Error loading Mattermost User ID", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "mmuid_0404eb7ac36366cbc447d63a3acd7a5d").Return(nil, &model.AppError{Message: "Load failed"}) + }, + assertions: func(t *testing.T, userID string, err error) { + require.Empty(t, userID) + require.EqualError(t, err, "failed plugin KVGet: Load failed") + }, + }, + { + name: "Success loading Mattermost User ID", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "mmuid_0404eb7ac36366cbc447d63a3acd7a5d").Return([]byte(MockMMUserID), nil) + }, + assertions: func(t *testing.T, userID string, err error) { + require.NoError(t, err) + require.Equal(t, MockMMUserID, userID) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + userID, err := store.LoadMattermostUserID(MockRemoteUserID) + + tt.assertions(t, userID, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestLoadUserIndex(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, UserIndex, error) + }{ + { + name: "Error loading UserIndex", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return(nil, &model.AppError{Message: "Load failed"}) + }, + assertions: func(t *testing.T, userIndex UserIndex, err error) { + require.Nil(t, userIndex) + require.EqualError(t, err, "failed plugin KVGet: Load failed") + }, + }, + { + name: "Success loading UserIndex", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(`[{"mm_username": "mockMMUsername"}]`), nil) + }, + assertions: func(t *testing.T, userIndex UserIndex, err error) { + require.NoError(t, err) + require.Len(t, userIndex, 1) + require.Equal(t, MockMMUsername, userIndex[0].MattermostUsername) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + userIndex, err := store.LoadUserIndex() + + tt.assertions(t, userIndex, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestLoadUserFromIndex(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, *UserShort, error) + }{ + { + name: "Error loading UserIndex", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return(nil, &model.AppError{Message: "Load failed"}) + }, + assertions: func(t *testing.T, user *UserShort, err error) { + require.Nil(t, user) + require.EqualError(t, err, "failed plugin KVGet: Load failed") + }, + }, + { + name: "User not found in index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(InvalidMockUserIndexJSON), nil) + }, + assertions: func(t *testing.T, user *UserShort, err error) { + require.Nil(t, user) + require.Equal(t, ErrNotFound, err) + }, + }, + { + name: "Success loading User from index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(MockUserIndexJSON), nil) + }, + assertions: func(t *testing.T, user *UserShort, err error) { + require.NoError(t, err) + require.Equal(t, MockMMUserID, user.MattermostUserID) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + user, err := store.LoadUserFromIndex(MockMMUserID) + + tt.assertions(t, user, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestStoreUser(t *testing.T) { + user := GetMockUser() + + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error storing user JSON", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(&model.AppError{Message: "Failed to store user"}) + }, + assertions: func(t *testing.T, err error) { + require.ErrorContains(t, err, "failed plugin KVSet", "Failed to store user") + }, + }, + { + name: "Error storing Mattermost User ID", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(nil).Times(1) + mockAPI.On("KVSet", "mmuid_e138a0f218087f9324d8c77f87d5f3a0", []byte(MockMMUserID)).Return(&model.AppError{Message: "Failed to store Mattermost User ID"}).Times(1) + mockAPI.On("KVDelete", "user_c3b5020d58a049787bc969768465b890").Return(nil) + }, + assertions: func(t *testing.T, err error) { + require.ErrorContains(t, err, "failed plugin KVSet", "Failed to store user") + }, + }, + { + name: "Success storing user", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(nil) + mockAPI.On("KVSet", "mmuid_e138a0f218087f9324d8c77f87d5f3a0", []byte(MockMMUserID)).Return(nil) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + err := store.StoreUser(user) + + tt.assertions(t, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestDeleteUser(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error loading user", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return(nil, &model.AppError{Message: "KVGet failed"}) + }, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "failed plugin KVGet: KVGet failed") + }, + }, + { + name: "Error deleting user", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(`{}`), nil) + mockAPI.On("KVDelete", "user_c3b5020d58a049787bc969768465b890").Return(&model.AppError{Message: "error deleting user"}) + }, + assertions: func(t *testing.T, err error) { + require.ErrorContains(t, err, "error deleting user") + }, + }, + { + name: "Error deleting mattermost user", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(MockRemoteJSON), nil) + mockAPI.On("KVDelete", "user_c3b5020d58a049787bc969768465b890").Return(nil).Times(1) + mockAPI.On("KVDelete", "mmuid_e138a0f218087f9324d8c77f87d5f3a0").Return(&model.AppError{Message: "error deleting mattermost user"}) + }, + assertions: func(t *testing.T, err error) { + require.ErrorContains(t, err, "error deleting mattermost user") + }, + }, + { + name: "Error getting user details", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(MockRemoteJSON), nil).Times(1) + mockAPI.On("KVDelete", "user_c3b5020d58a049787bc969768465b890").Return(nil).Times(1) + mockAPI.On("KVDelete", "mmuid_e138a0f218087f9324d8c77f87d5f3a0").Return(nil).Times(1) + mockAPI.On("KVGet", "userindex_").Return(nil, &model.AppError{Message: "error getting user details"}) + }, + assertions: func(t *testing.T, err error) { + require.ErrorContains(t, err, "error getting user details") + }, + }, + { + name: "error storing user", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(MockRemoteJSON), nil).Times(1) + mockAPI.On("KVDelete", "user_c3b5020d58a049787bc969768465b890").Return(nil).Times(1) + mockAPI.On("KVDelete", "mmuid_e138a0f218087f9324d8c77f87d5f3a0").Return(nil).Times(1) + mockAPI.On("KVGet", "userindex_").Return([]byte(`[]`), nil).Times(1) + mockAPI.On("KVSet", "userindex_", mock.Anything).Return(&model.AppError{Message: "error storing user"}) + }, + assertions: func(t *testing.T, err error) { + require.ErrorContains(t, err, "error storing user") + }, + }, + { + name: "Success deleting user", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(MockRemoteJSON), nil).Times(1) + mockAPI.On("KVDelete", "user_c3b5020d58a049787bc969768465b890").Return(nil).Times(1) + mockAPI.On("KVDelete", "mmuid_e138a0f218087f9324d8c77f87d5f3a0").Return(nil).Times(1) + mockAPI.On("KVGet", "userindex_").Return([]byte(`[]`), nil).Times(1) + mockAPI.On("KVSet", "userindex_", mock.Anything).Return(nil) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + err := store.DeleteUser(MockMMUserID) + + tt.assertions(t, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestStoreUserInIndex(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error loading user index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return(nil, &model.AppError{Message: "KVGet failed"}).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "modification error: failed plugin KVGet: KVGet failed") + }, + }, + { + name: "Error unmarshalling existing user index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(`invalid json`), nil).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "modification error: invalid character 'i' looking for beginning of value") + }, + }, + { + name: "Error storing updated user index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(`[]`), nil).Times(1) + mockAPI.On("KVSetWithOptions", "userindex_", mock.Anything, mock.Anything).Return(false, &model.AppError{Message: "KVSet failed"}).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.ErrorContains(t, err, "problem writing value", "KVSet failed") + }, + }, + { + name: "Successfully update an existing user in index with matching IDs", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(MockUserJSON), nil).Times(1) + mockAPI.On("KVSetWithOptions", "userindex_", mock.Anything, mock.Anything).Return(true, nil).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + { + name: "Successfully store a new user in index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(`[]`), nil).Times(1) + mockAPI.On("KVSetWithOptions", "userindex_", mock.Anything, mock.Anything).Return(true, nil).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + { + name: "Successfully update an existing user in index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(MockUserJSON), nil).Times(1) + mockAPI.On("KVSetWithOptions", "userindex_", mock.Anything, mock.Anything).Return(true, nil).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + user := GetMockUser() + err := store.StoreUserInIndex(user) + + tt.assertions(t, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestDeleteUserFromIndex(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error loading user index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return(nil, &model.AppError{Message: "KVGet failed"}).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "modification error: failed plugin KVGet: KVGet failed") + }, + }, + { + name: "Error unmarshalling existing user index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(`invalid json`), nil).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "modification error: invalid character 'i' looking for beginning of value") + }, + }, + { + name: "User not found in index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(`[]`), nil).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + { + name: "Successfully delete a user from index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_"). + Return([]byte(`[{"MattermostUserID":"mockMMUserID","RemoteID":"mockRemoteID"},{"MattermostUserID":"otherUserID","RemoteID":"otherRemoteID"}]`), nil).Times(1) + mockAPI.On("KVSetWithOptions", "userindex_", mock.Anything, mock.Anything).Return(true, nil).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + { + name: "Error storing updated user index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(MockUserJSON), nil).Times(1) + mockAPI.On("KVSetWithOptions", "userindex_", mock.Anything, mock.Anything).Return(false, &model.AppError{Message: "KVSet failed"}).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.ErrorContains(t, err, "problem writing value", "KVSet failed") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + err := store.DeleteUserFromIndex(MockMMUserID) + + tt.assertions(t, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestSearchInUserIndex(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + term string + limit int + assertions func(*testing.T, UserIndex, error) + }{ + { + name: "Error loading user index", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return(nil, &model.AppError{Message: "KVGet failed"}).Times(1) + }, + term: "searchTerm", + limit: 5, + assertions: func(t *testing.T, result UserIndex, err error) { + require.EqualError(t, err, "error searching user in index: failed plugin KVGet: KVGet failed") + require.Nil(t, result) + }, + }, + { + name: "No matches found", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(`[]`), nil).Times(1) + }, + term: "searchTerm", + limit: 5, + assertions: func(t *testing.T, result UserIndex, err error) { + require.NoError(t, err) + require.Empty(t, result) + }, + }, + { + name: "Matches found within limit", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(GetRemoteUserJSON(2)), nil).Times(1) + }, + term: "user", + limit: 1, + assertions: func(t *testing.T, result UserIndex, err error) { + require.NoError(t, err) + require.Len(t, result, 1) + require.Equal(t, "user1", result[0].MattermostUserID) + }, + }, + { + name: "Matches not found within limit despite existing matches", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(GetRemoteUserJSON(3)), nil).Times(1) + }, + term: "nonexistent", + limit: 2, + assertions: func(t *testing.T, result UserIndex, err error) { + require.NoError(t, err) + require.Len(t, result, 0) + }, + }, + { + name: "Limit exceeded but only returns available matches", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "userindex_").Return([]byte(GetRemoteUserJSON(3)), nil).Times(1) + }, + term: "user", + limit: 2, + assertions: func(t *testing.T, result UserIndex, err error) { + require.NoError(t, err) + require.Len(t, result, 2) + require.Equal(t, "user1", result[0].MattermostUserID) + require.Equal(t, "user2", result[1].MattermostUserID) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + result, err := store.SearchInUserIndex(tt.term, tt.limit) + + tt.assertions(t, result, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestStoreUserActiveEvents(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + events []string + assertions func(*testing.T, error) + }{ + { + name: "Error loading user", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return(nil, &model.AppError{Message: "User not found"}).Times(1) + }, + events: []string{"event1", "event2"}, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "failed plugin KVGet: User not found") + }, + }, + { + name: "Error storing active events", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(MockUserDetailsWithEventJSON), nil).Times(1) + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(&model.AppError{Message: "Failed to store events"}).Times(1) + }, + events: []string{"event1", "event2"}, + assertions: func(t *testing.T, err error) { + require.ErrorContains(t, err, "Failed to store events") + }, + }, + { + name: "Store active events successfully", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(MockUserDetailsWithEventJSON), nil).Times(1) + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(nil).Times(1) + }, + events: []string{"event1", "event2"}, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + err := store.StoreUserActiveEvents(MockMMUserID, tt.events) + + tt.assertions(t, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestStoreUserLinkedEvent(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + eventID string + channelID string + assertions func(*testing.T, error) + }{ + { + name: "Error loading user", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return(nil, &model.AppError{Message: "User not found"}).Times(1) + }, + eventID: MockEventID, + channelID: MockChannelID, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "failed plugin KVGet: User not found") + }, + }, + { + name: "Error storing linked event", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(`{"mm_id":"mockUserID","channel_events": {}}`), nil).Times(1) + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(&model.AppError{Message: "Failed to store linked event"}).Times(1) + }, + eventID: MockEventID, + channelID: MockChannelID, + assertions: func(t *testing.T, err error) { + require.ErrorContains(t, err, "Failed to store linked event") + }, + }, + { + name: "Store linked event successfully with empty ChannelEvents", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(`{"mm_id":"mockUserID","channel_events": {}}`), nil).Times(1) + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(nil).Times(1) + }, + eventID: MockEventID, + channelID: MockChannelID, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + { + name: "Store linked event successfully with existing ChannelEvents", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(`{"mm_id":"mockUserID","channel_events": {"mockEventID": "mockChannelID"}}`), nil).Times(1) + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(nil).Times(1) + }, + eventID: "mockEventID2", + channelID: "mockChannelID2", + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + err := store.StoreUserLinkedEvent(MockMMUserID, tt.eventID, tt.channelID) + + tt.assertions(t, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestStoreUserCustomStatusUpdates(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error loading user", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return(nil, &model.AppError{Message: "User not found"}).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.EqualError(t, err, "failed plugin KVGet: User not found") + }, + }, + { + name: "Error storing custom status update", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(`{"mm_id":"mockMMUserID","is_custom_status_set": false}`), nil).Times(1) + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(&model.AppError{Message: "Failed to store custom status"}).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.ErrorContains(t, err, "Failed to store custom status") + }, + }, + { + name: "Store custom status update successfully", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "user_c3b5020d58a049787bc969768465b890").Return([]byte(`{"mm_id":"mockMMUserID","is_custom_status_set": false}`), nil).Times(1) + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(nil).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + err := store.StoreUserCustomStatusUpdates(MockMMUserID, true) + + tt.assertions(t, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestUserIndex_ByMattermostID(t *testing.T) { + index := UserIndex{ + &UserShort{MattermostUserID: "user1"}, + &UserShort{MattermostUserID: "user2"}, + } + + result := index.ByMattermostID() + require.Len(t, result, 2) + require.Contains(t, result, "user1") + require.Contains(t, result, "user2") +} + +func TestUserIndex_ByRemoteID(t *testing.T) { + index := UserIndex{ + &UserShort{RemoteID: "remote1"}, + &UserShort{RemoteID: "remote2"}, + } + + result := index.ByRemoteID() + require.Len(t, result, 2) + require.Contains(t, result, "remote1") + require.Contains(t, result, "remote2") +} + +func TestUserIndex_ByEmail(t *testing.T) { + index := UserIndex{ + &UserShort{Email: "user1@example.com"}, + &UserShort{Email: "user2@example.com"}, + } + + result := index.ByEmail() + require.Len(t, result, 2) + require.Contains(t, result, "user1@example.com") + require.Contains(t, result, "user2@example.com") +} + +func TestUserIndex_GetMattermostUserIDs(t *testing.T) { + index := UserIndex{ + &UserShort{MattermostUserID: "user1"}, + &UserShort{MattermostUserID: "user2"}, + } + + result := index.GetMattermostUserIDs() + require.Len(t, result, 2) + require.Contains(t, result, "user1") + require.Contains(t, result, "user2") +} + +func TestIsConfiguredForStatusUpdates(t *testing.T) { + tests := []struct { + name string + settings Settings + expectedResult bool + }{ + { + name: "UpdateStatusFromOptions is AwayStatusOption", + settings: Settings{ + UpdateStatusFromOptions: AwayStatusOption, + }, + expectedResult: true, + }, + { + name: "UpdateStatusFromOptions is DNDStatusOption", + settings: Settings{ + UpdateStatusFromOptions: DNDStatusOption, + }, + expectedResult: true, + }, + { + name: "UpdateStatusFromOptions is empty, UpdateStatus is true, notifications during meeting are false", + settings: Settings{ + UpdateStatusFromOptions: "", + UpdateStatus: true, + ReceiveNotificationsDuringMeeting: false, + }, + expectedResult: true, + }, + { + name: "UpdateStatusFromOptions is empty, UpdateStatus is true, notifications during meeting are true", + settings: Settings{ + UpdateStatusFromOptions: "", + UpdateStatus: true, + ReceiveNotificationsDuringMeeting: true, + }, + expectedResult: true, + }, + { + name: "UpdateStatusFromOptions is empty, UpdateStatus is false", + settings: Settings{ + UpdateStatusFromOptions: "", + UpdateStatus: false, + }, + expectedResult: false, + }, + { + name: "UpdateStatusFromOptions is not configured", + settings: Settings{ + UpdateStatusFromOptions: "other", + }, + expectedResult: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + user := &User{ + Settings: tt.settings, + } + + result := user.IsConfiguredForStatusUpdates() + require.Equal(t, tt.expectedResult, result) + }) + } +} + +func TestIsConfiguredForCustomStatusUpdates(t *testing.T) { + tests := []struct { + name string + settings Settings + expectedResult bool + }{ + { + name: "Custom status is configured", + settings: Settings{ + SetCustomStatus: true, + }, + expectedResult: true, + }, + { + name: "Custom status is not configured", + settings: Settings{ + SetCustomStatus: false, + }, + expectedResult: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + user := &User{ + Settings: tt.settings, + } + + result := user.IsConfiguredForCustomStatusUpdates() + require.Equal(t, tt.expectedResult, result, "Expected %v but got %v", tt.expectedResult, result) + }) + } +} From f3cda2e86f46d97c764e7857ee72ff3087959112 Mon Sep 17 00:00:00 2001 From: kshitij katiyar <90389917+Kshitij-Katiyar@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:10:37 +0530 Subject: [PATCH 10/11] Added server testcases for store/subscription_store.go --- calendar/store/subscription_store_test.go | 182 ++++++++++++++++++++++ calendar/store/test_util.go | 39 +++-- 2 files changed, 209 insertions(+), 12 deletions(-) create mode 100644 calendar/store/subscription_store_test.go diff --git a/calendar/store/subscription_store_test.go b/calendar/store/subscription_store_test.go new file mode 100644 index 00000000..61176668 --- /dev/null +++ b/calendar/store/subscription_store_test.go @@ -0,0 +1,182 @@ +package store + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/testutil" + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/utils/bot/mock_bot" + + "github.com/mattermost/mattermost/server/public/model" +) + +func TestLoadSubscription(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, *Subscription, error) + }{ + { + name: "Error loading subscription", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "sub_0c47c5b7e2a88ec9256c8ac0e71b0f6e").Return(nil, &model.AppError{Message: "Subscription not found"}).Times(1) + }, + assertions: func(t *testing.T, sub *Subscription, err error) { + require.Error(t, err) + require.Nil(t, sub) + require.EqualError(t, err, "failed plugin KVGet: Subscription not found") + }, + }, + { + name: "Successful Load", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "sub_0c47c5b7e2a88ec9256c8ac0e71b0f6e").Return([]byte(`{"PluginVersion":"1.0","Remote":{"ID":"mockRemoteUserID","CreatorID":"mockCreatorID"}}`), nil).Times(1) + }, + assertions: func(t *testing.T, sub *Subscription, err error) { + require.NoError(t, err) + require.NotNil(t, sub) + require.Equal(t, "1.0", sub.PluginVersion) + require.Equal(t, MockRemoteUserID, sub.Remote.ID) + require.Equal(t, MockCreatorID, sub.Remote.CreatorID) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + sub, err := store.LoadSubscription(MockSubscriptionID) + + tt.assertions(t, sub, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestStoreUserSubscription(t *testing.T) { + mockUser := GetMockUser() + mockSubscription := GetMockSubscription() + + tests := []struct { + name string + setup func(*testutil.MockPluginAPI, *mock_bot.MockLogger, *mock_bot.MockLogger) + assertions func(*testing.T, error) + }{ + { + name: "User does not match subscription creator", + setup: func(_ *testutil.MockPluginAPI, _ *mock_bot.MockLogger, _ *mock_bot.MockLogger) {}, + assertions: func(t *testing.T, err error) { + require.Error(t, err) + require.EqualError(t, err, `user "mockRemoteID" does not match the subscription creator "mockCreatorID"`) + }, + }, + { + name: "Error storing subscription", + setup: func(mockAPI *testutil.MockPluginAPI, _ *mock_bot.MockLogger, _ *mock_bot.MockLogger) { + mockSubscription.Remote.CreatorID = mockUser.Remote.ID + mockAPI.On("KVSet", "sub_0c47c5b7e2a88ec9256c8ac0e71b0f6e", mock.Anything).Return(&model.AppError{Message: "Failed to store subscription"}).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.Error(t, err) + require.ErrorContains(t, err, "Failed to store subscription") + }, + }, + { + name: "Error storing user settings", + setup: func(mockAPI *testutil.MockPluginAPI, _ *mock_bot.MockLogger, _ *mock_bot.MockLogger) { + mockAPI.ExpectedCalls = nil + mockAPI.On("KVSet", "sub_0c47c5b7e2a88ec9256c8ac0e71b0f6e", mock.Anything).Return(nil).Times(1) + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(&model.AppError{Message: "Failed to store user settings"}).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.Error(t, err) + require.ErrorContains(t, err, "Failed to store user settings") + }, + }, + { + name: "Successful Store", + setup: func(mockAPI *testutil.MockPluginAPI, mockLogger *mock_bot.MockLogger, mockLoggerWith *mock_bot.MockLogger) { + mockAPI.ExpectedCalls = nil + mockAPI.On("KVSet", "sub_0c47c5b7e2a88ec9256c8ac0e71b0f6e", mock.Anything).Return(nil).Times(1) + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(nil).Times(1) + mockLogger.EXPECT().With(gomock.Any()).Return(mockLoggerWith).Times(1) + mockLoggerWith.EXPECT().Debugf("store: stored mattermost user subscription.").Times(1) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, mockLogger, mockLoggerWith, _ := GetMockSetup(t) + tt.setup(mockAPI, mockLogger, mockLoggerWith) + + err := store.StoreUserSubscription(mockUser, mockSubscription) + + tt.assertions(t, err) + mockAPI.AssertExpectations(t) + }) + } +} + +func TestDeleteUserSubscription(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI, *mock_bot.MockLogger, *mock_bot.MockLogger) + assertions func(*testing.T, error) + }{ + { + name: "Error deleting subscription", + setup: func(mockAPI *testutil.MockPluginAPI, _ *mock_bot.MockLogger, _ *mock_bot.MockLogger) { + mockAPI.On("KVDelete", "sub_0c47c5b7e2a88ec9256c8ac0e71b0f6e").Return(&model.AppError{Message: "Failed to delete subscription"}).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.Error(t, err) + require.ErrorContains(t, err, "Failed to delete subscription") + }, + }, + { + name: "Error updating user settings", + setup: func(mockAPI *testutil.MockPluginAPI, _ *mock_bot.MockLogger, _ *mock_bot.MockLogger) { + mockAPI.ExpectedCalls = nil + mockAPI.On("KVDelete", "sub_0c47c5b7e2a88ec9256c8ac0e71b0f6e").Return(nil).Times(1) + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(&model.AppError{Message: "Failed to update user settings"}).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.Error(t, err) + require.ErrorContains(t, err, "Failed to update user settings") + }, + }, + { + name: "Successful Delete", + setup: func(mockAPI *testutil.MockPluginAPI, mockLogger *mock_bot.MockLogger, mockLoggerWith *mock_bot.MockLogger) { + mockAPI.ExpectedCalls = nil + mockAPI.On("KVDelete", "sub_0c47c5b7e2a88ec9256c8ac0e71b0f6e").Return(nil).Times(1) + mockAPI.On("KVSet", "user_c3b5020d58a049787bc969768465b890", mock.Anything).Return(nil).Times(1) + mockAPI.On("KVSet", "mmuid_e138a0f218087f9324d8c77f87d5f3a0", mock.Anything).Return(nil).Times(1) + mockLogger.EXPECT().With(gomock.Any()).Return(mockLoggerWith).Times(1) + mockLoggerWith.EXPECT().Debugf("store: deleted mattermost user subscription.").Times(1) + }, + assertions: func(t *testing.T, err error) { + require.NoError(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, mockLogger, mockLoggerWith, _ := GetMockSetup(t) + tt.setup(mockAPI, mockLogger, mockLoggerWith) + + mockUser := GetMockUser() + err := store.DeleteUserSubscription(mockUser, MockSubscriptionID) + + tt.assertions(t, err) + mockAPI.AssertExpectations(t) + }) + } +} diff --git a/calendar/store/test_util.go b/calendar/store/test_util.go index 319209af..264acb3b 100644 --- a/calendar/store/test_util.go +++ b/calendar/store/test_util.go @@ -14,6 +14,9 @@ import ( ) const ( + MockEventSubscriptionID = "mockEventSubscriptionID" + MockSubscriptionID = "mockSubscriptionID" + MockCreatorID = "mockCreatorID" MockMMUsername = "mockMMUsername" MockMMDisplayName = "mockMMDisplayName" MockMMUserID = "mockMMUserID" @@ -41,6 +44,30 @@ func GetMockSetup(t *testing.T) (*testutil.MockPluginAPI, Store, *mock_bot.MockL return mockAPI, store, mockLogger, mockLoggerWith, mockTracker } +func GetMockUser() *User { + return &User{ + MattermostUserID: MockMMUserID, + MattermostUsername: MockMMUsername, + MattermostDisplayName: MockMMDisplayName, + Settings: Settings{ + EventSubscriptionID: MockEventSubscriptionID, + }, + Remote: &remote.User{ + ID: MockRemoteID, + Mail: MockRemoteMail, + }, + } +} + +func GetMockSubscription() *Subscription { + return &Subscription{ + Remote: &remote.Subscription{ + ID: MockSubscriptionID, + CreatorID: MockCreatorID, + }, + } +} + func GetRemoteUserJSON(noOfUsers int) string { type RemoteUser struct { MMUsername string `json:"mm_username"` @@ -63,15 +90,3 @@ func GetRemoteUserJSON(noOfUsers int) string { result, _ := json.Marshal(users) return string(result) } - -func GetMockUser() *User { - return &User{ - MattermostUserID: MockMMUserID, - MattermostUsername: MockMMUsername, - MattermostDisplayName: MockMMDisplayName, - Remote: &remote.User{ - ID: MockRemoteID, - Mail: MockRemoteMail, - }, - } -} From 2a8a9385db0f968d20c8532d5a91d6fbd02dafae Mon Sep 17 00:00:00 2001 From: kshitij katiyar <90389917+Kshitij-Katiyar@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:18:13 +0530 Subject: [PATCH 11/11] Added server testcases for store/oauth2_store.go --- calendar/store/oauth2_store_test.go | 103 ++++++++++++++++++++++++++++ calendar/store/test_util.go | 1 + 2 files changed, 104 insertions(+) create mode 100644 calendar/store/oauth2_store_test.go diff --git a/calendar/store/oauth2_store_test.go b/calendar/store/oauth2_store_test.go new file mode 100644 index 00000000..5e96cf9f --- /dev/null +++ b/calendar/store/oauth2_store_test.go @@ -0,0 +1,103 @@ +package store + +import ( + "testing" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/testutil" + + "github.com/mattermost/mattermost/server/public/model" +) + +func TestVerifyOAuth2State(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error loading state", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "oauth2_fb89cea34670836627b56ad5b94ce5e3").Return(nil, &model.AppError{Message: "Error getting state"}).Times(1) + mockAPI.On("KVDelete", "oauth2_fb89cea34670836627b56ad5b94ce5e3").Return(nil) + }, + assertions: func(t *testing.T, err error) { + require.Error(t, err) + require.EqualError(t, err, "failed plugin KVGet: Error getting state") + }, + }, + { + name: "Invalid Oauth state", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "oauth2_fb89cea34670836627b56ad5b94ce5e3").Return([]byte("invalidState"), nil).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.Error(t, err) + require.EqualError(t, err, "invalid oauth state, please try again") + }, + }, + { + name: "Successfull Oauth state verification", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVGet", "oauth2_fb89cea34670836627b56ad5b94ce5e3").Return([]byte(MockState), nil).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.Nil(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + err := store.VerifyOAuth2State(MockState) + + tt.assertions(t, err) + + mockAPI.AssertExpectations(t) + }) + } +} + +func TestStoreOAuth2State(t *testing.T) { + tests := []struct { + name string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error loading state", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVSetWithExpiry", "oauth2_fb89cea34670836627b56ad5b94ce5e3", mock.Anything, int64(oAuth2StateTimeToLive)).Return(&model.AppError{Message: "Error loading state"}).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.Error(t, err) + require.ErrorContains(t, err, "Error loading state") + }, + }, + { + name: "Successfull Oauth state verification", + setup: func(mockAPI *testutil.MockPluginAPI) { + mockAPI.On("KVSetWithExpiry", "oauth2_fb89cea34670836627b56ad5b94ce5e3", mock.Anything, int64(oAuth2StateTimeToLive)).Return(nil).Times(1) + }, + assertions: func(t *testing.T, err error) { + require.Nil(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockAPI, store, _, _, _ := GetMockSetup(t) + tt.setup(mockAPI) + + err := store.StoreOAuth2State(MockState) + + tt.assertions(t, err) + + mockAPI.AssertExpectations(t) + }) + } +} diff --git a/calendar/store/test_util.go b/calendar/store/test_util.go index 264acb3b..179e06be 100644 --- a/calendar/store/test_util.go +++ b/calendar/store/test_util.go @@ -30,6 +30,7 @@ const ( MockRemoteJSON = `{"remote": {"id": "mockRemoteID"}}` MockUserJSON = `[{"MattermostUserID":"mockMMUserID","RemoteID":"mockRemoteID"}]` MockUserDetailsWithEventJSON = `{"mm_id":"mockUserID","active_events": []}` + MockState = "mockState" ) func GetMockSetup(t *testing.T) (*testutil.MockPluginAPI, Store, *mock_bot.MockLogger, *mock_bot.MockLogger, *mock_tracker.MockTracker) {