focalboard/server/integrationtests/board_test.go
Miguel de la Cruz 3ae821d2e8
Refactor websockets state and lifecycle (#3315)
* Refactor websockets state and lifecycle

This PR moves the state of the authentication and subscriptions to the
websockets client, allowing for multiple components to communicate
with it and request subscriptions independently. With this change, the
lifecycle of the websockets client is now managed on a component, and
a hook is provided for easy access to it from individual components.

* Fix linter

* Integrating the new websockets in channels integration with the RHS and board selector

* Some small fixes around boards-channels relationship

* Make the boards unfurl to always use the current team

* Fixing weird behaviors in websockets and other small data related bugs in channel-board relationship

* Only warn if withWebSockets is used without a base connection

* Fix tests

* Fix linter

* Update snapshot

* Fixing plugin tests

Co-authored-by: Jesús Espino <jespinog@gmail.com>
2022-07-14 12:31:51 +02:00

2094 lines
58 KiB
Go

package integrationtests
import (
"encoding/json"
"sort"
"testing"
"time"
"github.com/mattermost/focalboard/server/client"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/utils"
"github.com/stretchr/testify/require"
)
const (
testTeamID = "team-id"
)
func TestGetBoards(t *testing.T) {
t.Run("a non authenticated client should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
th.Logout(th.Client)
teamID := "0"
newBoard := &model.Board{
TeamID: teamID,
Type: model.BoardTypeOpen,
}
board, err := th.Server.App().CreateBoard(newBoard, "user-id", false)
require.NoError(t, err)
require.NotNil(t, board)
boards, resp := th.Client.GetBoardsForTeam(teamID)
th.CheckUnauthorized(resp)
require.Nil(t, boards)
})
t.Run("should only return the boards that the user is a member of", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
teamID := "0"
otherTeamID := "other-team-id"
user1 := th.GetUser1()
user2 := th.GetUser2()
board1 := &model.Board{
TeamID: teamID,
Type: model.BoardTypeOpen,
Title: "Board 1",
}
rBoard1, err := th.Server.App().CreateBoard(board1, user1.ID, true)
require.NoError(t, err)
require.NotNil(t, rBoard1)
board2 := &model.Board{
TeamID: teamID,
Type: model.BoardTypeOpen,
Title: "Board 2",
}
rBoard2, err := th.Server.App().CreateBoard(board2, user2.ID, false)
require.NoError(t, err)
require.NotNil(t, rBoard2)
board3 := &model.Board{
TeamID: teamID,
Type: model.BoardTypePrivate,
Title: "Board 3",
}
rBoard3, err := th.Server.App().CreateBoard(board3, user1.ID, true)
require.NoError(t, err)
require.NotNil(t, rBoard3)
board4 := &model.Board{
TeamID: teamID,
Type: model.BoardTypePrivate,
Title: "Board 4",
}
rBoard4, err := th.Server.App().CreateBoard(board4, user1.ID, false)
require.NoError(t, err)
require.NotNil(t, rBoard4)
board5 := &model.Board{
TeamID: teamID,
Type: model.BoardTypePrivate,
Title: "Board 5",
}
rBoard5, err := th.Server.App().CreateBoard(board5, user2.ID, true)
require.NoError(t, err)
require.NotNil(t, rBoard5)
board6 := &model.Board{
TeamID: otherTeamID,
Type: model.BoardTypeOpen,
}
rBoard6, err := th.Server.App().CreateBoard(board6, user1.ID, true)
require.NoError(t, err)
require.NotNil(t, rBoard6)
boards, resp := th.Client.GetBoardsForTeam(teamID)
th.CheckOK(resp)
require.NotNil(t, boards)
require.ElementsMatch(t, []*model.Board{
rBoard1,
rBoard2,
rBoard3,
}, boards)
boardsFromOtherTeam, resp := th.Client.GetBoardsForTeam(otherTeamID)
th.CheckOK(resp)
require.NotNil(t, boardsFromOtherTeam)
require.Len(t, boardsFromOtherTeam, 1)
require.Equal(t, rBoard6.ID, boardsFromOtherTeam[0].ID)
})
}
func TestCreateBoard(t *testing.T) {
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
th.Logout(th.Client)
newBoard := &model.Board{
Title: "board title",
Type: model.BoardTypeOpen,
TeamID: testTeamID,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckUnauthorized(resp)
require.Nil(t, board)
})
t.Run("create public board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
me := th.GetUser1()
title := "board title 1"
teamID := testTeamID
newBoard := &model.Board{
Title: title,
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckOK(resp)
require.NoError(t, resp.Error)
require.NotNil(t, board)
require.NotNil(t, board.ID)
require.Equal(t, title, board.Title)
require.Equal(t, model.BoardTypeOpen, board.Type)
require.Equal(t, teamID, board.TeamID)
require.Equal(t, me.ID, board.CreatedBy)
require.Equal(t, me.ID, board.ModifiedBy)
t.Run("creating a board should make the creator an admin", func(t *testing.T) {
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 1)
require.Equal(t, me.ID, members[0].UserID)
require.Equal(t, board.ID, members[0].BoardID)
require.True(t, members[0].SchemeAdmin)
})
t.Run("creator should be able to access the public board and its blocks", func(t *testing.T) {
rbBoard, resp := th.Client.GetBoard(board.ID, "")
th.CheckOK(resp)
require.NotNil(t, rbBoard)
require.Equal(t, board, rbBoard)
rBlocks, resp := th.Client.GetBlocksForBoard(board.ID)
th.CheckOK(resp)
require.NotNil(t, rBlocks)
})
t.Run("A non-member user should be able to access the public board but not its blocks", func(t *testing.T) {
rbBoard, resp := th.Client2.GetBoard(board.ID, "")
th.CheckOK(resp)
require.NotNil(t, rbBoard)
require.Equal(t, board, rbBoard)
rBlocks, resp := th.Client2.GetBlocksForBoard(board.ID)
th.CheckForbidden(resp)
require.Nil(t, rBlocks)
})
})
t.Run("create private board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
me := th.GetUser1()
title := "private board title"
teamID := testTeamID
newBoard := &model.Board{
Title: title,
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckOK(resp)
require.NotNil(t, board)
require.NotNil(t, board.ID)
require.Equal(t, title, board.Title)
require.Equal(t, model.BoardTypePrivate, board.Type)
require.Equal(t, teamID, board.TeamID)
require.Equal(t, me.ID, board.CreatedBy)
require.Equal(t, me.ID, board.ModifiedBy)
t.Run("creating a board should make the creator an admin", func(t *testing.T) {
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 1)
require.Equal(t, me.ID, members[0].UserID)
require.Equal(t, board.ID, members[0].BoardID)
require.True(t, members[0].SchemeAdmin)
})
t.Run("creator should be able to access the private board and its blocks", func(t *testing.T) {
rbBoard, resp := th.Client.GetBoard(board.ID, "")
th.CheckOK(resp)
require.NotNil(t, rbBoard)
require.Equal(t, board, rbBoard)
rBlocks, resp := th.Client.GetBlocksForBoard(board.ID)
th.CheckOK(resp)
require.NotNil(t, rBlocks)
})
t.Run("unauthorized user should not be able to access the private board or its blocks", func(t *testing.T) {
rbBoard, resp := th.Client2.GetBoard(board.ID, "")
th.CheckForbidden(resp)
require.Nil(t, rbBoard)
rBlocks, resp := th.Client2.GetBlocksForBoard(board.ID)
th.CheckForbidden(resp)
require.Nil(t, rBlocks)
})
})
t.Run("create invalid board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
title := "invalid board title"
teamID := testTeamID
user1 := th.GetUser1()
t.Run("invalid board type", func(t *testing.T) {
var invalidBoardType model.BoardType = "invalid"
newBoard := &model.Board{
Title: title,
TeamID: testTeamID,
Type: invalidBoardType,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckBadRequest(resp)
require.Nil(t, board)
boards, err := th.Server.App().GetBoardsForUserAndTeam(user1.ID, teamID)
require.NoError(t, err)
require.Empty(t, boards)
})
t.Run("no type", func(t *testing.T) {
newBoard := &model.Board{
Title: title,
TeamID: teamID,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckBadRequest(resp)
require.Nil(t, board)
boards, err := th.Server.App().GetBoardsForUserAndTeam(user1.ID, teamID)
require.NoError(t, err)
require.Empty(t, boards)
})
t.Run("no team ID", func(t *testing.T) {
newBoard := &model.Board{
Title: title,
}
board, resp := th.Client.CreateBoard(newBoard)
// the request is unauthorized because the permission
// check fails on an empty teamID
th.CheckForbidden(resp)
require.Nil(t, board)
boards, err := th.Server.App().GetBoardsForUserAndTeam(user1.ID, teamID)
require.NoError(t, err)
require.Empty(t, boards)
})
})
}
func TestCreateBoardTemplate(t *testing.T) {
t.Run("create public board template", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
me := th.GetUser1()
title := "board template 1"
teamID := testTeamID
newBoard := &model.Board{
Title: title,
Type: model.BoardTypeOpen,
TeamID: teamID,
IsTemplate: true,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckOK(resp)
require.NoError(t, resp.Error)
require.NotNil(t, board)
require.NotNil(t, board.ID)
require.Equal(t, title, board.Title)
require.Equal(t, model.BoardTypeOpen, board.Type)
require.Equal(t, teamID, board.TeamID)
require.Equal(t, me.ID, board.CreatedBy)
require.Equal(t, me.ID, board.ModifiedBy)
t.Run("creating a board template should make the creator an admin", func(t *testing.T) {
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 1)
require.Equal(t, me.ID, members[0].UserID)
require.Equal(t, board.ID, members[0].BoardID)
require.True(t, members[0].SchemeAdmin)
})
t.Run("creator should be able to access the public board template and its blocks", func(t *testing.T) {
rbBoard, resp := th.Client.GetBoard(board.ID, "")
th.CheckOK(resp)
require.NotNil(t, rbBoard)
require.Equal(t, board, rbBoard)
rBlocks, resp := th.Client.GetBlocksForBoard(board.ID)
th.CheckOK(resp)
require.NotNil(t, rBlocks)
})
t.Run("another user should be able to access the public board template and its blocks", func(t *testing.T) {
rbBoard, resp := th.Client2.GetBoard(board.ID, "")
th.CheckOK(resp)
require.NotNil(t, rbBoard)
require.Equal(t, board, rbBoard)
rBlocks, resp := th.Client2.GetBlocksForBoard(board.ID)
th.CheckOK(resp)
require.NotNil(t, rBlocks)
})
})
t.Run("create private board template", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
me := th.GetUser1()
title := "private board template title"
teamID := testTeamID
newBoard := &model.Board{
Title: title,
Type: model.BoardTypePrivate,
TeamID: teamID,
IsTemplate: true,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckOK(resp)
require.NotNil(t, board)
require.NotNil(t, board.ID)
require.Equal(t, title, board.Title)
require.Equal(t, model.BoardTypePrivate, board.Type)
require.Equal(t, teamID, board.TeamID)
require.Equal(t, me.ID, board.CreatedBy)
require.Equal(t, me.ID, board.ModifiedBy)
t.Run("creating a board template should make the creator an admin", func(t *testing.T) {
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 1)
require.Equal(t, me.ID, members[0].UserID)
require.Equal(t, board.ID, members[0].BoardID)
require.True(t, members[0].SchemeAdmin)
})
t.Run("creator should be able to access the private board template and its blocks", func(t *testing.T) {
rbBoard, resp := th.Client.GetBoard(board.ID, "")
th.CheckOK(resp)
require.NotNil(t, rbBoard)
require.Equal(t, board, rbBoard)
rBlocks, resp := th.Client.GetBlocksForBoard(board.ID)
th.CheckOK(resp)
require.NotNil(t, rBlocks)
})
t.Run("unauthorized user should not be able to access the private board template or its blocks", func(t *testing.T) {
rbBoard, resp := th.Client2.GetBoard(board.ID, "")
th.CheckForbidden(resp)
require.Nil(t, rbBoard)
rBlocks, resp := th.Client2.GetBlocksForBoard(board.ID)
th.CheckForbidden(resp)
require.Nil(t, rBlocks)
})
})
}
func TestGetAllBlocksForBoard(t *testing.T) {
th := SetupTestHelperWithToken(t).Start()
defer th.TearDown()
board := th.CreateBoard("board-id", model.BoardTypeOpen)
parentBlockID := utils.NewID(utils.IDTypeBlock)
childBlockID1 := utils.NewID(utils.IDTypeBlock)
childBlockID2 := utils.NewID(utils.IDTypeBlock)
t.Run("Create the block structure", func(t *testing.T) {
newBlocks := []model.Block{
{
ID: parentBlockID,
BoardID: board.ID,
CreateAt: 1,
UpdateAt: 1,
Type: model.TypeCard,
},
{
ID: childBlockID1,
BoardID: board.ID,
ParentID: parentBlockID,
CreateAt: 2,
UpdateAt: 2,
Type: model.TypeCard,
},
{
ID: childBlockID2,
BoardID: board.ID,
ParentID: parentBlockID,
CreateAt: 2,
UpdateAt: 2,
Type: model.TypeCard,
},
}
insertedBlocks, resp := th.Client.InsertBlocks(board.ID, newBlocks)
require.NoError(t, resp.Error)
require.Len(t, insertedBlocks, len(newBlocks))
insertedBlockIDs := make([]string, len(insertedBlocks))
for i, b := range insertedBlocks {
insertedBlockIDs[i] = b.ID
}
fetchedBlocks, resp := th.Client.GetAllBlocksForBoard(board.ID)
require.NoError(t, resp.Error)
require.Len(t, fetchedBlocks, len(newBlocks))
fetchedblockIDs := make([]string, len(fetchedBlocks))
for i, b := range fetchedBlocks {
fetchedblockIDs[i] = b.ID
}
sort.Strings(insertedBlockIDs)
sort.Strings(fetchedblockIDs)
require.Equal(t, insertedBlockIDs, fetchedblockIDs)
})
}
func TestSearchBoards(t *testing.T) {
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
th.Logout(th.Client)
boards, resp := th.Client.SearchBoardsForTeam(testTeamID, "term")
th.CheckUnauthorized(resp)
require.Nil(t, boards)
})
t.Run("all the matching private boards that the user is a member of and all matching public boards should be returned", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
teamID := testTeamID
user1 := th.GetUser1()
board1 := &model.Board{
Title: "public board where user1 is admin",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
rBoard1, err := th.Server.App().CreateBoard(board1, user1.ID, true)
require.NoError(t, err)
board2 := &model.Board{
Title: "public board where user1 is not member",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
rBoard2, err := th.Server.App().CreateBoard(board2, user1.ID, false)
require.NoError(t, err)
board3 := &model.Board{
Title: "private board where user1 is admin",
Type: model.BoardTypePrivate,
TeamID: teamID,
}
rBoard3, err := th.Server.App().CreateBoard(board3, user1.ID, true)
require.NoError(t, err)
board4 := &model.Board{
Title: "private board where user1 is not member",
Type: model.BoardTypePrivate,
TeamID: teamID,
}
_, err = th.Server.App().CreateBoard(board4, user1.ID, false)
require.NoError(t, err)
board5 := &model.Board{
Title: "private board where user1 is admin, but in other team",
Type: model.BoardTypePrivate,
TeamID: "other-team-id",
}
_, err = th.Server.App().CreateBoard(board5, user1.ID, true)
require.NoError(t, err)
testCases := []struct {
Name string
Client *client.Client
Term string
ExpectedIDs []string
}{
{
Name: "should return all boards where user1 is member or that are public",
Client: th.Client,
Term: "board",
ExpectedIDs: []string{rBoard1.ID, rBoard2.ID, rBoard3.ID},
},
{
Name: "matching a full word",
Client: th.Client,
Term: "admin",
ExpectedIDs: []string{rBoard1.ID, rBoard3.ID},
},
{
Name: "matching part of the word",
Client: th.Client,
Term: "ubli",
ExpectedIDs: []string{rBoard1.ID, rBoard2.ID},
},
{
Name: "case insensitive",
Client: th.Client,
Term: "UBLI",
ExpectedIDs: []string{rBoard1.ID, rBoard2.ID},
},
{
Name: "user2 can only see the public boards, as he's not a member of any",
Client: th.Client2,
Term: "board",
ExpectedIDs: []string{rBoard1.ID, rBoard2.ID},
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
boards, resp := tc.Client.SearchBoardsForTeam(teamID, tc.Term)
th.CheckOK(resp)
boardIDs := []string{}
for _, board := range boards {
boardIDs = append(boardIDs, board.ID)
}
require.ElementsMatch(t, tc.ExpectedIDs, boardIDs)
})
}
})
}
func TestGetBoard(t *testing.T) {
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
th.Logout(th.Client)
board, resp := th.Client.GetBoard("boar-id", "")
th.CheckUnauthorized(resp)
require.Nil(t, board)
})
t.Run("valid read token should be enough to get the board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
th.Server.Config().EnablePublicSharedBoards = true
teamID := testTeamID
sharingToken := utils.NewID(utils.IDTypeToken)
board := &model.Board{
Title: "public board where user1 is admin",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
rBoard, err := th.Server.App().CreateBoard(board, th.GetUser1().ID, true)
require.NoError(t, err)
sharing := &model.Sharing{
ID: rBoard.ID,
Enabled: true,
Token: sharingToken,
UpdateAt: 1,
}
success, resp := th.Client.PostSharing(sharing)
th.CheckOK(resp)
require.True(t, success)
// the client logs out
th.Logout(th.Client)
// we make sure that the client cannot currently retrieve the
// board with no session
board, resp = th.Client.GetBoard(rBoard.ID, "")
th.CheckUnauthorized(resp)
require.Nil(t, board)
// it should be able to retrieve it with the read token
board, resp = th.Client.GetBoard(rBoard.ID, sharingToken)
th.CheckOK(resp)
require.NotNil(t, board)
})
t.Run("nonexisting board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
board, resp := th.Client.GetBoard("nonexistent board", "")
th.CheckNotFound(resp)
require.Nil(t, board)
})
t.Run("a user that doesn't have permissions to a private board cannot retrieve it", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
teamID := testTeamID
newBoard := &model.Board{
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, false)
require.NoError(t, err)
rBoard, resp := th.Client.GetBoard(board.ID, "")
th.CheckForbidden(resp)
require.Nil(t, rBoard)
})
t.Run("a user that has permissions to a private board can retrieve it", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
teamID := testTeamID
newBoard := &model.Board{
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
rBoard, resp := th.Client.GetBoard(board.ID, "")
th.CheckOK(resp)
require.NotNil(t, rBoard)
})
t.Run("a user that doesn't have permissions to a public board but have them to its team can retrieve it", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
teamID := testTeamID
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, false)
require.NoError(t, err)
rBoard, resp := th.Client.GetBoard(board.ID, "")
th.CheckOK(resp)
require.NotNil(t, rBoard)
})
}
func TestGetBoardMetadata(t *testing.T) {
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelperWithLicense(t, LicenseEnterprise).InitBasic()
defer th.TearDown()
th.Logout(th.Client)
boardMetadata, resp := th.Client.GetBoardMetadata("boar-id", "")
th.CheckUnauthorized(resp)
require.Nil(t, boardMetadata)
})
t.Run("getBoardMetadata query is correct", func(t *testing.T) {
th := SetupTestHelperWithLicense(t, LicenseEnterprise).InitBasic()
defer th.TearDown()
th.Server.Config().EnablePublicSharedBoards = true
teamID := testTeamID
board := &model.Board{
Title: "public board where user1 is admin",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
rBoard, err := th.Server.App().CreateBoard(board, th.GetUser1().ID, true)
require.NoError(t, err)
// Check metadata
boardMetadata, resp := th.Client.GetBoardMetadata(rBoard.ID, "")
th.CheckOK(resp)
require.NotNil(t, boardMetadata)
require.Equal(t, rBoard.CreatedBy, boardMetadata.CreatedBy)
require.Equal(t, rBoard.CreateAt, boardMetadata.DescendantFirstUpdateAt)
require.Equal(t, rBoard.UpdateAt, boardMetadata.DescendantLastUpdateAt)
require.Equal(t, rBoard.ModifiedBy, boardMetadata.LastModifiedBy)
// Insert card1
card1 := model.Block{
ID: "card1",
BoardID: rBoard.ID,
Title: "Card 1",
}
time.Sleep(20 * time.Millisecond)
require.NoError(t, th.Server.App().InsertBlock(card1, th.GetUser2().ID))
rCard1, err := th.Server.App().GetBlockByID(card1.ID)
require.NoError(t, err)
// Check updated metadata
boardMetadata, resp = th.Client.GetBoardMetadata(rBoard.ID, "")
th.CheckOK(resp)
require.NotNil(t, boardMetadata)
require.Equal(t, rBoard.CreatedBy, boardMetadata.CreatedBy)
require.Equal(t, rBoard.CreateAt, boardMetadata.DescendantFirstUpdateAt)
require.Equal(t, rCard1.UpdateAt, boardMetadata.DescendantLastUpdateAt)
require.Equal(t, rCard1.ModifiedBy, boardMetadata.LastModifiedBy)
// Insert card2
card2 := model.Block{
ID: "card2",
BoardID: rBoard.ID,
Title: "Card 2",
}
time.Sleep(20 * time.Millisecond)
require.NoError(t, th.Server.App().InsertBlock(card2, th.GetUser1().ID))
rCard2, err := th.Server.App().GetBlockByID(card2.ID)
require.NoError(t, err)
// Check updated metadata
boardMetadata, resp = th.Client.GetBoardMetadata(rBoard.ID, "")
th.CheckOK(resp)
require.NotNil(t, boardMetadata)
require.Equal(t, rBoard.CreatedBy, boardMetadata.CreatedBy)
require.Equal(t, rBoard.CreateAt, boardMetadata.DescendantFirstUpdateAt)
require.Equal(t, rCard2.UpdateAt, boardMetadata.DescendantLastUpdateAt)
require.Equal(t, rCard2.ModifiedBy, boardMetadata.LastModifiedBy)
t.Run("After delete board", func(t *testing.T) {
// Delete board
time.Sleep(20 * time.Millisecond)
require.NoError(t, th.Server.App().DeleteBoard(rBoard.ID, th.GetUser1().ID))
// Check updated metadata
boardMetadata, resp = th.Client.GetBoardMetadata(rBoard.ID, "")
th.CheckOK(resp)
require.NotNil(t, boardMetadata)
require.Equal(t, rBoard.CreatedBy, boardMetadata.CreatedBy)
require.Equal(t, rBoard.CreateAt, boardMetadata.DescendantFirstUpdateAt)
require.Greater(t, boardMetadata.DescendantLastUpdateAt, rCard2.UpdateAt)
require.Equal(t, th.GetUser1().ID, boardMetadata.LastModifiedBy)
})
})
t.Run("getBoardMetadata should fail with no license", func(t *testing.T) {
th := SetupTestHelperWithLicense(t, LicenseNone).InitBasic()
defer th.TearDown()
th.Server.Config().EnablePublicSharedBoards = true
teamID := testTeamID
board := &model.Board{
Title: "public board where user1 is admin",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
rBoard, err := th.Server.App().CreateBoard(board, th.GetUser1().ID, true)
require.NoError(t, err)
// Check metadata
boardMetadata, resp := th.Client.GetBoardMetadata(rBoard.ID, "")
th.CheckNotImplemented(resp)
require.Nil(t, boardMetadata)
})
t.Run("getBoardMetadata should fail on Professional license", func(t *testing.T) {
th := SetupTestHelperWithLicense(t, LicenseProfessional).InitBasic()
defer th.TearDown()
th.Server.Config().EnablePublicSharedBoards = true
teamID := testTeamID
board := &model.Board{
Title: "public board where user1 is admin",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
rBoard, err := th.Server.App().CreateBoard(board, th.GetUser1().ID, true)
require.NoError(t, err)
// Check metadata
boardMetadata, resp := th.Client.GetBoardMetadata(rBoard.ID, "")
th.CheckNotImplemented(resp)
require.Nil(t, boardMetadata)
})
t.Run("valid read token should not get the board metadata", func(t *testing.T) {
th := SetupTestHelperWithLicense(t, LicenseEnterprise).InitBasic()
defer th.TearDown()
th.Server.Config().EnablePublicSharedBoards = true
teamID := testTeamID
sharingToken := utils.NewID(utils.IDTypeToken)
userID := th.GetUser1().ID
board := &model.Board{
Title: "public board where user1 is admin",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
rBoard, err := th.Server.App().CreateBoard(board, userID, true)
require.NoError(t, err)
sharing := &model.Sharing{
ID: rBoard.ID,
Enabled: true,
Token: sharingToken,
UpdateAt: 1,
}
success, resp := th.Client.PostSharing(sharing)
th.CheckOK(resp)
require.True(t, success)
// the client logs out
th.Logout(th.Client)
// we make sure that the client cannot currently retrieve the
// board with no session
boardMetadata, resp := th.Client.GetBoardMetadata(rBoard.ID, "")
th.CheckUnauthorized(resp)
require.Nil(t, boardMetadata)
// it should not be able to retrieve it with the read token either
boardMetadata, resp = th.Client.GetBoardMetadata(rBoard.ID, sharingToken)
th.CheckUnauthorized(resp)
require.Nil(t, boardMetadata)
})
}
func TestPatchBoard(t *testing.T) {
teamID := testTeamID
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
th.Logout(th.Client)
initialTitle := "title 1"
newBoard := &model.Board{
Title: initialTitle,
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, "user-id", false)
require.NoError(t, err)
newTitle := "a new title 1"
patch := &model.BoardPatch{Title: &newTitle}
rBoard, resp := th.Client.PatchBoard(board.ID, patch)
th.CheckUnauthorized(resp)
require.Nil(t, rBoard)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.Equal(t, initialTitle, dbBoard.Title)
})
t.Run("non existing board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newTitle := "a new title 2"
patch := &model.BoardPatch{Title: &newTitle}
board, resp := th.Client.PatchBoard("non-existing-board", patch)
th.CheckNotFound(resp)
require.Nil(t, board)
})
t.Run("invalid patch on a board with permissions", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
user1 := th.GetUser1()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, user1.ID, true)
require.NoError(t, err)
var invalidPatchType model.BoardType = "invalid"
patch := &model.BoardPatch{Type: &invalidPatchType}
rBoard, resp := th.Client.PatchBoard(board.ID, patch)
th.CheckBadRequest(resp)
require.Nil(t, rBoard)
})
t.Run("valid patch on a board with permissions", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
user1 := th.GetUser1()
initialTitle := "title"
newBoard := &model.Board{
Title: initialTitle,
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, user1.ID, true)
require.NoError(t, err)
newTitle := "a new title"
patch := &model.BoardPatch{Title: &newTitle}
rBoard, resp := th.Client.PatchBoard(board.ID, patch)
th.CheckOK(resp)
require.NotNil(t, rBoard)
require.Equal(t, newTitle, rBoard.Title)
})
t.Run("valid patch on a board without permissions", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
user1 := th.GetUser1()
initialTitle := "title"
newBoard := &model.Board{
Title: initialTitle,
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, user1.ID, false)
require.NoError(t, err)
newTitle := "a new title"
patch := &model.BoardPatch{Title: &newTitle}
rBoard, resp := th.Client.PatchBoard(board.ID, patch)
th.CheckForbidden(resp)
require.Nil(t, rBoard)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.Equal(t, initialTitle, dbBoard.Title)
})
}
func TestDeleteBoard(t *testing.T) {
teamID := testTeamID
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
th.Logout(th.Client)
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, "user-id", false)
require.NoError(t, err)
success, resp := th.Client.DeleteBoard(board.ID)
th.CheckUnauthorized(resp)
require.False(t, success)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.NotNil(t, dbBoard)
})
t.Run("a user without permissions should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, "some-user-id", false)
require.NoError(t, err)
success, resp := th.Client.DeleteBoard(board.ID)
th.CheckForbidden(resp)
require.False(t, success)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.NotNil(t, dbBoard)
})
t.Run("non existing board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
success, resp := th.Client.DeleteBoard("non-existing-board")
th.CheckNotFound(resp)
require.False(t, success)
})
t.Run("an existing board should be correctly deleted", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
success, resp := th.Client.DeleteBoard(board.ID)
th.CheckOK(resp)
require.True(t, success)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.Nil(t, dbBoard)
})
}
func TestUndeleteBoard(t *testing.T) {
teamID := testTeamID
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
th.Logout(th.Client)
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, "user-id", false)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
err = th.Server.App().DeleteBoard(newBoard.ID, "user-id")
require.NoError(t, err)
success, resp := th.Client.UndeleteBoard(board.ID)
th.CheckUnauthorized(resp)
require.False(t, success)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.Nil(t, dbBoard)
})
t.Run("a user without membership should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, "some-user-id", false)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
err = th.Server.App().DeleteBoard(newBoard.ID, "some-user-id")
require.NoError(t, err)
success, resp := th.Client.UndeleteBoard(board.ID)
th.CheckForbidden(resp)
require.False(t, success)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.Nil(t, dbBoard)
})
t.Run("a user with membership but without permissions should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, "some-user-id", false)
require.NoError(t, err)
newUser2Member := &model.BoardMember{
UserID: "user-id",
BoardID: board.ID,
SchemeEditor: true,
}
_, err = th.Server.App().AddMemberToBoard(newUser2Member)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
err = th.Server.App().DeleteBoard(newBoard.ID, "some-user-id")
require.NoError(t, err)
success, resp := th.Client.UndeleteBoard(board.ID)
th.CheckForbidden(resp)
require.False(t, success)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.Nil(t, dbBoard)
})
t.Run("non existing board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
success, resp := th.Client.UndeleteBoard("non-existing-board")
th.CheckForbidden(resp)
require.False(t, success)
})
t.Run("an existing deleted board should be correctly undeleted", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
err = th.Server.App().DeleteBoard(newBoard.ID, "user-id")
require.NoError(t, err)
success, resp := th.Client.UndeleteBoard(board.ID)
th.CheckOK(resp)
require.True(t, success)
dbBoard, err := th.Server.App().GetBoard(board.ID)
require.NoError(t, err)
require.NotNil(t, dbBoard)
})
}
func TestGetMembersForBoard(t *testing.T) {
teamID := testTeamID
createBoardWithUsers := func(th *TestHelper) *model.Board {
user1 := th.GetUser1()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, user1.ID, true)
require.NoError(t, err)
newUser2Member := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
SchemeEditor: true,
}
user2Member, err := th.Server.App().AddMemberToBoard(newUser2Member)
require.NoError(t, err)
require.NotNil(t, user2Member)
return board
}
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
board := createBoardWithUsers(th)
th.Logout(th.Client)
members, resp := th.Client.GetMembersForBoard(board.ID)
th.CheckUnauthorized(resp)
require.Empty(t, members)
})
t.Run("a user without permissions should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
board := createBoardWithUsers(th)
_ = th.Server.App().DeleteBoardMember(board.ID, th.GetUser2().ID)
members, resp := th.Client2.GetMembersForBoard(board.ID)
th.CheckForbidden(resp)
require.Empty(t, members)
})
t.Run("non existing board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
members, resp := th.Client.GetMembersForBoard("non-existing-board")
th.CheckForbidden(resp)
require.Empty(t, members)
})
t.Run("should correctly return board members for a valid board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
board := createBoardWithUsers(th)
members, resp := th.Client.GetMembersForBoard(board.ID)
th.CheckOK(resp)
require.Len(t, members, 2)
})
}
func TestAddMember(t *testing.T) {
teamID := testTeamID
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
th.Logout(th.Client)
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, "user-id", false)
require.NoError(t, err)
newMember := &model.BoardMember{
UserID: "user1",
BoardID: board.ID,
SchemeEditor: true,
}
member, resp := th.Client.AddMemberToBoard(newMember)
th.CheckUnauthorized(resp)
require.Nil(t, member)
})
t.Run("a user without permissions should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, "user-id", false)
require.NoError(t, err)
newMember := &model.BoardMember{
UserID: "user1",
BoardID: board.ID,
SchemeEditor: true,
}
member, resp := th.Client.AddMemberToBoard(newMember)
th.CheckForbidden(resp)
require.Nil(t, member)
})
t.Run("non existing board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newMember := &model.BoardMember{
UserID: "user1",
BoardID: "non-existing-board-id",
SchemeEditor: true,
}
member, resp := th.Client.AddMemberToBoard(newMember)
th.CheckNotFound(resp)
require.Nil(t, member)
})
t.Run("should correctly add a new member for a valid board", func(t *testing.T) {
t.Run("a private board through an admin user", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
newMember := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
SchemeEditor: true,
}
member, resp := th.Client.AddMemberToBoard(newMember)
th.CheckOK(resp)
require.Equal(t, newMember.UserID, member.UserID)
require.Equal(t, newMember.BoardID, member.BoardID)
require.Equal(t, newMember.SchemeAdmin, member.SchemeAdmin)
require.Equal(t, newMember.SchemeEditor, member.SchemeEditor)
require.False(t, member.SchemeCommenter)
require.False(t, member.SchemeViewer)
})
t.Run("a public board through a user that is not yet a member", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
newMember := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
SchemeEditor: true,
}
member, resp := th.Client2.AddMemberToBoard(newMember)
th.CheckForbidden(resp)
require.Nil(t, member)
members, resp := th.Client2.GetMembersForBoard(board.ID)
th.CheckForbidden(resp)
require.Nil(t, members)
// Join board - will become an editor
member, resp = th.Client2.JoinBoard(board.ID)
th.CheckOK(resp)
require.NoError(t, resp.Error)
require.NotNil(t, member)
require.Equal(t, board.ID, member.BoardID)
require.Equal(t, th.GetUser2().ID, member.UserID)
member, resp = th.Client2.AddMemberToBoard(newMember)
th.CheckForbidden(resp)
require.Nil(t, member)
members, resp = th.Client2.GetMembersForBoard(board.ID)
th.CheckOK(resp)
require.Len(t, members, 2)
})
t.Run("should always add a new member as an editor", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
newMember := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
SchemeAdmin: true,
SchemeEditor: false,
}
member, resp := th.Client.AddMemberToBoard(newMember)
th.CheckOK(resp)
require.Equal(t, newMember.UserID, member.UserID)
require.Equal(t, newMember.BoardID, member.BoardID)
require.False(t, member.SchemeAdmin)
require.True(t, member.SchemeEditor)
})
})
t.Run("should do nothing if the member already exists", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
newMember := &model.BoardMember{
UserID: th.GetUser1().ID,
BoardID: board.ID,
SchemeAdmin: false,
SchemeEditor: true,
}
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 1)
require.True(t, members[0].SchemeAdmin)
require.True(t, members[0].SchemeEditor)
member, resp := th.Client.AddMemberToBoard(newMember)
th.CheckOK(resp)
require.True(t, member.SchemeAdmin)
require.True(t, member.SchemeEditor)
members, err = th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 1)
require.True(t, members[0].SchemeAdmin)
require.True(t, members[0].SchemeEditor)
})
}
func TestUpdateMember(t *testing.T) {
teamID := testTeamID
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
updatedMember := &model.BoardMember{
UserID: th.GetUser1().ID,
BoardID: board.ID,
SchemeEditor: true,
}
th.Logout(th.Client)
member, resp := th.Client.UpdateBoardMember(updatedMember)
th.CheckUnauthorized(resp)
require.Nil(t, member)
})
t.Run("a user without permissions should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
updatedMember := &model.BoardMember{
UserID: th.GetUser1().ID,
BoardID: board.ID,
SchemeEditor: true,
}
member, resp := th.Client2.UpdateBoardMember(updatedMember)
th.CheckForbidden(resp)
require.Nil(t, member)
})
t.Run("non existing board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
updatedMember := &model.BoardMember{
UserID: th.GetUser1().ID,
BoardID: "non-existent-board-id",
SchemeEditor: true,
}
member, resp := th.Client.UpdateBoardMember(updatedMember)
th.CheckForbidden(resp)
require.Nil(t, member)
})
t.Run("should correctly update a member for a valid board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
newUser2Member := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
SchemeEditor: true,
}
user2Member, err := th.Server.App().AddMemberToBoard(newUser2Member)
require.NoError(t, err)
require.NotNil(t, user2Member)
require.False(t, user2Member.SchemeAdmin)
require.True(t, user2Member.SchemeEditor)
memberUpdate := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
SchemeAdmin: true,
SchemeEditor: true,
}
updatedUser2Member, resp := th.Client.UpdateBoardMember(memberUpdate)
th.CheckOK(resp)
require.True(t, updatedUser2Member.SchemeAdmin)
require.True(t, updatedUser2Member.SchemeEditor)
})
t.Run("should not update a member if that means that a board will not have any admin", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
memberUpdate := &model.BoardMember{
UserID: th.GetUser1().ID,
BoardID: board.ID,
SchemeEditor: true,
}
updatedUser1Member, resp := th.Client.UpdateBoardMember(memberUpdate)
th.CheckBadRequest(resp)
require.Nil(t, updatedUser1Member)
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 1)
require.True(t, members[0].SchemeAdmin)
})
}
func TestDeleteMember(t *testing.T) {
teamID := testTeamID
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
member := &model.BoardMember{
UserID: th.GetUser1().ID,
BoardID: board.ID,
}
th.Logout(th.Client)
success, resp := th.Client.DeleteBoardMember(member)
th.CheckUnauthorized(resp)
require.False(t, success)
})
t.Run("a user without permissions should be rejected", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
member := &model.BoardMember{
UserID: th.GetUser1().ID,
BoardID: board.ID,
}
success, resp := th.Client2.DeleteBoardMember(member)
th.CheckForbidden(resp)
require.False(t, success)
})
t.Run("non existing board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
updatedMember := &model.BoardMember{
UserID: th.GetUser1().ID,
BoardID: "non-existent-board-id",
}
success, resp := th.Client.DeleteBoardMember(updatedMember)
th.CheckNotFound(resp)
require.False(t, success)
})
t.Run("should correctly delete a member for a valid board", func(t *testing.T) {
//nolint:dupl
t.Run("admin removing a user", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
newUser2Member := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
SchemeEditor: true,
}
user2Member, err := th.Server.App().AddMemberToBoard(newUser2Member)
require.NoError(t, err)
require.NotNil(t, user2Member)
require.False(t, user2Member.SchemeAdmin)
require.True(t, user2Member.SchemeEditor)
memberToDelete := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
}
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 2)
success, resp := th.Client.DeleteBoardMember(memberToDelete)
th.CheckOK(resp)
require.True(t, success)
members, err = th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 1)
})
//nolint:dupl
t.Run("user removing themselves", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
newUser2Member := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
SchemeEditor: true,
}
user2Member, err := th.Server.App().AddMemberToBoard(newUser2Member)
require.NoError(t, err)
require.NotNil(t, user2Member)
require.False(t, user2Member.SchemeAdmin)
require.True(t, user2Member.SchemeEditor)
memberToDelete := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
}
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 2)
// Should fail - must call leave to leave a board
success, resp := th.Client2.DeleteBoardMember(memberToDelete)
th.CheckForbidden(resp)
require.False(t, success)
members, err = th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 2)
})
//nolint:dupl
t.Run("a non admin user should not be able to remove another user", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
newUser2Member := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
SchemeEditor: true,
}
user2Member, err := th.Server.App().AddMemberToBoard(newUser2Member)
require.NoError(t, err)
require.NotNil(t, user2Member)
require.False(t, user2Member.SchemeAdmin)
require.True(t, user2Member.SchemeEditor)
memberToDelete := &model.BoardMember{
UserID: th.GetUser1().ID,
BoardID: board.ID,
}
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 2)
success, resp := th.Client2.DeleteBoardMember(memberToDelete)
th.CheckForbidden(resp)
require.False(t, success)
members, err = th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 2)
})
})
t.Run("should not delete a member if that means that a board will not have any admin", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
memberToDelete := &model.BoardMember{
UserID: th.GetUser1().ID,
BoardID: board.ID,
}
success, resp := th.Client.DeleteBoardMember(memberToDelete)
th.CheckBadRequest(resp)
require.False(t, success)
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 1)
require.True(t, members[0].SchemeAdmin)
})
}
func TestGetTemplates(t *testing.T) {
t.Run("should be able to retrieve built-in templates", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
err := th.Server.App().InitTemplates()
require.NoError(t, err, "InitTemplates should not fail")
teamID := "my-team-id"
rBoards, resp := th.Client.GetTemplatesForTeam("0")
th.CheckOK(resp)
require.NotNil(t, rBoards)
require.GreaterOrEqual(t, len(rBoards), 6)
t.Log("\n\n")
for _, board := range rBoards {
t.Logf("Test get template: %s - %s\n", board.Title, board.ID)
rBoard, resp := th.Client.GetBoard(board.ID, "")
th.CheckOK(resp)
require.NotNil(t, rBoard)
require.Equal(t, board, rBoard)
rBlocks, resp := th.Client.GetAllBlocksForBoard(board.ID)
th.CheckOK(resp)
require.NotNil(t, rBlocks)
require.Greater(t, len(rBlocks), 0)
t.Logf("Got %d block(s)\n", len(rBlocks))
rBoardsAndBlock, resp := th.Client.DuplicateBoard(board.ID, false, teamID)
th.CheckOK(resp)
require.NotNil(t, rBoardsAndBlock)
require.Greater(t, len(rBoardsAndBlock.Boards), 0)
require.Greater(t, len(rBoardsAndBlock.Blocks), 0)
rBoard2 := rBoardsAndBlock.Boards[0]
require.Contains(t, board.Title, rBoard2.Title)
require.False(t, rBoard2.IsTemplate)
t.Logf("Duplicate template: %s - %s, %d block(s)\n", rBoard2.Title, rBoard2.ID, len(rBoardsAndBlock.Blocks))
rBoard3, resp := th.Client.GetBoard(rBoard2.ID, "")
th.CheckOK(resp)
require.NotNil(t, rBoard3)
require.Equal(t, rBoard2, rBoard3)
rBlocks2, resp := th.Client.GetAllBlocksForBoard(rBoard2.ID)
th.CheckOK(resp)
require.NotNil(t, rBlocks2)
require.Equal(t, len(rBoardsAndBlock.Blocks), len(rBlocks2))
}
t.Log("\n\n")
})
}
func TestDuplicateBoard(t *testing.T) {
t.Run("create and duplicate public board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
me := th.GetUser1()
title := "Public board"
teamID := testTeamID
newBoard := &model.Board{
Title: title,
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckOK(resp)
require.NoError(t, resp.Error)
require.NotNil(t, board)
require.NotNil(t, board.ID)
require.Equal(t, title, board.Title)
require.Equal(t, model.BoardTypeOpen, board.Type)
require.Equal(t, teamID, board.TeamID)
require.Equal(t, me.ID, board.CreatedBy)
require.Equal(t, me.ID, board.ModifiedBy)
newBlocks := []model.Block{
{
ID: utils.NewID(utils.IDTypeBlock),
BoardID: board.ID,
CreateAt: 1,
UpdateAt: 1,
Title: "View 1",
Type: model.TypeView,
},
}
newBlocks, resp = th.Client.InsertBlocks(board.ID, newBlocks)
require.NoError(t, resp.Error)
require.Len(t, newBlocks, 1)
newUserMember := &model.BoardMember{
UserID: th.GetUser2().ID,
BoardID: board.ID,
SchemeEditor: true,
}
th.Client.AddMemberToBoard(newUserMember)
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 2)
// Duplicate the board
rBoardsAndBlock, resp := th.Client.DuplicateBoard(board.ID, false, teamID)
th.CheckOK(resp)
require.NotNil(t, rBoardsAndBlock)
require.Equal(t, len(rBoardsAndBlock.Boards), 1)
require.Equal(t, len(rBoardsAndBlock.Blocks), 1)
duplicateBoard := rBoardsAndBlock.Boards[0]
require.Equal(t, duplicateBoard.Type, model.BoardTypePrivate, "Duplicated board should be private")
members, err = th.Server.App().GetMembersForBoard(duplicateBoard.ID)
require.NoError(t, err)
require.Len(t, members, 1, "Duplicated board should only have one member")
require.Equal(t, me.ID, members[0].UserID)
require.Equal(t, duplicateBoard.ID, members[0].BoardID)
require.True(t, members[0].SchemeAdmin)
})
}
func TestJoinBoard(t *testing.T) {
t.Run("create and join public board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
me := th.GetUser1()
title := "Test Public board"
teamID := testTeamID
newBoard := &model.Board{
Title: title,
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckOK(resp)
require.NoError(t, resp.Error)
require.NotNil(t, board)
require.NotNil(t, board.ID)
require.Equal(t, title, board.Title)
require.Equal(t, model.BoardTypeOpen, board.Type)
require.Equal(t, teamID, board.TeamID)
require.Equal(t, me.ID, board.CreatedBy)
require.Equal(t, me.ID, board.ModifiedBy)
require.Equal(t, model.BoardRoleNone, board.MinimumRole)
member, resp := th.Client2.JoinBoard(board.ID)
th.CheckOK(resp)
require.NoError(t, resp.Error)
require.NotNil(t, member)
require.Equal(t, board.ID, member.BoardID)
require.Equal(t, th.GetUser2().ID, member.UserID)
s, _ := json.MarshalIndent(member, "", "\t")
t.Log(string(s))
})
t.Run("create and join public board should match the minimumRole in the membership", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
me := th.GetUser1()
title := "Public board for commenters"
teamID := testTeamID
newBoard := &model.Board{
Title: title,
Type: model.BoardTypeOpen,
TeamID: teamID,
MinimumRole: model.BoardRoleCommenter,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckOK(resp)
require.NoError(t, resp.Error)
require.NotNil(t, board)
require.NotNil(t, board.ID)
require.Equal(t, title, board.Title)
require.Equal(t, model.BoardTypeOpen, board.Type)
require.Equal(t, teamID, board.TeamID)
require.Equal(t, me.ID, board.CreatedBy)
require.Equal(t, me.ID, board.ModifiedBy)
member, resp := th.Client2.JoinBoard(board.ID)
th.CheckOK(resp)
require.NoError(t, resp.Error)
require.NotNil(t, member)
require.Equal(t, board.ID, member.BoardID)
require.Equal(t, th.GetUser2().ID, member.UserID)
require.False(t, member.SchemeAdmin, "new member should not be admin")
require.False(t, member.SchemeEditor, "new member should not be editor")
require.True(t, member.SchemeCommenter, "new member should be commenter")
require.False(t, member.SchemeViewer, "new member should not be viewer")
s, _ := json.MarshalIndent(member, "", "\t")
t.Log(string(s))
})
t.Run("create and join public board should match editor role in the membership when MinimumRole is empty", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
me := th.GetUser1()
title := "Public board for editors"
teamID := testTeamID
newBoard := &model.Board{
Title: title,
Type: model.BoardTypeOpen,
TeamID: teamID,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckOK(resp)
require.NoError(t, resp.Error)
require.NotNil(t, board)
require.NotNil(t, board.ID)
require.Equal(t, title, board.Title)
require.Equal(t, model.BoardTypeOpen, board.Type)
require.Equal(t, teamID, board.TeamID)
require.Equal(t, me.ID, board.CreatedBy)
require.Equal(t, me.ID, board.ModifiedBy)
member, resp := th.Client2.JoinBoard(board.ID)
th.CheckOK(resp)
require.NoError(t, resp.Error)
require.NotNil(t, member)
require.Equal(t, board.ID, member.BoardID)
require.Equal(t, th.GetUser2().ID, member.UserID)
require.False(t, member.SchemeAdmin, "new member should not be admin")
require.True(t, member.SchemeEditor, "new member should be editor")
require.False(t, member.SchemeCommenter, "new member should not be commenter")
require.False(t, member.SchemeViewer, "new member should not be viewer")
s, _ := json.MarshalIndent(member, "", "\t")
t.Log(string(s))
})
t.Run("create and join private board (should not succeed)", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
me := th.GetUser1()
title := "Private board"
teamID := testTeamID
newBoard := &model.Board{
Title: title,
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, resp := th.Client.CreateBoard(newBoard)
th.CheckOK(resp)
require.NoError(t, resp.Error)
require.NotNil(t, board)
require.NotNil(t, board.ID)
require.Equal(t, title, board.Title)
require.Equal(t, model.BoardTypePrivate, board.Type)
require.Equal(t, teamID, board.TeamID)
require.Equal(t, me.ID, board.CreatedBy)
require.Equal(t, me.ID, board.ModifiedBy)
member, resp := th.Client2.JoinBoard(board.ID)
th.CheckForbidden(resp)
require.Nil(t, member)
})
t.Run("join invalid board", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
member, resp := th.Client2.JoinBoard("nonexistent-board-ID")
th.CheckNotFound(resp)
require.Nil(t, member)
})
}