From ef7d05518e71fafc757a04c2047d78b3ef013a95 Mon Sep 17 00:00:00 2001 From: kshitij katiyar <90389917+Kshitij-Katiyar@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:06:38 +0530 Subject: [PATCH] Added server testcase for kvstore/plugin_store.go file (#413) * [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 * updated go.mod and go.sum entries * refactored mock plugin setup --------- Co-authored-by: Doug Lauder Co-authored-by: Raghav Aggarwal --- calendar/testutil/mock_setup.go | 55 ++++ calendar/utils/kvstore/plugin_store_test.go | 277 ++++++++++++++++++++ go.mod | 1 + 3 files changed, 333 insertions(+) create mode 100644 calendar/testutil/mock_setup.go create mode 100644 calendar/utils/kvstore/plugin_store_test.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 new file mode 100644 index 00000000..53429834 --- /dev/null +++ b/calendar/utils/kvstore/plugin_store_test.go @@ -0,0 +1,277 @@ +package kvstore + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/mattermost/mattermost/server/public/model" + + "github.com/mattermost/mattermost-plugin-mscalendar/calendar/testutil" +) + +func TestLoad(t *testing.T) { + tests := []struct { + name string + key string + setup func(*testutil.MockPluginAPI) + assertions func(*testing.T, []byte, error) + }{ + { + name: "Error during KVGet", + key: "error-key", + 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) { + 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 *testutil.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 *testutil.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 := &testutil.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(*testutil.MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error during KVSet with TTL", + expiryTime: 60, + 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) { + 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 *testutil.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 *testutil.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 *testutil.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 := &testutil.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(*testutil.MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error during storing with TTL", + 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) { + require.EqualError(t, err, "failed plugin KVSet (ttl: 60s) \"mockKey\": KVSet failed", "unexpected error message") + }, + }, + { + name: "Store with TTL successfully", + setup: func(mockAPI *testutil.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 := &testutil.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(*testutil.MockPluginAPI) + assertions func(*testing.T, bool, error) + }{ + { + name: "Error during KVSetWithOptions", + expiryTime: 60, + opts: model.PluginKVSetOptions{ + ExpireInSeconds: 30, + }, + 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) { + 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 *testutil.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 *testutil.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 := &testutil.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(*testutil.MockPluginAPI) + assertions func(*testing.T, error) + }{ + { + name: "Error during KVDelete", + setup: func(mockAPI *testutil.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 *testutil.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 := &testutil.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 e44821f9..81d53191 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,7 @@ 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.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