From 6cb7e49a2b39f88bc6acf8140a23b38f84e19b4e Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Wed, 26 May 2021 04:38:43 -0600 Subject: [PATCH] add unit tests (#422) * add unit tests * cleanup * cleanup * rename helper file, remove gomock.eq() * update import --- server/app/auth_test.go | 185 ++++++++++++++++++++++++++++++++++++++ server/app/blocks_test.go | 24 ++--- server/app/helper_test.go | 39 ++++++++ server/auth/auth_test.go | 135 ++++++++++++++++++++++++++++ 4 files changed, 364 insertions(+), 19 deletions(-) create mode 100644 server/app/auth_test.go create mode 100644 server/app/helper_test.go create mode 100644 server/auth/auth_test.go diff --git a/server/app/auth_test.go b/server/app/auth_test.go new file mode 100644 index 000000000..6a4ac064f --- /dev/null +++ b/server/app/auth_test.go @@ -0,0 +1,185 @@ +package app + +import ( + "fmt" + "testing" + + "github.com/golang/mock/gomock" + "github.com/mattermost/focalboard/server/model" + "github.com/mattermost/focalboard/server/services/auth" + "github.com/mattermost/focalboard/server/utils" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" +) + +var mockUser = &model.User{ + ID: utils.CreateGUID(), + Username: "testUsername", + Email: "testEmail", + Password: auth.HashPassword("testPassword"), +} + +func TestLogin(t *testing.T) { + th := SetupTestHelper(t) + + testcases := []struct { + title string + userName string + email string + password string + mfa string + isError bool + }{ + {"fail, missing login information", "", "", "", "", true}, + {"fail, invalid username", "badUsername", "", "", "", true}, + {"fail, invalid email", "", "badEmail", "", "", true}, + {"fail, invalid password", "testUsername", "", "badPassword", "", true}, + {"success, using username", "testUsername", "", "testPassword", "", false}, + {"success, using email", "", "testEmail", "testPassword", "", false}, + } + + th.Store.EXPECT().GetUserByUsername("badUsername").Return(nil, errors.New("Bad Username")) + th.Store.EXPECT().GetUserByEmail("badEmail").Return(nil, errors.New("Bad Email")) + th.Store.EXPECT().GetUserByUsername("testUsername").Return(mockUser, nil).Times(2) + th.Store.EXPECT().GetUserByEmail("testEmail").Return(mockUser, nil) + th.Store.EXPECT().CreateSession(gomock.Any()).Return(nil).Times(2) + + for _, test := range testcases { + t.Run(test.title, func(t *testing.T) { + token, err := th.App.Login(test.userName, test.email, test.password, test.mfa) + if test.isError { + require.Error(t, err) + } else { + require.NoError(t, err) + require.NotNil(t, token) + } + }) + } +} + +func TestGetUser(t *testing.T) { + th := SetupTestHelper(t) + + testcases := []struct { + title string + id string + isError bool + }{ + {"fail, missing id", "", true}, + {"fail, invalid id", "badID", true}, + {"success", "goodID", false}, + } + + th.Store.EXPECT().GetUserById("badID").Return(nil, errors.New("Bad Id")) + th.Store.EXPECT().GetUserById("goodID").Return(mockUser, nil) + + for _, test := range testcases { + t.Run(test.title, func(t *testing.T) { + token, err := th.App.GetUser(test.id) + if test.isError { + require.Error(t, err) + } else { + require.NoError(t, err) + require.NotNil(t, token) + } + }) + } +} + +func TestRegisterUser(t *testing.T) { + th := SetupTestHelper(t) + + testcases := []struct { + title string + userName string + email string + password string + isError bool + }{ + {"fail, missing login information", "", "", "", true}, + {"fail, username exists", "existingUsername", "", "", true}, + {"fail, email exists", "", "existingEmail", "", true}, + {"fail, invalid password", "newUsername", "", "test", true}, + {"success, using email", "", "newEmail", "testPassword", false}, + } + + th.Store.EXPECT().GetUserByUsername("existingUsername").Return(mockUser, nil) + th.Store.EXPECT().GetUserByUsername("newUsername").Return(mockUser, errors.New("user not found")) + th.Store.EXPECT().GetUserByEmail("existingEmail").Return(mockUser, nil) + th.Store.EXPECT().GetUserByEmail("newEmail").Return(nil, errors.New("email not found")) + th.Store.EXPECT().CreateUser(gomock.Any()).Return(nil) + + for _, test := range testcases { + t.Run(test.title, func(t *testing.T) { + fmt.Println(test.email) + err := th.App.RegisterUser(test.userName, test.email, test.password) + if test.isError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestUpdateUserPassword(t *testing.T) { + th := SetupTestHelper(t) + + testcases := []struct { + title string + userName string + password string + isError bool + }{ + {"fail, missing login information", "", "", true}, + {"fail, invalid username", "badUsername", "", true}, + {"success, username", "testUsername", "testPassword", false}, + } + + th.Store.EXPECT().UpdateUserPassword("", gomock.Any()).Return(errors.New("user not found")) + th.Store.EXPECT().UpdateUserPassword("badUsername", gomock.Any()).Return(errors.New("user not found")) + th.Store.EXPECT().UpdateUserPassword("testUsername", gomock.Any()).Return(nil) + + for _, test := range testcases { + t.Run(test.title, func(t *testing.T) { + err := th.App.UpdateUserPassword(test.userName, test.password) + if test.isError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestChangePassword(t *testing.T) { + th := SetupTestHelper(t) + + testcases := []struct { + title string + userName string + oldPassword string + password string + isError bool + }{ + {"fail, missing login information", "", "", "", true}, + {"fail, invalid userId", "badID", "", "", true}, + {"fail, invalid password", mockUser.ID, "wrongPassword", "newPassword", true}, + {"success, using username", mockUser.ID, "testPassword", "newPassword", false}, + } + + th.Store.EXPECT().GetUserById("badID").Return(nil, errors.New("userID not found")) + th.Store.EXPECT().GetUserById(mockUser.ID).Return(mockUser, nil).Times(2) + th.Store.EXPECT().UpdateUserPasswordByID(mockUser.ID, gomock.Any()).Return(nil) + + for _, test := range testcases { + t.Run(test.title, func(t *testing.T) { + err := th.App.ChangePassword(test.userName, test.oldPassword, test.password) + if test.isError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/server/app/blocks_test.go b/server/app/blocks_test.go index e6f27c820..0708422ce 100644 --- a/server/app/blocks_test.go +++ b/server/app/blocks_test.go @@ -5,40 +5,26 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/mattermost/focalboard/server/auth" - "github.com/mattermost/focalboard/server/services/config" st "github.com/mattermost/focalboard/server/services/store" - "github.com/mattermost/focalboard/server/services/store/mockstore" - "github.com/mattermost/focalboard/server/services/webhook" - "github.com/mattermost/focalboard/server/ws" - "github.com/mattermost/mattermost-server/v5/shared/filestore/mocks" "github.com/stretchr/testify/require" ) func TestGetParentID(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - cfg := config.Configuration{} - store := mockstore.NewMockStore(ctrl) - auth := auth.New(&cfg, store) - sessionToken := "TESTTOKEN" - wsserver := ws.NewServer(auth, sessionToken, false) - webhook := webhook.NewClient(&cfg) - app := New(&cfg, store, auth, wsserver, &mocks.FileBackend{}, webhook) + th := SetupTestHelper(t) container := st.Container{ WorkspaceID: "0", } t.Run("success query", func(t *testing.T) { - store.EXPECT().GetParentID(gomock.Eq(container), gomock.Eq("test-id")).Return("test-parent-id", nil) - result, err := app.GetParentID(container, "test-id") + th.Store.EXPECT().GetParentID(gomock.Eq(container), gomock.Eq("test-id")).Return("test-parent-id", nil) + result, err := th.App.GetParentID(container, "test-id") require.NoError(t, err) require.Equal(t, "test-parent-id", result) }) t.Run("fail query", func(t *testing.T) { - store.EXPECT().GetParentID(gomock.Eq(container), gomock.Eq("test-id")).Return("", errors.New("block-not-found")) - _, err := app.GetParentID(container, "test-id") + th.Store.EXPECT().GetParentID(gomock.Eq(container), gomock.Eq("test-id")).Return("", errors.New("block-not-found")) + _, err := th.App.GetParentID(container, "test-id") require.Error(t, err) require.Equal(t, "block-not-found", err.Error()) }) diff --git a/server/app/helper_test.go b/server/app/helper_test.go new file mode 100644 index 000000000..3d887fd49 --- /dev/null +++ b/server/app/helper_test.go @@ -0,0 +1,39 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +package app + +import ( + "testing" + + "github.com/golang/mock/gomock" + + "github.com/mattermost/focalboard/server/auth" + "github.com/mattermost/focalboard/server/services/config" + "github.com/mattermost/focalboard/server/services/store/mockstore" + "github.com/mattermost/focalboard/server/services/webhook" + "github.com/mattermost/focalboard/server/ws" + "github.com/mattermost/mattermost-server/v5/shared/filestore/mocks" +) + +type TestHelper struct { + App *App + Store *mockstore.MockStore +} + +func SetupTestHelper(t *testing.T) *TestHelper { + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + cfg := config.Configuration{} + store := mockstore.NewMockStore(ctrl) + auth := auth.New(&cfg, store) + sessionToken := "TESTTOKEN" + wsserver := ws.NewServer(auth, sessionToken, false) + webhook := webhook.NewClient(&cfg) + app2 := New(&cfg, store, auth, wsserver, &mocks.FileBackend{}, webhook) + + return &TestHelper{ + App: app2, + Store: store, + } +} diff --git a/server/auth/auth_test.go b/server/auth/auth_test.go new file mode 100644 index 000000000..df8a50ee7 --- /dev/null +++ b/server/auth/auth_test.go @@ -0,0 +1,135 @@ +package auth + +import ( + "database/sql" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/mattermost/focalboard/server/model" + "github.com/mattermost/focalboard/server/services/config" + "github.com/mattermost/focalboard/server/services/store" + "github.com/mattermost/focalboard/server/services/store/mockstore" + "github.com/mattermost/focalboard/server/utils" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" +) + +type TestHelper struct { + Auth *Auth + Session model.Session + Store *mockstore.MockStore +} + +var mockSession = &model.Session{ + ID: utils.CreateGUID(), + Token: "goodToken", + UserID: "12345", + CreateAt: time.Now().Unix() - 2000, + UpdateAt: time.Now().Unix() - 2000, +} + +func setupTestHelper(t *testing.T) *TestHelper { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + cfg := config.Configuration{} + mockStore := mockstore.NewMockStore(ctrl) + newAuth := New(&cfg, mockStore) + + return &TestHelper{ + Auth: newAuth, + Session: *mockSession, + Store: mockStore, + } + +} + +func TestGetSession(t *testing.T) { + + th := setupTestHelper(t) + + testcases := []struct { + title string + token string + refreshTime int64 + isError bool + }{ + {"fail, no token", "", 0, true}, + {"fail, invalid username", "badToken", 0, true}, + {"sucess, good token", "goodToken", 1000, false}, + } + + th.Store.EXPECT().GetSession("badToken", gomock.Any()).Return(nil, errors.New("Invalid Token")) + th.Store.EXPECT().GetSession("goodToken", gomock.Any()).Return(mockSession, nil) + th.Store.EXPECT().RefreshSession(gomock.Any()).Return(nil) + + for _, test := range testcases { + t.Run(test.title, func(t *testing.T) { + if test.refreshTime > 0 { + th.Auth.config.SessionRefreshTime = test.refreshTime + } + + session, err := th.Auth.GetSession(test.token) + if test.isError { + require.Error(t, err) + } else { + require.NoError(t, err) + require.NotNil(t, session) + } + }) + } +} + +func TestIsValidReadToken(t *testing.T) { + th := setupTestHelper(t) + + validBlockID := "testBlockID" + mockContainer := store.Container{ + WorkspaceID: "testWorkspaceID", + } + validReadToken := "testReadToken" + mockSharing := model.Sharing{ + ID: "testRootID", + Enabled: true, + Token: validReadToken, + } + + testcases := []struct { + title string + container store.Container + blockID string + readToken string + isError bool + isSuccess bool + }{ + {"fail, error GetRootID", mockContainer, "badBlock", "", true, false}, + {"fail, rootID not found", mockContainer, "goodBlockID", "", false, false}, + {"fail, sharing throws error", mockContainer, "goodBlockID2", "", true, false}, + {"fail, bad readToken", mockContainer, validBlockID, "invalidReadToken", false, false}, + {"success", mockContainer, validBlockID, validReadToken, false, true}, + } + + th.Store.EXPECT().GetRootID(gomock.Eq(mockContainer), "badBlock").Return("", errors.New("invalid block")) + th.Store.EXPECT().GetRootID(gomock.Eq(mockContainer), "goodBlockID").Return("rootNotFound", nil) + th.Store.EXPECT().GetRootID(gomock.Eq(mockContainer), "goodBlockID2").Return("rootError", nil) + th.Store.EXPECT().GetRootID(gomock.Eq(mockContainer), validBlockID).Return("testRootID", nil).Times(2) + th.Store.EXPECT().GetSharing(gomock.Eq(mockContainer), "rootNotFound").Return(nil, sql.ErrNoRows) + th.Store.EXPECT().GetSharing(gomock.Eq(mockContainer), "rootError").Return(nil, errors.New("another error")) + th.Store.EXPECT().GetSharing(gomock.Eq(mockContainer), "testRootID").Return(&mockSharing, nil).Times(2) + + for _, test := range testcases { + t.Run(test.title, func(t *testing.T) { + success, err := th.Auth.IsValidReadToken(test.container, test.blockID, test.readToken) + if test.isError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + if test.isSuccess { + require.True(t, success) + } else { + require.False(t, success) + } + }) + } +}