MM-21552: Adding SaveMultiple to posts (#13766)

* Adding SaveMultiple to posts

* Improving tests

* fixing i18n

* Fixing tests

* Improving testing on top of Save and SaveMultiple

* Fixing shadow variables

* Addressing some PR comments

* More clear update post test

* Addressing some PR comments

* Addressing some PR comments and simplifying the code

* Improting replies in bulk too

* Fixing reply count and processing last imported replies

* Adding OverwriteMultiple to posts aggregating everything in the same transaction

* Adding 2 pending tests to implement

* Adding tests for overwrite multiple posts

* Adding tests for TeamStore.GetByNames method

* Fixing shadow variables

* Addressing PR comments

* Extracting i18n strings

* Fixing tests

* Fixing tests

* Adding more test cases

* Using a variable instead of a fake timestamp
Этот коммит содержится в:
Jesús Espino
2020-03-11 14:29:32 +01:00
коммит произвёл GitHub
родитель 2bec92a404
Коммит 27d536b212
14 изменённых файлов: 2208 добавлений и 767 удалений

Просмотреть файл

@@ -30,6 +30,33 @@ type SqlPostStore struct {
func (s *SqlPostStore) ClearCaches() {
}
func postSliceColumns() []string {
return []string{"Id", "CreateAt", "UpdateAt", "EditAt", "DeleteAt", "IsPinned", "UserId", "ChannelId", "RootId", "ParentId", "OriginalId", "Message", "Type", "Props", "Hashtags", "Filenames", "FileIds", "HasReactions"}
}
func postToSlice(post *model.Post) []interface{} {
return []interface{}{
post.Id,
post.CreateAt,
post.UpdateAt,
post.EditAt,
post.DeleteAt,
post.IsPinned,
post.UserId,
post.ChannelId,
post.RootId,
post.ParentId,
post.OriginalId,
post.Message,
post.Type,
model.StringInterfaceToJson(post.Props),
post.Hashtags,
model.ArrayToJson(post.Filenames),
model.ArrayToJson(post.FileIds),
post.HasReactions,
}
}
func newSqlPostStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.PostStore {
s := &SqlPostStore{
SqlStore: sqlStore,
@@ -72,48 +99,97 @@ func (s *SqlPostStore) createIndexesIfNotExists() {
s.CreateFullTextIndexIfNotExists("idx_posts_hashtags_txt", "Posts", "Hashtags")
}
func (s *SqlPostStore) Save(post *model.Post) (*model.Post, *model.AppError) {
if len(post.Id) > 0 {
return nil, model.NewAppError("SqlPostStore.Save", "store.sql_post.save.existing.app_error", nil, "id="+post.Id, http.StatusBadRequest)
}
maxPostSize := s.GetMaxPostSize()
post.PreSave()
if err := post.IsValid(maxPostSize); err != nil {
return nil, err
}
if err := s.GetMaster().Insert(post); err != nil {
return nil, model.NewAppError("SqlPostStore.Save", "store.sql_post.save.app_error", nil, "id="+post.Id+", "+err.Error(), http.StatusInternalServerError)
}
time := post.UpdateAt
if !post.IsJoinLeaveMessage() {
if _, err := s.GetMaster().Exec("UPDATE Channels SET LastPostAt = GREATEST(:LastPostAt, LastPostAt), TotalMsgCount = TotalMsgCount + 1 WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": time, "ChannelId": post.ChannelId}); err != nil {
mlog.Error("Error updating Channel LastPostAt.", mlog.Err(err))
func (s *SqlPostStore) SaveMultiple(posts []*model.Post) ([]*model.Post, *model.AppError) {
channelNewPosts := make(map[string]int)
maxDateNewPosts := make(map[string]int64)
rootIds := make(map[string]int)
maxDateRootIds := make(map[string]int64)
for _, post := range posts {
if len(post.Id) > 0 {
return nil, model.NewAppError("SqlPostStore.Save", "store.sql_post.save.existing.app_error", nil, "id="+post.Id, http.StatusBadRequest)
}
} else {
// don't update TotalMsgCount for unimportant messages so that the channel isn't marked as unread
if _, err := s.GetMaster().Exec("UPDATE Channels SET LastPostAt = :LastPostAt WHERE Id = :ChannelId AND LastPostAt < :LastPostAt", map[string]interface{}{"LastPostAt": time, "ChannelId": post.ChannelId}); err != nil {
post.PreSave()
maxPostSize := s.GetMaxPostSize()
if err := post.IsValid(maxPostSize); err != nil {
return nil, err
}
currentChannelCount, ok := channelNewPosts[post.ChannelId]
if !ok {
if post.IsJoinLeaveMessage() {
channelNewPosts[post.ChannelId] = 0
} else {
channelNewPosts[post.ChannelId] = 1
}
maxDateNewPosts[post.ChannelId] = post.CreateAt
} else {
if !post.IsJoinLeaveMessage() {
channelNewPosts[post.ChannelId] = currentChannelCount + 1
}
if post.CreateAt > maxDateNewPosts[post.ChannelId] {
maxDateNewPosts[post.ChannelId] = post.CreateAt
}
}
if len(post.RootId) == 0 {
continue
}
currentRootCount, ok := rootIds[post.RootId]
if !ok {
rootIds[post.RootId] = 1
maxDateRootIds[post.RootId] = post.CreateAt
} else {
rootIds[post.RootId] = currentRootCount + 1
if post.CreateAt > maxDateRootIds[post.RootId] {
maxDateRootIds[post.RootId] = post.CreateAt
}
}
}
query := s.getQueryBuilder().Insert("Posts").Columns(postSliceColumns()...)
for _, post := range posts {
query = query.Values(postToSlice(post)...)
}
sql, args, err := query.ToSql()
if err != nil {
return nil, model.NewAppError("SqlPostStore.Save", "store.sql_post.save.app_error", nil, err.Error(), http.StatusInternalServerError)
}
if _, err := s.GetMaster().Exec(sql, args...); err != nil {
return nil, model.NewAppError("SqlPostStore.Save", "store.sql_post.save.app_error", nil, err.Error(), http.StatusInternalServerError)
}
for channelId, count := range channelNewPosts {
if _, err := s.GetMaster().Exec("UPDATE Channels SET LastPostAt = GREATEST(:LastPostAt, LastPostAt), TotalMsgCount = TotalMsgCount + :Count WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": maxDateNewPosts[channelId], "ChannelId": channelId, "Count": count}); err != nil {
mlog.Error("Error updating Channel LastPostAt.", mlog.Err(err))
}
}
if len(post.RootId) > 0 {
if _, err := s.GetMaster().Exec("UPDATE Posts SET UpdateAt = :UpdateAt WHERE Id = :RootId", map[string]interface{}{"UpdateAt": time, "RootId": post.RootId}); err != nil {
for rootId := range rootIds {
if _, err := s.GetMaster().Exec("UPDATE Posts SET UpdateAt = :UpdateAt WHERE Id = :RootId", map[string]interface{}{"UpdateAt": maxDateRootIds[rootId], "RootId": rootId}); err != nil {
mlog.Error("Error updating Post UpdateAt.", mlog.Err(err))
}
} else {
if count, err := s.GetMaster().SelectInt("SELECT COUNT(*) FROM Posts WHERE RootId = :Id", map[string]interface{}{"Id": post.Id}); err != nil {
mlog.Error("Error fetching post's thread.", mlog.Err(err))
} else {
post.ReplyCount = count
}
for _, post := range posts {
if len(post.RootId) == 0 {
count, ok := rootIds[post.Id]
if ok {
post.ReplyCount += int64(count)
}
}
}
return post, nil
return posts, nil
}
func (s *SqlPostStore) Save(post *model.Post) (*model.Post, *model.AppError) {
posts, err := s.SaveMultiple([]*model.Post{post})
if err != nil {
return nil, err
}
return posts[0], nil
}
func (s *SqlPostStore) Update(newPost *model.Post, oldPost *model.Post) (*model.Post, *model.AppError) {
@@ -149,19 +225,45 @@ func (s *SqlPostStore) Update(newPost *model.Post, oldPost *model.Post) (*model.
return newPost, nil
}
func (s *SqlPostStore) Overwrite(post *model.Post) (*model.Post, *model.AppError) {
post.UpdateAt = model.GetMillis()
func (s *SqlPostStore) OverwriteMultiple(posts []*model.Post) ([]*model.Post, *model.AppError) {
updateAt := model.GetMillis()
maxPostSize := s.GetMaxPostSize()
if appErr := post.IsValid(maxPostSize); appErr != nil {
return nil, appErr
for _, post := range posts {
post.UpdateAt = updateAt
if appErr := post.IsValid(maxPostSize); appErr != nil {
return nil, appErr
}
}
if _, err := s.GetMaster().Update(post); err != nil {
return nil, model.NewAppError("SqlPostStore.Overwrite", "store.sql_post.overwrite.app_error", nil, "id="+post.Id+", "+err.Error(), http.StatusInternalServerError)
tx, err := s.GetMaster().Begin()
if err != nil {
return nil, model.NewAppError("SqlPostStore.Overwrite", "store.sql_post.overwrite.app_error", nil, err.Error(), http.StatusInternalServerError)
}
for _, post := range posts {
if _, err = tx.Update(post); err != nil {
txErr := tx.Rollback()
if txErr != nil {
return nil, model.NewAppError("SqlPostStore.Overwrite", "store.sql_post.overwrite.app_error", nil, txErr.Error(), http.StatusInternalServerError)
}
return nil, model.NewAppError("SqlPostStore.Overwrite", "store.sql_post.overwrite.app_error", nil, "id="+post.Id+", "+err.Error(), http.StatusInternalServerError)
}
}
err = tx.Commit()
if err != nil {
return nil, model.NewAppError("SqlPostStore.Overwrite", "store.sql_post.overwrite.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return post, nil
return posts, nil
}
func (s *SqlPostStore) Overwrite(post *model.Post) (*model.Post, *model.AppError) {
posts, err := s.OverwriteMultiple([]*model.Post{post})
if err != nil {
return nil, err
}
return posts[0], nil
}
func (s *SqlPostStore) GetFlaggedPosts(userId string, offset int, limit int) (*model.PostList, *model.AppError) {

Просмотреть файл

@@ -13,6 +13,7 @@ import (
"github.com/mattermost/gorp"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/store"
"github.com/mattermost/mattermost-server/v5/utils"
)
const (
@@ -287,6 +288,33 @@ func (s SqlTeamStore) GetByName(name string) (*model.Team, *model.AppError) {
return &team, nil
}
func (s SqlTeamStore) GetByNames(names []string) ([]*model.Team, *model.AppError) {
uniqueNames := utils.RemoveDuplicatesFromStringArray(names)
query := s.getQueryBuilder().
Select("*").
From("Teams").
Where(sq.Eq{"Name": uniqueNames})
queryString, args, err := query.ToSql()
if err != nil {
return nil, model.NewAppError("SqlTeamStore.GetByNames", "store.sql_team.get_by_names.app_error", nil, err.Error(), http.StatusInternalServerError)
}
teams := []*model.Team{}
_, err = s.GetReplica().Select(&teams, queryString, args...)
if err != nil {
if err == sql.ErrNoRows {
return nil, model.NewAppError("SqlTeamStore.GetByNames", "store.sql_team.get_by_names.missing.app_error", nil, err.Error(), http.StatusNotFound)
}
return nil, model.NewAppError("SqlTeamStore.GetByNames", "store.sql_team.get_by_names.app_error", nil, err.Error(), http.StatusInternalServerError)
}
if len(teams) != len(uniqueNames) {
return nil, model.NewAppError("SqlTeamStore.GetByNames", "store.sql_team.get_by_names.missing.app_error", nil, "", http.StatusNotFound)
}
return teams, nil
}
func (s SqlTeamStore) SearchAll(term string) ([]*model.Team, *model.AppError) {
var teams []*model.Team

Просмотреть файл

@@ -67,6 +67,7 @@ type TeamStore interface {
Update(team *model.Team) (*model.Team, *model.AppError)
Get(id string) (*model.Team, *model.AppError)
GetByName(name string) (*model.Team, *model.AppError)
GetByNames(name []string) ([]*model.Team, *model.AppError)
SearchAll(term string) ([]*model.Team, *model.AppError)
SearchAllPaged(term string, page int, perPage int) ([]*model.Team, int64, *model.AppError)
SearchOpen(term string) ([]*model.Team, *model.AppError)
@@ -218,6 +219,7 @@ type ChannelMemberHistoryStore interface {
}
type PostStore interface {
SaveMultiple(posts []*model.Post) ([]*model.Post, *model.AppError)
Save(post *model.Post) (*model.Post, *model.AppError)
Update(newPost *model.Post, oldPost *model.Post) (*model.Post, *model.AppError)
Get(id string, skipFetchThreads bool) (*model.PostList, *model.AppError)
@@ -245,6 +247,7 @@ type PostStore interface {
InvalidateLastPostTimeCache(channelId string)
GetPostsCreatedAt(channelId string, time int64) ([]*model.Post, *model.AppError)
Overwrite(post *model.Post) (*model.Post, *model.AppError)
OverwriteMultiple(posts []*model.Post) ([]*model.Post, *model.AppError)
GetPostsByIds(postIds []string) ([]*model.Post, *model.AppError)
GetPostsBatchForIndexing(startTime int64, endTime int64, limit int) ([]*model.PostForIndexing, *model.AppError)
PermanentDeleteBatch(endTime int64, limit int64) (int64, *model.AppError)

Просмотреть файл

@@ -637,6 +637,31 @@ func (_m *PostStore) Overwrite(post *model.Post) (*model.Post, *model.AppError)
return r0, r1
}
// OverwriteMultiple provides a mock function with given fields: posts
func (_m *PostStore) OverwriteMultiple(posts []*model.Post) ([]*model.Post, *model.AppError) {
ret := _m.Called(posts)
var r0 []*model.Post
if rf, ok := ret.Get(0).(func([]*model.Post) []*model.Post); ok {
r0 = rf(posts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Post)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func([]*model.Post) *model.AppError); ok {
r1 = rf(posts)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// PermanentDeleteBatch provides a mock function with given fields: endTime, limit
func (_m *PostStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, *model.AppError) {
ret := _m.Called(endTime, limit)
@@ -717,6 +742,31 @@ func (_m *PostStore) Save(post *model.Post) (*model.Post, *model.AppError) {
return r0, r1
}
// SaveMultiple provides a mock function with given fields: posts
func (_m *PostStore) SaveMultiple(posts []*model.Post) ([]*model.Post, *model.AppError) {
ret := _m.Called(posts)
var r0 []*model.Post
if rf, ok := ret.Get(0).(func([]*model.Post) []*model.Post); ok {
r0 = rf(posts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Post)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func([]*model.Post) *model.AppError); ok {
r1 = rf(posts)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// Search provides a mock function with given fields: teamId, userId, params
func (_m *PostStore) Search(teamId string, userId string, params *model.SearchParams) (*model.PostList, *model.AppError) {
ret := _m.Called(teamId, userId, params)

Просмотреть файл

@@ -425,6 +425,31 @@ func (_m *TeamStore) GetByName(name string) (*model.Team, *model.AppError) {
return r0, r1
}
// GetByNames provides a mock function with given fields: name
func (_m *TeamStore) GetByNames(name []string) ([]*model.Team, *model.AppError) {
ret := _m.Called(name)
var r0 []*model.Team
if rf, ok := ret.Get(0).(func([]string) []*model.Team); ok {
r0 = rf(name)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Team)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func([]string) *model.AppError); ok {
r1 = rf(name)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// GetChannelUnreadsForAllTeams provides a mock function with given fields: excludeTeamId, userId
func (_m *TeamStore) GetChannelUnreadsForAllTeams(excludeTeamId string, userId string) ([]*model.ChannelUnread, *model.AppError) {
ret := _m.Called(excludeTeamId, userId)

Просмотреть файл

@@ -18,6 +18,7 @@ import (
)
func TestPostStore(t *testing.T, ss store.Store, s SqlSupplier) {
t.Run("SaveMultiple", func(t *testing.T) { testPostStoreSaveMultiple(t, ss) })
t.Run("Save", func(t *testing.T) { testPostStoreSave(t, ss) })
t.Run("SaveAndUpdateChannelMsgCounts", func(t *testing.T) { testPostStoreSaveChannelMsgCounts(t, ss) })
t.Run("Get", func(t *testing.T) { testPostStoreGet(t, ss) })
@@ -41,6 +42,7 @@ func TestPostStore(t *testing.T, ss store.Store, s SqlSupplier) {
t.Run("GetFlaggedPostsForChannel", func(t *testing.T) { testPostStoreGetFlaggedPostsForChannel(t, ss) })
t.Run("GetPostsCreatedAt", func(t *testing.T) { testPostStoreGetPostsCreatedAt(t, ss) })
t.Run("Overwrite", func(t *testing.T) { testPostStoreOverwrite(t, ss) })
t.Run("OverwriteMultiple", func(t *testing.T) { testPostStoreOverwriteMultiple(t, ss) })
t.Run("GetPostsByIds", func(t *testing.T) { testPostStoreGetPostsByIds(t, ss) })
t.Run("GetPostsBatchForIndexing", func(t *testing.T) { testPostStoreGetPostsBatchForIndexing(t, ss) })
t.Run("PermanentDeleteBatch", func(t *testing.T) { testPostStorePermanentDeleteBatch(t, ss) })
@@ -54,16 +56,223 @@ func TestPostStore(t *testing.T, ss store.Store, s SqlSupplier) {
}
func testPostStoreSave(t *testing.T, ss store.Store) {
o1 := model.Post{}
o1.ChannelId = model.NewId()
o1.UserId = model.NewId()
o1.Message = "zz" + model.NewId() + "b"
t.Run("Save post", func(t *testing.T) {
o1 := model.Post{}
o1.ChannelId = model.NewId()
o1.UserId = model.NewId()
o1.Message = "zz" + model.NewId() + "b"
_, err := ss.Post().Save(&o1)
require.Nil(t, err, "couldn't save item")
_, err := ss.Post().Save(&o1)
require.Nil(t, err, "couldn't save item")
})
_, err = ss.Post().Save(&o1)
require.NotNil(t, err, "shouldn't be able to update from save")
t.Run("Try to save existing post", func(t *testing.T) {
o1 := model.Post{}
o1.ChannelId = model.NewId()
o1.UserId = model.NewId()
o1.Message = "zz" + model.NewId() + "b"
_, err := ss.Post().Save(&o1)
require.Nil(t, err, "couldn't save item")
_, err = ss.Post().Save(&o1)
require.NotNil(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) {
rootPost := model.Post{}
rootPost.ChannelId = model.NewId()
rootPost.UserId = model.NewId()
rootPost.Message = "zz" + model.NewId() + "b"
_, err := ss.Post().Save(&rootPost)
require.Nil(t, err)
replyPost := model.Post{}
replyPost.ChannelId = rootPost.ChannelId
replyPost.UserId = model.NewId()
replyPost.Message = "zz" + model.NewId() + "b"
replyPost.RootId = rootPost.Id
_, err = ss.Post().Save(&replyPost)
require.Nil(t, err)
rrootPost, err := ss.Post().GetSingle(rootPost.Id)
require.Nil(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 = "zz" + model.NewId() + "b"
channel.DisplayName = "zz" + model.NewId() + "b"
channel.Type = model.CHANNEL_OPEN
_, err := ss.Channel().Save(&channel, 100)
require.Nil(t, err)
post := model.Post{}
post.ChannelId = channel.Id
post.UserId = model.NewId()
post.Message = "zz" + model.NewId() + "b"
_, err = ss.Post().Save(&post)
require.Nil(t, err)
rchannel, err := ss.Channel().Get(channel.Id, false)
require.Nil(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 = "zz" + model.NewId() + "b"
post.CreateAt = 5
_, err = ss.Post().Save(&post)
require.Nil(t, err)
rchannel2, err := ss.Channel().Get(channel.Id, false)
require.Nil(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 = "zz" + model.NewId() + "b"
_, err = ss.Post().Save(&post)
require.Nil(t, err)
rchannel3, err := ss.Channel().Get(channel.Id, false)
require.Nil(t, err)
assert.Greater(t, rchannel3.LastPostAt, rchannel2.LastPostAt)
assert.Equal(t, int64(3), rchannel3.TotalMsgCount)
})
}
func testPostStoreSaveMultiple(t *testing.T, ss store.Store) {
p1 := model.Post{}
p1.ChannelId = model.NewId()
p1.UserId = model.NewId()
p1.Message = "zz" + model.NewId() + "b"
p2 := model.Post{}
p2.ChannelId = model.NewId()
p2.UserId = model.NewId()
p2.Message = "zz" + model.NewId() + "b"
p3 := model.Post{}
p3.ChannelId = model.NewId()
p3.UserId = model.NewId()
p3.Message = "zz" + model.NewId() + "b"
p4 := model.Post{}
p4.ChannelId = model.NewId()
p4.UserId = model.NewId()
p4.Message = "zz" + model.NewId() + "b"
t.Run("Save correctly a new set of posts", func(t *testing.T) {
newPosts, err := ss.Post().SaveMultiple([]*model.Post{&p1, &p2, &p3})
require.Nil(t, err)
for _, post := range newPosts {
storedPost, err := ss.Post().GetSingle(post.Id)
assert.Nil(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("Try to save mixed, already saved and not saved posts", func(t *testing.T) {
newPosts, err := ss.Post().SaveMultiple([]*model.Post{&p4, &p3})
require.NotNil(t, err)
require.Nil(t, newPosts)
storedPost, err := ss.Post().GetSingle(p3.Id)
assert.Nil(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(p4.Id)
assert.NotNil(t, err)
assert.Nil(t, storedPost)
})
t.Run("Update reply should update the UpdateAt of the root post", func(t *testing.T) {
rootPost := model.Post{}
rootPost.ChannelId = model.NewId()
rootPost.UserId = model.NewId()
rootPost.Message = "zz" + model.NewId() + "b"
replyPost := model.Post{}
replyPost.ChannelId = rootPost.ChannelId
replyPost.UserId = model.NewId()
replyPost.Message = "zz" + model.NewId() + "b"
replyPost.RootId = rootPost.Id
_, err := ss.Post().SaveMultiple([]*model.Post{&rootPost, &replyPost})
require.Nil(t, err)
rrootPost, err := ss.Post().GetSingle(rootPost.Id)
require.Nil(t, err)
assert.Equal(t, rrootPost.UpdateAt, rootPost.UpdateAt)
replyPost2 := model.Post{}
replyPost2.ChannelId = rootPost.ChannelId
replyPost2.UserId = model.NewId()
replyPost2.Message = "zz" + model.NewId() + "b"
replyPost2.RootId = rootPost.Id
replyPost3 := model.Post{}
replyPost3.ChannelId = rootPost.ChannelId
replyPost3.UserId = model.NewId()
replyPost3.Message = "zz" + model.NewId() + "b"
replyPost3.RootId = rootPost.Id
_, err = ss.Post().SaveMultiple([]*model.Post{&replyPost2, &replyPost3})
require.Nil(t, err)
rrootPost2, err := ss.Post().GetSingle(rootPost.Id)
require.Nil(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 = "zz" + model.NewId() + "b"
channel.DisplayName = "zz" + model.NewId() + "b"
channel.Type = model.CHANNEL_OPEN
_, err := ss.Channel().Save(&channel, 100)
require.Nil(t, err)
post1 := model.Post{}
post1.ChannelId = channel.Id
post1.UserId = model.NewId()
post1.Message = "zz" + model.NewId() + "b"
post2 := model.Post{}
post2.ChannelId = channel.Id
post2.UserId = model.NewId()
post2.Message = "zz" + model.NewId() + "b"
post2.CreateAt = 5
post3 := model.Post{}
post3.ChannelId = channel.Id
post3.UserId = model.NewId()
post3.Message = "zz" + model.NewId() + "b"
_, err = ss.Post().SaveMultiple([]*model.Post{&post1, &post2, &post3})
require.Nil(t, err)
rchannel, err := ss.Channel().Get(channel.Id, false)
require.Nil(t, err)
assert.Greater(t, rchannel.LastPostAt, channel.LastPostAt)
assert.Equal(t, int64(3), rchannel.TotalMsgCount)
})
}
func testPostStoreSaveChannelMsgCounts(t *testing.T, ss store.Store) {
@@ -2005,6 +2214,136 @@ func testPostStoreGetPostsCreatedAt(t *testing.T, ss store.Store) {
assert.Equal(t, 2, len(r1))
}
func testPostStoreOverwriteMultiple(t *testing.T, ss store.Store) {
o1 := &model.Post{}
o1.ChannelId = model.NewId()
o1.UserId = model.NewId()
o1.Message = "zz" + model.NewId() + "AAAAAAAAAAA"
o1, err := ss.Post().Save(o1)
require.Nil(t, err)
o2 := &model.Post{}
o2.ChannelId = o1.ChannelId
o2.UserId = model.NewId()
o2.Message = "zz" + model.NewId() + "CCCCCCCCC"
o2.ParentId = o1.Id
o2.RootId = o1.Id
o2, err = ss.Post().Save(o2)
require.Nil(t, err)
o3 := &model.Post{}
o3.ChannelId = o1.ChannelId
o3.UserId = model.NewId()
o3.Message = "zz" + model.NewId() + "QQQQQQQQQQ"
o3, err = ss.Post().Save(o3)
require.Nil(t, err)
o4, err := ss.Post().Save(&model.Post{
ChannelId: model.NewId(),
UserId: model.NewId(),
Message: model.NewId(),
Filenames: []string{"test"},
})
require.Nil(t, err)
o5, err := ss.Post().Save(&model.Post{
ChannelId: model.NewId(),
UserId: model.NewId(),
Message: model.NewId(),
Filenames: []string{"test2", "test3"},
})
require.Nil(t, err)
r1, err := ss.Post().Get(o1.Id, false)
require.Nil(t, err)
ro1 := r1.Posts[o1.Id]
r2, err := ss.Post().Get(o2.Id, false)
require.Nil(t, err)
ro2 := r2.Posts[o2.Id]
r3, err := ss.Post().Get(o3.Id, false)
require.Nil(t, err)
ro3 := r3.Posts[o3.Id]
r4, err := ss.Post().Get(o4.Id, false)
require.Nil(t, err)
ro4 := r4.Posts[o4.Id]
r5, err := ss.Post().Get(o5.Id, false)
require.Nil(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 := &model.Post{}
*o1a = *ro1
o1a.Message = ro1.Message + "BBBBBBBBBB"
o2a := &model.Post{}
*o2a = *ro2
o2a.Message = ro2.Message + "DDDDDDD"
o3a := &model.Post{}
*o3a = *ro3
o3a.Message = ro3.Message + "WWWWWWW"
_, err = ss.Post().OverwriteMultiple([]*model.Post{o1a, o2a, o3a})
require.Nil(t, err)
r1, err = ss.Post().Get(o1.Id, false)
require.Nil(t, err)
ro1a := r1.Posts[o1.Id]
r2, err = ss.Post().Get(o1.Id, false)
require.Nil(t, err)
ro2a := r2.Posts[o2.Id]
r3, err = ss.Post().Get(o3.Id, false)
require.Nil(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 := &model.Post{}
*o4a = *ro4
o4a.Filenames = []string{}
o4a.FileIds = []string{model.NewId()}
o5a := &model.Post{}
*o5a = *ro5
o5a.Filenames = []string{}
o5a.FileIds = []string{}
_, err = ss.Post().OverwriteMultiple([]*model.Post{o4a, o5a})
require.Nil(t, err)
r4, err = ss.Post().Get(o4.Id, false)
require.Nil(t, err)
ro4a := r4.Posts[o4.Id]
r5, err = ss.Post().Get(o5.Id, false)
require.Nil(t, err)
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, ss store.Store) {
o1 := &model.Post{}
o1.ChannelId = model.NewId()
@@ -2029,56 +2368,6 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) {
o3, err = ss.Post().Save(o3)
require.Nil(t, err)
r1, err := ss.Post().Get(o1.Id, false)
require.Nil(t, err)
ro1 := r1.Posts[o1.Id]
r2, err := ss.Post().Get(o1.Id, false)
require.Nil(t, err)
ro2 := r2.Posts[o2.Id]
r3, err := ss.Post().Get(o3.Id, false)
require.Nil(t, err)
ro3 := r3.Posts[o3.Id]
require.Equal(t, ro1.Message, o1.Message, "Failed to save/get")
o1a := &model.Post{}
*o1a = *ro1
o1a.Message = ro1.Message + "BBBBBBBBBB"
_, err = ss.Post().Overwrite(o1a)
require.Nil(t, err)
r1, err = ss.Post().Get(o1.Id, false)
require.Nil(t, err)
ro1a := r1.Posts[o1.Id]
require.Equal(t, ro1a.Message, o1a.Message, "Failed to overwrite/get")
o2a := &model.Post{}
*o2a = *ro2
o2a.Message = ro2.Message + "DDDDDDD"
_, err = ss.Post().Overwrite(o2a)
require.Nil(t, err)
r2, err = ss.Post().Get(o1.Id, false)
require.Nil(t, err)
ro2a := r2.Posts[o2.Id]
require.Equal(t, ro2a.Message, o2a.Message, "Failed to overwrite/get")
o3a := &model.Post{}
*o3a = *ro3
o3a.Message = ro3.Message + "WWWWWWW"
_, err = ss.Post().Overwrite(o3a)
require.Nil(t, err)
r3, err = ss.Post().Get(o3.Id, false)
require.Nil(t, err)
ro3a := r3.Posts[o3.Id]
require.Equal(t, ro3a.Message, o3a.Message, "Failed to overwrite/get")
o4, err := ss.Post().Save(&model.Post{
ChannelId: model.NewId(),
UserId: model.NewId(),
@@ -2087,23 +2376,78 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) {
})
require.Nil(t, err)
r1, err := ss.Post().Get(o1.Id, false)
require.Nil(t, err)
ro1 := r1.Posts[o1.Id]
r2, err := ss.Post().Get(o2.Id, false)
require.Nil(t, err)
ro2 := r2.Posts[o2.Id]
r3, err := ss.Post().Get(o3.Id, false)
require.Nil(t, err)
ro3 := r3.Posts[o3.Id]
r4, err := ss.Post().Get(o4.Id, false)
require.Nil(t, err)
ro4 := r4.Posts[o4.Id]
o4a := &model.Post{}
*o4a = *ro4
o4a.Filenames = []string{}
o4a.FileIds = []string{model.NewId()}
_, err = ss.Post().Overwrite(o4a)
require.Nil(t, err)
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")
r4, err = ss.Post().Get(o4.Id, false)
require.Nil(t, err)
t.Run("overwrite changing message", func(t *testing.T) {
o1a := &model.Post{}
*o1a = *ro1
o1a.Message = ro1.Message + "BBBBBBBBBB"
_, err = ss.Post().Overwrite(o1a)
require.Nil(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")
o2a := &model.Post{}
*o2a = *ro2
o2a.Message = ro2.Message + "DDDDDDD"
_, err = ss.Post().Overwrite(o2a)
require.Nil(t, err)
o3a := &model.Post{}
*o3a = *ro3
o3a.Message = ro3.Message + "WWWWWWW"
_, err = ss.Post().Overwrite(o3a)
require.Nil(t, err)
r1, err = ss.Post().Get(o1.Id, false)
require.Nil(t, err)
ro1a := r1.Posts[o1.Id]
r2, err = ss.Post().Get(o1.Id, false)
require.Nil(t, err)
ro2a := r2.Posts[o2.Id]
r3, err = ss.Post().Get(o3.Id, false)
require.Nil(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 := &model.Post{}
*o4a = *ro4
o4a.Filenames = []string{}
o4a.FileIds = []string{model.NewId()}
_, err = ss.Post().Overwrite(o4a)
require.Nil(t, err)
r4, err = ss.Post().Get(o4.Id, false)
require.Nil(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, ss store.Store) {

Просмотреть файл

@@ -30,6 +30,7 @@ func TestTeamStore(t *testing.T, ss store.Store) {
t.Run("Update", func(t *testing.T) { testTeamStoreUpdate(t, ss) })
t.Run("Get", func(t *testing.T) { testTeamStoreGet(t, ss) })
t.Run("GetByName", func(t *testing.T) { testTeamStoreGetByName(t, ss) })
t.Run("GetByNames", func(t *testing.T) { testTeamStoreGetByNames(t, ss) })
t.Run("SearchAll", func(t *testing.T) { testTeamStoreSearchAll(t, ss) })
t.Run("SearchOpen", func(t *testing.T) { testTeamStoreSearchOpen(t, ss) })
t.Run("SearchPrivate", func(t *testing.T) { testTeamStoreSearchPrivate(t, ss) })
@@ -122,6 +123,59 @@ func testTeamStoreGet(t *testing.T, ss store.Store) {
require.NotNil(t, err, "Missing id should have failed")
}
func testTeamStoreGetByNames(t *testing.T, ss store.Store) {
o1 := model.Team{}
o1.DisplayName = "DisplayName"
o1.Name = "z-z-z" + model.NewId() + "b"
o1.Email = MakeEmail()
o1.Type = model.TEAM_OPEN
_, err := ss.Team().Save(&o1)
require.Nil(t, err)
o2 := model.Team{}
o2.DisplayName = "DisplayName2"
o2.Name = "z-z-z" + model.NewId() + "b"
o2.Email = MakeEmail()
o2.Type = model.TEAM_OPEN
_, err = ss.Team().Save(&o2)
require.Nil(t, err)
t.Run("Get empty list", func(t *testing.T) {
var teams []*model.Team
teams, err = ss.Team().GetByNames([]string{})
require.Nil(t, err)
require.Empty(t, teams)
})
t.Run("Get existing teams", func(t *testing.T) {
var teams []*model.Team
teams, err = ss.Team().GetByNames([]string{o1.Name, o2.Name})
require.Nil(t, err)
teamsIds := []string{}
for _, team := range teams {
teamsIds = append(teamsIds, team.Id)
}
assert.Contains(t, teamsIds, o1.Id, "invalid returned team")
assert.Contains(t, teamsIds, o2.Id, "invalid returned team")
})
t.Run("Get existing team and one invalid team name", func(t *testing.T) {
_, err = ss.Team().GetByNames([]string{o1.Name, ""})
require.NotNil(t, err)
})
t.Run("Get existing team and not existing team", func(t *testing.T) {
_, err = ss.Team().GetByNames([]string{o1.Name, "not-existing-team-name"})
require.NotNil(t, err)
})
t.Run("Get not existing teams", func(t *testing.T) {
_, err = ss.Team().GetByNames([]string{"not-existing-team-name", "not-existing-team-name-2"})
require.NotNil(t, err)
})
}
func testTeamStoreGetByName(t *testing.T, ss store.Store) {
o1 := model.Team{}
o1.DisplayName = "DisplayName"
@@ -132,12 +186,22 @@ func testTeamStoreGetByName(t *testing.T, ss store.Store) {
_, err := ss.Team().Save(&o1)
require.Nil(t, err)
team, err := ss.Team().GetByName(o1.Name)
require.Nil(t, err)
require.Equal(t, *team, o1, "invalid returned team")
t.Run("Get existing team", func(t *testing.T) {
var team *model.Team
team, err = ss.Team().GetByName(o1.Name)
require.Nil(t, err)
require.Equal(t, *team, o1, "invalid returned team")
})
_, err = ss.Team().GetByName("")
require.NotNil(t, err, "Missing id should have failed")
t.Run("Get invalid team name", func(t *testing.T) {
_, err = ss.Team().GetByName("")
require.NotNil(t, err, "Missing id should have failed")
})
t.Run("Get not existing team", func(t *testing.T) {
_, err = ss.Team().GetByName("not-existing-team-name")
require.NotNil(t, err, "Missing id should have failed")
})
}
func testTeamStoreSearchAll(t *testing.T, ss store.Store) {

Просмотреть файл

@@ -4412,6 +4412,22 @@ func (s *TimerLayerPostStore) Save(post *model.Post) (*model.Post, *model.AppErr
return resultVar0, resultVar1
}
func (s *TimerLayerPostStore) SaveMultiple(posts []*model.Post) ([]*model.Post, *model.AppError) {
start := timemodule.Now()
resultVar0, resultVar1 := s.PostStore.SaveMultiple(posts)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar1 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PostStore.SaveMultiple", success, elapsed)
}
return resultVar0, resultVar1
}
func (s *TimerLayerPostStore) Search(teamId string, userId string, params *model.SearchParams) (*model.PostList, *model.AppError) {
start := timemodule.Now()