MM-46410: adds urgency on mention counts (#20999)
* MM-46410: adds urgency on mention counts We have introduced priority for posts in https://github.com/mattermost/mattermost-webapp/pull/10951. We do need to color the mention badges in the webapp with a prominent color when a mention is posted in an urgent message. A thread has urgent mentions if the root post is marked as urgent, and the replies contain mentions to the user viewing the thread. This PR adds a column, urgentmentioncount, in channelmembers. Furthermore when asking for team/thread mention counts, we also return urgent mention counts for the user. Adds a new table to hold posts priorities Refactors priority out of the props and into the new table We are nilifying Metadata when post.ForPlugin(), which didn't save Priority for a post when Boards was enabled. This commit copies metadata again to the post, so metadata are reinstated. Co-authored-by: Mattermod <mattermod@users.noreply.github.com> Co-authored-by: Vishal Choudhary <vish9812@gmail.com>
Этот коммит содержится в:
@@ -104,6 +104,7 @@ func TestChannelStore(t *testing.T, ss store.Store, s SqlStore) {
|
||||
t.Run("GetMembersForUserWithCursor", func(t *testing.T) { testChannelStoreGetMembersForUserWithCursor(t, ss) })
|
||||
t.Run("GetMembersForUserWithPagination", func(t *testing.T) { testChannelStoreGetMembersForUserWithPagination(t, ss) })
|
||||
t.Run("CountPostsAfter", func(t *testing.T) { testCountPostsAfter(t, ss) })
|
||||
t.Run("CountUrgentPostsAfter", func(t *testing.T) { testCountUrgentPostsAfter(t, ss) })
|
||||
t.Run("UpdateLastViewedAt", func(t *testing.T) { testChannelStoreUpdateLastViewedAt(t, ss) })
|
||||
t.Run("IncrementMentionCount", func(t *testing.T) { testChannelStoreIncrementMentionCount(t, ss) })
|
||||
t.Run("UpdateChannelMember", func(t *testing.T) { testUpdateChannelMember(t, ss) })
|
||||
@@ -4833,6 +4834,66 @@ func testCountPostsAfter(t *testing.T, ss store.Store) {
|
||||
})
|
||||
}
|
||||
|
||||
func testCountUrgentPostsAfter(t *testing.T, ss store.Store) {
|
||||
t.Run("should count all posts with or without the given user ID", func(t *testing.T) {
|
||||
userId1 := model.NewId()
|
||||
userId2 := model.NewId()
|
||||
|
||||
channelId := model.NewId()
|
||||
|
||||
p1, err := ss.Post().Save(&model.Post{
|
||||
UserId: userId1,
|
||||
ChannelId: channelId,
|
||||
CreateAt: 1000,
|
||||
Metadata: &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(false),
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = ss.Post().Save(&model.Post{
|
||||
UserId: userId1,
|
||||
ChannelId: channelId,
|
||||
CreateAt: 1001,
|
||||
Metadata: &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString("important"),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(false),
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = ss.Post().Save(&model.Post{
|
||||
UserId: userId2,
|
||||
ChannelId: channelId,
|
||||
CreateAt: 1002,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
count, err := ss.Channel().CountUrgentPostsAfter(channelId, p1.CreateAt-1, "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
count, err = ss.Channel().CountUrgentPostsAfter(channelId, p1.CreateAt, "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
|
||||
count, err = ss.Channel().CountUrgentPostsAfter(channelId, p1.CreateAt-1, userId1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
count, err = ss.Channel().CountUrgentPostsAfter(channelId, p1.CreateAt, userId1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
})
|
||||
}
|
||||
|
||||
func testChannelStoreUpdateLastViewedAt(t *testing.T, ss store.Store) {
|
||||
o1 := model.Channel{}
|
||||
o1.TeamId = model.NewId()
|
||||
@@ -4912,16 +4973,16 @@ func testChannelStoreIncrementMentionCount(t *testing.T, ss store.Store) {
|
||||
_, err := ss.Channel().SaveMember(&m1)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ss.Channel().IncrementMentionCount(m1.ChannelId, []string{m1.UserId}, false)
|
||||
err = ss.Channel().IncrementMentionCount(m1.ChannelId, []string{m1.UserId}, false, false)
|
||||
require.NoError(t, err, "failed to update")
|
||||
|
||||
err = ss.Channel().IncrementMentionCount(m1.ChannelId, []string{"missing id"}, false)
|
||||
err = ss.Channel().IncrementMentionCount(m1.ChannelId, []string{"missing id"}, false, false)
|
||||
require.NoError(t, err, "failed to update")
|
||||
|
||||
err = ss.Channel().IncrementMentionCount("missing id", []string{m1.UserId}, false)
|
||||
err = ss.Channel().IncrementMentionCount("missing id", []string{m1.UserId}, false, false)
|
||||
require.NoError(t, err, "failed to update")
|
||||
|
||||
err = ss.Channel().IncrementMentionCount("missing id", []string{"missing id"}, false)
|
||||
err = ss.Channel().IncrementMentionCount("missing id", []string{"missing id"}, false, false)
|
||||
require.NoError(t, err, "failed to update")
|
||||
}
|
||||
|
||||
|
||||
@@ -192,6 +192,27 @@ func (_m *ChannelStore) CountPostsAfter(channelID string, timestamp int64, userI
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// CountUrgentPostsAfter provides a mock function with given fields: channelID, timestamp, userID
|
||||
func (_m *ChannelStore) CountUrgentPostsAfter(channelID string, timestamp int64, userID string) (int, error) {
|
||||
ret := _m.Called(channelID, timestamp, userID)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(string, int64, string) int); ok {
|
||||
r0 = rf(channelID, timestamp, userID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, int64, string) error); ok {
|
||||
r1 = rf(channelID, timestamp, userID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CreateDirectChannel provides a mock function with given fields: userID, otherUserID, channelOptions
|
||||
func (_m *ChannelStore) CreateDirectChannel(userID *model.User, otherUserID *model.User, channelOptions ...model.ChannelOption) (*model.Channel, error) {
|
||||
_va := make([]interface{}, len(channelOptions))
|
||||
@@ -1646,13 +1667,13 @@ func (_m *ChannelStore) GroupSyncedChannelCount() (int64, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// IncrementMentionCount provides a mock function with given fields: channelID, userIDs, isRoot
|
||||
func (_m *ChannelStore) IncrementMentionCount(channelID string, userIDs []string, isRoot bool) error {
|
||||
ret := _m.Called(channelID, userIDs, isRoot)
|
||||
// IncrementMentionCount provides a mock function with given fields: channelID, userIDs, isRoot, isUrgent
|
||||
func (_m *ChannelStore) IncrementMentionCount(channelID string, userIDs []string, isRoot bool, isUrgent bool) error {
|
||||
ret := _m.Called(channelID, userIDs, isRoot, isUrgent)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, []string, bool) error); ok {
|
||||
r0 = rf(channelID, userIDs, isRoot)
|
||||
if rf, ok := ret.Get(0).(func(string, []string, bool, bool) error); ok {
|
||||
r0 = rf(channelID, userIDs, isRoot, isUrgent)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
@@ -2192,13 +2213,13 @@ func (_m *ChannelStore) UpdateLastViewedAt(channelIds []string, userID string) (
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateLastViewedAtPost provides a mock function with given fields: unreadPost, userID, mentionCount, mentionCountRoot, setUnreadCountRoot
|
||||
func (_m *ChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
|
||||
ret := _m.Called(unreadPost, userID, mentionCount, mentionCountRoot, setUnreadCountRoot)
|
||||
// UpdateLastViewedAtPost provides a mock function with given fields: unreadPost, userID, mentionCount, mentionCountRoot, urgentMentionCount, setUnreadCountRoot
|
||||
func (_m *ChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, urgentMentionCount int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
|
||||
ret := _m.Called(unreadPost, userID, mentionCount, mentionCountRoot, urgentMentionCount, setUnreadCountRoot)
|
||||
|
||||
var r0 *model.ChannelUnreadAt
|
||||
if rf, ok := ret.Get(0).(func(*model.Post, string, int, int, bool) *model.ChannelUnreadAt); ok {
|
||||
r0 = rf(unreadPost, userID, mentionCount, mentionCountRoot, setUnreadCountRoot)
|
||||
if rf, ok := ret.Get(0).(func(*model.Post, string, int, int, int, bool) *model.ChannelUnreadAt); ok {
|
||||
r0 = rf(unreadPost, userID, mentionCount, mentionCountRoot, urgentMentionCount, setUnreadCountRoot)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.ChannelUnreadAt)
|
||||
@@ -2206,8 +2227,8 @@ func (_m *ChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID st
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*model.Post, string, int, int, bool) error); ok {
|
||||
r1 = rf(unreadPost, userID, mentionCount, mentionCountRoot, setUnreadCountRoot)
|
||||
if rf, ok := ret.Get(1).(func(*model.Post, string, int, int, int, bool) error); ok {
|
||||
r1 = rf(unreadPost, userID, mentionCount, mentionCountRoot, urgentMentionCount, setUnreadCountRoot)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
61
store/storetest/mocks/PostPriorityStore.go
Обычный файл
61
store/storetest/mocks/PostPriorityStore.go
Обычный файл
@@ -0,0 +1,61 @@
|
||||
// Code generated by mockery v2.10.4. DO NOT EDIT.
|
||||
|
||||
// Regenerate this file using `make store-mocks`.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
model "github.com/mattermost/mattermost-server/v6/model"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// PostPriorityStore is an autogenerated mock type for the PostPriorityStore type
|
||||
type PostPriorityStore struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GetForPost provides a mock function with given fields: postId
|
||||
func (_m *PostPriorityStore) GetForPost(postId string) (*model.PostPriority, error) {
|
||||
ret := _m.Called(postId)
|
||||
|
||||
var r0 *model.PostPriority
|
||||
if rf, ok := ret.Get(0).(func(string) *model.PostPriority); ok {
|
||||
r0 = rf(postId)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.PostPriority)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(postId)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetForPosts provides a mock function with given fields: ids
|
||||
func (_m *PostPriorityStore) GetForPosts(ids []string) ([]*model.PostPriority, error) {
|
||||
ret := _m.Called(ids)
|
||||
|
||||
var r0 []*model.PostPriority
|
||||
if rf, ok := ret.Get(0).(func([]string) []*model.PostPriority); ok {
|
||||
r0 = rf(ids)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.PostPriority)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func([]string) error); ok {
|
||||
r1 = rf(ids)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
@@ -475,6 +475,22 @@ func (_m *Store) Post() store.PostStore {
|
||||
return r0
|
||||
}
|
||||
|
||||
// PostPriority provides a mock function with given fields:
|
||||
func (_m *Store) PostPriority() store.PostPriorityStore {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 store.PostPriorityStore
|
||||
if rf, ok := ret.Get(0).(func() store.PostPriorityStore); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.PostPriorityStore)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Preference provides a mock function with given fields:
|
||||
func (_m *Store) Preference() store.PreferenceStore {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -119,13 +119,13 @@ func (_m *ThreadStore) GetMembershipsForUser(userId string, teamID string) ([]*m
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetTeamsUnreadForUser provides a mock function with given fields: userID, teamIDs
|
||||
func (_m *ThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string) (map[string]*model.TeamUnread, error) {
|
||||
ret := _m.Called(userID, teamIDs)
|
||||
// GetTeamsUnreadForUser provides a mock function with given fields: userID, teamIDs, includeUrgentMentionCount
|
||||
func (_m *ThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string, includeUrgentMentionCount bool) (map[string]*model.TeamUnread, error) {
|
||||
ret := _m.Called(userID, teamIDs, includeUrgentMentionCount)
|
||||
|
||||
var r0 map[string]*model.TeamUnread
|
||||
if rf, ok := ret.Get(0).(func(string, []string) map[string]*model.TeamUnread); ok {
|
||||
r0 = rf(userID, teamIDs)
|
||||
if rf, ok := ret.Get(0).(func(string, []string, bool) map[string]*model.TeamUnread); ok {
|
||||
r0 = rf(userID, teamIDs, includeUrgentMentionCount)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(map[string]*model.TeamUnread)
|
||||
@@ -133,8 +133,8 @@ func (_m *ThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string) (m
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, []string) error); ok {
|
||||
r1 = rf(userID, teamIDs)
|
||||
if rf, ok := ret.Get(1).(func(string, []string, bool) error); ok {
|
||||
r1 = rf(userID, teamIDs, includeUrgentMentionCount)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -165,13 +165,13 @@ func (_m *ThreadStore) GetThreadFollowers(threadID string, fetchOnlyActive bool)
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetThreadForUser provides a mock function with given fields: threadMembership, extended
|
||||
func (_m *ThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool) (*model.ThreadResponse, error) {
|
||||
ret := _m.Called(threadMembership, extended)
|
||||
// GetThreadForUser provides a mock function with given fields: threadMembership, extended, postPriorityIsEnabled
|
||||
func (_m *ThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool, postPriorityIsEnabled bool) (*model.ThreadResponse, error) {
|
||||
ret := _m.Called(threadMembership, extended, postPriorityIsEnabled)
|
||||
|
||||
var r0 *model.ThreadResponse
|
||||
if rf, ok := ret.Get(0).(func(*model.ThreadMembership, bool) *model.ThreadResponse); ok {
|
||||
r0 = rf(threadMembership, extended)
|
||||
if rf, ok := ret.Get(0).(func(*model.ThreadMembership, bool, bool) *model.ThreadResponse); ok {
|
||||
r0 = rf(threadMembership, extended, postPriorityIsEnabled)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.ThreadResponse)
|
||||
@@ -179,8 +179,8 @@ func (_m *ThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*model.ThreadMembership, bool) error); ok {
|
||||
r1 = rf(threadMembership, extended)
|
||||
if rf, ok := ret.Get(1).(func(*model.ThreadMembership, bool, bool) error); ok {
|
||||
r1 = rf(threadMembership, extended, postPriorityIsEnabled)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -341,6 +341,27 @@ func (_m *ThreadStore) GetTotalUnreadThreads(userId string, teamID string, opts
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetTotalUnreadUrgentMentions provides a mock function with given fields: userId, teamID, opts
|
||||
func (_m *ThreadStore) GetTotalUnreadUrgentMentions(userId string, teamID string, opts model.GetUserThreadsOpts) (int64, error) {
|
||||
ret := _m.Called(userId, teamID, opts)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(string, string, model.GetUserThreadsOpts) int64); ok {
|
||||
r0 = rf(userId, teamID, opts)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, string, model.GetUserThreadsOpts) error); ok {
|
||||
r1 = rf(userId, teamID, opts)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MaintainMembership provides a mock function with given fields: userID, postID, opts
|
||||
func (_m *ThreadStore) MaintainMembership(userID string, postID string, opts store.ThreadMembershipOpts) (*model.ThreadMembership, error) {
|
||||
ret := _m.Called(userID, postID, opts)
|
||||
|
||||
72
store/storetest/post_priority_store.go
Обычный файл
72
store/storetest/post_priority_store.go
Обычный файл
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package storetest
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/store"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPostPriorityStore(t *testing.T, ss store.Store, s SqlStore) {
|
||||
t.Run("GetForPost", func(t *testing.T) { testPostPriorityStoreGetForPost(t, ss) })
|
||||
}
|
||||
|
||||
func testPostPriorityStoreGetForPost(t *testing.T, ss store.Store) {
|
||||
|
||||
t.Run("Save post priority when in post's metadata", func(t *testing.T) {
|
||||
p1 := model.Post{}
|
||||
p1.ChannelId = model.NewId()
|
||||
p1.UserId = model.NewId()
|
||||
p1.Message = NewTestId()
|
||||
p1.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString("important"),
|
||||
RequestedAck: model.NewBool(true),
|
||||
PersistentNotifications: model.NewBool(false),
|
||||
},
|
||||
}
|
||||
|
||||
p2 := model.Post{}
|
||||
p2.ChannelId = model.NewId()
|
||||
p2.UserId = model.NewId()
|
||||
p2.Message = NewTestId()
|
||||
p2.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p3 := model.Post{}
|
||||
p3.ChannelId = model.NewId()
|
||||
p3.UserId = model.NewId()
|
||||
p3.Message = NewTestId()
|
||||
|
||||
_, errIdx, err := ss.Post().SaveMultiple([]*model.Post{&p1, &p2, &p3})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, -1, errIdx)
|
||||
|
||||
pp1, err := ss.PostPriority().GetForPost(p1.Id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "important", *pp1.Priority)
|
||||
assert.Equal(t, true, *pp1.RequestedAck)
|
||||
assert.Equal(t, false, *pp1.PersistentNotifications)
|
||||
|
||||
pp2, err := ss.PostPriority().GetForPost(p2.Id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, model.PostPriorityUrgent, *pp2.Priority)
|
||||
assert.Equal(t, false, *pp2.RequestedAck)
|
||||
assert.Equal(t, true, *pp2.PersistentNotifications)
|
||||
|
||||
_, err = ss.PostPriority().GetForPost(p3.Id)
|
||||
assert.True(t, errors.Is(err, sql.ErrNoRows))
|
||||
})
|
||||
}
|
||||
@@ -238,6 +238,31 @@ func testPostStoreSave(t *testing.T, ss store.Store) {
|
||||
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.NewString("important"),
|
||||
RequestedAck: model.NewBool(true),
|
||||
PersistentNotifications: model.NewBool(false),
|
||||
},
|
||||
}
|
||||
|
||||
p, err := ss.Post().Save(&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, ss store.Store) {
|
||||
|
||||
@@ -56,6 +56,7 @@ type Store struct {
|
||||
ProductNoticesStore mocks.ProductNoticesStore
|
||||
context context.Context
|
||||
NotifyAdminStore mocks.NotifyAdminStore
|
||||
PostPriorityStore mocks.PostPriorityStore
|
||||
}
|
||||
|
||||
func (s *Store) SetContext(context context.Context) { s.context = context }
|
||||
@@ -100,6 +101,7 @@ func (s *Store) NotifyAdmin() store.NotifyAdminStore { return &s.NotifyAdmin
|
||||
func (s *Store) Group() store.GroupStore { return &s.GroupStore }
|
||||
func (s *Store) LinkMetadata() store.LinkMetadataStore { return &s.LinkMetadataStore }
|
||||
func (s *Store) SharedChannel() store.SharedChannelStore { return &s.SharedChannelStore }
|
||||
func (s *Store) PostPriority() store.PostPriorityStore { return &s.PostPriorityStore }
|
||||
func (s *Store) MarkSystemRanUnitTests() { /* do nothing */ }
|
||||
func (s *Store) Close() { /* do nothing */ }
|
||||
func (s *Store) LockToMaster() { /* do nothing */ }
|
||||
@@ -158,5 +160,6 @@ func (s *Store) AssertExpectations(t mock.TestingT) bool {
|
||||
&s.ProductNoticesStore,
|
||||
&s.SharedChannelStore,
|
||||
&s.NotifyAdminStore,
|
||||
&s.PostPriorityStore,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestThreadStore(t *testing.T, ss store.Store, s SqlStore) {
|
||||
}
|
||||
|
||||
func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
makeSomePosts := func() []*model.Post {
|
||||
makeSomePosts := func(urgent bool) []*model.Post {
|
||||
|
||||
u1 := model.User{
|
||||
Email: MakeEmail(),
|
||||
@@ -61,6 +61,16 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
o.UserId = u.Id
|
||||
o.Message = NewTestId()
|
||||
|
||||
if urgent {
|
||||
o.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(false),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
otmp, err3 := ss.Post().Save(&o)
|
||||
require.NoError(t, err3)
|
||||
o2 := model.Post{}
|
||||
@@ -100,7 +110,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
return newPosts
|
||||
}
|
||||
t.Run("Save replies creates a thread", func(t *testing.T) {
|
||||
newPosts := makeSomePosts()
|
||||
newPosts := makeSomePosts(false)
|
||||
thread, err := ss.Thread().Get(newPosts[0].Id)
|
||||
require.NoError(t, err, "couldn't get thread")
|
||||
require.NotNil(t, thread)
|
||||
@@ -133,7 +143,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
})
|
||||
|
||||
t.Run("Delete a reply updates count on a thread", func(t *testing.T) {
|
||||
newPosts := makeSomePosts()
|
||||
newPosts := makeSomePosts(false)
|
||||
thread, err := ss.Thread().Get(newPosts[0].Id)
|
||||
require.NoError(t, err, "couldn't get thread")
|
||||
require.NotNil(t, thread)
|
||||
@@ -307,7 +317,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
})
|
||||
|
||||
t.Run("Thread membership 'viewed' timestamp is updated properly", func(t *testing.T) {
|
||||
newPosts := makeSomePosts()
|
||||
newPosts := makeSomePosts(false)
|
||||
|
||||
opts := store.ThreadMembershipOpts{
|
||||
Following: true,
|
||||
@@ -341,7 +351,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
})
|
||||
|
||||
t.Run("Thread membership 'viewed' timestamp is updated properly for new membership", func(t *testing.T) {
|
||||
newPosts := makeSomePosts()
|
||||
newPosts := makeSomePosts(false)
|
||||
|
||||
opts := store.ThreadMembershipOpts{
|
||||
Following: true,
|
||||
@@ -356,7 +366,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
})
|
||||
|
||||
t.Run("Updating post does not make thread unread", func(t *testing.T) {
|
||||
newPosts := makeSomePosts()
|
||||
newPosts := makeSomePosts(false)
|
||||
opts := store.ThreadMembershipOpts{
|
||||
Following: true,
|
||||
IncrementMentions: false,
|
||||
@@ -366,14 +376,14 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
}
|
||||
m, err := ss.Thread().MaintainMembership(newPosts[0].UserId, newPosts[0].Id, opts)
|
||||
require.NoError(t, err)
|
||||
th, err := ss.Thread().GetThreadForUser(m, false)
|
||||
th, err := ss.Thread().GetThreadForUser(m, false, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(2), th.UnreadReplies)
|
||||
|
||||
m.LastViewed = newPosts[2].UpdateAt + 1
|
||||
_, err = ss.Thread().UpdateMembership(m)
|
||||
require.NoError(t, err)
|
||||
th, err = ss.Thread().GetThreadForUser(m, false)
|
||||
th, err = ss.Thread().GetThreadForUser(m, false, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(0), th.UnreadReplies)
|
||||
|
||||
@@ -382,13 +392,13 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
_, err = ss.Post().Update(editedPost, newPosts[2])
|
||||
require.NoError(t, err)
|
||||
|
||||
th, err = ss.Thread().GetThreadForUser(m, false)
|
||||
th, err = ss.Thread().GetThreadForUser(m, false, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(0), th.UnreadReplies)
|
||||
})
|
||||
|
||||
t.Run("Empty participantID should not appear in thread response", func(t *testing.T) {
|
||||
newPosts := makeSomePosts()
|
||||
newPosts := makeSomePosts(false)
|
||||
opts := store.ThreadMembershipOpts{
|
||||
Following: true,
|
||||
IncrementMentions: false,
|
||||
@@ -399,7 +409,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
m, err := ss.Thread().MaintainMembership("", newPosts[0].Id, opts)
|
||||
require.NoError(t, err)
|
||||
m.UserId = newPosts[0].UserId
|
||||
th, err := ss.Thread().GetThreadForUser(m, true)
|
||||
th, err := ss.Thread().GetThreadForUser(m, true, false)
|
||||
require.NoError(t, err)
|
||||
for _, user := range th.Participants {
|
||||
require.NotNil(t, user)
|
||||
@@ -407,7 +417,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
})
|
||||
t.Run("Get unread reply counts for thread", func(t *testing.T) {
|
||||
t.Skip("MM-41797")
|
||||
newPosts := makeSomePosts()
|
||||
newPosts := makeSomePosts(false)
|
||||
opts := store.ThreadMembershipOpts{
|
||||
Following: true,
|
||||
IncrementMentions: false,
|
||||
@@ -435,6 +445,36 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(2), unreads)
|
||||
})
|
||||
|
||||
testCases := []bool{true, false}
|
||||
|
||||
for _, isUrgent := range testCases {
|
||||
t.Run("Return is urgent for user thread/s", func(t *testing.T) {
|
||||
newPosts := makeSomePosts(isUrgent)
|
||||
opts := store.ThreadMembershipOpts{
|
||||
Following: true,
|
||||
IncrementMentions: false,
|
||||
UpdateFollowing: true,
|
||||
UpdateViewedTimestamp: true,
|
||||
UpdateParticipants: false,
|
||||
}
|
||||
|
||||
userID := newPosts[0].UserId
|
||||
_, e := ss.Thread().MaintainMembership(userID, newPosts[0].Id, opts)
|
||||
require.NoError(t, e)
|
||||
|
||||
m, e := ss.Thread().GetMembershipForUser(userID, newPosts[0].Id)
|
||||
require.NoError(t, e)
|
||||
|
||||
th, e := ss.Thread().GetThreadForUser(m, false, true)
|
||||
require.NoError(t, e)
|
||||
require.Equal(t, isUrgent, th.IsUrgent)
|
||||
|
||||
threads, e := ss.Thread().GetThreadsForUser(userID, "", model.GetUserThreadsOpts{IncludeIsUrgent: true})
|
||||
require.NoError(t, e)
|
||||
require.Equal(t, isUrgent, threads[0].IsUrgent)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func threadStoreCreateReply(t *testing.T, ss store.Store, channelID, postID, userID string, createAt int64) *model.Post {
|
||||
@@ -660,7 +700,7 @@ func testGetTeamsUnreadForUser(t *testing.T, ss store.Store) {
|
||||
threadStoreCreateReply(t, ss, channel1.Id, post.Id, post.UserId, model.GetMillis())
|
||||
createThreadMembership(userID, post.Id)
|
||||
|
||||
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id})
|
||||
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id}, true)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, teamsUnread, 1)
|
||||
assert.Equal(t, int64(1), teamsUnread[team1.Id].ThreadCount)
|
||||
@@ -674,7 +714,7 @@ func testGetTeamsUnreadForUser(t *testing.T, ss store.Store) {
|
||||
threadStoreCreateReply(t, ss, channel1.Id, post.Id, post.UserId, model.GetMillis())
|
||||
createThreadMembership(userID, post.Id)
|
||||
|
||||
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id})
|
||||
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id}, true)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, teamsUnread, 1)
|
||||
assert.Equal(t, int64(2), teamsUnread[team1.Id].ThreadCount)
|
||||
@@ -693,16 +733,24 @@ func testGetTeamsUnreadForUser(t *testing.T, ss store.Store) {
|
||||
Type: model.ChannelTypeOpen,
|
||||
}, -1)
|
||||
require.NoError(t, err)
|
||||
|
||||
post2, err := ss.Post().Save(&model.Post{
|
||||
ChannelId: channel2.Id,
|
||||
UserId: userID,
|
||||
Message: model.NewRandomString(10),
|
||||
Metadata: &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(false),
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
threadStoreCreateReply(t, ss, channel2.Id, post2.Id, post2.UserId, model.GetMillis())
|
||||
createThreadMembership(userID, post2.Id)
|
||||
|
||||
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id, team2.Id})
|
||||
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id, team2.Id}, true)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, teamsUnread, 2)
|
||||
assert.Equal(t, int64(2), teamsUnread[team1.Id].ThreadCount)
|
||||
@@ -715,11 +763,12 @@ func testGetTeamsUnreadForUser(t *testing.T, ss store.Store) {
|
||||
_, err = ss.Thread().MaintainMembership(userID, post2.Id, opts)
|
||||
require.NoError(t, err)
|
||||
|
||||
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team2.Id})
|
||||
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team2.Id}, true)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, teamsUnread, 1)
|
||||
assert.Equal(t, int64(1), teamsUnread[team2.Id].ThreadCount)
|
||||
assert.Equal(t, int64(1), teamsUnread[team2.Id].ThreadMentionCount)
|
||||
assert.Equal(t, int64(1), teamsUnread[team2.Id].ThreadUrgentMentionCount)
|
||||
}
|
||||
|
||||
type byPostId []*model.Post
|
||||
@@ -831,6 +880,13 @@ func testVarious(t *testing.T, ss store.Store) {
|
||||
ChannelId: team1channel1.Id,
|
||||
UserId: user1ID,
|
||||
Message: model.NewRandomString(10),
|
||||
Metadata: &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(false),
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1032,6 +1088,33 @@ func testVarious(t *testing.T, ss store.Store) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetTotalUnreadUrgentMentions", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Description string
|
||||
UserID string
|
||||
TeamID string
|
||||
Options model.GetUserThreadsOpts
|
||||
ExpectedThreads []*model.Post
|
||||
}{
|
||||
{"all teams, user1", user1ID, "", model.GetUserThreadsOpts{}, []*model.Post{
|
||||
team1channel1post3,
|
||||
}},
|
||||
{"team1, user1", user1ID, team1.Id, model.GetUserThreadsOpts{}, []*model.Post{
|
||||
team1channel1post3,
|
||||
}},
|
||||
{"team2, user1", user1ID, team2.Id, model.GetUserThreadsOpts{}, []*model.Post{}},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.Description, func(t *testing.T) {
|
||||
totalUnreadUrgentMentions, err := ss.Thread().GetTotalUnreadUrgentMentions(testCase.UserID, testCase.TeamID, testCase.Options)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, int64(len(testCase.ExpectedThreads)), totalUnreadUrgentMentions)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
assertThreadPosts := func(t *testing.T, threads []*model.ThreadResponse, expectedPosts []*model.Post) {
|
||||
t.Helper()
|
||||
|
||||
@@ -1166,7 +1249,7 @@ func testMarkAllAsReadByChannels(t *testing.T, ss store.Store) {
|
||||
assertThreadReplyCount := func(t *testing.T, userID string, count int64) {
|
||||
t.Helper()
|
||||
|
||||
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id})
|
||||
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id}, false)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, teamsUnread, 1, "unexpected unread teams count")
|
||||
assert.Equal(t, count, teamsUnread[team1.Id].ThreadCount, "unexpected thread count")
|
||||
@@ -1623,7 +1706,7 @@ func testMarkAllAsReadByTeam(t *testing.T, ss store.Store) {
|
||||
assertThreadReplyCount := func(t *testing.T, userID, teamID string, count int64, message string) {
|
||||
t.Helper()
|
||||
|
||||
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{teamID})
|
||||
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{teamID}, true)
|
||||
require.NoError(t, err)
|
||||
require.Lenf(t, teamsUnread, 1, "unexpected unread teams count: %s", message)
|
||||
assert.Equalf(t, count, teamsUnread[teamID].ThreadCount, "unexpected thread count: %s", message)
|
||||
|
||||
@@ -2468,7 +2468,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
|
||||
// Post one message with mention to open channel
|
||||
_, nErr = ss.Post().Save(&p1)
|
||||
require.NoError(t, nErr)
|
||||
nErr = ss.Channel().IncrementMentionCount(c1.Id, []string{u2.Id, u3.Id}, false)
|
||||
nErr = ss.Channel().IncrementMentionCount(c1.Id, []string{u2.Id, u3.Id}, false, false)
|
||||
require.NoError(t, nErr)
|
||||
|
||||
// Post 2 messages without mention to direct channel
|
||||
@@ -2479,7 +2479,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
|
||||
|
||||
_, nErr = ss.Post().Save(&p2)
|
||||
require.NoError(t, nErr)
|
||||
nErr = ss.Channel().IncrementMentionCount(c2.Id, []string{u2.Id}, false)
|
||||
nErr = ss.Channel().IncrementMentionCount(c2.Id, []string{u2.Id}, false, false)
|
||||
require.NoError(t, nErr)
|
||||
|
||||
p3 := model.Post{}
|
||||
@@ -2489,7 +2489,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
|
||||
_, nErr = ss.Post().Save(&p3)
|
||||
require.NoError(t, nErr)
|
||||
|
||||
nErr = ss.Channel().IncrementMentionCount(c2.Id, []string{u2.Id}, false)
|
||||
nErr = ss.Channel().IncrementMentionCount(c2.Id, []string{u2.Id}, false, false)
|
||||
require.NoError(t, nErr)
|
||||
|
||||
badge, unreadCountErr := ss.User().GetUnreadCount(u2.Id, false)
|
||||
@@ -2501,7 +2501,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
|
||||
require.Equal(t, int64(1), badge, "should have 1 unread message")
|
||||
|
||||
// Increment root mentions by 1
|
||||
nErr = ss.Channel().IncrementMentionCount(c1.Id, []string{u3.Id}, true)
|
||||
nErr = ss.Channel().IncrementMentionCount(c1.Id, []string{u3.Id}, true, false)
|
||||
require.NoError(t, nErr)
|
||||
|
||||
// CRT is enabled, only root mentions are counted
|
||||
|
||||
Ссылка в новой задаче
Block a user