Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
5822 lines
179 KiB
Go
5822 lines
179 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package storetest
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/shared/request"
|
|
"github.com/mattermost/mattermost/server/v8/channels/store"
|
|
"github.com/mattermost/mattermost/server/v8/channels/utils"
|
|
)
|
|
|
|
func TestPostStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
t.Run("SaveMultiple", func(t *testing.T) { testPostStoreSaveMultiple(t, rctx, ss) })
|
|
t.Run("Save", func(t *testing.T) { testPostStoreSave(t, rctx, ss) })
|
|
t.Run("SaveAndUpdateChannelMsgCounts", func(t *testing.T) { testPostStoreSaveChannelMsgCounts(t, rctx, ss) })
|
|
t.Run("Get", func(t *testing.T) { testPostStoreGet(t, rctx, ss) })
|
|
t.Run("GetSingle", func(t *testing.T) { testPostStoreGetSingle(t, rctx, ss) })
|
|
t.Run("Update", func(t *testing.T) { testPostStoreUpdate(t, rctx, ss) })
|
|
t.Run("Delete", func(t *testing.T) { testPostStoreDelete(t, rctx, ss) })
|
|
t.Run("PermDelete1Level", func(t *testing.T) { testPostStorePermDelete1Level(t, rctx, ss) })
|
|
t.Run("PermDelete1Level2", func(t *testing.T) { testPostStorePermDelete1Level2(t, rctx, ss) })
|
|
t.Run("PermDeleteLimitExceeded", func(t *testing.T) { testPostStorePermDeleteLimitExceeded(t, rctx, ss) })
|
|
t.Run("GetWithChildren", func(t *testing.T) { testPostStoreGetWithChildren(t, rctx, ss) })
|
|
t.Run("GetPostsWithDetails", func(t *testing.T) { testPostStoreGetPostsWithDetails(t, rctx, ss) })
|
|
t.Run("GetPostsBeforeAfter", func(t *testing.T) { testPostStoreGetPostsBeforeAfter(t, rctx, ss) })
|
|
t.Run("GetPostsSince", func(t *testing.T) { testPostStoreGetPostsSince(t, rctx, ss) })
|
|
t.Run("GetPosts", func(t *testing.T) { testPostStoreGetPosts(t, rctx, ss) })
|
|
t.Run("GetPostBeforeAfter", func(t *testing.T) { testPostStoreGetPostBeforeAfter(t, rctx, ss) })
|
|
t.Run("UserCountsWithPostsByDay", func(t *testing.T) { testUserCountsWithPostsByDay(t, rctx, ss) })
|
|
t.Run("PostCountsByDuration", func(t *testing.T) { testPostCountsByDay(t, rctx, ss) })
|
|
t.Run("PostCounts", func(t *testing.T) { testPostCounts(t, rctx, ss) })
|
|
t.Run("GetFlaggedPostsForTeam", func(t *testing.T) { testPostStoreGetFlaggedPostsForTeam(t, rctx, ss, s) })
|
|
t.Run("GetFlaggedPosts", func(t *testing.T) { testPostStoreGetFlaggedPosts(t, rctx, ss) })
|
|
t.Run("GetFlaggedPostsForChannel", func(t *testing.T) { testPostStoreGetFlaggedPostsForChannel(t, rctx, ss) })
|
|
t.Run("GetPostsCreatedAt", func(t *testing.T) { testPostStoreGetPostsCreatedAt(t, rctx, ss) })
|
|
t.Run("Overwrite", func(t *testing.T) { testPostStoreOverwrite(t, rctx, ss) })
|
|
t.Run("OverwriteMultiple", func(t *testing.T) { testPostStoreOverwriteMultiple(t, rctx, ss) })
|
|
t.Run("GetPostsByIds", func(t *testing.T) { testPostStoreGetPostsByIds(t, rctx, ss) })
|
|
t.Run("GetPostsBatchForIndexing", func(t *testing.T) { testPostStoreGetPostsBatchForIndexing(t, rctx, ss) })
|
|
t.Run("PermanentDeleteBatch", func(t *testing.T) { testPostStorePermanentDeleteBatch(t, rctx, ss) })
|
|
t.Run("GetOldest", func(t *testing.T) { testPostStoreGetOldest(t, rctx, ss) })
|
|
t.Run("TestGetMaxPostSize", func(t *testing.T) { testGetMaxPostSize(t, rctx, ss) })
|
|
t.Run("GetParentsForExportAfter", func(t *testing.T) { testPostStoreGetParentsForExportAfter(t, rctx, ss) })
|
|
t.Run("GetRepliesForExport", func(t *testing.T) { testPostStoreGetRepliesForExport(t, rctx, ss) })
|
|
t.Run("GetDirectPostParentsForExportAfter", func(t *testing.T) { testPostStoreGetDirectPostParentsForExportAfter(t, rctx, ss, s) })
|
|
t.Run("GetDirectPostParentsForExportAfterDeleted", func(t *testing.T) { testPostStoreGetDirectPostParentsForExportAfterDeleted(t, rctx, ss, s) })
|
|
t.Run("GetDirectPostParentsForExportAfterBatched", func(t *testing.T) { testPostStoreGetDirectPostParentsForExportAfterBatched(t, rctx, ss, s) })
|
|
t.Run("GetForThread", func(t *testing.T) { testPostStoreGetForThread(t, rctx, ss) })
|
|
t.Run("HasAutoResponsePostByUserSince", func(t *testing.T) { testHasAutoResponsePostByUserSince(t, rctx, ss) })
|
|
t.Run("GetPostsSinceUpdateForSync", func(t *testing.T) { testGetPostsSinceUpdateForSync(t, rctx, ss, s) })
|
|
t.Run("GetPostsSinceCreateForSync", func(t *testing.T) { testGetPostsSinceCreateForSync(t, rctx, ss, s) })
|
|
t.Run("GetPostsSinceForSyncExcludeMetadata", func(t *testing.T) { testGetPostsSinceForSyncExcludeMetadata(t, rctx, ss, s) })
|
|
t.Run("SetPostReminder", func(t *testing.T) { testSetPostReminder(t, rctx, ss, s) })
|
|
t.Run("GetPostReminders", func(t *testing.T) { testGetPostReminders(t, rctx, ss, s) })
|
|
t.Run("GetPostReminderMetadata", func(t *testing.T) { testGetPostReminderMetadata(t, rctx, ss, s) })
|
|
t.Run("GetNthRecentPostTime", func(t *testing.T) { testGetNthRecentPostTime(t, rctx, ss) })
|
|
t.Run("GetEditHistoryForPost", func(t *testing.T) { testGetEditHistoryForPost(t, rctx, ss) })
|
|
t.Run("RestoreContentFlaggedPost", func(t *testing.T) { testRestoreContentFlaggedPost(t, rctx, ss) })
|
|
}
|
|
|
|
func testPostStoreSave(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("Save post", func(t *testing.T) {
|
|
o1 := model.Post{}
|
|
o1.ChannelId = model.NewId()
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
|
|
p, err := ss.Post().Save(rctx, &o1)
|
|
require.NoError(t, err, "couldn't save item")
|
|
assert.Equal(t, int64(0), p.ReplyCount)
|
|
})
|
|
|
|
t.Run("Save replies", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := model.Post{}
|
|
o1.ChannelId = channel1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.RootId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName2",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := model.Post{}
|
|
o2.ChannelId = channel2.Id
|
|
o2.UserId = model.NewId()
|
|
o2.RootId = o1.RootId
|
|
o2.Message = NewTestID()
|
|
|
|
channel3, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName3",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o3 := model.Post{}
|
|
o3.ChannelId = channel3.Id
|
|
o3.UserId = model.NewId()
|
|
o3.RootId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
|
|
p1, err := ss.Post().Save(rctx, &o1)
|
|
require.NoError(t, err, "couldn't save item")
|
|
assert.Equal(t, int64(1), p1.ReplyCount)
|
|
|
|
p2, err := ss.Post().Save(rctx, &o2)
|
|
require.NoError(t, err, "couldn't save item")
|
|
assert.Equal(t, int64(2), p2.ReplyCount)
|
|
|
|
p3, err := ss.Post().Save(rctx, &o3)
|
|
require.NoError(t, err, "couldn't save item")
|
|
assert.Equal(t, int64(1), p3.ReplyCount)
|
|
})
|
|
|
|
t.Run("Try to save existing post", func(t *testing.T) {
|
|
o1 := model.Post{}
|
|
o1.ChannelId = model.NewId()
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
|
|
_, err := ss.Post().Save(rctx, &o1)
|
|
require.NoError(t, err, "couldn't save item")
|
|
|
|
_, err = ss.Post().Save(rctx, &o1)
|
|
require.Error(t, err, "shouldn't be able to update from save")
|
|
})
|
|
|
|
t.Run("Update reply should update the UpdateAt of the root post", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
rootPost := model.Post{}
|
|
rootPost.ChannelId = channel.Id
|
|
rootPost.UserId = model.NewId()
|
|
rootPost.Message = NewTestID()
|
|
|
|
_, err = ss.Post().Save(rctx, &rootPost)
|
|
require.NoError(t, err)
|
|
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
replyPost := model.Post{}
|
|
replyPost.ChannelId = rootPost.ChannelId
|
|
replyPost.UserId = model.NewId()
|
|
replyPost.Message = NewTestID()
|
|
replyPost.RootId = rootPost.Id
|
|
|
|
// We need to sleep here to be sure the post is not created during the same millisecond
|
|
time.Sleep(time.Millisecond)
|
|
_, err = ss.Post().Save(rctx, &replyPost)
|
|
require.NoError(t, err)
|
|
|
|
rrootPost, err := ss.Post().GetSingle(rctx, rootPost.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Greater(t, rrootPost.UpdateAt, rootPost.UpdateAt)
|
|
})
|
|
|
|
t.Run("Create a post should update the channel LastPostAt and the total messages count by one", func(t *testing.T) {
|
|
channel := model.Channel{}
|
|
channel.Name = NewTestID()
|
|
channel.DisplayName = NewTestID()
|
|
channel.Type = model.ChannelTypeOpen
|
|
|
|
_, err := ss.Channel().Save(rctx, &channel, 100)
|
|
require.NoError(t, err)
|
|
|
|
post := model.Post{}
|
|
post.ChannelId = channel.Id
|
|
post.UserId = model.NewId()
|
|
post.Message = NewTestID()
|
|
|
|
// We need to sleep here to be sure the post is not created during the same millisecond
|
|
time.Sleep(time.Millisecond)
|
|
_, err = ss.Post().Save(rctx, &post)
|
|
require.NoError(t, err)
|
|
|
|
rchannel, err := ss.Channel().Get(channel.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Greater(t, rchannel.LastPostAt, channel.LastPostAt)
|
|
assert.Equal(t, int64(1), rchannel.TotalMsgCount)
|
|
|
|
post = model.Post{}
|
|
post.ChannelId = channel.Id
|
|
post.UserId = model.NewId()
|
|
post.Message = NewTestID()
|
|
post.CreateAt = 5
|
|
|
|
// We need to sleep here to be sure the post is not created during the same millisecond
|
|
time.Sleep(time.Millisecond)
|
|
_, err = ss.Post().Save(rctx, &post)
|
|
require.NoError(t, err)
|
|
|
|
rchannel2, err := ss.Channel().Get(channel.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, rchannel.LastPostAt, rchannel2.LastPostAt)
|
|
assert.Equal(t, int64(2), rchannel2.TotalMsgCount)
|
|
|
|
post = model.Post{}
|
|
post.ChannelId = channel.Id
|
|
post.UserId = model.NewId()
|
|
post.Message = NewTestID()
|
|
|
|
// We need to sleep here to be sure the post is not created during the same millisecond
|
|
time.Sleep(time.Millisecond)
|
|
_, err = ss.Post().Save(rctx, &post)
|
|
require.NoError(t, err)
|
|
|
|
rchannel3, err := ss.Channel().Get(channel.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Greater(t, rchannel3.LastPostAt, rchannel2.LastPostAt)
|
|
assert.Equal(t, int64(3), rchannel3.TotalMsgCount)
|
|
})
|
|
|
|
t.Run("Save post with priority metadata set", func(t *testing.T) {
|
|
o1 := model.Post{}
|
|
o1.ChannelId = model.NewId()
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
|
|
o1.Metadata = &model.PostMetadata{
|
|
Priority: &model.PostPriority{
|
|
Priority: model.NewPointer("important"),
|
|
RequestedAck: model.NewPointer(true),
|
|
PersistentNotifications: model.NewPointer(false),
|
|
},
|
|
}
|
|
|
|
p, err := ss.Post().Save(rctx, &o1)
|
|
require.NoError(t, err, "couldn't save item")
|
|
assert.Equal(t, int64(0), p.ReplyCount)
|
|
|
|
pp, err := ss.PostPriority().GetForPost(p.Id)
|
|
require.NoError(t, err, "couldn't save item")
|
|
assert.Equal(t, "important", *pp.Priority)
|
|
assert.Equal(t, true, *pp.RequestedAck)
|
|
assert.Equal(t, false, *pp.PersistentNotifications)
|
|
})
|
|
}
|
|
|
|
func testPostStoreSaveMultiple(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
p1 := model.Post{}
|
|
p1.ChannelId = model.NewId()
|
|
p1.UserId = model.NewId()
|
|
p1.Message = NewTestID()
|
|
|
|
p2 := model.Post{}
|
|
p2.ChannelId = model.NewId()
|
|
p2.UserId = model.NewId()
|
|
p2.Message = NewTestID()
|
|
|
|
p3 := model.Post{}
|
|
p3.ChannelId = model.NewId()
|
|
p3.UserId = model.NewId()
|
|
p3.Message = NewTestID()
|
|
|
|
p4 := model.Post{}
|
|
p4.ChannelId = model.NewId()
|
|
p4.UserId = model.NewId()
|
|
p4.Message = NewTestID()
|
|
|
|
t.Run("Save correctly a new set of posts", func(t *testing.T) {
|
|
newPosts, errIdx, err := ss.Post().SaveMultiple(rctx, []*model.Post{&p1, &p2, &p3})
|
|
require.NoError(t, err)
|
|
require.Equal(t, -1, errIdx)
|
|
for _, post := range newPosts {
|
|
storedPost, err := ss.Post().GetSingle(rctx, post.Id, false)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, post.ChannelId, storedPost.ChannelId)
|
|
assert.Equal(t, post.Message, storedPost.Message)
|
|
assert.Equal(t, post.UserId, storedPost.UserId)
|
|
}
|
|
})
|
|
|
|
t.Run("Save replies", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName2",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channel3, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName3",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channel4, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName4",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
o1 := model.Post{}
|
|
o1.ChannelId = channel1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.RootId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
|
|
o2 := model.Post{}
|
|
o2.ChannelId = channel2.Id
|
|
o2.UserId = model.NewId()
|
|
o2.RootId = o1.RootId
|
|
o2.Message = NewTestID()
|
|
|
|
o3 := model.Post{}
|
|
o3.ChannelId = channel3.Id
|
|
o3.UserId = model.NewId()
|
|
o3.RootId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
|
|
o4 := model.Post{}
|
|
o4.ChannelId = channel4.Id
|
|
o4.UserId = model.NewId()
|
|
o4.Message = NewTestID()
|
|
|
|
newPosts, errIdx, err := ss.Post().SaveMultiple(rctx, []*model.Post{&o1, &o2, &o3, &o4})
|
|
require.NoError(t, err, "couldn't save item")
|
|
require.Equal(t, -1, errIdx)
|
|
assert.Len(t, newPosts, 4)
|
|
assert.Equal(t, int64(2), newPosts[0].ReplyCount)
|
|
assert.Equal(t, int64(2), newPosts[1].ReplyCount)
|
|
assert.Equal(t, int64(1), newPosts[2].ReplyCount)
|
|
assert.Equal(t, int64(0), newPosts[3].ReplyCount)
|
|
})
|
|
|
|
t.Run("Try to save mixed, already saved and not saved posts", func(t *testing.T) {
|
|
newPosts, errIdx, err := ss.Post().SaveMultiple(rctx, []*model.Post{&p4, &p3})
|
|
require.Error(t, err)
|
|
require.Equal(t, 1, errIdx)
|
|
require.Nil(t, newPosts)
|
|
storedPost, err := ss.Post().GetSingle(rctx, p3.Id, false)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, p3.ChannelId, storedPost.ChannelId)
|
|
assert.Equal(t, p3.Message, storedPost.Message)
|
|
assert.Equal(t, p3.UserId, storedPost.UserId)
|
|
|
|
storedPost, err = ss.Post().GetSingle(rctx, p4.Id, false)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, storedPost)
|
|
})
|
|
|
|
t.Run("Update reply should update the UpdateAt of the root post", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
rootPost := model.Post{}
|
|
rootPost.ChannelId = channel.Id
|
|
rootPost.UserId = model.NewId()
|
|
rootPost.Message = NewTestID()
|
|
|
|
replyPost := model.Post{}
|
|
replyPost.ChannelId = rootPost.ChannelId
|
|
replyPost.UserId = model.NewId()
|
|
replyPost.Message = NewTestID()
|
|
replyPost.RootId = rootPost.Id
|
|
|
|
_, _, err = ss.Post().SaveMultiple(rctx, []*model.Post{&rootPost, &replyPost})
|
|
require.NoError(t, err)
|
|
|
|
rrootPost, err := ss.Post().GetSingle(rctx, rootPost.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, rrootPost.UpdateAt, rootPost.UpdateAt)
|
|
|
|
replyPost2 := model.Post{}
|
|
replyPost2.ChannelId = rootPost.ChannelId
|
|
replyPost2.UserId = model.NewId()
|
|
replyPost2.Message = NewTestID()
|
|
replyPost2.RootId = rootPost.Id
|
|
|
|
replyPost3 := model.Post{}
|
|
replyPost3.ChannelId = rootPost.ChannelId
|
|
replyPost3.UserId = model.NewId()
|
|
replyPost3.Message = NewTestID()
|
|
replyPost3.RootId = rootPost.Id
|
|
|
|
// Ensure update does not occur in the same timestamp as creation
|
|
time.Sleep(time.Millisecond)
|
|
|
|
_, _, err = ss.Post().SaveMultiple(rctx, []*model.Post{&replyPost2, &replyPost3})
|
|
require.NoError(t, err)
|
|
|
|
rrootPost2, err := ss.Post().GetSingle(rctx, rootPost.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Greater(t, rrootPost2.UpdateAt, rrootPost.UpdateAt)
|
|
})
|
|
|
|
t.Run("Create a post should update the channel LastPostAt and the total messages count by one", func(t *testing.T) {
|
|
channel := model.Channel{}
|
|
channel.Name = NewTestID()
|
|
channel.DisplayName = NewTestID()
|
|
channel.Type = model.ChannelTypeOpen
|
|
|
|
_, err := ss.Channel().Save(rctx, &channel, 100)
|
|
require.NoError(t, err)
|
|
|
|
post1 := model.Post{}
|
|
post1.ChannelId = channel.Id
|
|
post1.UserId = model.NewId()
|
|
post1.Message = NewTestID()
|
|
|
|
post2 := model.Post{}
|
|
post2.ChannelId = channel.Id
|
|
post2.UserId = model.NewId()
|
|
post2.Message = NewTestID()
|
|
post2.CreateAt = 5
|
|
|
|
post3 := model.Post{}
|
|
post3.ChannelId = channel.Id
|
|
post3.UserId = model.NewId()
|
|
post3.Message = NewTestID()
|
|
|
|
_, _, err = ss.Post().SaveMultiple(rctx, []*model.Post{&post1, &post2, &post3})
|
|
require.NoError(t, err)
|
|
|
|
rchannel, err := ss.Channel().Get(channel.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Greater(t, rchannel.LastPostAt, channel.LastPostAt)
|
|
assert.Equal(t, int64(3), rchannel.TotalMsgCount)
|
|
})
|
|
|
|
t.Run("Thread participants", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := model.Post{}
|
|
o1.ChannelId = channel1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = "jessica hyde" + model.NewId() + "b"
|
|
|
|
root, err := ss.Post().Save(rctx, &o1)
|
|
require.NoError(t, err)
|
|
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName2",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channel3, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName3",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channel4, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName4",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channel5, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName5",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
o2 := model.Post{}
|
|
o2.ChannelId = channel2.Id
|
|
o2.UserId = model.NewId()
|
|
o2.RootId = root.Id
|
|
o2.Message = "zz" + model.NewId() + "b"
|
|
|
|
o3 := model.Post{}
|
|
o3.ChannelId = channel3.Id
|
|
o3.UserId = model.NewId()
|
|
o3.RootId = root.Id
|
|
o3.Message = "zz" + model.NewId() + "b"
|
|
|
|
o4 := model.Post{}
|
|
o4.ChannelId = channel4.Id
|
|
o4.UserId = o2.UserId
|
|
o4.RootId = root.Id
|
|
o4.Message = "zz" + model.NewId() + "b"
|
|
|
|
o5 := model.Post{}
|
|
o5.ChannelId = channel5.Id
|
|
o5.UserId = o1.UserId
|
|
o5.RootId = root.Id
|
|
o5.Message = "zz" + model.NewId() + "b"
|
|
|
|
_, err = ss.Post().Save(rctx, &o2)
|
|
require.NoError(t, err)
|
|
thread, errT := ss.Thread().Get(root.Id)
|
|
require.NoError(t, errT)
|
|
|
|
assert.Equal(t, int64(1), thread.ReplyCount)
|
|
assert.Equal(t, int(1), len(thread.Participants))
|
|
assert.Equal(t, model.StringArray{o2.UserId}, thread.Participants)
|
|
|
|
_, err = ss.Post().Save(rctx, &o3)
|
|
require.NoError(t, err)
|
|
thread, errT = ss.Thread().Get(root.Id)
|
|
require.NoError(t, errT)
|
|
|
|
assert.Equal(t, int64(2), thread.ReplyCount)
|
|
assert.Equal(t, int(2), len(thread.Participants))
|
|
assert.Equal(t, model.StringArray{o2.UserId, o3.UserId}, thread.Participants)
|
|
|
|
_, err = ss.Post().Save(rctx, &o4)
|
|
require.NoError(t, err)
|
|
thread, errT = ss.Thread().Get(root.Id)
|
|
require.NoError(t, errT)
|
|
|
|
assert.Equal(t, int64(3), thread.ReplyCount)
|
|
assert.Equal(t, int(2), len(thread.Participants))
|
|
assert.Equal(t, model.StringArray{o3.UserId, o2.UserId}, thread.Participants)
|
|
|
|
_, err = ss.Post().Save(rctx, &o5)
|
|
require.NoError(t, err)
|
|
thread, errT = ss.Thread().Get(root.Id)
|
|
require.NoError(t, errT)
|
|
|
|
assert.Equal(t, int64(4), thread.ReplyCount)
|
|
assert.Equal(t, int(3), len(thread.Participants))
|
|
assert.Equal(t, model.StringArray{o3.UserId, o2.UserId, o1.UserId}, thread.Participants)
|
|
})
|
|
}
|
|
|
|
func testPostStoreSaveChannelMsgCounts(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
c1 := &model.Channel{Name: model.NewId(), DisplayName: "posttestchannel", Type: model.ChannelTypeOpen, TeamId: model.NewId()}
|
|
_, err := ss.Channel().Save(rctx, c1, 1000000)
|
|
require.NoError(t, err)
|
|
|
|
o1 := model.Post{}
|
|
o1.ChannelId = c1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
|
|
_, err = ss.Post().Save(rctx, &o1)
|
|
require.NoError(t, err)
|
|
|
|
c1, err = ss.Channel().Get(c1.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(1), c1.TotalMsgCount, "Message count should update by 1")
|
|
|
|
o1.Id = ""
|
|
o1.Type = model.PostTypeAddToTeam
|
|
_, err = ss.Post().Save(rctx, &o1)
|
|
require.NoError(t, err)
|
|
|
|
o1.Id = ""
|
|
o1.Type = model.PostTypeRemoveFromTeam
|
|
_, err = ss.Post().Save(rctx, &o1)
|
|
require.NoError(t, err)
|
|
|
|
c1, err = ss.Channel().Get(c1.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(1), c1.TotalMsgCount, "Message count should not update for team add/removed message")
|
|
|
|
oldLastPostAt := c1.LastPostAt
|
|
|
|
o2 := model.Post{}
|
|
o2.ChannelId = c1.Id
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2.CreateAt = int64(7)
|
|
_, err = ss.Post().Save(rctx, &o2)
|
|
require.NoError(t, err)
|
|
|
|
c1, err = ss.Channel().Get(c1.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, oldLastPostAt, c1.LastPostAt, "LastPostAt should not update for old message save")
|
|
}
|
|
|
|
func testPostStoreGet(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channel.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
|
|
etag1 := ss.Post().GetEtag(o1.ChannelId, false, false)
|
|
require.Equal(t, 0, strings.Index(etag1, model.CurrentVersion+"."), "Invalid Etag")
|
|
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
etag2 := ss.Post().GetEtag(o1.ChannelId, false, false)
|
|
require.Equal(t, 0, strings.Index(etag2, fmt.Sprintf("%v.%v", model.CurrentVersion, o1.UpdateAt)), "Invalid Etag")
|
|
|
|
r1, err := ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, r1.Posts[o1.Id].CreateAt, o1.CreateAt, "invalid returned post")
|
|
|
|
_, err = ss.Post().Get(rctx, "123", model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Missing id should have failed")
|
|
|
|
_, err = ss.Post().Get(rctx, "", model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "should fail for blank post ids")
|
|
}
|
|
|
|
func testPostStoreGetForThread(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("Post thread is followed", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{ChannelId: channel.Id, UserId: model.NewId(), Message: NewTestID()}
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
_, err = ss.Post().Save(rctx, &model.Post{ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestID(), RootId: o1.Id})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Thread().MaintainMembership(o1.UserId, o1.Id, store.ThreadMembershipOpts{
|
|
Following: true,
|
|
UpdateFollowing: true,
|
|
})
|
|
require.NoError(t, err)
|
|
opts := model.GetPostsOptions{
|
|
CollapsedThreads: true,
|
|
}
|
|
r1, err := ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, r1.Posts[o1.Id].CreateAt, o1.CreateAt, "invalid returned post")
|
|
require.True(t, *r1.Posts[o1.Id].IsFollowing)
|
|
})
|
|
|
|
t.Run("Post thread is explicitly not followed", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{ChannelId: channel.Id, UserId: model.NewId(), Message: NewTestID()}
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
_, err = ss.Post().Save(rctx, &model.Post{ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestID(), RootId: o1.Id})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Thread().MaintainMembership(o1.UserId, o1.Id, store.ThreadMembershipOpts{
|
|
Following: false,
|
|
UpdateFollowing: true,
|
|
})
|
|
require.NoError(t, err)
|
|
opts := model.GetPostsOptions{
|
|
CollapsedThreads: true,
|
|
}
|
|
r1, err := ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, r1.Posts[o1.Id].CreateAt, o1.CreateAt, "invalid returned post")
|
|
require.False(t, *r1.Posts[o1.Id].IsFollowing)
|
|
})
|
|
|
|
t.Run("Post threadmembership does not exist", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{ChannelId: channel.Id, UserId: model.NewId(), Message: NewTestID()}
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
_, err = ss.Post().Save(rctx, &model.Post{ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestID(), RootId: o1.Id})
|
|
require.NoError(t, err)
|
|
|
|
opts := model.GetPostsOptions{
|
|
CollapsedThreads: true,
|
|
}
|
|
r1, err := ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, r1.Posts[o1.Id].CreateAt, o1.CreateAt, "invalid returned post")
|
|
require.Nil(t, r1.Posts[o1.Id].IsFollowing)
|
|
})
|
|
|
|
t.Run("Pagination", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
now := model.GetMillis()
|
|
o1, err := ss.Post().Save(rctx, &model.Post{CreateAt: now, ChannelId: channel.Id, UserId: model.NewId(), Message: NewTestID()})
|
|
require.NoError(t, err)
|
|
_, err = ss.Post().Save(rctx, &model.Post{CreateAt: now + 1, ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestID(), RootId: o1.Id})
|
|
require.NoError(t, err)
|
|
m1, err := ss.Post().Save(rctx, &model.Post{CreateAt: now + 2, ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestID(), RootId: o1.Id})
|
|
require.NoError(t, err)
|
|
_, err = ss.Post().Save(rctx, &model.Post{CreateAt: now + 3, ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestID(), RootId: o1.Id})
|
|
require.NoError(t, err)
|
|
_, err = ss.Post().Save(rctx, &model.Post{CreateAt: now + 4, ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestID(), RootId: o1.Id})
|
|
require.NoError(t, err)
|
|
|
|
opts := model.GetPostsOptions{
|
|
CollapsedThreads: true,
|
|
PerPage: 2,
|
|
Direction: "down",
|
|
}
|
|
r1, err := ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Len(t, r1.Order, 3) // including the root post
|
|
require.Len(t, r1.Posts, 3)
|
|
assert.True(t, *r1.HasNext)
|
|
|
|
lastPostID := r1.Order[len(r1.Order)-1]
|
|
lastPostCreateAt := r1.Posts[lastPostID].CreateAt
|
|
|
|
opts = model.GetPostsOptions{
|
|
CollapsedThreads: true,
|
|
PerPage: 2,
|
|
Direction: "down",
|
|
FromPost: lastPostID,
|
|
FromCreateAt: lastPostCreateAt,
|
|
}
|
|
r1, err = ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Len(t, r1.Order, 3) // including the root post
|
|
require.Len(t, r1.Posts, 3)
|
|
assert.GreaterOrEqual(t, r1.Posts[r1.Order[len(r1.Order)-1]].CreateAt, lastPostCreateAt)
|
|
assert.False(t, *r1.HasNext)
|
|
|
|
// Going from bottom to top now.
|
|
firstPostCreateAt := r1.Posts[r1.Order[1]].CreateAt
|
|
opts = model.GetPostsOptions{
|
|
CollapsedThreads: true,
|
|
PerPage: 2,
|
|
Direction: "up",
|
|
FromPost: r1.Order[1],
|
|
FromCreateAt: firstPostCreateAt,
|
|
}
|
|
r1, err = ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Len(t, r1.Order, 3) // including the root post
|
|
require.Len(t, r1.Posts, 3)
|
|
assert.LessOrEqual(t, r1.Posts[r1.Order[1]].CreateAt, firstPostCreateAt)
|
|
assert.False(t, *r1.HasNext)
|
|
|
|
// Only with CreateAt
|
|
opts = model.GetPostsOptions{
|
|
CollapsedThreads: false,
|
|
PerPage: 1,
|
|
Direction: "up",
|
|
FromCreateAt: m1.CreateAt,
|
|
SkipFetchThreads: false,
|
|
}
|
|
r1, err = ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, r1.Posts[r1.Order[0]].ReplyCount, int64(4))
|
|
require.Equal(t, r1.Posts[r1.Order[1]].ReplyCount, int64(4))
|
|
require.Len(t, r1.Order, 2) // including the root post
|
|
require.Len(t, r1.Posts, 2)
|
|
assert.LessOrEqual(t, r1.Posts[r1.Order[1]].CreateAt, m1.CreateAt)
|
|
assert.True(t, *r1.HasNext)
|
|
|
|
// Non-CRT mode
|
|
opts = model.GetPostsOptions{
|
|
CollapsedThreads: false,
|
|
PerPage: 2,
|
|
Direction: "down",
|
|
SkipFetchThreads: false,
|
|
}
|
|
r1, err = ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Len(t, r1.Order, 2)
|
|
require.Len(t, r1.Posts, 2)
|
|
assert.True(t, *r1.HasNext)
|
|
|
|
lastPostID = r1.Order[len(r1.Order)-1]
|
|
lastPostCreateAt = r1.Posts[lastPostID].CreateAt
|
|
|
|
opts = model.GetPostsOptions{
|
|
CollapsedThreads: false,
|
|
PerPage: 3,
|
|
Direction: "down",
|
|
FromPost: lastPostID,
|
|
FromCreateAt: lastPostCreateAt,
|
|
SkipFetchThreads: false,
|
|
}
|
|
r1, err = ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, r1.Posts[r1.Order[0]].ReplyCount, int64(4))
|
|
require.Equal(t, r1.Posts[r1.Order[1]].ReplyCount, int64(4))
|
|
require.Equal(t, r1.Posts[r1.Order[2]].ReplyCount, int64(4))
|
|
require.Equal(t, r1.Posts[r1.Order[3]].ReplyCount, int64(4))
|
|
require.Len(t, r1.Order, 4) // including the root post
|
|
require.Len(t, r1.Posts, 4)
|
|
assert.GreaterOrEqual(t, r1.Posts[r1.Order[len(r1.Order)-1]].CreateAt, lastPostCreateAt)
|
|
assert.False(t, *r1.HasNext)
|
|
|
|
// Going from bottom to top now.
|
|
firstPostCreateAt = r1.Posts[r1.Order[1]].CreateAt
|
|
opts = model.GetPostsOptions{
|
|
CollapsedThreads: false,
|
|
PerPage: 2,
|
|
Direction: "up",
|
|
FromPost: r1.Order[1],
|
|
FromCreateAt: firstPostCreateAt,
|
|
SkipFetchThreads: false,
|
|
}
|
|
r1, err = ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Len(t, r1.Order, 2)
|
|
require.Len(t, r1.Posts, 2)
|
|
assert.LessOrEqual(t, r1.Posts[r1.Order[1]].CreateAt, firstPostCreateAt)
|
|
assert.False(t, *r1.HasNext)
|
|
|
|
// Only with CreateAt
|
|
opts = model.GetPostsOptions{
|
|
CollapsedThreads: false,
|
|
PerPage: 1,
|
|
Direction: "down",
|
|
FromCreateAt: m1.CreateAt,
|
|
SkipFetchThreads: false,
|
|
}
|
|
r1, err = ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Len(t, r1.Order, 2) // including the root post
|
|
require.Len(t, r1.Posts, 2)
|
|
assert.GreaterOrEqual(t, r1.Posts[r1.Order[1]].CreateAt, m1.CreateAt)
|
|
assert.True(t, *r1.HasNext)
|
|
})
|
|
|
|
t.Run("Pagination with UpdateAt", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
now := model.GetMillis()
|
|
o1 := &model.Post{CreateAt: now, ChannelId: channel.Id, UserId: model.NewId(), Message: NewTestID()}
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
// Create replies with explicit UpdateAt timestamps
|
|
o2 := &model.Post{CreateAt: now + 1, UpdateAt: now + 1, ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestID(), RootId: o1.Id}
|
|
_, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
m1 := &model.Post{CreateAt: now + 2, UpdateAt: now + 2, ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestID(), RootId: o1.Id}
|
|
m1, err = ss.Post().Save(rctx, m1)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{CreateAt: now + 3, UpdateAt: now + 3, ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestID(), RootId: o1.Id}
|
|
_, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
o4 := &model.Post{CreateAt: now + 4, UpdateAt: now + 4, ChannelId: o1.ChannelId, UserId: model.NewId(), Message: NewTestID(), RootId: o1.Id}
|
|
o4, err = ss.Post().Save(rctx, o4)
|
|
require.NoError(t, err)
|
|
|
|
// Test pagination with UpdateAt in "down" direction
|
|
opts := model.GetPostsOptions{
|
|
UpdatesOnly: true,
|
|
CollapsedThreads: true,
|
|
PerPage: 2,
|
|
Direction: "down",
|
|
}
|
|
r1, err := ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Len(t, r1.Order, 3) // including the root post
|
|
require.Len(t, r1.Posts, 3)
|
|
assert.Equal(t, r1.Posts[r1.Order[0]].UpdateAt, o4.CreateAt) // The root post always get updated with the createAt of the latest post in the thread.
|
|
assert.True(t, *r1.HasNext)
|
|
|
|
lastPostID := r1.Order[len(r1.Order)-1]
|
|
lastPostUpdateAt := r1.Posts[lastPostID].UpdateAt
|
|
|
|
// Continue pagination using UpdateAt
|
|
opts = model.GetPostsOptions{
|
|
UpdatesOnly: true,
|
|
CollapsedThreads: true,
|
|
PerPage: 2,
|
|
Direction: "down",
|
|
FromPost: lastPostID,
|
|
FromUpdateAt: lastPostUpdateAt,
|
|
}
|
|
r1, err = ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Len(t, r1.Order, 3) // including the root post
|
|
require.Len(t, r1.Posts, 3)
|
|
assert.GreaterOrEqual(t, r1.Posts[r1.Order[len(r1.Order)-1]].UpdateAt, lastPostUpdateAt)
|
|
assert.Equal(t, r1.Posts[r1.Order[0]].UpdateAt, o4.CreateAt) // The root post always get updated with the createAt of the latest post in the thread.
|
|
assert.False(t, *r1.HasNext)
|
|
|
|
// Non-CRT mode with UpdateAt pagination
|
|
opts = model.GetPostsOptions{
|
|
UpdatesOnly: true,
|
|
CollapsedThreads: false,
|
|
PerPage: 2,
|
|
Direction: "down",
|
|
SkipFetchThreads: false,
|
|
}
|
|
r1, err = ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
// Ordering by updateAt will move the root post down, so we will get more posts in the thread.
|
|
require.Len(t, r1.Order, 3)
|
|
require.Len(t, r1.Posts, 3)
|
|
require.True(t, *r1.HasNext)
|
|
|
|
lastPostID = r1.Order[len(r1.Order)-1]
|
|
lastPostUpdateAt = r1.Posts[lastPostID].UpdateAt
|
|
|
|
opts = model.GetPostsOptions{
|
|
UpdatesOnly: true,
|
|
CollapsedThreads: false,
|
|
PerPage: 3,
|
|
Direction: "down",
|
|
FromPost: lastPostID,
|
|
FromUpdateAt: lastPostUpdateAt,
|
|
SkipFetchThreads: false,
|
|
}
|
|
r1, err = ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Len(t, r1.Order, 3)
|
|
require.Len(t, r1.Posts, 3)
|
|
require.Equal(t, r1.Posts[r1.Order[0]].ReplyCount, int64(4))
|
|
require.Equal(t, r1.Posts[r1.Order[1]].ReplyCount, int64(4))
|
|
require.Equal(t, r1.Posts[r1.Order[2]].ReplyCount, int64(4))
|
|
require.GreaterOrEqual(t, r1.Posts[r1.Order[len(r1.Order)-1]].UpdateAt, lastPostUpdateAt)
|
|
assert.False(t, *r1.HasNext)
|
|
|
|
// Only with UpdateAt - direction down
|
|
opts = model.GetPostsOptions{
|
|
UpdatesOnly: true,
|
|
CollapsedThreads: false,
|
|
PerPage: 1,
|
|
Direction: "down",
|
|
FromUpdateAt: m1.UpdateAt,
|
|
SkipFetchThreads: false,
|
|
}
|
|
r1, err = ss.Post().Get(rctx, o1.Id, opts, o1.UserId, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Len(t, r1.Order, 2)
|
|
require.Len(t, r1.Posts, 2)
|
|
require.GreaterOrEqual(t, r1.Posts[r1.Order[1]].UpdateAt, m1.UpdateAt)
|
|
require.True(t, *r1.HasNext)
|
|
})
|
|
}
|
|
|
|
func testPostStoreGetSingle(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channel.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = o1.UserId
|
|
o2.Message = NewTestID()
|
|
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = o1.ChannelId
|
|
o3.UserId = o1.UserId
|
|
o3.Message = model.NewRandomString(10)
|
|
o3.RootId = o1.Id
|
|
|
|
o4 := &model.Post{}
|
|
o4.ChannelId = o1.ChannelId
|
|
o4.UserId = o1.UserId
|
|
o4.Message = model.NewRandomString(10)
|
|
o4.RootId = o1.Id
|
|
|
|
_, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
o4, err = ss.Post().Save(rctx, o4)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.Post().Delete(rctx, o2.Id, model.GetMillis(), o2.UserId)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.Post().Delete(rctx, o4.Id, model.GetMillis(), o4.UserId)
|
|
require.NoError(t, err)
|
|
|
|
post, err := ss.Post().GetSingle(rctx, o1.Id, false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, post.CreateAt, o1.CreateAt, "invalid returned post")
|
|
require.Equal(t, int64(1), post.ReplyCount, "wrong replyCount computed")
|
|
|
|
_, err = ss.Post().GetSingle(rctx, o2.Id, false)
|
|
require.Error(t, err, "should not return deleted post")
|
|
|
|
post, err = ss.Post().GetSingle(rctx, o2.Id, true)
|
|
require.NoError(t, err)
|
|
require.Equal(t, post.CreateAt, o2.CreateAt, "invalid returned post")
|
|
require.NotZero(t, post.DeleteAt, "DeleteAt should be non-zero")
|
|
require.Zero(t, post.ReplyCount, "Post without replies should return zero ReplyCount")
|
|
|
|
_, err = ss.Post().GetSingle(rctx, "123", false)
|
|
require.Error(t, err, "Missing id should have failed")
|
|
}
|
|
|
|
func testPostStoreUpdate(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channel.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2.RootId = o1.Id
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = o1.ChannelId
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
r1, err := ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro1 := r1.Posts[o1.Id]
|
|
|
|
r2, err := ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro2 := r2.Posts[o2.Id]
|
|
|
|
r3, err := ss.Post().Get(rctx, o3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro3 := r3.Posts[o3.Id]
|
|
|
|
require.Equal(t, ro1.Message, o1.Message, "Failed to save/get")
|
|
|
|
o1a := ro1.Clone()
|
|
o1a.Message = ro1.Message + "BBBBBBBBBB"
|
|
_, err = ss.Post().Update(rctx, o1a, ro1)
|
|
require.NoError(t, err)
|
|
|
|
r1, err = ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
ro1a := r1.Posts[o1.Id]
|
|
require.Equal(t, ro1a.Message, o1a.Message, "Failed to update/get")
|
|
|
|
o2a := ro2.Clone()
|
|
o2a.Message = ro2.Message + "DDDDDDD"
|
|
_, err = ss.Post().Update(rctx, o2a, ro2)
|
|
require.NoError(t, err)
|
|
|
|
r2, err = ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro2a := r2.Posts[o2.Id]
|
|
|
|
require.Equal(t, ro2a.Message, o2a.Message, "Failed to update/get")
|
|
|
|
o3a := ro3.Clone()
|
|
o3a.Message = ro3.Message + "WWWWWWW"
|
|
_, err = ss.Post().Update(rctx, o3a, ro3)
|
|
require.NoError(t, err)
|
|
|
|
r3, err = ss.Post().Get(rctx, o3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro3a := r3.Posts[o3.Id]
|
|
|
|
if ro3a.Message != o3a.Message {
|
|
require.Equal(t, ro3a.Hashtags, o3a.Hashtags, "Failed to update/get")
|
|
}
|
|
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
o4, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel2.Id,
|
|
UserId: model.NewId(),
|
|
Message: model.NewId(),
|
|
Filenames: []string{"test"},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
r4, err := ss.Post().Get(rctx, o4.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro4 := r4.Posts[o4.Id]
|
|
|
|
o4a := ro4.Clone()
|
|
o4a.Filenames = []string{}
|
|
o4a.FileIds = []string{model.NewId()}
|
|
_, err = ss.Post().Update(rctx, o4a, ro4)
|
|
require.NoError(t, err)
|
|
|
|
r4, err = ss.Post().Get(rctx, o4.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
ro4a := r4.Posts[o4.Id]
|
|
require.Empty(t, ro4a.Filenames, "Failed to clear Filenames")
|
|
require.Len(t, ro4a.FileIds, 1, "Failed to set FileIds")
|
|
}
|
|
|
|
func testPostStoreDelete(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("single post, no replies", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
// Create a post
|
|
rootPost, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel.Id,
|
|
UserId: model.NewId(),
|
|
Message: model.NewRandomString(10),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Verify etag generation for the channel containing the post.
|
|
etag1 := ss.Post().GetEtag(rootPost.ChannelId, false, false)
|
|
require.Equal(t, 0, strings.Index(etag1, model.CurrentVersion+"."), "Invalid Etag")
|
|
|
|
// Verify the created post.
|
|
r1, err := ss.Post().Get(rctx, rootPost.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, r1.Posts[rootPost.Id])
|
|
require.Equal(t, rootPost, r1.Posts[rootPost.Id])
|
|
|
|
// Mark the post as deleted by the user identified with deleteByID.
|
|
deleteByID := model.NewId()
|
|
err = ss.Post().Delete(rctx, rootPost.Id, model.GetMillis(), deleteByID)
|
|
require.NoError(t, err)
|
|
|
|
// Ensure the appropriate posts prop reflects the user deleting the post.
|
|
posts, err := ss.Post().GetPostsCreatedAt(rootPost.ChannelId, rootPost.CreateAt)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, posts)
|
|
assert.Equal(t, deleteByID, posts[0].GetProp(model.PostPropsDeleteBy), "unexpected Props[model.PostPropsDeleteBy]")
|
|
|
|
// Verify that the post is no longer fetched by default.
|
|
_, err = ss.Post().Get(rctx, rootPost.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "fetching deleted post should have failed")
|
|
require.IsType(t, &store.ErrNotFound{}, err)
|
|
|
|
// Verify etag generation for the channel containing the now deleted post.
|
|
etag2 := ss.Post().GetEtag(rootPost.ChannelId, false, false)
|
|
require.Equal(t, 0, strings.Index(etag2, model.CurrentVersion+"."), "Invalid Etag")
|
|
})
|
|
|
|
t.Run("thread with one reply", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
// Create a root post
|
|
rootPost, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel.Id,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Reply to that root post
|
|
replyPost, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: rootPost.ChannelId,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
RootId: rootPost.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Delete the root post
|
|
err = ss.Post().Delete(rctx, rootPost.Id, model.GetMillis(), "")
|
|
require.NoError(t, err)
|
|
|
|
// Verify the root post deleted
|
|
_, err = ss.Post().Get(rctx, rootPost.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
require.IsType(t, &store.ErrNotFound{}, err)
|
|
|
|
// Verify the reply post deleted
|
|
_, err = ss.Post().Get(rctx, replyPost.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
require.IsType(t, &store.ErrNotFound{}, err)
|
|
})
|
|
|
|
t.Run("thread with multiple replies", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
// Create a root post
|
|
rootPost1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel.Id,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Reply to that root post
|
|
replyPost1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: rootPost1.ChannelId,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
RootId: rootPost1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Reply to that root post a second time
|
|
replyPost2, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: rootPost1.ChannelId,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
RootId: rootPost1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
// Create another root post in a separate channel
|
|
rootPost2, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel2.Id,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Delete the root post
|
|
err = ss.Post().Delete(rctx, rootPost1.Id, model.GetMillis(), "")
|
|
require.NoError(t, err)
|
|
|
|
// Verify the root post and replies deleted
|
|
_, err = ss.Post().Get(rctx, rootPost1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
|
|
_, err = ss.Post().Get(rctx, replyPost1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
|
|
_, err = ss.Post().Get(rctx, replyPost2.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
|
|
// Verify other root posts remain undeleted.
|
|
_, err = ss.Post().Get(rctx, rootPost2.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("root post update at is updated upon reply delete", func(t *testing.T) {
|
|
teamId := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamId,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
// Create a root post
|
|
rootPost1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel.Id,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Reply to that root post
|
|
_, err = ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: rootPost1.ChannelId,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
RootId: rootPost1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Reply to that root post a second time
|
|
replyPost2, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: rootPost1.ChannelId,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
RootId: rootPost1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Reply to that root post a third time
|
|
_, err = ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: rootPost1.ChannelId,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
RootId: rootPost1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
updatedRootPost, err := ss.Post().GetSingle(rctx, rootPost1.Id, false)
|
|
require.NoError(t, err)
|
|
|
|
beforeDeleteTime := updatedRootPost.UpdateAt
|
|
|
|
// Delete the reply previous to last
|
|
err = ss.Post().Delete(rctx, replyPost2.Id, model.GetMillis(), "")
|
|
require.NoError(t, err)
|
|
|
|
updatedRootPost, err = ss.Post().GetSingle(rctx, rootPost1.Id, false)
|
|
require.NoError(t, err)
|
|
|
|
require.Greater(t, updatedRootPost.UpdateAt, beforeDeleteTime)
|
|
})
|
|
|
|
t.Run("thread with multiple replies, update thread last reply at", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
// Create a root post
|
|
rootPost1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel.Id,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Reply to that root post
|
|
replyPost1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: rootPost1.ChannelId,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
RootId: rootPost1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Reply to that root post a second time
|
|
replyPost2, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: rootPost1.ChannelId,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
RootId: rootPost1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Reply to that root post a third time
|
|
replyPost3, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: rootPost1.ChannelId,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
RootId: rootPost1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
thread, err := ss.Thread().Get(rootPost1.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, replyPost3.CreateAt, thread.LastReplyAt)
|
|
|
|
// Delete the reply previous to last
|
|
err = ss.Post().Delete(rctx, replyPost2.Id, model.GetMillis(), "")
|
|
require.NoError(t, err)
|
|
|
|
thread, err = ss.Thread().Get(rootPost1.Id)
|
|
require.NoError(t, err)
|
|
// last reply at should be unchanged
|
|
require.Equal(t, replyPost3.CreateAt, thread.LastReplyAt)
|
|
|
|
// Delete the last reply
|
|
err = ss.Post().Delete(rctx, replyPost3.Id, model.GetMillis(), "")
|
|
require.NoError(t, err)
|
|
|
|
thread, err = ss.Thread().Get(rootPost1.Id)
|
|
require.NoError(t, err)
|
|
// last reply at should have changed
|
|
require.Equal(t, replyPost1.CreateAt, thread.LastReplyAt)
|
|
|
|
// Delete the last reply
|
|
err = ss.Post().Delete(rctx, replyPost1.Id, model.GetMillis(), "")
|
|
require.NoError(t, err)
|
|
|
|
thread, err = ss.Thread().Get(rootPost1.Id)
|
|
require.NoError(t, err)
|
|
// last reply at should be 0
|
|
require.Equal(t, int64(0), thread.LastReplyAt)
|
|
})
|
|
|
|
t.Run("thread with file attachments", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
// Create a root post
|
|
rootPost1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel.Id,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Create another root post
|
|
rootPost2, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel.Id,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Reply to first root post with file attachments
|
|
replyPost1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: rootPost1.ChannelId,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
RootId: rootPost1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
file11, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
Id: model.NewId(),
|
|
PostId: replyPost1.Id,
|
|
CreatorId: replyPost1.UserId,
|
|
Path: "file1.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
file12, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
Id: model.NewId(),
|
|
PostId: replyPost1.Id,
|
|
CreatorId: replyPost1.UserId,
|
|
Path: "file2.png",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Reply to second root post with file attachments
|
|
replyPost2, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: rootPost2.ChannelId,
|
|
UserId: model.NewId(),
|
|
Message: NewTestID(),
|
|
RootId: rootPost2.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
file21, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
Id: model.NewId(),
|
|
PostId: replyPost2.Id,
|
|
CreatorId: replyPost2.UserId,
|
|
Path: "file1.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Delete the first root post
|
|
err = ss.Post().Delete(rctx, rootPost1.Id, model.GetMillis(), "")
|
|
require.NoError(t, err)
|
|
|
|
// Verify the reply post's files are deleted
|
|
_, err = ss.FileInfo().Get(file11.Id)
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
require.IsType(t, &store.ErrNotFound{}, err)
|
|
_, err = ss.FileInfo().Get(file12.Id)
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
require.IsType(t, &store.ErrNotFound{}, err)
|
|
|
|
// Verify the other reply post's files are NOT deleted
|
|
_, err = ss.FileInfo().Get(file21.Id)
|
|
require.NoError(t, err, "Not deleted id should have succeeded")
|
|
})
|
|
}
|
|
|
|
func testPostStorePermDelete1Level(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channel.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2.RootId = o1.Id
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
r1 := &model.Reaction{}
|
|
r1.ChannelId = o1.ChannelId
|
|
r1.UserId = o2.UserId
|
|
r1.PostId = o1.Id
|
|
r1.EmojiName = "smile"
|
|
r1, err = ss.Reaction().Save(r1)
|
|
require.NoError(t, err)
|
|
|
|
r2 := &model.Reaction{}
|
|
r2.ChannelId = o1.ChannelId
|
|
r2.UserId = o1.UserId
|
|
r2.PostId = o2.Id
|
|
r2.EmojiName = "wave"
|
|
_, err = ss.Reaction().Save(r2)
|
|
require.NoError(t, err)
|
|
|
|
r3 := &model.Reaction{}
|
|
r3.ChannelId = o1.ChannelId
|
|
r3.UserId = model.NewId()
|
|
r3.PostId = o1.Id
|
|
r3.EmojiName = "sad"
|
|
r3, err = ss.Reaction().Save(r3)
|
|
require.NoError(t, err)
|
|
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName2",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = channel2.Id
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
r4 := &model.Reaction{}
|
|
r4.ChannelId = channel2.Id
|
|
r4.UserId = model.NewId()
|
|
r4.PostId = o3.Id
|
|
r4.EmojiName = "angry"
|
|
_, err = ss.Reaction().Save(r4)
|
|
require.NoError(t, err)
|
|
|
|
channel3, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName3",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o4 := &model.Post{}
|
|
o4.ChannelId = channel3.Id
|
|
o4.RootId = o1.Id
|
|
o4.UserId = o2.UserId
|
|
o4.Message = NewTestID()
|
|
o4, err = ss.Post().Save(rctx, o4)
|
|
require.NoError(t, err)
|
|
|
|
o5 := &model.Post{}
|
|
o5.ChannelId = o3.ChannelId
|
|
o5.UserId = model.NewId()
|
|
o5.Message = NewTestID()
|
|
o5, err = ss.Post().Save(rctx, o5)
|
|
require.NoError(t, err)
|
|
|
|
o6 := &model.Post{}
|
|
o6.ChannelId = o3.ChannelId
|
|
o6.RootId = o5.Id
|
|
o6.UserId = model.NewId()
|
|
o6.Message = NewTestID()
|
|
o6, err = ss.Post().Save(rctx, o6)
|
|
require.NoError(t, err)
|
|
|
|
var thread *model.Thread
|
|
thread, err = ss.Thread().Get(o1.Id)
|
|
require.NoError(t, err)
|
|
|
|
require.EqualValues(t, 2, thread.ReplyCount)
|
|
require.EqualValues(t, model.StringArray{o2.UserId}, thread.Participants)
|
|
|
|
err2 := ss.Post().PermanentDeleteByUser(rctx, o2.UserId)
|
|
require.NoError(t, err2)
|
|
|
|
thread, err = ss.Thread().Get(o1.Id)
|
|
require.NoError(t, err)
|
|
|
|
require.EqualValues(t, 0, thread.ReplyCount)
|
|
require.EqualValues(t, model.StringArray{}, thread.Participants)
|
|
|
|
_, err = ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err, "Deleted id shouldn't have failed")
|
|
|
|
reactions, err := ss.Reaction().GetForPost(o1.Id, false)
|
|
require.NoError(t, err, "Reactions should exist")
|
|
require.Equal(t, 2, len(reactions))
|
|
emojis := []string{r1.EmojiName, r3.EmojiName}
|
|
for _, reaction := range reactions {
|
|
require.Contains(t, emojis, reaction.EmojiName)
|
|
}
|
|
|
|
_, err = ss.Post().Get(rctx, o2.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
|
|
reactions, err = ss.Reaction().GetForPost(o2.Id, false)
|
|
require.NoError(t, err, "No error for not found")
|
|
require.Equal(t, 0, len(reactions))
|
|
|
|
thread, err = ss.Thread().Get(o5.Id)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, thread)
|
|
|
|
err = ss.Post().PermanentDeleteByChannel(rctx, o3.ChannelId)
|
|
require.NoError(t, err)
|
|
|
|
thread, err = ss.Thread().Get(o5.Id)
|
|
require.NoError(t, err)
|
|
require.Nil(t, thread)
|
|
|
|
reactions, err = ss.Reaction().GetForPost(o3.Id, false)
|
|
require.NoError(t, err, "No error for not found")
|
|
require.Equal(t, 0, len(reactions))
|
|
|
|
reactions, err = ss.Reaction().GetForPost(o1.Id, false)
|
|
require.NoError(t, err, "Reactions should exist")
|
|
require.Equal(t, 2, len(reactions))
|
|
for _, reaction := range reactions {
|
|
require.Contains(t, emojis, reaction.EmojiName)
|
|
}
|
|
|
|
_, err = ss.Post().Get(rctx, o3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
|
|
_, err = ss.Post().Get(rctx, o4.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
|
|
_, err = ss.Post().Get(rctx, o5.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
|
|
_, err = ss.Post().Get(rctx, o6.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
}
|
|
|
|
func testPostStorePermDelete1Level2(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channel1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2.RootId = o1.Id
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName2",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = channel2.Id
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
err2 := ss.Post().PermanentDeleteByUser(rctx, o1.UserId)
|
|
require.NoError(t, err2)
|
|
|
|
_, err = ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
|
|
_, err = ss.Post().Get(rctx, o2.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Deleted id should have failed")
|
|
|
|
_, err = ss.Post().Get(rctx, o3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err, "Deleted id should have failed")
|
|
}
|
|
|
|
func testPostStorePermDeleteLimitExceeded(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
const maxPosts = 10000
|
|
teamID := model.NewId()
|
|
userID := model.NewId()
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "10KPosts",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
for range maxPosts + 100 {
|
|
post := &model.Post{
|
|
ChannelId: channel.Id,
|
|
UserId: userID,
|
|
Message: NewTestID(),
|
|
}
|
|
_, err = ss.Post().Save(rctx, post)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
err = ss.Post().PermanentDeleteByUser(rctx, userID)
|
|
var errLimitExceeded *store.ErrLimitExceeded
|
|
require.ErrorAs(t, err, &errLimitExceeded)
|
|
}
|
|
|
|
func testPostStoreGetWithChildren(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channel1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2.RootId = o1.Id
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = o1.ChannelId
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3.RootId = o1.Id
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
pl, err := ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, pl.Posts, 3, "invalid returned post")
|
|
|
|
dErr := ss.Post().Delete(rctx, o3.Id, model.GetMillis(), "")
|
|
require.NoError(t, dErr)
|
|
|
|
pl, err = ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, pl.Posts, 2, "invalid returned post")
|
|
|
|
dErr = ss.Post().Delete(rctx, o2.Id, model.GetMillis(), "")
|
|
require.NoError(t, dErr)
|
|
|
|
pl, err = ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, pl.Posts, 1, "invalid returned post")
|
|
}
|
|
|
|
func testPostStoreGetPostsWithDetails(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channel1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2.RootId = o1.Id
|
|
_, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o2a := &model.Post{}
|
|
o2a.ChannelId = o1.ChannelId
|
|
o2a.UserId = model.NewId()
|
|
o2a.Message = NewTestID()
|
|
o2a.RootId = o1.Id
|
|
o2a, err = ss.Post().Save(rctx, o2a)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = o1.ChannelId
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3.RootId = o1.Id
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o4 := &model.Post{}
|
|
o4.ChannelId = o1.ChannelId
|
|
o4.UserId = model.NewId()
|
|
o4.Message = NewTestID()
|
|
o4, err = ss.Post().Save(rctx, o4)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o5 := &model.Post{}
|
|
o5.ChannelId = o1.ChannelId
|
|
o5.UserId = model.NewId()
|
|
o5.Message = NewTestID()
|
|
o5.RootId = o4.Id
|
|
o5, err = ss.Post().Save(rctx, o5)
|
|
require.NoError(t, err)
|
|
|
|
r1, err := ss.Post().GetPosts(rctx, model.GetPostsOptions{ChannelId: o1.ChannelId, Page: 0, PerPage: 4}, false, map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, r1.Order[0], o5.Id, "invalid order")
|
|
require.Equal(t, r1.Order[1], o4.Id, "invalid order")
|
|
require.Equal(t, r1.Order[2], o3.Id, "invalid order")
|
|
require.Equal(t, r1.Order[3], o2a.Id, "invalid order")
|
|
|
|
//the last 4, + o1 (o2a and o3's parent) + o2 (in same thread as o2a and o3)
|
|
require.Len(t, r1.Posts, 6, "wrong size")
|
|
|
|
require.Equal(t, r1.Posts[o1.Id].Message, o1.Message, "Missing parent")
|
|
|
|
r2, err := ss.Post().GetPosts(rctx, model.GetPostsOptions{ChannelId: o1.ChannelId, Page: 0, PerPage: 4}, false, map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, r2.Order[0], o5.Id, "invalid order")
|
|
require.Equal(t, r2.Order[1], o4.Id, "invalid order")
|
|
require.Equal(t, r2.Order[2], o3.Id, "invalid order")
|
|
require.Equal(t, r2.Order[3], o2a.Id, "invalid order")
|
|
|
|
//the last 4, + o1 (o2a and o3's parent) + o2 (in same thread as o2a and o3)
|
|
require.Len(t, r2.Posts, 6, "wrong size")
|
|
|
|
require.Equal(t, r2.Posts[o1.Id].Message, o1.Message, "Missing parent")
|
|
|
|
// Run once to fill cache
|
|
_, err = ss.Post().GetPosts(rctx, model.GetPostsOptions{ChannelId: o1.ChannelId, Page: 0, PerPage: 30}, false, map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
o6 := &model.Post{}
|
|
o6.ChannelId = o1.ChannelId
|
|
o6.UserId = model.NewId()
|
|
o6.Message = NewTestID()
|
|
_, err = ss.Post().Save(rctx, o6)
|
|
require.NoError(t, err)
|
|
|
|
r3, err := ss.Post().GetPosts(rctx, model.GetPostsOptions{ChannelId: o1.ChannelId, Page: 0, PerPage: 30}, false, map[string]bool{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 7, len(r3.Order))
|
|
}
|
|
|
|
func testPostStoreGetPostsBeforeAfter(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("without threads", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channelID := channel1.Id
|
|
userID := model.NewId()
|
|
|
|
var posts []*model.Post
|
|
for range 10 {
|
|
post, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
posts = append(posts, post)
|
|
|
|
time.Sleep(time.Millisecond)
|
|
}
|
|
|
|
t.Run("should return error if negative Page/PerPage options are passed", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsAfter(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: posts[0].Id, Page: 0, PerPage: -1}, map[string]bool{})
|
|
assert.Nil(t, postList)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, &store.ErrInvalidInput{}, err)
|
|
|
|
postList, err = ss.Post().GetPostsAfter(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: posts[0].Id, Page: -1, PerPage: 10}, map[string]bool{})
|
|
assert.Nil(t, postList)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, &store.ErrInvalidInput{}, err)
|
|
})
|
|
|
|
t.Run("should not return anything before the first post", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsBefore(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: posts[0].Id, Page: 0, PerPage: 10}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{}, postList.Order)
|
|
assert.Equal(t, map[string]*model.Post{}, postList.Posts)
|
|
})
|
|
|
|
t.Run("should return posts before a post", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsBefore(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: posts[5].Id, Page: 0, PerPage: 10}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{posts[4].Id, posts[3].Id, posts[2].Id, posts[1].Id, posts[0].Id}, postList.Order)
|
|
assert.Equal(t, map[string]*model.Post{
|
|
posts[0].Id: posts[0],
|
|
posts[1].Id: posts[1],
|
|
posts[2].Id: posts[2],
|
|
posts[3].Id: posts[3],
|
|
posts[4].Id: posts[4],
|
|
}, postList.Posts)
|
|
})
|
|
|
|
t.Run("should limit posts before", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsBefore(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: posts[5].Id, PerPage: 2}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{posts[4].Id, posts[3].Id}, postList.Order)
|
|
assert.Equal(t, map[string]*model.Post{
|
|
posts[3].Id: posts[3],
|
|
posts[4].Id: posts[4],
|
|
}, postList.Posts)
|
|
})
|
|
|
|
t.Run("should not return anything after the last post", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsAfter(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: posts[len(posts)-1].Id, PerPage: 10}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{}, postList.Order)
|
|
assert.Equal(t, map[string]*model.Post{}, postList.Posts)
|
|
})
|
|
|
|
t.Run("should return posts after a post", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsAfter(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: posts[5].Id, PerPage: 10}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{posts[9].Id, posts[8].Id, posts[7].Id, posts[6].Id}, postList.Order)
|
|
assert.Equal(t, map[string]*model.Post{
|
|
posts[6].Id: posts[6],
|
|
posts[7].Id: posts[7],
|
|
posts[8].Id: posts[8],
|
|
posts[9].Id: posts[9],
|
|
}, postList.Posts)
|
|
})
|
|
|
|
t.Run("should limit posts after", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsAfter(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: posts[5].Id, PerPage: 2}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{posts[7].Id, posts[6].Id}, postList.Order)
|
|
assert.Equal(t, map[string]*model.Post{
|
|
posts[6].Id: posts[6],
|
|
posts[7].Id: posts[7],
|
|
}, postList.Posts)
|
|
})
|
|
})
|
|
t.Run("with threads", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channelID := channel1.Id
|
|
userID := model.NewId()
|
|
|
|
// This creates a series of posts that looks like:
|
|
// post1
|
|
// post2
|
|
// post3 (in response to post1)
|
|
// post4 (in response to post2)
|
|
// post5
|
|
// post6 (in response to post2)
|
|
|
|
post1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
post1.ReplyCount = 1
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post2, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
post2.ReplyCount = 2
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post3, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
RootId: post1.Id,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
post3.ReplyCount = 1
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post4, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
RootId: post2.Id,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
post4.ReplyCount = 2
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post5, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post6, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
RootId: post2.Id,
|
|
Message: "message",
|
|
})
|
|
post6.ReplyCount = 2
|
|
require.NoError(t, err)
|
|
|
|
// Adding a post to a thread changes the UpdateAt timestamp of the parent post
|
|
post1.UpdateAt = post3.UpdateAt
|
|
post2.UpdateAt = post6.UpdateAt
|
|
|
|
t.Run("should return each post and thread before a post", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsBefore(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: post4.Id, PerPage: 2}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{post3.Id, post2.Id}, postList.Order)
|
|
assert.Equal(t, map[string]*model.Post{
|
|
post1.Id: post1,
|
|
post2.Id: post2,
|
|
post3.Id: post3,
|
|
post4.Id: post4,
|
|
post6.Id: post6,
|
|
}, postList.Posts)
|
|
})
|
|
|
|
t.Run("should return each post and the root of each thread after a post", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsAfter(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: post4.Id, PerPage: 2}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{post6.Id, post5.Id}, postList.Order)
|
|
assert.Equal(t, map[string]*model.Post{
|
|
post2.Id: post2,
|
|
post4.Id: post4,
|
|
post5.Id: post5,
|
|
post6.Id: post6,
|
|
}, postList.Posts)
|
|
})
|
|
})
|
|
t.Run("with threads (skipFetchThreads)", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channelID := channel1.Id
|
|
userID := model.NewId()
|
|
|
|
// This creates a series of posts that looks like:
|
|
// post1
|
|
// post2
|
|
// post3 (in response to post1)
|
|
// post4 (in response to post2)
|
|
// post5
|
|
// post6 (in response to post2)
|
|
|
|
post1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "post1",
|
|
})
|
|
require.NoError(t, err)
|
|
post1.ReplyCount = 1
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post2, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "post2",
|
|
})
|
|
require.NoError(t, err)
|
|
post2.ReplyCount = 2
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post3, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
RootId: post1.Id,
|
|
Message: "post3",
|
|
})
|
|
require.NoError(t, err)
|
|
post3.ReplyCount = 1
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post4, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
RootId: post2.Id,
|
|
Message: "post4",
|
|
})
|
|
require.NoError(t, err)
|
|
post4.ReplyCount = 2
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post5, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "post5",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post6, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
RootId: post2.Id,
|
|
Message: "post6",
|
|
})
|
|
post6.ReplyCount = 2
|
|
require.NoError(t, err)
|
|
|
|
// Adding a post to a thread changes the UpdateAt timestamp of the parent post
|
|
post1.UpdateAt = post3.UpdateAt
|
|
post2.UpdateAt = post6.UpdateAt
|
|
|
|
t.Run("should return each post and thread before a post", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsBefore(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: post4.Id, PerPage: 2, SkipFetchThreads: true}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{post3.Id, post2.Id}, postList.Order)
|
|
assert.Equal(t, map[string]*model.Post{
|
|
post1.Id: post1,
|
|
post2.Id: post2,
|
|
post3.Id: post3,
|
|
}, postList.Posts)
|
|
})
|
|
|
|
t.Run("should return each post and thread before a post with limit", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsBefore(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: post4.Id, PerPage: 1, SkipFetchThreads: true}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{post3.Id}, postList.Order)
|
|
assert.Equal(t, map[string]*model.Post{
|
|
post1.Id: post1,
|
|
post3.Id: post3,
|
|
}, postList.Posts)
|
|
})
|
|
|
|
t.Run("should return each post and the root of each thread after a post", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsAfter(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: post4.Id, PerPage: 2, SkipFetchThreads: true}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{post6.Id, post5.Id}, postList.Order)
|
|
assert.Equal(t, map[string]*model.Post{
|
|
post2.Id: post2,
|
|
post5.Id: post5,
|
|
post6.Id: post6,
|
|
}, postList.Posts)
|
|
})
|
|
})
|
|
t.Run("with threads (collapsedThreads)", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channelID := channel1.Id
|
|
userID := model.NewId()
|
|
|
|
// This creates a series of posts that looks like:
|
|
// post1
|
|
// post2
|
|
// post3 (in response to post1)
|
|
// post4 (in response to post2)
|
|
// post5
|
|
// post6 (in response to post2)
|
|
|
|
post1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "post1",
|
|
})
|
|
require.NoError(t, err)
|
|
post1.ReplyCount = 1
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post2, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "post2",
|
|
})
|
|
require.NoError(t, err)
|
|
post2.ReplyCount = 2
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post3, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
RootId: post1.Id,
|
|
Message: "post3",
|
|
})
|
|
require.NoError(t, err)
|
|
post3.ReplyCount = 1
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post4, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
RootId: post2.Id,
|
|
Message: "post4",
|
|
})
|
|
require.NoError(t, err)
|
|
post4.ReplyCount = 2
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post5, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "post5",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post6, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
RootId: post2.Id,
|
|
Message: "post6",
|
|
})
|
|
post6.ReplyCount = 2
|
|
require.NoError(t, err)
|
|
|
|
// Adding a post to a thread changes the UpdateAt timestamp of the parent post
|
|
post1.UpdateAt = post3.UpdateAt
|
|
post2.UpdateAt = post6.UpdateAt
|
|
|
|
t.Run("should return each root post before a post", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsBefore(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: post4.Id, PerPage: 2, CollapsedThreads: true}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{post2.Id, post1.Id}, postList.Order)
|
|
})
|
|
|
|
t.Run("should return each root post before a post with limit", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsBefore(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: post4.Id, PerPage: 1, CollapsedThreads: true}, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{post2.Id}, postList.Order)
|
|
})
|
|
|
|
t.Run("should return each root after a post", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPostsAfter(rctx, model.GetPostsOptions{ChannelId: channelID, PostId: post4.Id, PerPage: 2, CollapsedThreads: true}, map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{post5.Id}, postList.Order)
|
|
})
|
|
})
|
|
}
|
|
|
|
func testPostStoreGetPostsSince(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("should return posts created after the given time", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channelID := channel1.Id
|
|
userID := model.NewId()
|
|
|
|
post1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
_, err = ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post3, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post4, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post5, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
RootId: post3.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post6, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
RootId: post1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
postList, err := ss.Post().GetPostsSince(rctx, model.GetPostsSinceOptions{ChannelId: channelID, Time: post3.CreateAt}, false, map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{
|
|
post6.Id,
|
|
post5.Id,
|
|
post4.Id,
|
|
post3.Id,
|
|
post1.Id,
|
|
}, postList.Order)
|
|
|
|
assert.Len(t, postList.Posts, 5)
|
|
assert.NotNil(t, postList.Posts[post1.Id], "should return the parent post")
|
|
assert.NotNil(t, postList.Posts[post3.Id])
|
|
assert.NotNil(t, postList.Posts[post4.Id])
|
|
assert.NotNil(t, postList.Posts[post5.Id])
|
|
assert.NotNil(t, postList.Posts[post6.Id])
|
|
})
|
|
|
|
t.Run("should return empty list when nothing has changed", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channelID := channel1.Id
|
|
userID := model.NewId()
|
|
|
|
post1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
postList, err := ss.Post().GetPostsSince(rctx, model.GetPostsSinceOptions{ChannelId: channelID, Time: post1.CreateAt}, false, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{}, postList.Order)
|
|
assert.Empty(t, postList.Posts)
|
|
})
|
|
|
|
t.Run("should not cache a timestamp of 0 when nothing has changed", func(t *testing.T) {
|
|
ss.Post().ClearCaches()
|
|
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channelID := channel1.Id
|
|
userID := model.NewId()
|
|
|
|
post1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
// Make a request that returns no results
|
|
postList, err := ss.Post().GetPostsSince(rctx, model.GetPostsSinceOptions{ChannelId: channelID, Time: post1.CreateAt}, true, map[string]bool{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, model.NewPostList(), postList)
|
|
|
|
// And then ensure that it doesn't cause future requests to also return no results
|
|
postList, err = ss.Post().GetPostsSince(rctx, model.GetPostsSinceOptions{ChannelId: channelID, Time: post1.CreateAt - 1}, true, map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{post1.Id}, postList.Order)
|
|
|
|
assert.Len(t, postList.Posts, 1)
|
|
assert.NotNil(t, postList.Posts[post1.Id])
|
|
})
|
|
}
|
|
|
|
func testPostStoreGetPosts(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channelID := channel1.Id
|
|
userID := model.NewId()
|
|
|
|
post1, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post2, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post3, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post4, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post5, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
RootId: post3.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post6, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
RootId: post1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
t.Run("should return the last posts created in a channel", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPosts(rctx, model.GetPostsOptions{ChannelId: channelID, Page: 0, PerPage: 30, SkipFetchThreads: false}, false, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{
|
|
post6.Id,
|
|
post5.Id,
|
|
post4.Id,
|
|
post3.Id,
|
|
post2.Id,
|
|
post1.Id,
|
|
}, postList.Order)
|
|
|
|
assert.Len(t, postList.Posts, 6)
|
|
assert.NotNil(t, postList.Posts[post1.Id])
|
|
assert.NotNil(t, postList.Posts[post2.Id])
|
|
assert.NotNil(t, postList.Posts[post3.Id])
|
|
assert.NotNil(t, postList.Posts[post4.Id])
|
|
assert.NotNil(t, postList.Posts[post5.Id])
|
|
assert.NotNil(t, postList.Posts[post6.Id])
|
|
})
|
|
|
|
t.Run("should return the last posts created in a channel and the threads and the reply count must be 0", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPosts(rctx, model.GetPostsOptions{ChannelId: channelID, Page: 0, PerPage: 2, SkipFetchThreads: false}, false, map[string]bool{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{
|
|
post6.Id,
|
|
post5.Id,
|
|
}, postList.Order)
|
|
|
|
assert.Len(t, postList.Posts, 4)
|
|
require.NotNil(t, postList.Posts[post1.Id])
|
|
require.NotNil(t, postList.Posts[post3.Id])
|
|
require.NotNil(t, postList.Posts[post5.Id])
|
|
require.NotNil(t, postList.Posts[post6.Id])
|
|
assert.Equal(t, int64(0), postList.Posts[post1.Id].ReplyCount)
|
|
assert.Equal(t, int64(0), postList.Posts[post3.Id].ReplyCount)
|
|
assert.Equal(t, int64(0), postList.Posts[post5.Id].ReplyCount)
|
|
assert.Equal(t, int64(0), postList.Posts[post6.Id].ReplyCount)
|
|
})
|
|
|
|
t.Run("should return the last posts created in a channel without the threads and the reply count must be correct", func(t *testing.T) {
|
|
postList, err := ss.Post().GetPosts(rctx, model.GetPostsOptions{ChannelId: channelID, Page: 0, PerPage: 2, SkipFetchThreads: true}, false, map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{
|
|
post6.Id,
|
|
post5.Id,
|
|
}, postList.Order)
|
|
|
|
assert.Len(t, postList.Posts, 4)
|
|
assert.NotNil(t, postList.Posts[post5.Id])
|
|
assert.NotNil(t, postList.Posts[post6.Id])
|
|
assert.Equal(t, int64(1), postList.Posts[post5.Id].ReplyCount)
|
|
assert.Equal(t, int64(1), postList.Posts[post6.Id].ReplyCount)
|
|
})
|
|
|
|
t.Run("should return all posts in a channel included deleted posts", func(t *testing.T) {
|
|
err := ss.Post().Delete(rctx, post1.Id, 1, userID)
|
|
require.NoError(t, err)
|
|
|
|
postList, err := ss.Post().GetPosts(rctx, model.GetPostsOptions{ChannelId: channelID, Page: 0, PerPage: 30, SkipFetchThreads: false, IncludeDeleted: true}, false, map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{
|
|
post6.Id,
|
|
post5.Id,
|
|
post4.Id,
|
|
post3.Id,
|
|
post2.Id,
|
|
post1.Id,
|
|
}, postList.Order)
|
|
|
|
assert.Len(t, postList.Posts, 6)
|
|
assert.NotNil(t, postList.Posts[post1.Id])
|
|
assert.NotNil(t, postList.Posts[post2.Id])
|
|
assert.NotNil(t, postList.Posts[post3.Id])
|
|
assert.NotNil(t, postList.Posts[post4.Id])
|
|
assert.NotNil(t, postList.Posts[post5.Id])
|
|
assert.NotNil(t, postList.Posts[post6.Id])
|
|
})
|
|
|
|
t.Run("should return all posts in a channel included deleted posts without threads", func(t *testing.T) {
|
|
err := ss.Post().Delete(rctx, post5.Id, 1, userID)
|
|
require.NoError(t, err)
|
|
|
|
postList, err := ss.Post().GetPosts(rctx, model.GetPostsOptions{ChannelId: channelID, Page: 0, PerPage: 30, SkipFetchThreads: true, IncludeDeleted: true}, false, map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{
|
|
post6.Id,
|
|
post5.Id,
|
|
post4.Id,
|
|
post3.Id,
|
|
post2.Id,
|
|
post1.Id,
|
|
}, postList.Order)
|
|
|
|
assert.Len(t, postList.Posts, 6)
|
|
assert.NotNil(t, postList.Posts[post5.Id])
|
|
assert.NotNil(t, postList.Posts[post6.Id])
|
|
assert.Equal(t, int64(1), postList.Posts[post5.Id].ReplyCount)
|
|
assert.Equal(t, int64(1), postList.Posts[post6.Id].ReplyCount)
|
|
})
|
|
|
|
t.Run("should return the lasts posts created in channel without include deleted posts", func(t *testing.T) {
|
|
err := ss.Post().Delete(rctx, post6.Id, 1, userID)
|
|
require.NoError(t, err)
|
|
|
|
postList, err := ss.Post().GetPosts(rctx, model.GetPostsOptions{ChannelId: channelID, Page: 0, PerPage: 30, SkipFetchThreads: true, IncludeDeleted: false}, false, map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{
|
|
post4.Id,
|
|
post3.Id,
|
|
post2.Id,
|
|
}, postList.Order)
|
|
|
|
assert.Len(t, postList.Posts, 3)
|
|
assert.NotNil(t, postList.Posts[post2.Id])
|
|
assert.NotNil(t, postList.Posts[post3.Id])
|
|
assert.NotNil(t, postList.Posts[post4.Id])
|
|
})
|
|
}
|
|
|
|
func testPostStoreGetPostBeforeAfter(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channelID := channel1.Id
|
|
|
|
o0 := &model.Post{}
|
|
o0.ChannelId = channelID
|
|
o0.UserId = model.NewId()
|
|
o0.Message = NewTestID()
|
|
_, err = ss.Post().Save(rctx, o0)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channelID
|
|
o1.Type = model.PostTypeJoinChannel
|
|
o1.UserId = model.NewId()
|
|
o1.Message = "system_join_channel message"
|
|
_, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o0a := &model.Post{}
|
|
o0a.ChannelId = channelID
|
|
o0a.UserId = model.NewId()
|
|
o0a.Message = NewTestID()
|
|
o0a.RootId = o1.Id
|
|
_, err = ss.Post().Save(rctx, o0a)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o0b := &model.Post{}
|
|
o0b.ChannelId = channelID
|
|
o0b.UserId = model.NewId()
|
|
o0b.Message = "deleted message"
|
|
o0b.RootId = o1.Id
|
|
o0b.DeleteAt = 1
|
|
_, err = ss.Post().Save(rctx, o0b)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName2",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
otherChannelPost := &model.Post{}
|
|
otherChannelPost.ChannelId = channel2.Id
|
|
otherChannelPost.UserId = model.NewId()
|
|
otherChannelPost.Message = NewTestID()
|
|
_, err = ss.Post().Save(rctx, otherChannelPost)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = channelID
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
_, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o2a := &model.Post{}
|
|
o2a.ChannelId = channelID
|
|
o2a.UserId = model.NewId()
|
|
o2a.Message = NewTestID()
|
|
o2a.RootId = o2.Id
|
|
_, err = ss.Post().Save(rctx, o2a)
|
|
require.NoError(t, err)
|
|
|
|
rPostID1, err := ss.Post().GetPostIdBeforeTime(channelID, o0a.CreateAt, false)
|
|
require.Equal(t, rPostID1, o1.Id, "should return before post o1")
|
|
require.NoError(t, err)
|
|
|
|
rPostID1, err = ss.Post().GetPostIdAfterTime(channelID, o0b.CreateAt, false)
|
|
require.Equal(t, rPostID1, o2.Id, "should return before post o2")
|
|
require.NoError(t, err)
|
|
|
|
rPost1, err := ss.Post().GetPostAfterTime(channelID, o0b.CreateAt, false)
|
|
require.Equal(t, rPost1.Id, o2.Id, "should return before post o2")
|
|
require.NoError(t, err)
|
|
|
|
rPostID2, err := ss.Post().GetPostIdBeforeTime(channelID, o0.CreateAt, false)
|
|
require.Empty(t, rPostID2, "should return no post")
|
|
require.NoError(t, err)
|
|
|
|
rPostID2, err = ss.Post().GetPostIdAfterTime(channelID, o0.CreateAt, false)
|
|
require.Equal(t, rPostID2, o1.Id, "should return before post o1")
|
|
require.NoError(t, err)
|
|
|
|
rPost2, err := ss.Post().GetPostAfterTime(channelID, o0.CreateAt, false)
|
|
require.Equal(t, rPost2.Id, o1.Id, "should return before post o1")
|
|
require.NoError(t, err)
|
|
|
|
rPostID3, err := ss.Post().GetPostIdBeforeTime(channelID, o2a.CreateAt, false)
|
|
require.Equal(t, rPostID3, o2.Id, "should return before post o2")
|
|
require.NoError(t, err)
|
|
|
|
rPostID3, err = ss.Post().GetPostIdAfterTime(channelID, o2a.CreateAt, false)
|
|
require.Empty(t, rPostID3, "should return no post")
|
|
require.NoError(t, err)
|
|
|
|
rPost3, err := ss.Post().GetPostAfterTime(channelID, o2a.CreateAt, false)
|
|
require.Empty(t, rPost3.Id, "should return no post")
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func testUserCountsWithPostsByDay(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t1 := &model.Team{}
|
|
t1.DisplayName = "DisplayName"
|
|
t1.Name = NewTestID()
|
|
t1.Email = MakeEmail()
|
|
t1.Type = model.TeamOpen
|
|
t1, err := ss.Team().Save(t1)
|
|
require.NoError(t, err)
|
|
|
|
c1 := &model.Channel{}
|
|
c1.TeamId = t1.Id
|
|
c1.DisplayName = "Channel2"
|
|
c1.Name = NewTestID()
|
|
c1.Type = model.ChannelTypeOpen
|
|
c1, nErr := ss.Channel().Save(rctx, c1, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = c1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.CreateAt = utils.MillisFromTime(utils.Yesterday())
|
|
o1.Message = NewTestID()
|
|
o1, nErr = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, nErr)
|
|
|
|
o1a := &model.Post{}
|
|
o1a.ChannelId = c1.Id
|
|
o1a.UserId = model.NewId()
|
|
o1a.CreateAt = o1.CreateAt
|
|
o1a.Message = NewTestID()
|
|
_, nErr = ss.Post().Save(rctx, o1a)
|
|
require.NoError(t, nErr)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = c1.Id
|
|
o2.UserId = model.NewId()
|
|
o2.CreateAt = o1.CreateAt - (1000 * 60 * 60 * 24)
|
|
o2.Message = NewTestID()
|
|
o2, nErr = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, nErr)
|
|
|
|
o2a := &model.Post{}
|
|
o2a.ChannelId = c1.Id
|
|
o2a.UserId = o2.UserId
|
|
o2a.CreateAt = o1.CreateAt - (1000 * 60 * 60 * 24)
|
|
o2a.Message = NewTestID()
|
|
_, nErr = ss.Post().Save(rctx, o2a)
|
|
require.NoError(t, nErr)
|
|
|
|
r1, err := ss.Post().AnalyticsUserCountsWithPostsByDay(t1.Id)
|
|
require.NoError(t, err)
|
|
|
|
row1 := r1[0]
|
|
require.Equal(t, float64(2), row1.Value, "wrong value")
|
|
|
|
row2 := r1[1]
|
|
require.Equal(t, float64(1), row2.Value, "wrong value")
|
|
}
|
|
|
|
func testPostCountsByDay(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t1 := &model.Team{}
|
|
t1.DisplayName = "DisplayName"
|
|
t1.Name = NewTestID()
|
|
t1.Email = MakeEmail()
|
|
t1.Type = model.TeamOpen
|
|
t1, err := ss.Team().Save(t1)
|
|
require.NoError(t, err)
|
|
|
|
c1 := &model.Channel{}
|
|
c1.TeamId = t1.Id
|
|
c1.DisplayName = "Channel2"
|
|
c1.Name = NewTestID()
|
|
c1.Type = model.ChannelTypeOpen
|
|
c1, nErr := ss.Channel().Save(rctx, c1, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = c1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.CreateAt = utils.MillisFromTime(utils.Yesterday())
|
|
o1.Message = NewTestID()
|
|
o1.Hashtags = "hashtag"
|
|
o1, nErr = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, nErr)
|
|
|
|
o1a := &model.Post{}
|
|
o1a.ChannelId = c1.Id
|
|
o1a.UserId = model.NewId()
|
|
o1a.CreateAt = o1.CreateAt
|
|
o1a.Message = NewTestID()
|
|
o1a.FileIds = []string{"fileId1"}
|
|
_, nErr = ss.Post().Save(rctx, o1a)
|
|
require.NoError(t, nErr)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = c1.Id
|
|
o2.UserId = model.NewId()
|
|
o2.CreateAt = o1.CreateAt - (1000 * 60 * 60 * 24 * 2)
|
|
o2.Message = NewTestID()
|
|
o2.Filenames = []string{"filename1"}
|
|
o2, nErr = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, nErr)
|
|
|
|
o2a := &model.Post{}
|
|
o2a.ChannelId = c1.Id
|
|
o2a.UserId = o2.UserId
|
|
o2a.CreateAt = o1.CreateAt - (1000 * 60 * 60 * 24 * 2)
|
|
o2a.Message = NewTestID()
|
|
o2a.Hashtags = "hashtag"
|
|
o2a.FileIds = []string{"fileId2"}
|
|
_, nErr = ss.Post().Save(rctx, o2a)
|
|
require.NoError(t, nErr)
|
|
|
|
bot1 := &model.Bot{
|
|
Username: "username",
|
|
Description: "a bot",
|
|
OwnerId: model.NewId(),
|
|
UserId: model.NewId(),
|
|
}
|
|
_, nErr = ss.Bot().Save(bot1)
|
|
require.NoError(t, nErr)
|
|
|
|
b1 := &model.Post{}
|
|
b1.Message = "bot message one"
|
|
b1.ChannelId = c1.Id
|
|
b1.UserId = bot1.UserId
|
|
b1.CreateAt = utils.MillisFromTime(utils.Yesterday())
|
|
_, nErr = ss.Post().Save(rctx, b1)
|
|
require.NoError(t, nErr)
|
|
|
|
b1a := &model.Post{}
|
|
b1a.Message = "bot message two"
|
|
b1a.ChannelId = c1.Id
|
|
b1a.UserId = bot1.UserId
|
|
b1a.CreateAt = utils.MillisFromTime(utils.Yesterday()) - (1000 * 60 * 60 * 24 * 2)
|
|
_, nErr = ss.Post().Save(rctx, b1a)
|
|
require.NoError(t, nErr)
|
|
|
|
require.NoError(t, ss.Post().RefreshPostStats())
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
// summary of posts
|
|
// yesterday - 2 non-bot user posts, 1 bot user post
|
|
// 3 days ago - 2 non-bot user posts, 1 bot user post
|
|
|
|
// last 31 days, all users (including bots)
|
|
postCountsOptions := &model.AnalyticsPostCountsOptions{TeamId: t1.Id, BotsOnly: false, YesterdayOnly: false}
|
|
r1, err := ss.Post().AnalyticsPostCountsByDay(postCountsOptions)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, float64(3), r1[0].Value)
|
|
assert.Equal(t, float64(3), r1[1].Value)
|
|
assert.Equal(t, utils.Yesterday().Format("2006-01-02"), r1[0].Name)
|
|
assert.Equal(t, utils.Yesterday().Add(-48*time.Hour).Format("2006-01-02"), r1[1].Name)
|
|
|
|
// last 31 days, bots only
|
|
postCountsOptions = &model.AnalyticsPostCountsOptions{TeamId: t1.Id, BotsOnly: true, YesterdayOnly: false}
|
|
r1, err = ss.Post().AnalyticsPostCountsByDay(postCountsOptions)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, float64(1), r1[0].Value)
|
|
assert.Equal(t, float64(1), r1[1].Value)
|
|
assert.Equal(t, utils.Yesterday().Format("2006-01-02"), r1[0].Name)
|
|
assert.Equal(t, utils.Yesterday().Add(-48*time.Hour).Format("2006-01-02"), r1[1].Name)
|
|
|
|
// yesterday only, all users (including bots)
|
|
postCountsOptions = &model.AnalyticsPostCountsOptions{TeamId: t1.Id, BotsOnly: false, YesterdayOnly: true}
|
|
r1, err = ss.Post().AnalyticsPostCountsByDay(postCountsOptions)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, float64(3), r1[0].Value)
|
|
assert.Equal(t, utils.Yesterday().Format("2006-01-02"), r1[0].Name)
|
|
|
|
// yesterday only, bots only
|
|
postCountsOptions = &model.AnalyticsPostCountsOptions{TeamId: t1.Id, BotsOnly: true, YesterdayOnly: true}
|
|
r1, err = ss.Post().AnalyticsPostCountsByDay(postCountsOptions)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, float64(1), r1[0].Value)
|
|
assert.Equal(t, utils.Yesterday().Format("2006-01-02"), r1[0].Name)
|
|
}
|
|
|
|
func testPostCounts(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
now := time.Now()
|
|
twentyMinAgo := now.Add(-20 * time.Minute).UnixMilli()
|
|
fifteenMinAgo := now.Add(-15 * time.Minute).UnixMilli()
|
|
tenMinAgo := now.Add(-10 * time.Minute).UnixMilli()
|
|
|
|
t1 := &model.Team{}
|
|
t1.DisplayName = "DisplayName"
|
|
t1.Name = NewTestID()
|
|
t1.Email = MakeEmail()
|
|
t1.Type = model.TeamOpen
|
|
t1, err := ss.Team().Save(t1)
|
|
require.NoError(t, err)
|
|
|
|
c1 := &model.Channel{}
|
|
c1.TeamId = t1.Id
|
|
c1.DisplayName = "Channel2"
|
|
c1.Name = NewTestID()
|
|
c1.Type = model.ChannelTypeOpen
|
|
c1, nErr := ss.Channel().Save(rctx, c1, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
// system post
|
|
p1 := &model.Post{}
|
|
p1.Type = "system_add_to_channel"
|
|
p1.ChannelId = c1.Id
|
|
p1.UserId = model.NewId()
|
|
p1.Message = NewTestID()
|
|
p1.CreateAt = twentyMinAgo
|
|
p1.UpdateAt = twentyMinAgo
|
|
_, nErr = ss.Post().Save(rctx, p1)
|
|
require.NoError(t, nErr)
|
|
|
|
p2 := &model.Post{}
|
|
p2.ChannelId = c1.Id
|
|
p2.UserId = model.NewId()
|
|
p2.Message = NewTestID()
|
|
p2.Hashtags = "hashtag"
|
|
p2.CreateAt = twentyMinAgo
|
|
p2.UpdateAt = twentyMinAgo
|
|
p2, nErr = ss.Post().Save(rctx, p2)
|
|
require.NoError(t, nErr)
|
|
|
|
p3 := &model.Post{}
|
|
p3.ChannelId = c1.Id
|
|
p3.UserId = model.NewId()
|
|
p3.Message = NewTestID()
|
|
p3.FileIds = []string{"fileId1"}
|
|
p3.CreateAt = twentyMinAgo
|
|
p3.UpdateAt = twentyMinAgo
|
|
_, nErr = ss.Post().Save(rctx, p3)
|
|
require.NoError(t, nErr)
|
|
|
|
p4 := &model.Post{}
|
|
p4.ChannelId = c1.Id
|
|
p4.UserId = model.NewId()
|
|
p4.Message = NewTestID()
|
|
p4.Filenames = []string{"filename1"}
|
|
p4.CreateAt = tenMinAgo
|
|
p4.UpdateAt = tenMinAgo
|
|
p4, nErr = ss.Post().Save(rctx, p4)
|
|
require.NoError(t, nErr)
|
|
|
|
p5 := &model.Post{}
|
|
p5.ChannelId = c1.Id
|
|
p5.UserId = p4.UserId
|
|
p5.Message = NewTestID()
|
|
p5.Hashtags = "hashtag"
|
|
p5.FileIds = []string{"fileId2"}
|
|
p5.CreateAt = tenMinAgo
|
|
p5.UpdateAt = tenMinAgo
|
|
_, nErr = ss.Post().Save(rctx, p5)
|
|
require.NoError(t, nErr)
|
|
|
|
bot1 := &model.Bot{
|
|
Username: "username",
|
|
Description: "a bot",
|
|
OwnerId: model.NewId(),
|
|
UserId: model.NewId(),
|
|
}
|
|
_, nErr = ss.Bot().Save(bot1)
|
|
require.NoError(t, nErr)
|
|
|
|
p6 := &model.Post{}
|
|
p6.Message = "bot message one"
|
|
p6.ChannelId = c1.Id
|
|
p6.UserId = bot1.UserId
|
|
p6.CreateAt = twentyMinAgo
|
|
p6.UpdateAt = twentyMinAgo
|
|
_, nErr = ss.Post().Save(rctx, p6)
|
|
require.NoError(t, nErr)
|
|
|
|
p7 := &model.Post{}
|
|
p7.Message = "bot message two"
|
|
p7.ChannelId = c1.Id
|
|
p7.UserId = bot1.UserId
|
|
p7.CreateAt = tenMinAgo
|
|
p7.UpdateAt = tenMinAgo
|
|
_, nErr = ss.Post().Save(rctx, p7)
|
|
require.NoError(t, nErr)
|
|
|
|
require.NoError(t, ss.Post().RefreshPostStats())
|
|
|
|
// total across all teams
|
|
c, err := ss.Post().AnalyticsPostCount(&model.PostCountOptions{})
|
|
require.NoError(t, err)
|
|
assert.GreaterOrEqual(t, c, int64(7))
|
|
|
|
// total for single team
|
|
c, err = ss.Post().AnalyticsPostCount(&model.PostCountOptions{TeamId: t1.Id})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(7), c)
|
|
|
|
c, err = ss.Post().AnalyticsPostCountByTeam(t1.Id)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(7), c)
|
|
|
|
// with files
|
|
c, err = ss.Post().AnalyticsPostCount(&model.PostCountOptions{TeamId: t1.Id, MustHaveFile: true})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(3), c)
|
|
|
|
// with hashtags
|
|
c, err = ss.Post().AnalyticsPostCount(&model.PostCountOptions{TeamId: t1.Id, MustHaveHashtag: true})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(2), c)
|
|
|
|
// with hashtags and files
|
|
c, err = ss.Post().AnalyticsPostCount(&model.PostCountOptions{TeamId: t1.Id, MustHaveFile: true, MustHaveHashtag: true})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(1), c)
|
|
|
|
// excluding system posts
|
|
c, err = ss.Post().AnalyticsPostCount(&model.PostCountOptions{TeamId: t1.Id, ExcludeSystemPosts: true})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(6), c)
|
|
|
|
// before update_at time
|
|
c, err = ss.Post().AnalyticsPostCount(&model.PostCountOptions{TeamId: t1.Id, SinceUpdateAt: fifteenMinAgo})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(3), c)
|
|
|
|
// equal to update_at time
|
|
c, err = ss.Post().AnalyticsPostCount(&model.PostCountOptions{TeamId: t1.Id, SinceUpdateAt: tenMinAgo})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(3), c)
|
|
|
|
// since update_at and since post id
|
|
tenMinAgoIDs := []string{p4.Id, p5.Id, p7.Id}
|
|
sort.Strings(tenMinAgoIDs)
|
|
c, err = ss.Post().AnalyticsPostCount(&model.PostCountOptions{TeamId: t1.Id, SinceUpdateAt: tenMinAgo, SincePostID: tenMinAgoIDs[0]})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(2), c)
|
|
|
|
// delete 1 post
|
|
err = ss.Post().Delete(rctx, p2.Id, 1, p2.UserId)
|
|
require.NoError(t, err)
|
|
|
|
// total for single team with the deleted post excluded
|
|
c, err = ss.Post().AnalyticsPostCount(&model.PostCountOptions{TeamId: t1.Id, ExcludeDeleted: true})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(6), c)
|
|
|
|
// total users only posts for single team with the deleted post excluded
|
|
c, err = ss.Post().AnalyticsPostCount(&model.PostCountOptions{TeamId: t1.Id, ExcludeDeleted: true, UsersPostsOnly: true})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(3), c)
|
|
}
|
|
|
|
func testPostStoreGetFlaggedPostsForTeam(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
c1 := &model.Channel{}
|
|
c1.TeamId = model.NewId()
|
|
c1.DisplayName = "Channel1"
|
|
c1.Name = NewTestID()
|
|
c1.Type = model.ChannelTypeOpen
|
|
c1, err := ss.Channel().Save(rctx, c1, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = c1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = o1.ChannelId
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3.DeleteAt = 1
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
m0 := &model.ChannelMember{}
|
|
m0.ChannelId = c1.Id
|
|
m0.UserId = o1.UserId
|
|
m0.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
_, err = ss.Channel().SaveMember(rctx, m0)
|
|
require.NoError(t, err)
|
|
|
|
teamID := model.NewId()
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName2",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o4 := &model.Post{}
|
|
o4.ChannelId = channel2.Id
|
|
o4.UserId = model.NewId()
|
|
o4.Message = NewTestID()
|
|
o4, err = ss.Post().Save(rctx, o4)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
c2 := &model.Channel{}
|
|
c2.DisplayName = "DMChannel1"
|
|
c2.Name = model.GetDMNameFromIds(NewTestID(), NewTestID())
|
|
c2.Type = model.ChannelTypeDirect
|
|
|
|
m1 := &model.ChannelMember{}
|
|
m1.ChannelId = c2.Id
|
|
m1.UserId = o1.UserId
|
|
m1.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
|
|
m2 := &model.ChannelMember{}
|
|
m2.ChannelId = c2.Id
|
|
m2.UserId = model.NewId()
|
|
m2.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
|
|
c2, err = ss.Channel().SaveDirectChannel(rctx, c2, m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
o5 := &model.Post{}
|
|
o5.ChannelId = c2.Id
|
|
o5.UserId = m2.UserId
|
|
o5.Message = NewTestID()
|
|
o5, err = ss.Post().Save(rctx, o5)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
// Post on channel where user is not a member
|
|
channel3, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName3",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o6 := &model.Post{}
|
|
o6.ChannelId = channel3.Id
|
|
o6.UserId = m2.UserId
|
|
o6.Message = NewTestID()
|
|
o6, err = ss.Post().Save(rctx, o6)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
r1, err := ss.Post().GetFlaggedPosts(o1.ChannelId, 0, 2)
|
|
require.NoError(t, err)
|
|
|
|
require.Empty(t, r1.Order, "should be empty")
|
|
|
|
preferences := model.Preferences{
|
|
{
|
|
UserId: o1.UserId,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: o1.Id,
|
|
Value: "true",
|
|
},
|
|
}
|
|
|
|
err = ss.Preference().Save(preferences)
|
|
require.NoError(t, err)
|
|
|
|
r2, err := ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, r2.Order, 1, "should have 1 post")
|
|
|
|
preferences = model.Preferences{
|
|
{
|
|
UserId: o1.UserId,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: o2.Id,
|
|
Value: "true",
|
|
},
|
|
}
|
|
|
|
err = ss.Preference().Save(preferences)
|
|
require.NoError(t, err)
|
|
|
|
r3, err := ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 1)
|
|
require.NoError(t, err)
|
|
require.Len(t, r3.Order, 1, "should have 1 post")
|
|
|
|
r3, err = ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 1, 1)
|
|
require.NoError(t, err)
|
|
require.Len(t, r3.Order, 1, "should have 1 post")
|
|
|
|
r3, err = ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 1000, 10)
|
|
require.NoError(t, err)
|
|
require.Empty(t, r3.Order, "should be empty")
|
|
|
|
r4, err := ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, r4.Order, 2, "should have 2 posts")
|
|
|
|
preferences = model.Preferences{
|
|
{
|
|
UserId: o1.UserId,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: o3.Id,
|
|
Value: "true",
|
|
},
|
|
}
|
|
|
|
err = ss.Preference().Save(preferences)
|
|
require.NoError(t, err)
|
|
|
|
r4, err = ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, r4.Order, 2, "should have 2 posts")
|
|
|
|
preferences = model.Preferences{
|
|
{
|
|
UserId: o1.UserId,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: o4.Id,
|
|
Value: "true",
|
|
},
|
|
}
|
|
err = ss.Preference().Save(preferences)
|
|
require.NoError(t, err)
|
|
|
|
r4, err = ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, r4.Order, 2, "should have 2 posts")
|
|
|
|
r4, err = ss.Post().GetFlaggedPostsForTeam(o1.UserId, model.NewId(), 0, 2)
|
|
require.NoError(t, err)
|
|
require.Empty(t, r4.Order, "should have 0 posts")
|
|
|
|
preferences = model.Preferences{
|
|
{
|
|
UserId: o1.UserId,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: o5.Id,
|
|
Value: "true",
|
|
},
|
|
}
|
|
err = ss.Preference().Save(preferences)
|
|
require.NoError(t, err)
|
|
|
|
r4, err = ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 10)
|
|
require.NoError(t, err)
|
|
require.Len(t, r4.Order, 3, "should have 3 posts")
|
|
|
|
preferences = model.Preferences{
|
|
{
|
|
UserId: o1.UserId,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: o6.Id,
|
|
Value: "true",
|
|
},
|
|
}
|
|
err = ss.Preference().Save(preferences)
|
|
require.NoError(t, err)
|
|
|
|
r4, err = ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 10)
|
|
require.NoError(t, err)
|
|
require.Len(t, r4.Order, 3, "should have 3 posts")
|
|
|
|
// Manually truncate Channels table until testlib can handle cleanups
|
|
s.GetMaster().Exec("TRUNCATE Channels")
|
|
}
|
|
|
|
func testPostStoreGetFlaggedPosts(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
c1 := &model.Channel{}
|
|
c1.TeamId = model.NewId()
|
|
c1.DisplayName = "Channel1"
|
|
c1.Name = NewTestID()
|
|
c1.Type = model.ChannelTypeOpen
|
|
c1, err := ss.Channel().Save(rctx, c1, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = c1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = o1.ChannelId
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3.DeleteAt = 1
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
// Post on channel where user is not a member
|
|
teamID := model.NewId()
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName2",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o4 := &model.Post{}
|
|
o4.ChannelId = channel2.Id
|
|
o4.UserId = model.NewId()
|
|
o4.Message = NewTestID()
|
|
o4, err = ss.Post().Save(rctx, o4)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
m0 := &model.ChannelMember{}
|
|
m0.ChannelId = o1.ChannelId
|
|
m0.UserId = o1.UserId
|
|
m0.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
_, err = ss.Channel().SaveMember(rctx, m0)
|
|
require.NoError(t, err)
|
|
|
|
r1, err := ss.Post().GetFlaggedPosts(o1.UserId, 0, 2)
|
|
require.NoError(t, err)
|
|
require.Empty(t, r1.Order, "should be empty")
|
|
|
|
preferences := model.Preferences{
|
|
{
|
|
UserId: o1.UserId,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: o1.Id,
|
|
Value: "true",
|
|
},
|
|
}
|
|
|
|
nErr := ss.Preference().Save(preferences)
|
|
require.NoError(t, nErr)
|
|
|
|
r2, err := ss.Post().GetFlaggedPosts(o1.UserId, 0, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, r2.Order, 1, "should have 1 post")
|
|
|
|
preferences = model.Preferences{
|
|
{
|
|
UserId: o1.UserId,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: o2.Id,
|
|
Value: "true",
|
|
},
|
|
}
|
|
|
|
nErr = ss.Preference().Save(preferences)
|
|
require.NoError(t, nErr)
|
|
|
|
r3, err := ss.Post().GetFlaggedPosts(o1.UserId, 0, 1)
|
|
require.NoError(t, err)
|
|
require.Len(t, r3.Order, 1, "should have 1 post")
|
|
|
|
r3, err = ss.Post().GetFlaggedPosts(o1.UserId, 1, 1)
|
|
require.NoError(t, err)
|
|
require.Len(t, r3.Order, 1, "should have 1 post")
|
|
|
|
r3, err = ss.Post().GetFlaggedPosts(o1.UserId, 1000, 10)
|
|
require.NoError(t, err)
|
|
require.Empty(t, r3.Order, "should be empty")
|
|
|
|
r4, err := ss.Post().GetFlaggedPosts(o1.UserId, 0, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, r4.Order, 2, "should have 2 posts")
|
|
|
|
preferences = model.Preferences{
|
|
{
|
|
UserId: o1.UserId,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: o3.Id,
|
|
Value: "true",
|
|
},
|
|
}
|
|
|
|
nErr = ss.Preference().Save(preferences)
|
|
require.NoError(t, nErr)
|
|
|
|
r4, err = ss.Post().GetFlaggedPosts(o1.UserId, 0, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, r4.Order, 2, "should have 2 posts")
|
|
|
|
preferences = model.Preferences{
|
|
{
|
|
UserId: o1.UserId,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: o4.Id,
|
|
Value: "true",
|
|
},
|
|
}
|
|
|
|
nErr = ss.Preference().Save(preferences)
|
|
require.NoError(t, nErr)
|
|
|
|
r4, err = ss.Post().GetFlaggedPosts(o1.UserId, 0, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, r4.Order, 2, "should have 2 posts")
|
|
}
|
|
|
|
func testPostStoreGetFlaggedPostsForChannel(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
c1 := &model.Channel{}
|
|
c1.TeamId = model.NewId()
|
|
c1.DisplayName = "Channel1"
|
|
c1.Name = NewTestID()
|
|
c1.Type = model.ChannelTypeOpen
|
|
c1, err := ss.Channel().Save(rctx, c1, -1)
|
|
require.NoError(t, err)
|
|
|
|
c2 := &model.Channel{}
|
|
c2.TeamId = model.NewId()
|
|
c2.DisplayName = "Channel2"
|
|
c2.Name = NewTestID()
|
|
c2.Type = model.ChannelTypeOpen
|
|
c2, err = ss.Channel().Save(rctx, c2, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = c1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
// deleted post
|
|
teamID := model.NewId()
|
|
channel3, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName3",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = channel3.Id
|
|
o3.UserId = o1.ChannelId
|
|
o3.Message = NewTestID()
|
|
o3.DeleteAt = 1
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o4 := &model.Post{}
|
|
o4.ChannelId = c2.Id
|
|
o4.UserId = model.NewId()
|
|
o4.Message = NewTestID()
|
|
o4, err = ss.Post().Save(rctx, o4)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
// Post on channel where user is not a member
|
|
channel4, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName4",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o5 := &model.Post{}
|
|
o5.ChannelId = channel4.Id
|
|
o5.UserId = model.NewId()
|
|
o5.Message = NewTestID()
|
|
o5, err = ss.Post().Save(rctx, o5)
|
|
require.NoError(t, err)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
m1 := &model.ChannelMember{}
|
|
m1.ChannelId = o1.ChannelId
|
|
m1.UserId = o1.UserId
|
|
m1.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
_, err = ss.Channel().SaveMember(rctx, m1)
|
|
require.NoError(t, err)
|
|
|
|
m2 := &model.ChannelMember{}
|
|
m2.ChannelId = o4.ChannelId
|
|
m2.UserId = o1.UserId
|
|
m2.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
_, err = ss.Channel().SaveMember(rctx, m2)
|
|
require.NoError(t, err)
|
|
|
|
r, err := ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 0, 10)
|
|
require.NoError(t, err)
|
|
require.Empty(t, r.Order, "should be empty")
|
|
|
|
preference := model.Preference{
|
|
UserId: o1.UserId,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: o1.Id,
|
|
Value: "true",
|
|
}
|
|
|
|
nErr := ss.Preference().Save(model.Preferences{preference})
|
|
require.NoError(t, nErr)
|
|
|
|
r, err = ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 0, 10)
|
|
require.NoError(t, err)
|
|
require.Len(t, r.Order, 1, "should have 1 post")
|
|
|
|
preference.Name = o2.Id
|
|
nErr = ss.Preference().Save(model.Preferences{preference})
|
|
require.NoError(t, nErr)
|
|
|
|
preference.Name = o3.Id
|
|
nErr = ss.Preference().Save(model.Preferences{preference})
|
|
require.NoError(t, nErr)
|
|
|
|
r, err = ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 0, 1)
|
|
require.NoError(t, err)
|
|
require.Len(t, r.Order, 1, "should have 1 post")
|
|
|
|
r, err = ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 1, 1)
|
|
require.NoError(t, err)
|
|
require.Len(t, r.Order, 1, "should have 1 post")
|
|
|
|
r, err = ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 1000, 10)
|
|
require.NoError(t, err)
|
|
require.Empty(t, r.Order, "should be empty")
|
|
|
|
r, err = ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 0, 10)
|
|
require.NoError(t, err)
|
|
require.Len(t, r.Order, 2, "should have 2 posts")
|
|
|
|
preference.Name = o4.Id
|
|
nErr = ss.Preference().Save(model.Preferences{preference})
|
|
require.NoError(t, nErr)
|
|
|
|
r, err = ss.Post().GetFlaggedPostsForChannel(o1.UserId, o4.ChannelId, 0, 10)
|
|
require.NoError(t, err)
|
|
require.Len(t, r.Order, 1, "should have 1 posts")
|
|
|
|
preference.Name = o5.Id
|
|
nErr = ss.Preference().Save(model.Preferences{preference})
|
|
require.NoError(t, nErr)
|
|
|
|
r, err = ss.Post().GetFlaggedPostsForChannel(o1.UserId, o5.ChannelId, 0, 10)
|
|
require.NoError(t, err)
|
|
require.Len(t, r.Order, 0, "should have 0 posts")
|
|
}
|
|
|
|
func testPostStoreGetPostsCreatedAt(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
createTime := model.GetMillis() + 1
|
|
|
|
o0 := &model.Post{}
|
|
o0.ChannelId = channel1.Id
|
|
o0.UserId = model.NewId()
|
|
o0.Message = NewTestID()
|
|
o0.CreateAt = createTime
|
|
o0, err = ss.Post().Save(rctx, o0)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = o0.ChannelId
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1.CreateAt = createTime
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2.RootId = o1.Id
|
|
o2.CreateAt = createTime + 1
|
|
_, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName2",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = channel2.Id
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3.CreateAt = createTime
|
|
_, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
r1, _ := ss.Post().GetPostsCreatedAt(o1.ChannelId, createTime)
|
|
assert.Equal(t, 2, len(r1))
|
|
}
|
|
|
|
func testPostStoreOverwriteMultiple(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channel1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2.RootId = o1.Id
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = o1.ChannelId
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName2",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o4, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel2.Id,
|
|
UserId: model.NewId(),
|
|
Message: model.NewId(),
|
|
Filenames: []string{"test"},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
channel3, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName3",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
o5, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel3.Id,
|
|
UserId: model.NewId(),
|
|
Message: model.NewId(),
|
|
Filenames: []string{"test2", "test3"},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
r1, err := ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro1 := r1.Posts[o1.Id]
|
|
|
|
r2, err := ss.Post().Get(rctx, o2.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro2 := r2.Posts[o2.Id]
|
|
|
|
r3, err := ss.Post().Get(rctx, o3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro3 := r3.Posts[o3.Id]
|
|
|
|
r4, err := ss.Post().Get(rctx, o4.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro4 := r4.Posts[o4.Id]
|
|
|
|
r5, err := ss.Post().Get(rctx, o5.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro5 := r5.Posts[o5.Id]
|
|
|
|
require.Equal(t, ro1.Message, o1.Message, "Failed to save/get")
|
|
require.Equal(t, ro2.Message, o2.Message, "Failed to save/get")
|
|
require.Equal(t, ro3.Message, o3.Message, "Failed to save/get")
|
|
require.Equal(t, ro4.Message, o4.Message, "Failed to save/get")
|
|
require.Equal(t, ro4.Filenames, o4.Filenames, "Failed to save/get")
|
|
require.Equal(t, ro5.Message, o5.Message, "Failed to save/get")
|
|
require.Equal(t, ro5.Filenames, o5.Filenames, "Failed to save/get")
|
|
|
|
t.Run("overwrite changing message", func(t *testing.T) {
|
|
o1a := ro1.Clone()
|
|
o1a.Message = ro1.Message + "BBBBBBBBBB"
|
|
|
|
o2a := ro2.Clone()
|
|
o2a.Message = ro2.Message + "DDDDDDD"
|
|
|
|
o3a := ro3.Clone()
|
|
o3a.Message = ro3.Message + "WWWWWWW"
|
|
|
|
_, errIdx, err := ss.Post().OverwriteMultiple(rctx, []*model.Post{o1a, o2a, o3a})
|
|
require.NoError(t, err)
|
|
require.Equal(t, -1, errIdx)
|
|
|
|
r1, nErr := ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, nErr)
|
|
ro1a := r1.Posts[o1.Id]
|
|
|
|
r2, nErr = ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, nErr)
|
|
ro2a := r2.Posts[o2.Id]
|
|
|
|
r3, nErr = ss.Post().Get(rctx, o3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, nErr)
|
|
ro3a := r3.Posts[o3.Id]
|
|
|
|
assert.Equal(t, ro1a.Message, o1a.Message, "Failed to overwrite/get")
|
|
assert.Equal(t, ro2a.Message, o2a.Message, "Failed to overwrite/get")
|
|
assert.Equal(t, ro3a.Message, o3a.Message, "Failed to overwrite/get")
|
|
})
|
|
|
|
t.Run("overwrite clearing filenames", func(t *testing.T) {
|
|
o4a := ro4.Clone()
|
|
o4a.Filenames = []string{}
|
|
o4a.FileIds = []string{model.NewId()}
|
|
|
|
o5a := ro5.Clone()
|
|
o5a.Filenames = []string{}
|
|
o5a.FileIds = []string{}
|
|
|
|
_, errIdx, err := ss.Post().OverwriteMultiple(rctx, []*model.Post{o4a, o5a})
|
|
require.NoError(t, err)
|
|
require.Equal(t, -1, errIdx)
|
|
|
|
r4, nErr := ss.Post().Get(rctx, o4.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, nErr)
|
|
ro4a := r4.Posts[o4.Id]
|
|
|
|
r5, nErr = ss.Post().Get(rctx, o5.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, nErr)
|
|
ro5a := r5.Posts[o5.Id]
|
|
|
|
require.Empty(t, ro4a.Filenames, "Failed to clear Filenames")
|
|
require.Len(t, ro4a.FileIds, 1, "Failed to set FileIds")
|
|
require.Empty(t, ro5a.Filenames, "Failed to clear Filenames")
|
|
require.Empty(t, ro5a.FileIds, "Failed to set FileIds")
|
|
})
|
|
}
|
|
|
|
func testPostStoreOverwrite(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channel1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2.RootId = o1.Id
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = o1.ChannelId
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
channel2, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName2",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
o4, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel2.Id,
|
|
UserId: model.NewId(),
|
|
Message: model.NewId(),
|
|
Filenames: []string{"test"},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
r1, err := ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro1 := r1.Posts[o1.Id]
|
|
|
|
r2, err := ss.Post().Get(rctx, o2.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro2 := r2.Posts[o2.Id]
|
|
|
|
r3, err := ss.Post().Get(rctx, o3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro3 := r3.Posts[o3.Id]
|
|
|
|
r4, err := ss.Post().Get(rctx, o4.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro4 := r4.Posts[o4.Id]
|
|
|
|
require.Equal(t, ro1.Message, o1.Message, "Failed to save/get")
|
|
require.Equal(t, ro2.Message, o2.Message, "Failed to save/get")
|
|
require.Equal(t, ro3.Message, o3.Message, "Failed to save/get")
|
|
require.Equal(t, ro4.Message, o4.Message, "Failed to save/get")
|
|
|
|
t.Run("overwrite changing message", func(t *testing.T) {
|
|
o1a := ro1.Clone()
|
|
o1a.Message = ro1.Message + "BBBBBBBBBB"
|
|
_, err = ss.Post().Overwrite(rctx, o1a)
|
|
require.NoError(t, err)
|
|
|
|
o2a := ro2.Clone()
|
|
o2a.Message = ro2.Message + "DDDDDDD"
|
|
_, err = ss.Post().Overwrite(rctx, o2a)
|
|
require.NoError(t, err)
|
|
|
|
o3a := ro3.Clone()
|
|
o3a.Message = ro3.Message + "WWWWWWW"
|
|
_, err = ss.Post().Overwrite(rctx, o3a)
|
|
require.NoError(t, err)
|
|
|
|
r1, err = ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro1a := r1.Posts[o1.Id]
|
|
|
|
r2, err = ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro2a := r2.Posts[o2.Id]
|
|
|
|
r3, err = ss.Post().Get(rctx, o3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro3a := r3.Posts[o3.Id]
|
|
|
|
assert.Equal(t, ro1a.Message, o1a.Message, "Failed to overwrite/get")
|
|
assert.Equal(t, ro2a.Message, o2a.Message, "Failed to overwrite/get")
|
|
assert.Equal(t, ro3a.Message, o3a.Message, "Failed to overwrite/get")
|
|
})
|
|
|
|
t.Run("overwrite clearing filenames", func(t *testing.T) {
|
|
o4a := ro4.Clone()
|
|
o4a.Filenames = []string{}
|
|
o4a.FileIds = []string{model.NewId()}
|
|
_, err = ss.Post().Overwrite(rctx, o4a)
|
|
require.NoError(t, err)
|
|
|
|
r4, err = ss.Post().Get(rctx, o4.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
|
|
ro4a := r4.Posts[o4.Id]
|
|
require.Empty(t, ro4a.Filenames, "Failed to clear Filenames")
|
|
require.Len(t, ro4a.FileIds, 1, "Failed to set FileIds")
|
|
})
|
|
}
|
|
|
|
func testPostStoreGetPostsByIds(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channel1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = o1.ChannelId
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
r1, err := ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro1 := r1.Posts[o1.Id]
|
|
|
|
r2, err := ss.Post().Get(rctx, o2.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro2 := r2.Posts[o2.Id]
|
|
|
|
r3, err := ss.Post().Get(rctx, o3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err)
|
|
ro3 := r3.Posts[o3.Id]
|
|
|
|
postIds := []string{
|
|
ro1.Id,
|
|
ro2.Id,
|
|
ro3.Id,
|
|
}
|
|
|
|
posts, err := ss.Post().GetPostsByIds(postIds)
|
|
require.NoError(t, err)
|
|
require.Len(t, posts, 3, "Expected 3 posts in results. Got %v", len(posts))
|
|
|
|
err = ss.Post().Delete(rctx, ro1.Id, model.GetMillis(), "")
|
|
require.NoError(t, err)
|
|
|
|
posts, err = ss.Post().GetPostsByIds(postIds)
|
|
require.NoError(t, err)
|
|
require.Len(t, posts, 3, "Expected 3 posts in results. Got %v", len(posts))
|
|
}
|
|
|
|
func testPostStoreGetPostsBatchForIndexing(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
c1 := &model.Channel{}
|
|
c1.TeamId = model.NewId()
|
|
c1.DisplayName = "Channel1"
|
|
c1.Name = NewTestID()
|
|
c1.Type = model.ChannelTypeOpen
|
|
c1, _ = ss.Channel().Save(rctx, c1, -1)
|
|
|
|
c2 := &model.Channel{}
|
|
c2.TeamId = model.NewId()
|
|
c2.DisplayName = "Channel2"
|
|
c2.Name = NewTestID()
|
|
c2.Type = model.ChannelTypeOpen
|
|
c2, _ = ss.Channel().Save(rctx, c2, -1)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = c1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1, err := ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = c2.Id
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
_, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = c1.Id
|
|
o3.UserId = model.NewId()
|
|
o3.RootId = o1.Id
|
|
o3.Message = NewTestID()
|
|
_, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
// Getting all
|
|
r, err := ss.Post().GetPostsBatchForIndexing(o1.CreateAt-1, "", 100)
|
|
require.NoError(t, err)
|
|
require.Len(t, r, 3, "Expected 3 posts in results. Got %v", len(r))
|
|
|
|
// Testing pagination
|
|
r, err = ss.Post().GetPostsBatchForIndexing(o1.CreateAt-1, "", 1)
|
|
require.NoError(t, err)
|
|
require.Len(t, r, 1, "Expected 1 post in results. Got %v", len(r))
|
|
|
|
r, err = ss.Post().GetPostsBatchForIndexing(r[0].CreateAt, r[0].Id, 1)
|
|
require.NoError(t, err)
|
|
require.Len(t, r, 1, "Expected 1 post in results. Got %v", len(r))
|
|
|
|
r, err = ss.Post().GetPostsBatchForIndexing(r[0].CreateAt, r[0].Id, 1)
|
|
require.NoError(t, err)
|
|
require.Len(t, r, 1, "Expected 1 post in results. Got %v", len(r))
|
|
|
|
r, err = ss.Post().GetPostsBatchForIndexing(r[0].CreateAt, r[0].Id, 1)
|
|
require.NoError(t, err)
|
|
require.Len(t, r, 0, "Expected 0 post in results. Got %v", len(r))
|
|
}
|
|
|
|
func testPostStorePermanentDeleteBatch(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
team, err := ss.Team().Save(&model.Team{
|
|
DisplayName: "DisplayName",
|
|
Name: "team" + model.NewId(),
|
|
Email: MakeEmail(),
|
|
Type: model.TeamOpen,
|
|
})
|
|
require.NoError(t, err)
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: team.Id,
|
|
DisplayName: "DisplayName",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = channel.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1.CreateAt = 1000
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = channel.Id
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2.CreateAt = 1000
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = channel.Id
|
|
o3.UserId = model.NewId()
|
|
o3.Message = NewTestID()
|
|
o3.CreateAt = 100000
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
deleted, _, err := ss.Post().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: 0,
|
|
GlobalPolicyEndTime: 2000,
|
|
Limit: 1000,
|
|
}, model.RetentionPolicyCursor{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(2), deleted)
|
|
|
|
_, err = ss.Post().Get(rctx, o1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Should have not found post 1 after purge")
|
|
|
|
_, err = ss.Post().Get(rctx, o2.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Should have not found post 2 after purge")
|
|
|
|
_, err = ss.Post().Get(rctx, o3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err, "Should have found post 3 after purge")
|
|
|
|
rows, err := ss.RetentionPolicy().GetIdsForDeletionByTableName("Posts", 1000)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(rows))
|
|
require.Equal(t, 2, len(rows[0].Ids))
|
|
// Clean up retention ids table
|
|
deleted, err = ss.Reaction().DeleteOrphanedRowsByIds(rows[0])
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), deleted)
|
|
|
|
t.Run("with pagination", func(t *testing.T) {
|
|
for range 3 {
|
|
_, err = ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel.Id,
|
|
UserId: model.NewId(),
|
|
Message: "message",
|
|
CreateAt: 1,
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
cursor := model.RetentionPolicyCursor{}
|
|
|
|
deleted, cursor, err = ss.Post().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: 0,
|
|
GlobalPolicyEndTime: 2,
|
|
Limit: 2,
|
|
}, cursor)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(2), deleted)
|
|
|
|
rows, err = ss.RetentionPolicy().GetIdsForDeletionByTableName("Posts", 1000)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(rows))
|
|
require.Equal(t, 2, len(rows[0].Ids))
|
|
|
|
// Clean up retention ids table
|
|
deleted, err = ss.Reaction().DeleteOrphanedRowsByIds(rows[0])
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), deleted)
|
|
|
|
deleted, _, err = ss.Post().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: 0,
|
|
GlobalPolicyEndTime: 2,
|
|
Limit: 2,
|
|
}, cursor)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(1), deleted)
|
|
|
|
rows, err = ss.RetentionPolicy().GetIdsForDeletionByTableName("Posts", 1000)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(rows))
|
|
require.Equal(t, 1, len(rows[0].Ids))
|
|
|
|
// Clean up retention ids table
|
|
deleted, err = ss.Reaction().DeleteOrphanedRowsByIds(rows[0])
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), deleted)
|
|
})
|
|
|
|
t.Run("with data retention policies", func(t *testing.T) {
|
|
channelPolicy, err2 := ss.RetentionPolicy().Save(&model.RetentionPolicyWithTeamAndChannelIDs{
|
|
RetentionPolicy: model.RetentionPolicy{
|
|
DisplayName: "DisplayName",
|
|
PostDurationDays: model.NewPointer(int64(30)),
|
|
},
|
|
ChannelIDs: []string{channel.Id},
|
|
})
|
|
require.NoError(t, err2)
|
|
post := &model.Post{
|
|
ChannelId: channel.Id,
|
|
UserId: model.NewId(),
|
|
Message: "message",
|
|
CreateAt: 1,
|
|
}
|
|
post, err2 = ss.Post().Save(rctx, post)
|
|
require.NoError(t, err2)
|
|
|
|
_, _, err2 = ss.Post().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: 0,
|
|
GlobalPolicyEndTime: 2000,
|
|
Limit: 1000,
|
|
}, model.RetentionPolicyCursor{})
|
|
require.NoError(t, err2)
|
|
_, err2 = ss.Post().Get(rctx, post.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err2, "global policy should have been ignored due to granular policy")
|
|
|
|
nowMillis := post.CreateAt + *channelPolicy.PostDurationDays*model.DayInMilliseconds + 1
|
|
_, _, err2 = ss.Post().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: nowMillis,
|
|
GlobalPolicyEndTime: 0,
|
|
Limit: 1000,
|
|
}, model.RetentionPolicyCursor{})
|
|
require.NoError(t, err2)
|
|
_, err2 = ss.Post().Get(rctx, post.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err2, "post should have been deleted by channel policy")
|
|
|
|
// Create a team policy which is stricter than the channel policy
|
|
teamPolicy, err2 := ss.RetentionPolicy().Save(&model.RetentionPolicyWithTeamAndChannelIDs{
|
|
RetentionPolicy: model.RetentionPolicy{
|
|
DisplayName: "DisplayName",
|
|
PostDurationDays: model.NewPointer(int64(20)),
|
|
},
|
|
TeamIDs: []string{team.Id},
|
|
})
|
|
require.NoError(t, err2)
|
|
post.Id = ""
|
|
post, err2 = ss.Post().Save(rctx, post)
|
|
require.NoError(t, err2)
|
|
|
|
nowMillis = post.CreateAt + *teamPolicy.PostDurationDays*model.DayInMilliseconds + 1
|
|
_, _, err2 = ss.Post().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: nowMillis,
|
|
GlobalPolicyEndTime: 0,
|
|
Limit: 1000,
|
|
}, model.RetentionPolicyCursor{})
|
|
require.NoError(t, err2)
|
|
_, err2 = ss.Post().Get(rctx, post.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err2, "channel policy should have overridden team policy")
|
|
|
|
// Delete channel policy and re-run team policy
|
|
err2 = ss.RetentionPolicy().RemoveChannels(channelPolicy.ID, []string{channel.Id})
|
|
require.NoError(t, err2)
|
|
|
|
err2 = ss.RetentionPolicy().Delete(channelPolicy.ID)
|
|
require.NoError(t, err2)
|
|
|
|
_, _, err2 = ss.Post().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: nowMillis,
|
|
GlobalPolicyEndTime: 0,
|
|
Limit: 1000,
|
|
}, model.RetentionPolicyCursor{})
|
|
require.NoError(t, err2)
|
|
_, err2 = ss.Post().Get(rctx, post.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err2, "post should have been deleted by team policy")
|
|
|
|
err2 = ss.RetentionPolicy().RemoveTeams(teamPolicy.ID, []string{team.Id})
|
|
require.NoError(t, err2)
|
|
|
|
err2 = ss.RetentionPolicy().Delete(teamPolicy.ID)
|
|
require.NoError(t, err2)
|
|
|
|
// Clean up retention ids table
|
|
rows, err = ss.RetentionPolicy().GetIdsForDeletionByTableName("Posts", 1000)
|
|
require.NoError(t, err)
|
|
for _, row := range rows {
|
|
deleted, err = ss.Reaction().DeleteOrphanedRowsByIds(row)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), deleted)
|
|
}
|
|
})
|
|
|
|
t.Run("with channel, team and global policies", func(t *testing.T) {
|
|
c1 := &model.Channel{}
|
|
c1.TeamId = model.NewId()
|
|
c1.DisplayName = "Channel1"
|
|
c1.Name = NewTestID()
|
|
c1.Type = model.ChannelTypeOpen
|
|
c1, _ = ss.Channel().Save(rctx, c1, -1)
|
|
|
|
c2 := &model.Channel{}
|
|
c2.TeamId = model.NewId()
|
|
c2.DisplayName = "Channel2"
|
|
c2.Name = NewTestID()
|
|
c2.Type = model.ChannelTypeOpen
|
|
c2, _ = ss.Channel().Save(rctx, c2, -1)
|
|
|
|
channelPolicy, err2 := ss.RetentionPolicy().Save(&model.RetentionPolicyWithTeamAndChannelIDs{
|
|
RetentionPolicy: model.RetentionPolicy{
|
|
DisplayName: "DisplayName",
|
|
PostDurationDays: model.NewPointer(int64(30)),
|
|
},
|
|
ChannelIDs: []string{c1.Id},
|
|
})
|
|
require.NoError(t, err2)
|
|
defer ss.RetentionPolicy().Delete(channelPolicy.ID)
|
|
teamPolicy, err2 := ss.RetentionPolicy().Save(&model.RetentionPolicyWithTeamAndChannelIDs{
|
|
RetentionPolicy: model.RetentionPolicy{
|
|
DisplayName: "DisplayName",
|
|
PostDurationDays: model.NewPointer(int64(30)),
|
|
},
|
|
TeamIDs: []string{team.Id},
|
|
})
|
|
require.NoError(t, err2)
|
|
defer ss.RetentionPolicy().Delete(teamPolicy.ID)
|
|
|
|
// This one should be deleted by the channel policy
|
|
_, err2 = ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: c1.Id,
|
|
UserId: model.NewId(),
|
|
Message: "message",
|
|
CreateAt: 1,
|
|
})
|
|
require.NoError(t, err2)
|
|
// This one, by the team policy
|
|
_, err2 = ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channel.Id,
|
|
UserId: model.NewId(),
|
|
Message: "message",
|
|
CreateAt: 1,
|
|
})
|
|
require.NoError(t, err2)
|
|
// This one, by the global policy
|
|
_, err2 = ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: c2.Id,
|
|
UserId: model.NewId(),
|
|
Message: "message",
|
|
CreateAt: 1,
|
|
})
|
|
require.NoError(t, err2)
|
|
|
|
nowMillis := int64(1 + 30*model.DayInMilliseconds + 1)
|
|
deleted, _, err2 = ss.Post().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: nowMillis,
|
|
GlobalPolicyEndTime: 2,
|
|
Limit: 1000,
|
|
}, model.RetentionPolicyCursor{})
|
|
require.NoError(t, err2)
|
|
require.Equal(t, int64(3), deleted)
|
|
|
|
rows, err = ss.RetentionPolicy().GetIdsForDeletionByTableName("Posts", 1000)
|
|
require.NoError(t, err)
|
|
// Each policy would generate it's own row
|
|
require.Equal(t, 3, len(rows))
|
|
|
|
// Clean up retention ids table
|
|
for _, row := range rows {
|
|
deleted, err = ss.Reaction().DeleteOrphanedRowsByIds(row)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), deleted)
|
|
}
|
|
})
|
|
|
|
t.Run("with preserve pinned posts true", func(t *testing.T) {
|
|
p1 := &model.Post{}
|
|
p1.ChannelId = channel.Id
|
|
p1.UserId = model.NewId()
|
|
p1.IsPinned = false
|
|
p1.Message = NewTestID()
|
|
p1.CreateAt = 1000
|
|
p1, err = ss.Post().Save(rctx, p1)
|
|
require.NoError(t, err)
|
|
|
|
p2 := &model.Post{}
|
|
p2.ChannelId = channel.Id
|
|
p2.UserId = model.NewId()
|
|
p2.IsPinned = false
|
|
p2.Message = NewTestID()
|
|
p2.CreateAt = 1000
|
|
p2, err = ss.Post().Save(rctx, p2)
|
|
require.NoError(t, err)
|
|
|
|
p3 := &model.Post{}
|
|
p3.ChannelId = channel.Id
|
|
p3.UserId = model.NewId()
|
|
p3.IsPinned = false
|
|
p3.Message = NewTestID()
|
|
p3.CreateAt = 1000
|
|
p3, err = ss.Post().Save(rctx, p3)
|
|
require.NoError(t, err)
|
|
|
|
np3 := p3.Clone()
|
|
np3.IsPinned = true
|
|
|
|
np3, err = ss.Post().Update(rctx, np3, p3)
|
|
|
|
deleted, _, err = ss.Post().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: 0,
|
|
GlobalPolicyEndTime: 2000,
|
|
Limit: 1000,
|
|
PreservePinnedPosts: true,
|
|
}, model.RetentionPolicyCursor{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(3), deleted)
|
|
|
|
_, err = ss.Post().Get(rctx, p1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Should have not found post 1 after purge")
|
|
|
|
_, err = ss.Post().Get(rctx, p2.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Should have not found post 2 after purge")
|
|
|
|
_, err = ss.Post().Get(rctx, p3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Should have not found post 3 before update after purge")
|
|
|
|
_, err = ss.Post().Get(rctx, np3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.NoError(t, err, "Should have found updated post 3 after purge")
|
|
|
|
rows, err = ss.RetentionPolicy().GetIdsForDeletionByTableName("Posts", 1000)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(rows))
|
|
require.Equal(t, 3, len(rows[0].Ids))
|
|
// Clean up retention ids table
|
|
deleted, err = ss.Reaction().DeleteOrphanedRowsByIds(rows[0])
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), deleted)
|
|
|
|
// Clean up pinned post
|
|
_, _, err = ss.Post().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: 0,
|
|
GlobalPolicyEndTime: 2000,
|
|
Limit: 1000,
|
|
PreservePinnedPosts: false,
|
|
}, model.RetentionPolicyCursor{})
|
|
require.NoError(t, err)
|
|
|
|
rows, err = ss.RetentionPolicy().GetIdsForDeletionByTableName("Posts", 1000)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(rows))
|
|
|
|
// Clean up retention ids table
|
|
_, err = ss.Reaction().DeleteOrphanedRowsByIds(rows[0])
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("with preserve pinned posts false", func(t *testing.T) {
|
|
p1 := &model.Post{}
|
|
p1.ChannelId = channel.Id
|
|
p1.UserId = model.NewId()
|
|
p1.IsPinned = false
|
|
p1.Message = NewTestID()
|
|
p1.CreateAt = 1000
|
|
p1, err = ss.Post().Save(rctx, p1)
|
|
require.NoError(t, err)
|
|
|
|
p2 := &model.Post{}
|
|
p2.ChannelId = channel.Id
|
|
p2.UserId = model.NewId()
|
|
p2.IsPinned = false
|
|
p2.Message = NewTestID()
|
|
p2.CreateAt = 1000
|
|
p2, err = ss.Post().Save(rctx, p2)
|
|
require.NoError(t, err)
|
|
|
|
p3 := &model.Post{}
|
|
p3.ChannelId = channel.Id
|
|
p3.UserId = model.NewId()
|
|
p3.IsPinned = false
|
|
p3.Message = NewTestID()
|
|
p3.CreateAt = 1000
|
|
p3, err = ss.Post().Save(rctx, p3)
|
|
require.NoError(t, err)
|
|
|
|
np3 := p3.Clone()
|
|
np3.IsPinned = true
|
|
|
|
np3, err = ss.Post().Update(rctx, np3, p3)
|
|
|
|
deleted, _, err = ss.Post().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: 0,
|
|
GlobalPolicyEndTime: 2000,
|
|
Limit: 1000,
|
|
PreservePinnedPosts: false,
|
|
}, model.RetentionPolicyCursor{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(4), deleted)
|
|
|
|
_, err = ss.Post().Get(rctx, p1.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Should have not found post 1 after purge")
|
|
|
|
_, err = ss.Post().Get(rctx, p2.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Should have not found post 2 after purge")
|
|
|
|
_, err = ss.Post().Get(rctx, p3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Should have not found post 3 before update after purge")
|
|
|
|
_, err = ss.Post().Get(rctx, np3.Id, model.GetPostsOptions{}, "", map[string]bool{})
|
|
require.Error(t, err, "Should have not found updated post 3 after purge")
|
|
|
|
rows, err = ss.RetentionPolicy().GetIdsForDeletionByTableName("Posts", 1000)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(rows))
|
|
require.Equal(t, 4, len(rows[0].Ids))
|
|
// Clean up retention ids table
|
|
deleted, err = ss.Reaction().DeleteOrphanedRowsByIds(rows[0])
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), deleted)
|
|
})
|
|
}
|
|
|
|
func testPostStoreGetOldest(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
o0 := &model.Post{}
|
|
o0.ChannelId = channel1.Id
|
|
o0.UserId = model.NewId()
|
|
o0.Message = NewTestID()
|
|
o0.CreateAt = 3
|
|
o0, err = ss.Post().Save(rctx, o0)
|
|
require.NoError(t, err)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = o0.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = NewTestID()
|
|
o1.CreateAt = 2
|
|
o1, err = ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = o1.ChannelId
|
|
o2.UserId = model.NewId()
|
|
o2.Message = NewTestID()
|
|
o2.CreateAt = 1
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
r1, err := ss.Post().GetOldest()
|
|
|
|
require.NoError(t, err)
|
|
assert.EqualValues(t, o2.Id, r1.Id)
|
|
}
|
|
|
|
func testGetMaxPostSize(t *testing.T, _ request.CTX, ss store.Store) {
|
|
assert.Equal(t, model.PostMessageMaxRunesV2, ss.Post().GetMaxPostSize())
|
|
}
|
|
|
|
func testPostStoreGetParentsForExportAfter(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t1 := model.Team{}
|
|
t1.DisplayName = "Name"
|
|
t1.Name = NewTestID()
|
|
t1.Email = MakeEmail()
|
|
t1.Type = model.TeamOpen
|
|
_, err := ss.Team().Save(&t1)
|
|
require.NoError(t, err)
|
|
|
|
c1 := model.Channel{}
|
|
c1.TeamId = t1.Id
|
|
c1.DisplayName = "Channel1"
|
|
c1.Name = NewTestID()
|
|
c1.Type = model.ChannelTypeOpen
|
|
_, nErr := ss.Channel().Save(rctx, &c1, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
c2 := model.Channel{}
|
|
c2.TeamId = t1.Id
|
|
c2.DisplayName = "Channel2"
|
|
c2.Name = NewTestID()
|
|
c2.Type = model.ChannelTypeOpen
|
|
_, nErr = ss.Channel().Save(rctx, &c2, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
u1 := model.User{}
|
|
u1.Username = model.NewUsername()
|
|
u1.Email = MakeEmail()
|
|
u1.Nickname = model.NewId()
|
|
_, err = ss.User().Save(rctx, &u1)
|
|
require.NoError(t, err)
|
|
|
|
p1 := &model.Post{}
|
|
p1.ChannelId = c1.Id
|
|
p1.UserId = u1.Id
|
|
p1.Message = NewTestID()
|
|
p1.CreateAt = 1000
|
|
p1, nErr = ss.Post().Save(rctx, p1)
|
|
require.NoError(t, nErr)
|
|
|
|
p2 := &model.Post{}
|
|
p2.ChannelId = c2.Id
|
|
p2.UserId = u1.Id
|
|
p2.Message = NewTestID()
|
|
p2.CreateAt = 1000
|
|
p2, nErr = ss.Post().Save(rctx, p2)
|
|
require.NoError(t, nErr)
|
|
nErr = ss.Channel().Delete(c2.Id, model.GetMillis())
|
|
require.NoError(t, nErr)
|
|
|
|
t.Run("without archived channels", func(t *testing.T) {
|
|
posts, err := ss.Post().GetParentsForExportAfter(10000, strings.Repeat("0", 26), false)
|
|
assert.NoError(t, err)
|
|
|
|
found := false
|
|
foundArchived := false
|
|
for _, p := range posts {
|
|
if p.Id == p1.Id {
|
|
found = true
|
|
assert.Equal(t, p.Id, p1.Id)
|
|
assert.Equal(t, p.Message, p1.Message)
|
|
assert.Equal(t, p.Username, u1.Username)
|
|
assert.Equal(t, p.TeamName, t1.Name)
|
|
assert.Equal(t, p.ChannelName, c1.Name)
|
|
}
|
|
if p.Id == p2.Id {
|
|
foundArchived = true
|
|
}
|
|
}
|
|
assert.True(t, found)
|
|
assert.False(t, foundArchived, "posts from archived channel should not be returned")
|
|
})
|
|
|
|
t.Run("with archived channels", func(t *testing.T) {
|
|
posts, err := ss.Post().GetParentsForExportAfter(10000, strings.Repeat("0", 26), true)
|
|
assert.NoError(t, err)
|
|
|
|
found := false
|
|
for _, p := range posts {
|
|
if p.Id == p2.Id {
|
|
found = true
|
|
assert.Equal(t, p.Id, p2.Id)
|
|
assert.Equal(t, p.Message, p2.Message)
|
|
assert.Equal(t, p.Username, u1.Username)
|
|
assert.Equal(t, p.TeamName, t1.Name)
|
|
assert.Equal(t, p.ChannelName, c2.Name)
|
|
}
|
|
}
|
|
assert.True(t, found)
|
|
})
|
|
|
|
t.Run("with flagged post", func(t *testing.T) {
|
|
err := ss.Preference().Save(model.Preferences([]model.Preference{
|
|
{
|
|
UserId: u1.Id,
|
|
Category: model.PreferenceCategoryFlaggedPost,
|
|
Name: p1.Id,
|
|
Value: "true",
|
|
},
|
|
}))
|
|
require.NoError(t, err)
|
|
|
|
posts, err := ss.Post().GetParentsForExportAfter(10000, strings.Repeat("0", 26), false)
|
|
assert.NoError(t, err)
|
|
|
|
for _, p := range posts {
|
|
if p.Id == p1.Id {
|
|
require.NotNil(t, p.FlaggedBy)
|
|
assert.Equal(t, model.StringArray([]string{u1.Username}), p.FlaggedBy)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func testPostStoreGetRepliesForExport(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t1 := model.Team{}
|
|
t1.DisplayName = "Name"
|
|
t1.Name = NewTestID()
|
|
t1.Email = MakeEmail()
|
|
t1.Type = model.TeamOpen
|
|
_, err := ss.Team().Save(&t1)
|
|
require.NoError(t, err)
|
|
|
|
c1 := model.Channel{}
|
|
c1.TeamId = t1.Id
|
|
c1.DisplayName = "Channel1"
|
|
c1.Name = NewTestID()
|
|
c1.Type = model.ChannelTypeOpen
|
|
_, nErr := ss.Channel().Save(rctx, &c1, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
u1 := model.User{}
|
|
u1.Email = MakeEmail()
|
|
u1.Nickname = model.NewId()
|
|
_, err = ss.User().Save(rctx, &u1)
|
|
require.NoError(t, err)
|
|
|
|
p1 := &model.Post{}
|
|
p1.ChannelId = c1.Id
|
|
p1.UserId = u1.Id
|
|
p1.Message = NewTestID()
|
|
p1.CreateAt = 1000
|
|
p1, nErr = ss.Post().Save(rctx, p1)
|
|
require.NoError(t, nErr)
|
|
|
|
p2 := &model.Post{}
|
|
p2.ChannelId = c1.Id
|
|
p2.UserId = u1.Id
|
|
p2.Message = NewTestID()
|
|
p2.CreateAt = 1001
|
|
p2.RootId = p1.Id
|
|
p2, nErr = ss.Post().Save(rctx, p2)
|
|
require.NoError(t, nErr)
|
|
|
|
r1, err := ss.Post().GetRepliesForExport(p1.Id)
|
|
assert.NoError(t, err)
|
|
|
|
require.Len(t, r1, 1)
|
|
|
|
reply1 := r1[0]
|
|
assert.Equal(t, reply1.Id, p2.Id)
|
|
assert.Equal(t, reply1.Message, p2.Message)
|
|
assert.Equal(t, reply1.Username, u1.Username)
|
|
|
|
// Checking whether replies by deleted user are exported
|
|
u1.DeleteAt = 1002
|
|
_, err = ss.User().Update(rctx, &u1, false)
|
|
require.NoError(t, err)
|
|
|
|
r1, err = ss.Post().GetRepliesForExport(p1.Id)
|
|
assert.NoError(t, err)
|
|
|
|
require.Len(t, r1, 1)
|
|
|
|
reply1 = r1[0]
|
|
assert.Equal(t, reply1.Id, p2.Id)
|
|
assert.Equal(t, reply1.Message, p2.Message)
|
|
assert.Equal(t, reply1.Username, u1.Username)
|
|
}
|
|
|
|
func testPostStoreGetDirectPostParentsForExportAfter(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
teamID := model.NewId()
|
|
|
|
o1 := model.Channel{}
|
|
o1.TeamId = teamID
|
|
o1.DisplayName = "Name"
|
|
o1.Name = model.GetDMNameFromIds(NewTestID(), NewTestID())
|
|
o1.Type = model.ChannelTypeDirect
|
|
|
|
u1 := &model.User{}
|
|
u1.Email = MakeEmail()
|
|
u1.Nickname = model.NewId()
|
|
_, err := ss.User().Save(rctx, u1)
|
|
require.NoError(t, err)
|
|
_, nErr := ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: model.NewId(), UserId: u1.Id}, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
u2 := &model.User{}
|
|
u2.Email = MakeEmail()
|
|
u2.Nickname = model.NewId()
|
|
_, err = ss.User().Save(rctx, u2)
|
|
require.NoError(t, err)
|
|
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: model.NewId(), UserId: u2.Id}, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
m1 := model.ChannelMember{}
|
|
m1.ChannelId = o1.Id
|
|
m1.UserId = u1.Id
|
|
m1.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
|
|
m2 := model.ChannelMember{}
|
|
m2.ChannelId = o1.Id
|
|
m2.UserId = u2.Id
|
|
m2.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
|
|
ss.Channel().SaveDirectChannel(rctx, &o1, &m1, &m2)
|
|
|
|
p1 := &model.Post{}
|
|
p1.ChannelId = o1.Id
|
|
p1.UserId = u1.Id
|
|
p1.Message = NewTestID()
|
|
p1.CreateAt = 1000
|
|
p1, nErr = ss.Post().Save(rctx, p1)
|
|
require.NoError(t, nErr)
|
|
|
|
r1, nErr := ss.Post().GetDirectPostParentsForExportAfter(10000, strings.Repeat("0", 26), false)
|
|
assert.NoError(t, nErr)
|
|
|
|
assert.Equal(t, p1.Message, r1[0].Message)
|
|
|
|
// Manually truncate Channels table until testlib can handle cleanups
|
|
s.GetMaster().Exec("TRUNCATE Channels")
|
|
}
|
|
|
|
func testPostStoreGetDirectPostParentsForExportAfterDeleted(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
teamID := model.NewId()
|
|
|
|
o1 := model.Channel{}
|
|
o1.TeamId = teamID
|
|
o1.DisplayName = "Name"
|
|
o1.Name = model.GetDMNameFromIds(NewTestID(), NewTestID())
|
|
o1.Type = model.ChannelTypeDirect
|
|
|
|
u1 := &model.User{}
|
|
u1.DeleteAt = 1
|
|
u1.Email = MakeEmail()
|
|
u1.Nickname = model.NewId()
|
|
_, err := ss.User().Save(rctx, u1)
|
|
require.NoError(t, err)
|
|
_, nErr := ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: model.NewId(), UserId: u1.Id}, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
u2 := &model.User{}
|
|
u2.DeleteAt = 1
|
|
u2.Email = MakeEmail()
|
|
u2.Nickname = model.NewId()
|
|
_, err = ss.User().Save(rctx, u2)
|
|
require.NoError(t, err)
|
|
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: model.NewId(), UserId: u2.Id}, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
m1 := model.ChannelMember{}
|
|
m1.ChannelId = o1.Id
|
|
m1.UserId = u1.Id
|
|
m1.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
|
|
m2 := model.ChannelMember{}
|
|
m2.ChannelId = o1.Id
|
|
m2.UserId = u2.Id
|
|
m2.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
|
|
ss.Channel().SaveDirectChannel(rctx, &o1, &m1, &m2)
|
|
|
|
o1.DeleteAt = 1
|
|
nErr = ss.Channel().SetDeleteAt(o1.Id, 1, 1)
|
|
assert.NoError(t, nErr)
|
|
|
|
p1 := &model.Post{}
|
|
p1.ChannelId = o1.Id
|
|
p1.UserId = u1.Id
|
|
p1.Message = NewTestID()
|
|
p1.CreateAt = 1000
|
|
_, nErr = ss.Post().Save(rctx, p1)
|
|
require.NoError(t, nErr)
|
|
|
|
r1, nErr := ss.Post().GetDirectPostParentsForExportAfter(10000, strings.Repeat("0", 26), false)
|
|
assert.NoError(t, nErr)
|
|
assert.Equal(t, 0, len(r1))
|
|
|
|
r1, nErr = ss.Post().GetDirectPostParentsForExportAfter(10000, strings.Repeat("0", 26), true)
|
|
assert.NoError(t, nErr)
|
|
assert.Equal(t, 1, len(r1))
|
|
|
|
// Manually truncate Channels table until testlib can handle cleanups
|
|
s.GetMaster().Exec("TRUNCATE Channels")
|
|
}
|
|
|
|
func testPostStoreGetDirectPostParentsForExportAfterBatched(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
teamID := model.NewId()
|
|
|
|
o1 := model.Channel{}
|
|
o1.TeamId = teamID
|
|
o1.DisplayName = "Name"
|
|
o1.Name = model.GetDMNameFromIds(NewTestID(), NewTestID())
|
|
o1.Type = model.ChannelTypeDirect
|
|
|
|
var postIds []string
|
|
for range 150 {
|
|
u1 := &model.User{}
|
|
u1.Email = MakeEmail()
|
|
u1.Nickname = model.NewId()
|
|
_, err := ss.User().Save(rctx, u1)
|
|
require.NoError(t, err)
|
|
_, nErr := ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: model.NewId(), UserId: u1.Id}, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
u2 := &model.User{}
|
|
u2.Email = MakeEmail()
|
|
u2.Nickname = model.NewId()
|
|
_, err = ss.User().Save(rctx, u2)
|
|
require.NoError(t, err)
|
|
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: model.NewId(), UserId: u2.Id}, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
m1 := model.ChannelMember{}
|
|
m1.ChannelId = o1.Id
|
|
m1.UserId = u1.Id
|
|
m1.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
|
|
m2 := model.ChannelMember{}
|
|
m2.ChannelId = o1.Id
|
|
m2.UserId = u2.Id
|
|
m2.NotifyProps = model.GetDefaultChannelNotifyProps()
|
|
|
|
ss.Channel().SaveDirectChannel(rctx, &o1, &m1, &m2)
|
|
|
|
p1 := &model.Post{}
|
|
p1.ChannelId = o1.Id
|
|
p1.UserId = u1.Id
|
|
p1.Message = NewTestID()
|
|
p1.CreateAt = 1000
|
|
p1, nErr = ss.Post().Save(rctx, p1)
|
|
require.NoError(t, nErr)
|
|
postIds = append(postIds, p1.Id)
|
|
}
|
|
slices.Sort(postIds)
|
|
|
|
// Get all posts
|
|
r1, err := ss.Post().GetDirectPostParentsForExportAfter(10000, strings.Repeat("0", 26), false)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, len(postIds), len(r1))
|
|
var exportedPostIds []string
|
|
for i := range r1 {
|
|
exportedPostIds = append(exportedPostIds, r1[i].Id)
|
|
}
|
|
slices.Sort(exportedPostIds)
|
|
assert.ElementsMatch(t, postIds, exportedPostIds)
|
|
|
|
// Get 100
|
|
r1, err = ss.Post().GetDirectPostParentsForExportAfter(100, strings.Repeat("0", 26), false)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 100, len(r1))
|
|
exportedPostIds = []string{}
|
|
for i := range r1 {
|
|
exportedPostIds = append(exportedPostIds, r1[i].Id)
|
|
}
|
|
slices.Sort(exportedPostIds)
|
|
assert.ElementsMatch(t, postIds[:100], exportedPostIds)
|
|
|
|
// Manually truncate Channels table until testlib can handle cleanups
|
|
s.GetMaster().Exec("TRUNCATE Channels")
|
|
}
|
|
|
|
func testHasAutoResponsePostByUserSince(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("should return posts created after the given time", func(t *testing.T) {
|
|
teamID := model.NewId()
|
|
channel1, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: teamID,
|
|
DisplayName: "DisplayName1",
|
|
Name: "channel" + model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, -1)
|
|
require.NoError(t, err)
|
|
|
|
channelID := channel1.Id
|
|
userID := model.NewId()
|
|
|
|
_, err = ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
// We need to sleep because SendAutoResponseIfNecessary
|
|
// runs in a goroutine.
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post2, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "message",
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
post3, err := ss.Post().Save(rctx, &model.Post{
|
|
ChannelId: channelID,
|
|
UserId: userID,
|
|
Message: "auto response message",
|
|
Type: model.PostTypeAutoResponder,
|
|
})
|
|
require.NoError(t, err)
|
|
time.Sleep(time.Millisecond)
|
|
|
|
exists, err := ss.Post().HasAutoResponsePostByUserSince(model.GetPostsSinceOptions{ChannelId: channelID, Time: post2.CreateAt}, userID)
|
|
require.NoError(t, err)
|
|
assert.True(t, exists)
|
|
|
|
err = ss.Post().Delete(rctx, post3.Id, time.Now().Unix(), userID)
|
|
require.NoError(t, err)
|
|
|
|
exists, err = ss.Post().HasAutoResponsePostByUserSince(model.GetPostsSinceOptions{ChannelId: channelID, Time: post2.CreateAt}, userID)
|
|
require.NoError(t, err)
|
|
assert.False(t, exists)
|
|
})
|
|
}
|
|
|
|
func testGetPostsSinceUpdateForSync(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
// create some posts.
|
|
channelID := model.NewId()
|
|
remoteID := model.NewPointer(model.NewId())
|
|
first := model.GetMillis()
|
|
|
|
data := []*model.Post{
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 0"},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 1"},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 2"},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 3", RemoteId: remoteID},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 4", RemoteId: remoteID},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 5", RemoteId: remoteID},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 6", RemoteId: remoteID},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 7"},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 8", DeleteAt: model.GetMillis()},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 9", DeleteAt: model.GetMillis()},
|
|
}
|
|
|
|
for i, p := range data {
|
|
p.UpdateAt = first + (int64(i) * 300000)
|
|
if p.RemoteId == nil {
|
|
p.RemoteId = model.NewPointer(model.NewId())
|
|
}
|
|
_, err := ss.Post().Save(rctx, p)
|
|
require.NoError(t, err, "couldn't save post")
|
|
}
|
|
|
|
t.Run("Invalid channel id", func(t *testing.T) {
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: model.NewId(),
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts, cursorOut, err := ss.Post().GetPostsSinceForSync(opt, cursor, 100)
|
|
require.NoError(t, err)
|
|
require.Empty(t, posts, "should return zero posts")
|
|
require.Equal(t, cursor, cursorOut)
|
|
})
|
|
|
|
t.Run("Get by channel, exclude remotes, exclude deleted", func(t *testing.T) {
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: channelID,
|
|
ExcludeRemoteId: *remoteID,
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts, _, err := ss.Post().GetPostsSinceForSync(opt, cursor, 100)
|
|
require.NoError(t, err)
|
|
|
|
require.ElementsMatch(t, getPostIds(data[0:3], data[7]), getPostIds(posts))
|
|
})
|
|
|
|
t.Run("Include deleted", func(t *testing.T) {
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: channelID,
|
|
IncludeDeleted: true,
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts, _, err := ss.Post().GetPostsSinceForSync(opt, cursor, 100)
|
|
require.NoError(t, err)
|
|
|
|
require.ElementsMatch(t, getPostIds(data), getPostIds(posts))
|
|
})
|
|
|
|
t.Run("Limit and cursor", func(t *testing.T) {
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: channelID,
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts1, cursor, err := ss.Post().GetPostsSinceForSync(opt, cursor, 5)
|
|
require.NoError(t, err)
|
|
require.Len(t, posts1, 5, "should get 5 posts")
|
|
|
|
posts2, _, err := ss.Post().GetPostsSinceForSync(opt, cursor, 5)
|
|
require.NoError(t, err)
|
|
require.Len(t, posts2, 3, "should get 3 posts")
|
|
|
|
require.ElementsMatch(t, getPostIds(data[0:8]), getPostIds(posts1, posts2...))
|
|
})
|
|
|
|
t.Run("UpdateAt collisions", func(t *testing.T) {
|
|
// this test requires all the UpdateAt timestamps to be the same.
|
|
result, err := s.GetMaster().Exec("UPDATE Posts SET UpdateAt = ?", model.GetMillis())
|
|
require.NoError(t, err)
|
|
rows, err := result.RowsAffected()
|
|
require.NoError(t, err)
|
|
require.Greater(t, rows, int64(0))
|
|
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: channelID,
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts1, cursor, err := ss.Post().GetPostsSinceForSync(opt, cursor, 5)
|
|
require.NoError(t, err)
|
|
require.Len(t, posts1, 5, "should get 5 posts")
|
|
|
|
posts2, _, err := ss.Post().GetPostsSinceForSync(opt, cursor, 5)
|
|
require.NoError(t, err)
|
|
require.Len(t, posts2, 3, "should get 3 posts")
|
|
|
|
require.ElementsMatch(t, getPostIds(data[0:8]), getPostIds(posts1, posts2...))
|
|
})
|
|
}
|
|
|
|
func testGetPostsSinceCreateForSync(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
// create some posts.
|
|
channelID := model.NewId()
|
|
remoteID := model.NewPointer(model.NewId())
|
|
first := model.GetMillis()
|
|
|
|
data := []*model.Post{
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 0"},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 1"},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 2"},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 3", RemoteId: remoteID},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 4", RemoteId: remoteID},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 5", RemoteId: remoteID},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 6", RemoteId: remoteID},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 7"},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 8", DeleteAt: model.GetMillis()},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "test post 9", DeleteAt: model.GetMillis()},
|
|
}
|
|
|
|
for i, p := range data {
|
|
p.CreateAt = first + (int64(i) * 300000)
|
|
if p.RemoteId == nil {
|
|
p.RemoteId = model.NewPointer(model.NewId())
|
|
}
|
|
_, err := ss.Post().Save(rctx, p)
|
|
require.NoError(t, err, "couldn't save post")
|
|
}
|
|
|
|
t.Run("Invalid channel id", func(t *testing.T) {
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: model.NewId(),
|
|
SinceCreateAt: true,
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts, cursorOut, err := ss.Post().GetPostsSinceForSync(opt, cursor, 100)
|
|
require.NoError(t, err)
|
|
require.Empty(t, posts, "should return zero posts")
|
|
require.Equal(t, cursor, cursorOut)
|
|
})
|
|
|
|
t.Run("Get by channel, exclude remotes, exclude deleted", func(t *testing.T) {
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: channelID,
|
|
ExcludeRemoteId: *remoteID,
|
|
SinceCreateAt: true,
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts, _, err := ss.Post().GetPostsSinceForSync(opt, cursor, 100)
|
|
require.NoError(t, err)
|
|
|
|
require.ElementsMatch(t, getPostIds(data[0:3], data[7]), getPostIds(posts))
|
|
})
|
|
|
|
t.Run("Include deleted", func(t *testing.T) {
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: channelID,
|
|
IncludeDeleted: true,
|
|
SinceCreateAt: true,
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts, _, err := ss.Post().GetPostsSinceForSync(opt, cursor, 100)
|
|
require.NoError(t, err)
|
|
|
|
require.ElementsMatch(t, getPostIds(data), getPostIds(posts))
|
|
})
|
|
|
|
t.Run("Limit and cursor", func(t *testing.T) {
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: channelID,
|
|
SinceCreateAt: true,
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts1, cursor, err := ss.Post().GetPostsSinceForSync(opt, cursor, 5)
|
|
require.NoError(t, err)
|
|
require.Len(t, posts1, 5, "should get 5 posts")
|
|
|
|
posts2, _, err := ss.Post().GetPostsSinceForSync(opt, cursor, 5)
|
|
require.NoError(t, err)
|
|
require.Len(t, posts2, 3, "should get 3 posts")
|
|
|
|
require.ElementsMatch(t, getPostIds(data[0:8]), getPostIds(posts1, posts2...))
|
|
})
|
|
|
|
t.Run("CreateAt collisions", func(t *testing.T) {
|
|
// this test requires all the CreateAt timestamps to be the same.
|
|
result, err := s.GetMaster().Exec("UPDATE Posts SET CreateAt = ?", model.GetMillis())
|
|
require.NoError(t, err)
|
|
rows, err := result.RowsAffected()
|
|
require.NoError(t, err)
|
|
require.Greater(t, rows, int64(0))
|
|
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: channelID,
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts1, cursor, err := ss.Post().GetPostsSinceForSync(opt, cursor, 5)
|
|
require.NoError(t, err)
|
|
require.Len(t, posts1, 5, "should get 5 posts")
|
|
|
|
posts2, _, err := ss.Post().GetPostsSinceForSync(opt, cursor, 5)
|
|
require.NoError(t, err)
|
|
require.Len(t, posts2, 3, "should get 3 posts")
|
|
|
|
require.ElementsMatch(t, getPostIds(data[0:8]), getPostIds(posts1, posts2...))
|
|
})
|
|
}
|
|
|
|
func testSetPostReminder(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
// Basic
|
|
userID := NewTestID()
|
|
|
|
p1 := &model.Post{
|
|
UserId: userID,
|
|
ChannelId: NewTestID(),
|
|
Message: "hi there",
|
|
Type: model.PostTypeDefault,
|
|
}
|
|
p1, err := ss.Post().Save(rctx, p1)
|
|
require.NoError(t, err)
|
|
|
|
reminder := &model.PostReminder{
|
|
TargetTime: 1234,
|
|
PostId: p1.Id,
|
|
UserId: userID,
|
|
}
|
|
|
|
require.NoError(t, ss.Post().SetPostReminder(reminder))
|
|
|
|
out := model.PostReminder{}
|
|
require.NoError(t, s.GetMaster().Get(&out, `SELECT PostId, UserId, TargetTime FROM PostReminders WHERE PostId=? AND UserId=?`, reminder.PostId, reminder.UserId))
|
|
assert.Equal(t, reminder, &out)
|
|
|
|
reminder.PostId = "notfound"
|
|
err = ss.Post().SetPostReminder(reminder)
|
|
var nfErr *store.ErrNotFound
|
|
require.True(t, errors.As(err, &nfErr))
|
|
|
|
// Upsert
|
|
reminder = &model.PostReminder{
|
|
TargetTime: 12345,
|
|
PostId: p1.Id,
|
|
UserId: userID,
|
|
}
|
|
|
|
require.NoError(t, ss.Post().SetPostReminder(reminder))
|
|
require.NoError(t, s.GetMaster().Get(&out, `SELECT PostId, UserId, TargetTime FROM PostReminders WHERE PostId=? AND UserId=?`, reminder.PostId, reminder.UserId))
|
|
assert.Equal(t, reminder, &out)
|
|
}
|
|
|
|
func testGetPostReminders(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
times := []int64{100, 101, 102}
|
|
for _, tt := range times {
|
|
userID := NewTestID()
|
|
|
|
p1 := &model.Post{
|
|
UserId: userID,
|
|
ChannelId: NewTestID(),
|
|
Message: "hi there",
|
|
Type: model.PostTypeDefault,
|
|
}
|
|
p1, err := ss.Post().Save(rctx, p1)
|
|
require.NoError(t, err)
|
|
|
|
reminder := &model.PostReminder{
|
|
TargetTime: tt,
|
|
PostId: p1.Id,
|
|
UserId: userID,
|
|
}
|
|
|
|
require.NoError(t, ss.Post().SetPostReminder(reminder))
|
|
}
|
|
|
|
reminders, err := ss.Post().GetPostReminders(101)
|
|
require.NoError(t, err)
|
|
require.Len(t, reminders, 2)
|
|
|
|
// assert one reminder is left
|
|
reminders, err = ss.Post().GetPostReminders(102)
|
|
require.NoError(t, err)
|
|
require.Len(t, reminders, 1)
|
|
|
|
// assert everything is deleted.
|
|
reminders, err = ss.Post().GetPostReminders(103)
|
|
require.NoError(t, err)
|
|
require.Len(t, reminders, 0)
|
|
}
|
|
|
|
func testGetPostReminderMetadata(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
team := &model.Team{
|
|
Name: "teamname",
|
|
DisplayName: "display",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, err := ss.Team().Save(team)
|
|
require.NoError(t, err)
|
|
|
|
ch := &model.Channel{
|
|
TeamId: team.Id,
|
|
DisplayName: "channeldisplay",
|
|
Name: NewTestID(),
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
ch, err = ss.Channel().Save(rctx, ch, -1)
|
|
require.NoError(t, err)
|
|
|
|
ch2 := &model.Channel{
|
|
TeamId: "",
|
|
DisplayName: "GM_display",
|
|
Name: NewTestID(),
|
|
Type: model.ChannelTypeGroup,
|
|
}
|
|
ch2, err = ss.Channel().Save(rctx, ch2, -1)
|
|
require.NoError(t, err)
|
|
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
Locale: "es",
|
|
}
|
|
|
|
u1, err = ss.User().Save(rctx, u1)
|
|
require.NoError(t, err)
|
|
|
|
p1 := &model.Post{
|
|
UserId: u1.Id,
|
|
ChannelId: ch.Id,
|
|
Message: "hi there",
|
|
Type: model.PostTypeDefault,
|
|
}
|
|
p1, err = ss.Post().Save(rctx, p1)
|
|
require.NoError(t, err)
|
|
|
|
p2 := &model.Post{
|
|
UserId: u1.Id,
|
|
ChannelId: ch2.Id,
|
|
Message: "hi there 2",
|
|
Type: model.PostTypeDefault,
|
|
}
|
|
p2, err = ss.Post().Save(rctx, p2)
|
|
require.NoError(t, err)
|
|
|
|
meta, err := ss.Post().GetPostReminderMetadata(p1.Id)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, meta.ChannelID, ch.Id)
|
|
assert.Equal(t, meta.TeamName, team.Name)
|
|
assert.Equal(t, meta.Username, u1.Username)
|
|
assert.Equal(t, meta.UserLocale, u1.Locale)
|
|
|
|
meta, err = ss.Post().GetPostReminderMetadata(p2.Id)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, meta.ChannelID, ch2.Id)
|
|
assert.Equal(t, meta.TeamName, "")
|
|
assert.Equal(t, meta.Username, u1.Username)
|
|
assert.Equal(t, meta.UserLocale, u1.Locale)
|
|
}
|
|
|
|
func getPostIds(posts []*model.Post, morePosts ...*model.Post) []string {
|
|
ids := make([]string, 0, len(posts)+len(morePosts))
|
|
for _, p := range posts {
|
|
ids = append(ids, p.Id)
|
|
}
|
|
for _, p := range morePosts {
|
|
ids = append(ids, p.Id)
|
|
}
|
|
return ids
|
|
}
|
|
|
|
func testGetNthRecentPostTime(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Skip("https://mattermost.atlassian.net/browse/MM-64438")
|
|
|
|
_, err := ss.Post().GetNthRecentPostTime(0)
|
|
assert.Error(t, err)
|
|
_, err = ss.Post().GetNthRecentPostTime(-1)
|
|
assert.Error(t, err)
|
|
|
|
diff := int64(10000)
|
|
now := utils.MillisFromTime(time.Now()) + diff
|
|
|
|
p1 := &model.Post{}
|
|
p1.ChannelId = model.NewId()
|
|
p1.UserId = model.NewId()
|
|
p1.Message = "test"
|
|
p1.CreateAt = now
|
|
p1, err = ss.Post().Save(rctx, p1)
|
|
require.NoError(t, err)
|
|
|
|
p2 := &model.Post{}
|
|
p2.ChannelId = p1.ChannelId
|
|
p2.UserId = p1.UserId
|
|
p2.Message = p1.Message
|
|
now = now + diff
|
|
p2.CreateAt = now
|
|
p2, err = ss.Post().Save(rctx, p2)
|
|
require.NoError(t, err)
|
|
|
|
bot1 := &model.Bot{
|
|
Username: "username",
|
|
Description: "a bot",
|
|
OwnerId: model.NewId(),
|
|
UserId: model.NewId(),
|
|
}
|
|
_, err = ss.Bot().Save(bot1)
|
|
require.NoError(t, err)
|
|
|
|
b1 := &model.Post{}
|
|
b1.Message = "bot test"
|
|
b1.ChannelId = p1.ChannelId
|
|
b1.UserId = bot1.UserId
|
|
now = now + diff
|
|
b1.CreateAt = now
|
|
_, err = ss.Post().Save(rctx, b1)
|
|
require.NoError(t, err)
|
|
|
|
p3 := &model.Post{}
|
|
p3.ChannelId = p1.ChannelId
|
|
p3.UserId = p1.UserId
|
|
p3.Message = p1.Message
|
|
now = now + diff
|
|
p3.CreateAt = now
|
|
p3, err = ss.Post().Save(rctx, p3)
|
|
require.NoError(t, err)
|
|
|
|
s1 := &model.Post{}
|
|
s1.Type = model.PostTypeJoinChannel
|
|
s1.ChannelId = p1.ChannelId
|
|
s1.UserId = model.NewId()
|
|
s1.Message = "system_join_channel message"
|
|
now = now + diff
|
|
s1.CreateAt = now
|
|
_, err = ss.Post().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
p4 := &model.Post{}
|
|
p4.ChannelId = p1.ChannelId
|
|
p4.UserId = p1.UserId
|
|
p4.Message = p1.Message
|
|
now = now + diff
|
|
p4.CreateAt = now
|
|
p4, err = ss.Post().Save(rctx, p4)
|
|
require.NoError(t, err)
|
|
|
|
r, err := ss.Post().GetNthRecentPostTime(1)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, p4.CreateAt, r)
|
|
|
|
// Skip system post
|
|
r, err = ss.Post().GetNthRecentPostTime(2)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, p3.CreateAt, r)
|
|
|
|
// Skip system & bot post
|
|
r, err = ss.Post().GetNthRecentPostTime(3)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, p2.CreateAt, r)
|
|
|
|
r, err = ss.Post().GetNthRecentPostTime(4)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, p1.CreateAt, r)
|
|
|
|
_, err = ss.Post().GetNthRecentPostTime(10000)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, &store.ErrNotFound{}, err)
|
|
}
|
|
|
|
func testGetEditHistoryForPost(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("should return edit history for post", func(t *testing.T) {
|
|
// create a post
|
|
post := &model.Post{
|
|
ChannelId: model.NewId(),
|
|
UserId: model.NewId(),
|
|
Message: "test",
|
|
}
|
|
originalPost, err := ss.Post().Save(rctx, post)
|
|
require.NoError(t, err)
|
|
// create an edit
|
|
updatedPost := originalPost.Clone()
|
|
updatedPost.Message = "test edited"
|
|
savedUpdatedPost, err := ss.Post().Update(rctx, updatedPost, originalPost)
|
|
require.NoError(t, err)
|
|
// get edit history
|
|
edits, err := ss.Post().GetEditHistoryForPost(savedUpdatedPost.Id)
|
|
require.NoError(t, err)
|
|
require.Len(t, edits, 1)
|
|
require.Equal(t, originalPost.Id, edits[0].Id)
|
|
require.Equal(t, originalPost.UserId, edits[0].UserId)
|
|
require.Equal(t, originalPost.Message, edits[0].Message)
|
|
})
|
|
|
|
t.Run("should return error for not edited posts", func(t *testing.T) {
|
|
// create a post
|
|
post := &model.Post{
|
|
ChannelId: model.NewId(),
|
|
UserId: model.NewId(),
|
|
Message: "test",
|
|
}
|
|
originalPost, err := ss.Post().Save(rctx, post)
|
|
require.NoError(t, err)
|
|
// get edit history
|
|
_, err = ss.Post().GetEditHistoryForPost(originalPost.Id)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("should return error for non-existent post", func(t *testing.T) {
|
|
// get edit history
|
|
_, err := ss.Post().GetEditHistoryForPost("non-existent")
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("should return error for deleted post", func(t *testing.T) {
|
|
// create a post
|
|
post := &model.Post{
|
|
ChannelId: model.NewId(),
|
|
UserId: model.NewId(),
|
|
Message: "test",
|
|
}
|
|
originalPost, err := ss.Post().Save(rctx, post)
|
|
require.NoError(t, err)
|
|
// delete post
|
|
err = ss.Post().Delete(rctx, post.Id, 100, post.UserId)
|
|
require.NoError(t, err)
|
|
// get edit history
|
|
_, err = ss.Post().GetEditHistoryForPost(originalPost.Id)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("should return error for deleted edit", func(t *testing.T) {
|
|
// create a post
|
|
post := &model.Post{
|
|
ChannelId: model.NewId(),
|
|
UserId: model.NewId(),
|
|
Message: "test",
|
|
}
|
|
originalPost, err := ss.Post().Save(rctx, post)
|
|
require.NoError(t, err)
|
|
// create an edit
|
|
updatedPost := originalPost.Clone()
|
|
updatedPost.Message = "test edited"
|
|
savedUpdatedPost, err := ss.Post().Update(rctx, updatedPost, originalPost)
|
|
require.NoError(t, err)
|
|
// delete edit
|
|
err = ss.Post().Delete(rctx, savedUpdatedPost.Id, 100, savedUpdatedPost.UserId)
|
|
require.NoError(t, err)
|
|
// get edit history
|
|
_, err = ss.Post().GetEditHistoryForPost(savedUpdatedPost.Id)
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
// testGetPostsSinceForSyncExcludeMetadata tests the ExcludeChannelMetadataSystemPosts option
|
|
// in the GetPostsSinceForSync function to verify that database-level filtering works correctly
|
|
func testGetPostsSinceForSyncExcludeMetadata(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
// Create a channel
|
|
channelID := model.NewId()
|
|
|
|
// Create test posts - mix of regular posts and channel metadata system posts
|
|
first := model.GetMillis()
|
|
|
|
data := []*model.Post{
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "regular post 1", Type: model.PostTypeDefault},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "changed header", Type: model.PostTypeHeaderChange},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "regular post 2", Type: model.PostTypeDefault},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "changed display name", Type: model.PostTypeDisplaynameChange},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "regular post 3", Type: model.PostTypeDefault},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "changed purpose", Type: model.PostTypePurposeChange},
|
|
{Id: model.NewId(), ChannelId: channelID, UserId: model.NewId(), Message: "regular post 4", Type: model.PostTypeDefault},
|
|
}
|
|
|
|
// Save posts
|
|
for i, p := range data {
|
|
p.UpdateAt = first + (int64(i) * 300000)
|
|
p.CreateAt = first + (int64(i) * 300000)
|
|
p.RemoteId = model.NewPointer(model.NewId())
|
|
_, err := ss.Post().Save(rctx, p)
|
|
require.NoError(t, err, "couldn't save post")
|
|
}
|
|
|
|
t.Run("ExcludeChannelMetadataSystemPosts=true should filter out metadata posts", func(t *testing.T) {
|
|
// Set options with ExcludeChannelMetadataSystemPosts = true
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: channelID,
|
|
ExcludeChannelMetadataSystemPosts: true,
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts, _, err := ss.Post().GetPostsSinceForSync(opt, cursor, 100)
|
|
require.NoError(t, err)
|
|
|
|
// Verify only regular posts are returned
|
|
require.Len(t, posts, 4, "should return only 4 regular posts")
|
|
|
|
// Check that only default post types are returned
|
|
for _, post := range posts {
|
|
require.Equal(t, model.PostTypeDefault, post.Type, "only default posts should be returned")
|
|
}
|
|
|
|
// Verify we have the expected post IDs (only regular posts)
|
|
expectedIDs := []string{
|
|
data[0].Id, // regular post 1
|
|
data[2].Id, // regular post 2
|
|
data[4].Id, // regular post 3
|
|
data[6].Id, // regular post 4
|
|
}
|
|
|
|
postIDs := make([]string, 0, len(posts))
|
|
for _, p := range posts {
|
|
postIDs = append(postIDs, p.Id)
|
|
}
|
|
|
|
require.ElementsMatch(t, expectedIDs, postIDs, "returned posts should only be regular posts")
|
|
})
|
|
|
|
t.Run("ExcludeChannelMetadataSystemPosts=false should include all posts", func(t *testing.T) {
|
|
// Set options with ExcludeChannelMetadataSystemPosts = false
|
|
opt := model.GetPostsSinceForSyncOptions{
|
|
ChannelId: channelID,
|
|
ExcludeChannelMetadataSystemPosts: false,
|
|
}
|
|
cursor := model.GetPostsSinceForSyncCursor{}
|
|
posts, _, err := ss.Post().GetPostsSinceForSync(opt, cursor, 100)
|
|
require.NoError(t, err)
|
|
|
|
// Verify all posts are returned
|
|
require.Len(t, posts, 7, "should return all 7 posts when not excluding metadata posts")
|
|
|
|
// Verify all post types are included by counting each type
|
|
postTypeCount := make(map[string]int)
|
|
for _, post := range posts {
|
|
postTypeCount[post.Type]++
|
|
}
|
|
|
|
require.Equal(t, 4, postTypeCount[model.PostTypeDefault], "should have 4 regular posts")
|
|
require.Equal(t, 1, postTypeCount[model.PostTypeHeaderChange], "should have 1 header change post")
|
|
require.Equal(t, 1, postTypeCount[model.PostTypeDisplaynameChange], "should have 1 display name change post")
|
|
require.Equal(t, 1, postTypeCount[model.PostTypePurposeChange], "should have 1 purpose change post")
|
|
})
|
|
}
|
|
|
|
func testRestoreContentFlaggedPost(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
channel := &model.Channel{
|
|
DisplayName: "Test Channel",
|
|
Name: "test_channel",
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel, err := ss.Channel().Save(rctx, channel, -1)
|
|
require.NoError(t, err)
|
|
|
|
botId := model.NewId()
|
|
statusFieldId := model.NewId()
|
|
contentFlaggingManagedFieldId := model.NewId()
|
|
groupId := model.NewId()
|
|
|
|
setupFlaggedPost := func(rootId string) *model.Post {
|
|
post := &model.Post{}
|
|
post.ChannelId = channel.Id
|
|
post.UserId = model.NewId()
|
|
post.Message = NewTestID()
|
|
|
|
if rootId != "" {
|
|
post.RootId = rootId
|
|
}
|
|
|
|
var err error
|
|
post, err = ss.Post().Save(rctx, post)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.Post().Delete(rctx, post.Id, model.GetMillis(), botId)
|
|
require.NoError(t, err)
|
|
|
|
statusPropertyValue := &model.PropertyValue{
|
|
TargetID: post.Id,
|
|
FieldID: statusFieldId,
|
|
Value: fmt.Appendf([]byte{}, "\"%s\"", model.ContentFlaggingStatusPending),
|
|
TargetType: model.PropertyValueTargetTypePost,
|
|
GroupID: groupId,
|
|
}
|
|
_, err = ss.PropertyValue().Create(statusPropertyValue)
|
|
require.NoError(t, err)
|
|
|
|
contentFlaggingManagedPropertyValue := &model.PropertyValue{
|
|
TargetID: post.Id,
|
|
FieldID: contentFlaggingManagedFieldId,
|
|
Value: json.RawMessage("true"),
|
|
TargetType: model.PropertyValueTargetTypePost,
|
|
GroupID: groupId,
|
|
}
|
|
_, err = ss.PropertyValue().Create(contentFlaggingManagedPropertyValue)
|
|
require.NoError(t, err)
|
|
|
|
return post
|
|
}
|
|
|
|
t.Run("Should restore a single root post", func(t *testing.T) {
|
|
post := setupFlaggedPost("")
|
|
|
|
fetchedPost, err := ss.Post().GetSingle(rctx, post.Id, true)
|
|
require.NoError(t, err)
|
|
require.Greater(t, fetchedPost.DeleteAt, int64(0))
|
|
|
|
err = ss.Post().RestoreContentFlaggedPost(post, statusFieldId, contentFlaggingManagedFieldId)
|
|
require.NoError(t, err)
|
|
|
|
fetchedPost, err = ss.Post().GetSingle(rctx, post.Id, false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), fetchedPost.DeleteAt)
|
|
})
|
|
|
|
t.Run("Should restore a thread reply and update thread's reply count", func(t *testing.T) {
|
|
rootPost := &model.Post{}
|
|
rootPost.ChannelId = channel.Id
|
|
rootPost.UserId = model.NewId()
|
|
rootPost.Message = NewTestID()
|
|
|
|
var err error
|
|
rootPost, err = ss.Post().Save(rctx, rootPost)
|
|
require.NoError(t, err)
|
|
|
|
post := setupFlaggedPost(rootPost.Id)
|
|
|
|
err = ss.Post().RestoreContentFlaggedPost(post, statusFieldId, contentFlaggingManagedFieldId)
|
|
require.NoError(t, err)
|
|
|
|
fetchedPost, err := ss.Post().GetSingle(rctx, post.Id, false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), fetchedPost.DeleteAt)
|
|
|
|
thread, err := ss.Thread().Get(rootPost.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(1), thread.ReplyCount)
|
|
})
|
|
}
|