mirror of
https://github.com/mattermost/focalboard.git
synced 2024-12-21 13:38:56 +02:00
03f4717e96
* WIP * Added migrations * Updating store method * WIP * WIP * Updated DND * WIP * WIP * WIP * WIP * WIP * wip * WIP * Adding new DB tool * Used migration functions in new migrations * Unique constraint migration * Unique constraint migration * Added SQLITE migrations * Added SQLITE support in few more migrations * Added SQLITE support in few more migrations * WIP * Used old-fashioned way to add unique constraint * Using oldsqlite method * Using oldsqlite method * Fixed all store and app layer tests * fixed integration tests * test and lint fix * Updated migration for MySQL and Postgres on personal server * Types fix * sqlite fix * fix typo * misc cleanup * added new tests * added new tests * de-duping input for postgres * integration tests, rmeoved uneeded migration * Added some migration tests * Added some migration tests * Fixed a test * completed migration tests * completed migration tests * Removed leftover debug statements Co-authored-by: Mattermost Build <build@mattermost.com>
569 lines
16 KiB
Go
569 lines
16 KiB
Go
package app
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/mattermost/focalboard/server/utils"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/mattermost/focalboard/server/model"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestAddMemberToBoard(t *testing.T) {
|
|
th, tearDown := SetupTestHelper(t)
|
|
defer tearDown()
|
|
|
|
t.Run("base case", func(t *testing.T) {
|
|
const boardID = "board_id_1"
|
|
const userID = "user_id_1"
|
|
|
|
boardMember := &model.BoardMember{
|
|
BoardID: boardID,
|
|
UserID: userID,
|
|
SchemeEditor: true,
|
|
}
|
|
|
|
th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{
|
|
ID: "board_id_1",
|
|
TeamID: "team_id_1",
|
|
}, nil)
|
|
|
|
th.Store.EXPECT().GetMemberForBoard(boardID, userID).Return(nil, nil)
|
|
|
|
th.Store.EXPECT().SaveMember(mock.MatchedBy(func(i interface{}) bool {
|
|
p := i.(*model.BoardMember)
|
|
return p.BoardID == boardID && p.UserID == userID
|
|
})).Return(&model.BoardMember{
|
|
BoardID: boardID,
|
|
}, nil)
|
|
|
|
// for WS change broadcast
|
|
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil)
|
|
|
|
th.Store.EXPECT().GetUserCategoryBoards("user_id_1", "team_id_1").Return([]model.CategoryBoards{
|
|
{
|
|
Category: model.Category{
|
|
ID: "default_category_id",
|
|
Name: "Boards",
|
|
Type: "system",
|
|
},
|
|
},
|
|
}, nil).Times(2)
|
|
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", "default_category_id", []string{"board_id_1"}).Return(nil)
|
|
|
|
addedBoardMember, err := th.App.AddMemberToBoard(boardMember)
|
|
require.NoError(t, err)
|
|
require.Equal(t, boardID, addedBoardMember.BoardID)
|
|
})
|
|
|
|
t.Run("return existing non-synthetic membership if any", func(t *testing.T) {
|
|
const boardID = "board_id_1"
|
|
const userID = "user_id_1"
|
|
|
|
boardMember := &model.BoardMember{
|
|
BoardID: boardID,
|
|
UserID: userID,
|
|
SchemeEditor: true,
|
|
}
|
|
|
|
th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{
|
|
TeamID: "team_id_1",
|
|
}, nil)
|
|
|
|
th.Store.EXPECT().GetMemberForBoard(boardID, userID).Return(&model.BoardMember{
|
|
UserID: userID,
|
|
BoardID: boardID,
|
|
Synthetic: false,
|
|
}, nil)
|
|
|
|
addedBoardMember, err := th.App.AddMemberToBoard(boardMember)
|
|
require.NoError(t, err)
|
|
require.Equal(t, boardID, addedBoardMember.BoardID)
|
|
})
|
|
|
|
t.Run("should convert synthetic membership into natural membership", func(t *testing.T) {
|
|
const boardID = "board_id_1"
|
|
const userID = "user_id_1"
|
|
|
|
boardMember := &model.BoardMember{
|
|
BoardID: boardID,
|
|
UserID: userID,
|
|
SchemeEditor: true,
|
|
}
|
|
|
|
th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{
|
|
ID: "board_id_1",
|
|
TeamID: "team_id_1",
|
|
}, nil)
|
|
|
|
th.Store.EXPECT().GetMemberForBoard(boardID, userID).Return(&model.BoardMember{
|
|
UserID: userID,
|
|
BoardID: boardID,
|
|
Synthetic: true,
|
|
}, nil)
|
|
|
|
th.Store.EXPECT().SaveMember(mock.MatchedBy(func(i interface{}) bool {
|
|
p := i.(*model.BoardMember)
|
|
return p.BoardID == boardID && p.UserID == userID
|
|
})).Return(&model.BoardMember{
|
|
UserID: userID,
|
|
BoardID: boardID,
|
|
Synthetic: false,
|
|
}, nil)
|
|
|
|
// for WS change broadcast
|
|
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil)
|
|
|
|
th.Store.EXPECT().GetUserCategoryBoards("user_id_1", "team_id_1").Return([]model.CategoryBoards{
|
|
{
|
|
Category: model.Category{
|
|
ID: "default_category_id",
|
|
Name: "Boards",
|
|
Type: "system",
|
|
},
|
|
},
|
|
}, nil).Times(2)
|
|
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", "default_category_id", []string{"board_id_1"}).Return(nil)
|
|
|
|
addedBoardMember, err := th.App.AddMemberToBoard(boardMember)
|
|
require.NoError(t, err)
|
|
require.Equal(t, boardID, addedBoardMember.BoardID)
|
|
})
|
|
}
|
|
|
|
func TestPatchBoard(t *testing.T) {
|
|
th, tearDown := SetupTestHelper(t)
|
|
defer tearDown()
|
|
|
|
t.Run("base case, title patch", func(t *testing.T) {
|
|
const boardID = "board_id_1"
|
|
const userID = "user_id_1"
|
|
const teamID = "team_id_1"
|
|
|
|
patchTitle := "Patched Title"
|
|
patch := &model.BoardPatch{
|
|
Title: &patchTitle,
|
|
}
|
|
|
|
th.Store.EXPECT().PatchBoard(boardID, patch, userID).Return(
|
|
&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
Title: patchTitle,
|
|
},
|
|
nil)
|
|
|
|
// for WS BroadcastBoardChange
|
|
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil).Times(1)
|
|
|
|
patchedBoard, err := th.App.PatchBoard(patch, boardID, userID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, patchTitle, patchedBoard.Title)
|
|
})
|
|
|
|
t.Run("patch type open, no users", func(t *testing.T) {
|
|
const boardID = "board_id_1"
|
|
const userID = "user_id_2"
|
|
const teamID = "team_id_1"
|
|
|
|
patchType := model.BoardTypeOpen
|
|
patch := &model.BoardPatch{
|
|
Type: &patchType,
|
|
}
|
|
|
|
// Type not nil, will cause board to be reteived
|
|
// to check isTemplate
|
|
th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
IsTemplate: true,
|
|
}, nil)
|
|
|
|
// Type not null will retrieve team members
|
|
th.Store.EXPECT().GetUsersByTeam(teamID, "", false, false).Return([]*model.User{}, nil)
|
|
|
|
th.Store.EXPECT().PatchBoard(boardID, patch, userID).Return(
|
|
&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
},
|
|
nil)
|
|
|
|
// Should call GetMembersForBoard 2 times
|
|
// - for WS BroadcastBoardChange
|
|
// - for AddTeamMembers check
|
|
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil).Times(2)
|
|
|
|
patchedBoard, err := th.App.PatchBoard(patch, boardID, userID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, boardID, patchedBoard.ID)
|
|
})
|
|
|
|
t.Run("patch type private, no users", func(t *testing.T) {
|
|
const boardID = "board_id_1"
|
|
const userID = "user_id_2"
|
|
const teamID = "team_id_1"
|
|
|
|
patchType := model.BoardTypePrivate
|
|
patch := &model.BoardPatch{
|
|
Type: &patchType,
|
|
}
|
|
|
|
// Type not nil, will cause board to be reteived
|
|
// to check isTemplate
|
|
th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
IsTemplate: true,
|
|
}, nil)
|
|
|
|
// Type not null will retrieve team members
|
|
th.Store.EXPECT().GetUsersByTeam(teamID, "", false, false).Return([]*model.User{}, nil)
|
|
|
|
th.Store.EXPECT().PatchBoard(boardID, patch, userID).Return(
|
|
&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
},
|
|
nil)
|
|
|
|
// Should call GetMembersForBoard 2 times
|
|
// - for WS BroadcastBoardChange
|
|
// - for AddTeamMembers check
|
|
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil).Times(2)
|
|
|
|
patchedBoard, err := th.App.PatchBoard(patch, boardID, userID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, boardID, patchedBoard.ID)
|
|
})
|
|
|
|
t.Run("patch type open, single user", func(t *testing.T) {
|
|
const boardID = "board_id_1"
|
|
const userID = "user_id_2"
|
|
const teamID = "team_id_1"
|
|
|
|
patchType := model.BoardTypeOpen
|
|
patch := &model.BoardPatch{
|
|
Type: &patchType,
|
|
}
|
|
|
|
// Type not nil, will cause board to be reteived
|
|
// to check isTemplate
|
|
th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
IsTemplate: true,
|
|
}, nil)
|
|
// Type not null will retrieve team members
|
|
th.Store.EXPECT().GetUsersByTeam(teamID, "", false, false).Return([]*model.User{{ID: userID}}, nil)
|
|
|
|
th.Store.EXPECT().PatchBoard(boardID, patch, userID).Return(
|
|
&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
},
|
|
nil)
|
|
|
|
// Should call GetMembersForBoard 3 times
|
|
// for WS BroadcastBoardChange
|
|
// for AddTeamMembers check
|
|
// for WS BroadcastMemberChange
|
|
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil).Times(3)
|
|
|
|
patchedBoard, err := th.App.PatchBoard(patch, boardID, userID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, boardID, patchedBoard.ID)
|
|
})
|
|
|
|
t.Run("patch type private, single user", func(t *testing.T) {
|
|
const boardID = "board_id_1"
|
|
const userID = "user_id_2"
|
|
const teamID = "team_id_1"
|
|
|
|
patchType := model.BoardTypePrivate
|
|
patch := &model.BoardPatch{
|
|
Type: &patchType,
|
|
}
|
|
|
|
// Type not nil, will cause board to be reteived
|
|
// to check isTemplate
|
|
th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
IsTemplate: true,
|
|
}, nil)
|
|
// Type not null will retrieve team members
|
|
th.Store.EXPECT().GetUsersByTeam(teamID, "", false, false).Return([]*model.User{{ID: userID}}, nil)
|
|
|
|
th.Store.EXPECT().PatchBoard(boardID, patch, userID).Return(
|
|
&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
},
|
|
nil)
|
|
|
|
// Should call GetMembersForBoard 3 times
|
|
// for WS BroadcastBoardChange
|
|
// for AddTeamMembers check
|
|
// for WS BroadcastMemberChange
|
|
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil).Times(3)
|
|
|
|
patchedBoard, err := th.App.PatchBoard(patch, boardID, userID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, boardID, patchedBoard.ID)
|
|
})
|
|
|
|
t.Run("patch type open, user with member", func(t *testing.T) {
|
|
const boardID = "board_id_1"
|
|
const userID = "user_id_2"
|
|
const teamID = "team_id_1"
|
|
|
|
patchType := model.BoardTypeOpen
|
|
patch := &model.BoardPatch{
|
|
Type: &patchType,
|
|
}
|
|
|
|
// Type not nil, will cause board to be reteived
|
|
// to check isTemplate
|
|
th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
IsTemplate: true,
|
|
}, nil)
|
|
// Type not null will retrieve team members
|
|
th.Store.EXPECT().GetUsersByTeam(teamID, "", false, false).Return([]*model.User{{ID: userID}}, nil)
|
|
|
|
th.Store.EXPECT().PatchBoard(boardID, patch, userID).Return(
|
|
&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
},
|
|
nil)
|
|
|
|
// Should call GetMembersForBoard 2 times
|
|
// for WS BroadcastBoardChange
|
|
// for AddTeamMembers check
|
|
// We are returning the user as a direct Board Member, so BroadcastMemberDelete won't be called
|
|
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{{BoardID: boardID, UserID: userID, SchemeEditor: true}}, nil).Times(2)
|
|
|
|
patchedBoard, err := th.App.PatchBoard(patch, boardID, userID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, boardID, patchedBoard.ID)
|
|
})
|
|
|
|
t.Run("patch type private, user with member", func(t *testing.T) {
|
|
const boardID = "board_id_1"
|
|
const userID = "user_id_2"
|
|
const teamID = "team_id_1"
|
|
|
|
patchType := model.BoardTypePrivate
|
|
patch := &model.BoardPatch{
|
|
Type: &patchType,
|
|
}
|
|
|
|
// Type not nil, will cause board to be reteived
|
|
// to check isTemplate
|
|
th.Store.EXPECT().GetBoard(boardID).Return(&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
IsTemplate: true,
|
|
}, nil)
|
|
// Type not null will retrieve team members
|
|
th.Store.EXPECT().GetUsersByTeam(teamID, "", false, false).Return([]*model.User{{ID: userID}}, nil)
|
|
|
|
th.Store.EXPECT().PatchBoard(boardID, patch, userID).Return(
|
|
&model.Board{
|
|
ID: boardID,
|
|
TeamID: teamID,
|
|
},
|
|
nil)
|
|
|
|
// Should call GetMembersForBoard 2 times
|
|
// for WS BroadcastBoardChange
|
|
// for AddTeamMembers check
|
|
// We are returning the user as a direct Board Member, so BroadcastMemberDelete won't be called
|
|
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{{BoardID: boardID, UserID: userID, SchemeEditor: true}}, nil).Times(2)
|
|
|
|
patchedBoard, err := th.App.PatchBoard(patch, boardID, userID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, boardID, patchedBoard.ID)
|
|
})
|
|
}
|
|
|
|
func TestGetBoardCount(t *testing.T) {
|
|
th, tearDown := SetupTestHelper(t)
|
|
defer tearDown()
|
|
|
|
t.Run("base case", func(t *testing.T) {
|
|
boardCount := int64(100)
|
|
th.Store.EXPECT().GetBoardCount().Return(boardCount, nil)
|
|
|
|
count, err := th.App.GetBoardCount()
|
|
require.NoError(t, err)
|
|
require.Equal(t, boardCount, count)
|
|
})
|
|
}
|
|
|
|
func TestBoardCategory(t *testing.T) {
|
|
th, tearDown := SetupTestHelper(t)
|
|
defer tearDown()
|
|
|
|
t.Run("no boards default category exists", func(t *testing.T) {
|
|
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{
|
|
{
|
|
Category: model.Category{ID: "category_id_1", Name: "Category 1"},
|
|
BoardMetadata: []model.CategoryBoardMetadata{
|
|
{BoardID: "board_id_1"},
|
|
{BoardID: "board_id_2"},
|
|
},
|
|
},
|
|
{
|
|
Category: model.Category{ID: "category_id_2", Name: "Category 2"},
|
|
BoardMetadata: []model.CategoryBoardMetadata{
|
|
{BoardID: "board_id_3"},
|
|
},
|
|
},
|
|
{
|
|
Category: model.Category{ID: "category_id_3", Name: "Category 3"},
|
|
BoardMetadata: []model.CategoryBoardMetadata{},
|
|
},
|
|
}, nil).Times(1)
|
|
|
|
// when this function is called the second time, the default category is created
|
|
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{
|
|
{
|
|
Category: model.Category{ID: "category_id_1", Name: "Category 1"},
|
|
BoardMetadata: []model.CategoryBoardMetadata{
|
|
{BoardID: "board_id_1"},
|
|
{BoardID: "board_id_2"},
|
|
},
|
|
},
|
|
{
|
|
Category: model.Category{ID: "category_id_2", Name: "Category 2"},
|
|
BoardMetadata: []model.CategoryBoardMetadata{
|
|
{BoardID: "board_id_3"},
|
|
},
|
|
},
|
|
{
|
|
Category: model.Category{ID: "category_id_3", Name: "Category 3"},
|
|
BoardMetadata: []model.CategoryBoardMetadata{},
|
|
},
|
|
{
|
|
Category: model.Category{ID: "default_category_id", Type: model.CategoryTypeSystem, Name: "Boards"},
|
|
},
|
|
}, nil).Times(1)
|
|
|
|
th.Store.EXPECT().CreateCategory(utils.Anything).Return(nil)
|
|
th.Store.EXPECT().GetCategory(utils.Anything).Return(&model.Category{
|
|
ID: "default_category_id",
|
|
Name: "Boards",
|
|
}, nil)
|
|
th.Store.EXPECT().GetMembersForUser("user_id").Return([]*model.BoardMember{}, nil)
|
|
th.Store.EXPECT().GetBoardsForUserAndTeam("user_id", "team_id", false).Return([]*model.Board{}, nil)
|
|
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", "default_category_id", []string{
|
|
"board_id_1",
|
|
"board_id_2",
|
|
"board_id_3",
|
|
}).Return(nil)
|
|
|
|
boards := []*model.Board{
|
|
{ID: "board_id_1"},
|
|
{ID: "board_id_2"},
|
|
{ID: "board_id_3"},
|
|
}
|
|
|
|
err := th.App.addBoardsToDefaultCategory("user_id", "team_id", boards)
|
|
assert.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestDuplicateBoard(t *testing.T) {
|
|
th, tearDown := SetupTestHelper(t)
|
|
defer tearDown()
|
|
|
|
t.Run("base case", func(t *testing.T) {
|
|
board := &model.Board{
|
|
ID: "board_id_2",
|
|
Title: "Duplicated Board",
|
|
}
|
|
|
|
block := &model.Block{
|
|
ID: "block_id_1",
|
|
Type: "image",
|
|
}
|
|
|
|
th.Store.EXPECT().DuplicateBoard("board_id_1", "user_id_1", "team_id_1", false).Return(
|
|
&model.BoardsAndBlocks{
|
|
Boards: []*model.Board{
|
|
board,
|
|
},
|
|
Blocks: []*model.Block{
|
|
block,
|
|
},
|
|
},
|
|
[]*model.BoardMember{},
|
|
nil,
|
|
)
|
|
|
|
th.Store.EXPECT().GetBoard("board_id_1").Return(&model.Board{}, nil)
|
|
|
|
th.Store.EXPECT().GetUserCategoryBoards("user_id_1", "team_id_1").Return([]model.CategoryBoards{
|
|
{
|
|
Category: model.Category{
|
|
ID: "category_id_1",
|
|
Name: "Boards",
|
|
Type: "system",
|
|
},
|
|
},
|
|
}, nil).Times(3)
|
|
|
|
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", "category_id_1", utils.Anything).Return(nil)
|
|
|
|
// for WS change broadcast
|
|
th.Store.EXPECT().GetMembersForBoard(utils.Anything).Return([]*model.BoardMember{}, nil).Times(2)
|
|
|
|
bab, members, err := th.App.DuplicateBoard("board_id_1", "user_id_1", "team_id_1", false)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, bab)
|
|
assert.NotNil(t, members)
|
|
})
|
|
|
|
t.Run("duplicating board as template should not set it's category", func(t *testing.T) {
|
|
board := &model.Board{
|
|
ID: "board_id_2",
|
|
Title: "Duplicated Board",
|
|
}
|
|
|
|
block := &model.Block{
|
|
ID: "block_id_1",
|
|
Type: "image",
|
|
}
|
|
|
|
th.Store.EXPECT().DuplicateBoard("board_id_1", "user_id_1", "team_id_1", true).Return(
|
|
&model.BoardsAndBlocks{
|
|
Boards: []*model.Board{
|
|
board,
|
|
},
|
|
Blocks: []*model.Block{
|
|
block,
|
|
},
|
|
},
|
|
[]*model.BoardMember{},
|
|
nil,
|
|
)
|
|
|
|
th.Store.EXPECT().GetBoard("board_id_1").Return(&model.Board{}, nil)
|
|
|
|
// for WS change broadcast
|
|
th.Store.EXPECT().GetMembersForBoard(utils.Anything).Return([]*model.BoardMember{}, nil).Times(2)
|
|
|
|
bab, members, err := th.App.DuplicateBoard("board_id_1", "user_id_1", "team_id_1", true)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, bab)
|
|
assert.NotNil(t, members)
|
|
})
|
|
}
|