// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. package storetests import ( "testing" "time" "github.com/stretchr/testify/require" "github.com/mattermost/focalboard/server/model" storeservice "github.com/mattermost/focalboard/server/services/store" "github.com/mattermost/focalboard/server/utils" ) func StoreTestCloudStore(t *testing.T, setup func(t *testing.T) (storeservice.Store, func())) { t.Run("GetUsedCardsCount", func(t *testing.T) { store, tearDown := setup(t) defer tearDown() testGetUsedCardsCount(t, store) }) t.Run("TestGetCardLimitTimestamp", func(t *testing.T) { store, tearDown := setup(t) defer tearDown() testGetCardLimitTimestamp(t, store) }) t.Run("TestUpdateCardLimitTimestamp", func(t *testing.T) { store, tearDown := setup(t) defer tearDown() testUpdateCardLimitTimestamp(t, store) }) } func testGetUsedCardsCount(t *testing.T, store storeservice.Store) { userID := "user-id" t.Run("should return zero when no cards have been created", func(t *testing.T) { count, err := store.GetUsedCardsCount() require.NoError(t, err) require.Zero(t, count) }) t.Run("should correctly return the cards of all boards", func(t *testing.T) { // two boards for _, boardID := range []string{"board1", "board2"} { boardType := model.BoardTypeOpen if boardID == "board2" { boardType = model.BoardTypePrivate } board := &model.Board{ ID: boardID, TeamID: testTeamID, Type: boardType, } _, err := store.InsertBoard(board, userID) require.NoError(t, err) } // board 1 has three cards for _, cardID := range []string{"card1", "card2", "card3"} { card := model.Block{ ID: cardID, ParentID: "board1", BoardID: "board1", Type: model.TypeCard, } require.NoError(t, store.InsertBlock(&card, userID)) } // board 2 has two cards for _, cardID := range []string{"card4", "card5"} { card := model.Block{ ID: cardID, ParentID: "board2", BoardID: "board2", Type: model.TypeCard, } require.NoError(t, store.InsertBlock(&card, userID)) } count, err := store.GetUsedCardsCount() require.NoError(t, err) require.Equal(t, 5, count) }) t.Run("should not take into account content blocks", func(t *testing.T) { // we add a couple of content blocks text := model.Block{ ID: "text-id", ParentID: "card1", BoardID: "board1", Type: model.TypeText, } require.NoError(t, store.InsertBlock(&text, userID)) view := model.Block{ ID: "view-id", ParentID: "board1", BoardID: "board1", Type: model.TypeView, } require.NoError(t, store.InsertBlock(&view, userID)) // and count should not change count, err := store.GetUsedCardsCount() require.NoError(t, err) require.Equal(t, 5, count) }) t.Run("should not take into account cards belonging to templates", func(t *testing.T) { // we add a template with cards templateID := "template-id" boardTemplate := model.Block{ ID: templateID, BoardID: templateID, Type: model.TypeBoard, Fields: map[string]interface{}{ "isTemplate": true, }, } require.NoError(t, store.InsertBlock(&boardTemplate, userID)) for _, cardID := range []string{"card6", "card7", "card8"} { card := model.Block{ ID: cardID, ParentID: templateID, BoardID: templateID, Type: model.TypeCard, } require.NoError(t, store.InsertBlock(&card, userID)) } // and count should still be the same count, err := store.GetUsedCardsCount() require.NoError(t, err) require.Equal(t, 5, count) }) t.Run("should not take into account deleted cards", func(t *testing.T) { // we create a ninth card on the first board card9 := model.Block{ ID: "card9", ParentID: "board1", BoardID: "board1", Type: model.TypeCard, DeleteAt: utils.GetMillis(), } require.NoError(t, store.InsertBlock(&card9, userID)) // and count should still be the same count, err := store.GetUsedCardsCount() require.NoError(t, err) require.Equal(t, 5, count) }) t.Run("should not take into account cards from deleted boards", func(t *testing.T) { require.NoError(t, store.DeleteBoard("board2", "user-id")) count, err := store.GetUsedCardsCount() require.NoError(t, err) require.Equal(t, 3, count) }) } func testGetCardLimitTimestamp(t *testing.T, store storeservice.Store) { t.Run("should return 0 if there is no entry in the database", func(t *testing.T) { rawValue, err := store.GetSystemSetting(storeservice.CardLimitTimestampSystemKey) require.NoError(t, err) require.Equal(t, "", rawValue) cardLimitTimestamp, err := store.GetCardLimitTimestamp() require.NoError(t, err) require.Zero(t, cardLimitTimestamp) }) t.Run("should return an int64 representation of the value", func(t *testing.T) { require.NoError(t, store.SetSystemSetting(storeservice.CardLimitTimestampSystemKey, "1234")) cardLimitTimestamp, err := store.GetCardLimitTimestamp() require.NoError(t, err) require.Equal(t, int64(1234), cardLimitTimestamp) }) t.Run("should return an invalid value error if the value is not a number", func(t *testing.T) { require.NoError(t, store.SetSystemSetting(storeservice.CardLimitTimestampSystemKey, "abc")) cardLimitTimestamp, err := store.GetCardLimitTimestamp() require.ErrorContains(t, err, "card limit value is invalid") require.Zero(t, cardLimitTimestamp) }) } func testUpdateCardLimitTimestamp(t *testing.T, store storeservice.Store) { userID := "user-id" // two boards for _, boardID := range []string{"board1", "board2"} { boardType := model.BoardTypeOpen if boardID == "board2" { boardType = model.BoardTypePrivate } board := &model.Board{ ID: boardID, TeamID: testTeamID, Type: boardType, } _, err := store.InsertBoard(board, userID) require.NoError(t, err) } // board 1 has five cards for _, cardID := range []string{"card1", "card2", "card3", "card4", "card5"} { card := model.Block{ ID: cardID, ParentID: "board1", BoardID: "board1", Type: model.TypeCard, } require.NoError(t, store.InsertBlock(&card, userID)) time.Sleep(10 * time.Millisecond) } // board 2 has five cards for _, cardID := range []string{"card6", "card7", "card8", "card9", "card10"} { card := model.Block{ ID: cardID, ParentID: "board2", BoardID: "board2", Type: model.TypeCard, } require.NoError(t, store.InsertBlock(&card, userID)) time.Sleep(10 * time.Millisecond) } t.Run("should set the timestamp to zero if the card limit is zero", func(t *testing.T) { cardLimitTimestamp, err := store.UpdateCardLimitTimestamp(0) require.NoError(t, err) require.Zero(t, cardLimitTimestamp) cardLimitTimestampStr, err := store.GetSystemSetting(storeservice.CardLimitTimestampSystemKey) require.NoError(t, err) require.Equal(t, "0", cardLimitTimestampStr) }) t.Run("should correctly modify the limit several times in a row", func(t *testing.T) { cardLimitTimestamp, err := store.UpdateCardLimitTimestamp(0) require.NoError(t, err) require.Zero(t, cardLimitTimestamp) cardLimitTimestamp, err = store.UpdateCardLimitTimestamp(10) require.NoError(t, err) require.NotZero(t, cardLimitTimestamp) cardLimitTimestampStr, err := store.GetSystemSetting(storeservice.CardLimitTimestampSystemKey) require.NoError(t, err) require.NotEqual(t, "0", cardLimitTimestampStr) cardLimitTimestamp, err = store.UpdateCardLimitTimestamp(0) require.NoError(t, err) require.Zero(t, cardLimitTimestamp) cardLimitTimestampStr, err = store.GetSystemSetting(storeservice.CardLimitTimestampSystemKey) require.NoError(t, err) require.Equal(t, "0", cardLimitTimestampStr) }) t.Run("should set the correct timestamp", func(t *testing.T) { t.Run("limit 10", func(t *testing.T) { // we fetch the first block card1, err := store.GetBlock("card1") require.NoError(t, err) // and assert that if the limit is 10, the stored // timestamp corresponds to the card's update_at cardLimitTimestamp, err := store.UpdateCardLimitTimestamp(10) require.NoError(t, err) require.Equal(t, card1.UpdateAt, cardLimitTimestamp) }) t.Run("limit 5", func(t *testing.T) { // if the limit is 5, the timestamp should be the one from // the sixth card (the first five are older and out of the card6, err := store.GetBlock("card6") require.NoError(t, err) cardLimitTimestamp, err := store.UpdateCardLimitTimestamp(5) require.NoError(t, err) require.Equal(t, card6.UpdateAt, cardLimitTimestamp) }) t.Run("limit should be zero if we have less cards than the limit", func(t *testing.T) { cardLimitTimestamp, err := store.UpdateCardLimitTimestamp(100) require.NoError(t, err) require.Zero(t, cardLimitTimestamp) }) t.Run("we update the first inserted card and assert that with limit 1 that's the limit that is set", func(t *testing.T) { time.Sleep(10 * time.Millisecond) card1, err := store.GetBlock("card1") require.NoError(t, err) card1.Title = "New title" require.NoError(t, store.InsertBlock(card1, userID)) newCard1, err := store.GetBlock("card1") require.NoError(t, err) cardLimitTimestamp, err := store.UpdateCardLimitTimestamp(1) require.NoError(t, err) require.Equal(t, newCard1.UpdateAt, cardLimitTimestamp) }) t.Run("limit should stop applying if we remove the last card", func(t *testing.T) { initialCardLimitTimestamp, err := store.GetCardLimitTimestamp() require.NoError(t, err) require.NotZero(t, initialCardLimitTimestamp) time.Sleep(10 * time.Millisecond) require.NoError(t, store.DeleteBlock("card1", userID)) cardLimitTimestamp, err := store.UpdateCardLimitTimestamp(10) require.NoError(t, err) require.Zero(t, cardLimitTimestamp) }) }) }