271 lines
9.4 KiB
Go
271 lines
9.4 KiB
Go
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||
|
// See LICENSE.txt for license information.
|
||
|
|
||
|
package storetests
|
||
|
|
||
|
import (
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
|
||
|
"github.com/mattermost/focalboard/server/model"
|
||
|
"github.com/mattermost/focalboard/server/services/store"
|
||
|
"github.com/mattermost/focalboard/server/utils"
|
||
|
)
|
||
|
|
||
|
func StoreTestNotificationHintsStore(t *testing.T, setup func(t *testing.T) (store.Store, func())) {
|
||
|
container := store.Container{
|
||
|
WorkspaceID: "0",
|
||
|
}
|
||
|
|
||
|
t.Run("UpsertNotificationHint", func(t *testing.T) {
|
||
|
store, tearDown := setup(t)
|
||
|
defer tearDown()
|
||
|
testUpsertNotificationHint(t, store, container)
|
||
|
})
|
||
|
|
||
|
t.Run("DeleteNotificationHint", func(t *testing.T) {
|
||
|
store, tearDown := setup(t)
|
||
|
defer tearDown()
|
||
|
testDeleteNotificationHint(t, store, container)
|
||
|
})
|
||
|
|
||
|
t.Run("GetNotificationHint", func(t *testing.T) {
|
||
|
store, tearDown := setup(t)
|
||
|
defer tearDown()
|
||
|
testGetNotificationHint(t, store, container)
|
||
|
})
|
||
|
|
||
|
t.Run("GetNextNotificationHint", func(t *testing.T) {
|
||
|
store, tearDown := setup(t)
|
||
|
defer tearDown()
|
||
|
testGetNextNotificationHint(t, store, container)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func testUpsertNotificationHint(t *testing.T, store store.Store, container store.Container) {
|
||
|
t.Run("create notification hint", func(t *testing.T) {
|
||
|
hint := &model.NotificationHint{
|
||
|
BlockType: model.TypeCard,
|
||
|
BlockID: utils.NewID(utils.IDTypeBlock),
|
||
|
ModifiedByID: utils.NewID(utils.IDTypeUser),
|
||
|
WorkspaceID: container.WorkspaceID,
|
||
|
}
|
||
|
|
||
|
hintNew, err := store.UpsertNotificationHint(hint, time.Second*15)
|
||
|
require.NoError(t, err, "upsert notification hint should not error")
|
||
|
assert.Equal(t, hint.BlockID, hintNew.BlockID)
|
||
|
assert.NoError(t, hintNew.IsValid())
|
||
|
})
|
||
|
|
||
|
t.Run("duplicate notification hint", func(t *testing.T) {
|
||
|
hint := &model.NotificationHint{
|
||
|
BlockType: model.TypeCard,
|
||
|
BlockID: utils.NewID(utils.IDTypeBlock),
|
||
|
ModifiedByID: utils.NewID(utils.IDTypeUser),
|
||
|
WorkspaceID: container.WorkspaceID,
|
||
|
}
|
||
|
hintNew, err := store.UpsertNotificationHint(hint, time.Second*15)
|
||
|
require.NoError(t, err, "upsert notification hint should not error")
|
||
|
|
||
|
// sleep a short time so the notify_at timestamps won't collide
|
||
|
time.Sleep(time.Millisecond * 20)
|
||
|
|
||
|
hint = &model.NotificationHint{
|
||
|
BlockType: model.TypeCard,
|
||
|
BlockID: hintNew.BlockID,
|
||
|
ModifiedByID: hintNew.ModifiedByID,
|
||
|
WorkspaceID: container.WorkspaceID,
|
||
|
}
|
||
|
hintDup, err := store.UpsertNotificationHint(hint, time.Second*15)
|
||
|
|
||
|
require.NoError(t, err, "upsert notification hint should not error")
|
||
|
// notify_at should be updated
|
||
|
assert.Greater(t, hintDup.NotifyAt, hintNew.NotifyAt)
|
||
|
})
|
||
|
|
||
|
t.Run("invalid notification hint", func(t *testing.T) {
|
||
|
hint := &model.NotificationHint{}
|
||
|
|
||
|
_, err := store.UpsertNotificationHint(hint, time.Second*15)
|
||
|
assert.ErrorAs(t, err, &model.ErrInvalidNotificationHint{}, "invalid notification hint should error")
|
||
|
|
||
|
hint.BlockType = model.TypeBoard
|
||
|
_, err = store.UpsertNotificationHint(hint, time.Second*15)
|
||
|
assert.ErrorAs(t, err, &model.ErrInvalidNotificationHint{}, "invalid notification hint should error")
|
||
|
|
||
|
hint.WorkspaceID = container.WorkspaceID
|
||
|
_, err = store.UpsertNotificationHint(hint, time.Second*15)
|
||
|
assert.ErrorAs(t, err, &model.ErrInvalidNotificationHint{}, "invalid notification hint should error")
|
||
|
|
||
|
hint.ModifiedByID = utils.NewID(utils.IDTypeUser)
|
||
|
_, err = store.UpsertNotificationHint(hint, time.Second*15)
|
||
|
assert.ErrorAs(t, err, &model.ErrInvalidNotificationHint{}, "invalid notification hint should error")
|
||
|
|
||
|
hint.BlockID = utils.NewID(utils.IDTypeBlock)
|
||
|
hintNew, err := store.UpsertNotificationHint(hint, time.Second*15)
|
||
|
assert.NoError(t, err, "valid notification hint should not error")
|
||
|
assert.NoError(t, hintNew.IsValid(), "created notification hint should be valid")
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func testDeleteNotificationHint(t *testing.T, store store.Store, container store.Container) {
|
||
|
t.Run("delete notification hint", func(t *testing.T) {
|
||
|
hint := &model.NotificationHint{
|
||
|
BlockType: model.TypeCard,
|
||
|
BlockID: utils.NewID(utils.IDTypeBlock),
|
||
|
ModifiedByID: utils.NewID(utils.IDTypeUser),
|
||
|
WorkspaceID: container.WorkspaceID,
|
||
|
}
|
||
|
hintNew, err := store.UpsertNotificationHint(hint, time.Second*15)
|
||
|
require.NoError(t, err, "create notification hint should not error")
|
||
|
|
||
|
// check the notification hint exists
|
||
|
hint, err = store.GetNotificationHint(container, hintNew.BlockID)
|
||
|
require.NoError(t, err, "get notification hint should not error")
|
||
|
assert.Equal(t, hintNew.BlockID, hint.BlockID)
|
||
|
assert.Equal(t, hintNew.CreateAt, hint.CreateAt)
|
||
|
|
||
|
err = store.DeleteNotificationHint(container, hintNew.BlockID)
|
||
|
require.NoError(t, err, "delete notification hint should not error")
|
||
|
|
||
|
// check the notification hint was deleted
|
||
|
hint, err = store.GetNotificationHint(container, hintNew.BlockID)
|
||
|
require.True(t, store.IsErrNotFound(err), "error should be of type store.ErrNotFound")
|
||
|
assert.Nil(t, hint)
|
||
|
})
|
||
|
|
||
|
t.Run("delete non-existent notification hint", func(t *testing.T) {
|
||
|
err := store.DeleteNotificationHint(container, "bogus")
|
||
|
require.True(t, store.IsErrNotFound(err), "error should be of type store.ErrNotFound")
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func testGetNotificationHint(t *testing.T, store store.Store, container store.Container) {
|
||
|
t.Run("get notification hint", func(t *testing.T) {
|
||
|
hint := &model.NotificationHint{
|
||
|
BlockType: model.TypeCard,
|
||
|
BlockID: utils.NewID(utils.IDTypeBlock),
|
||
|
ModifiedByID: utils.NewID(utils.IDTypeUser),
|
||
|
WorkspaceID: container.WorkspaceID,
|
||
|
}
|
||
|
hintNew, err := store.UpsertNotificationHint(hint, time.Second*15)
|
||
|
require.NoError(t, err, "create notification hint should not error")
|
||
|
|
||
|
// make sure notification hint can be fetched
|
||
|
hint, err = store.GetNotificationHint(container, hintNew.BlockID)
|
||
|
require.NoError(t, err, "get notification hint should not error")
|
||
|
assert.Equal(t, hintNew, hint)
|
||
|
})
|
||
|
|
||
|
t.Run("get non-existent notification hint", func(t *testing.T) {
|
||
|
hint, err := store.GetNotificationHint(container, "bogus")
|
||
|
require.True(t, store.IsErrNotFound(err), "error should be of type store.ErrNotFound")
|
||
|
assert.Nil(t, hint, "hint should be nil")
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func testGetNextNotificationHint(t *testing.T, store store.Store, container store.Container) {
|
||
|
t.Run("get next notification hint", func(t *testing.T) {
|
||
|
const loops = 5
|
||
|
ids := [5]string{}
|
||
|
modifiedBy := utils.NewID(utils.IDTypeUser)
|
||
|
|
||
|
// create some hints with unique notifyAt
|
||
|
for i := 0; i < loops; i++ {
|
||
|
hint := &model.NotificationHint{
|
||
|
BlockType: model.TypeCard,
|
||
|
BlockID: utils.NewID(utils.IDTypeBlock),
|
||
|
ModifiedByID: modifiedBy,
|
||
|
WorkspaceID: container.WorkspaceID,
|
||
|
}
|
||
|
hintNew, err := store.UpsertNotificationHint(hint, time.Second*15)
|
||
|
require.NoError(t, err, "create notification hint should not error")
|
||
|
|
||
|
ids[i] = hintNew.BlockID
|
||
|
time.Sleep(time.Millisecond * 20) // ensure next timestamp is unique
|
||
|
}
|
||
|
|
||
|
// check the hints come back in the right order
|
||
|
notifyAt := utils.GetMillisForTime(time.Now().Add(time.Millisecond * 50))
|
||
|
for i := 0; i < loops; i++ {
|
||
|
hint, err := store.GetNextNotificationHint(false)
|
||
|
require.NoError(t, err, "get next notification hint should not error")
|
||
|
require.NotNil(t, hint, "get next notification hint should not return nil")
|
||
|
assert.Equal(t, ids[i], hint.BlockID)
|
||
|
assert.Less(t, notifyAt, hint.NotifyAt)
|
||
|
notifyAt = hint.NotifyAt
|
||
|
|
||
|
err = store.DeleteNotificationHint(container, hint.BlockID)
|
||
|
require.NoError(t, err, "delete notification hint should not error")
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("get next notification hint from empty table", func(t *testing.T) {
|
||
|
// empty the table
|
||
|
err := emptyNotificationHintTable(store)
|
||
|
require.NoError(t, err, "emptying notification hint table should not error")
|
||
|
|
||
|
for {
|
||
|
hint, err2 := store.GetNextNotificationHint(false)
|
||
|
if store.IsErrNotFound(err2) {
|
||
|
break
|
||
|
}
|
||
|
require.NoError(t, err2, "get next notification hint should not error")
|
||
|
|
||
|
err2 = store.DeleteNotificationHint(container, hint.BlockID)
|
||
|
require.NoError(t, err2, "delete notification hint should not error")
|
||
|
}
|
||
|
|
||
|
_, err = store.GetNextNotificationHint(false)
|
||
|
require.True(t, store.IsErrNotFound(err), "error should be of type store.ErrNotFound")
|
||
|
})
|
||
|
|
||
|
t.Run("get next notification hint and remove", func(t *testing.T) {
|
||
|
// empty the table
|
||
|
err := emptyNotificationHintTable(store)
|
||
|
require.NoError(t, err, "emptying notification hint table should not error")
|
||
|
|
||
|
hint := &model.NotificationHint{
|
||
|
BlockType: model.TypeCard,
|
||
|
BlockID: utils.NewID(utils.IDTypeBlock),
|
||
|
ModifiedByID: utils.NewID(utils.IDTypeUser),
|
||
|
WorkspaceID: container.WorkspaceID,
|
||
|
}
|
||
|
hintNew, err := store.UpsertNotificationHint(hint, time.Second*1)
|
||
|
require.NoError(t, err, "create notification hint should not error")
|
||
|
|
||
|
hintDeleted, err := store.GetNextNotificationHint(true)
|
||
|
require.NoError(t, err, "get next notification hint should not error")
|
||
|
require.NotNil(t, hintDeleted, "get next notification hint should not return nil")
|
||
|
assert.Equal(t, hintNew.BlockID, hintDeleted.BlockID)
|
||
|
|
||
|
// should be no hint left
|
||
|
_, err = store.GetNextNotificationHint(false)
|
||
|
require.True(t, store.IsErrNotFound(err), "error should be of type store.ErrNotFound")
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func emptyNotificationHintTable(store store.Store) error {
|
||
|
for {
|
||
|
hint, err := store.GetNextNotificationHint(false)
|
||
|
if store.IsErrNotFound(err) {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
c := containerForWorkspace(hint.WorkspaceID)
|
||
|
|
||
|
err = store.DeleteNotificationHint(c, hint.BlockID)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|