mattermost/server/channels/app/post_test.go

6077 lines
203 KiB
Go
Raw Permalink Normal View History

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"encoding/json"
"errors"
"fmt"
"net/http"
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
"os"
"strconv"
"sync"
"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/plugin/plugintest/mock"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/v8/channels/store"
storemocks "github.com/mattermost/mattermost/server/v8/channels/store/storetest/mocks"
"github.com/mattermost/mattermost/server/v8/channels/testlib"
"github.com/mattermost/mattermost/server/v8/platform/services/imageproxy"
"github.com/mattermost/mattermost/server/v8/platform/services/searchengine/mocks"
)
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
func enableBoRFeature(th *TestHelper) {
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.ServiceSettings.EnableBurnOnRead = model.NewPointer(true)
})
}
func makePendingPostId(user *model.User) string {
return fmt.Sprintf("%s:%s", user.Id, strconv.FormatInt(model.GetMillis(), 10))
}
func TestCreatePostDeduplicate(t *testing.T) {
th := Setup(t).InitBasic(t)
t.Run("duplicate create post is idempotent", func(t *testing.T) {
session := &model.Session{
UserId: th.BasicUser.Id,
}
session, err := th.App.CreateSession(th.Context, session)
require.Nil(t, err)
pendingPostId := makePendingPostId(th.BasicUser)
post, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "message",
PendingPostId: pendingPostId,
}, session.Id, true)
require.Nil(t, err)
require.Equal(t, "message", post.Message)
duplicatePost, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "message",
PendingPostId: pendingPostId,
}, session.Id, true)
require.Nil(t, err)
require.Equal(t, post.Id, duplicatePost.Id, "should have returned previously created post id")
require.Equal(t, "message", duplicatePost.Message)
})
t.Run("post rejected by plugin leaves cache ready for non-deduplicated try", func(t *testing.T) {
setupPluginAPITest(t, `
package main
import (
"github.com/mattermost/mattermost/server/public/plugin"
"github.com/mattermost/mattermost/server/public/model"
)
type MyPlugin struct {
plugin.MattermostPlugin
allow bool
}
func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
if !p.allow {
p.allow = true
return nil, "rejected"
}
return nil, ""
}
func main() {
plugin.ClientMain(&MyPlugin{})
}
`, `{"id": "testrejectfirstpost", "server": {"executable": "backend.exe"}}`, "testrejectfirstpost", th.App, th.Context)
session := &model.Session{
UserId: th.BasicUser.Id,
}
session, err := th.App.CreateSession(th.Context, session)
require.Nil(t, err)
pendingPostId := makePendingPostId(th.BasicUser)
post, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "message",
PendingPostId: pendingPostId,
}, session.Id, true)
require.NotNil(t, err)
require.Equal(t, "Post rejected by plugin. rejected", err.Id)
require.Nil(t, post)
duplicatePost, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "message",
PendingPostId: pendingPostId,
}, session.Id, true)
require.Nil(t, err)
require.Equal(t, "message", duplicatePost.Message)
})
t.Run("slow posting after cache entry blocks duplicate request", func(t *testing.T) {
setupPluginAPITest(t, `
package main
import (
"github.com/mattermost/mattermost/server/public/plugin"
"github.com/mattermost/mattermost/server/public/model"
"time"
)
type MyPlugin struct {
plugin.MattermostPlugin
instant bool
}
func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
if !p.instant {
p.instant = true
time.Sleep(3 * time.Second)
}
return nil, ""
}
func main() {
plugin.ClientMain(&MyPlugin{})
}
`, `{"id": "testdelayfirstpost", "server": {"executable": "backend.exe"}}`, "testdelayfirstpost", th.App, th.Context)
session := &model.Session{
UserId: th.BasicUser.Id,
}
session, err := th.App.CreateSession(th.Context, session)
require.Nil(t, err)
var post *model.Post
pendingPostId := makePendingPostId(th.BasicUser)
wg := sync.WaitGroup{}
// Launch a goroutine to make the first CreatePost call that will get delayed
// by the plugin above.
wg.Go(func() {
var appErr *model.AppError
post, _, appErr = th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "plugin delayed",
PendingPostId: pendingPostId,
}, session.Id, true)
require.Nil(t, appErr)
require.Equal(t, post.Message, "plugin delayed")
})
// Give the goroutine above a chance to start and get delayed by the plugin.
time.Sleep(2 * time.Second)
// Try creating a duplicate post
duplicatePost, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "plugin delayed",
PendingPostId: pendingPostId,
}, session.Id, true)
require.NotNil(t, err)
require.Equal(t, "api.post.deduplicate_create_post.pending", err.Id)
require.Nil(t, duplicatePost)
// Wait for the first CreatePost to finish to ensure assertions are made.
wg.Wait()
})
t.Run("duplicate create post after cache expires is not idempotent", func(t *testing.T) {
originalCacheTTL := pendingPostIDsCacheTTL
pendingPostIDsCacheTTL = time.Second
t.Cleanup(func() {
pendingPostIDsCacheTTL = originalCacheTTL
})
session := &model.Session{
UserId: th.BasicUser.Id,
}
session, err := th.App.CreateSession(th.Context, session)
require.Nil(t, err)
pendingPostId := makePendingPostId(th.BasicUser)
post, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "message",
PendingPostId: pendingPostId,
}, session.Id, true)
require.Nil(t, err)
require.Equal(t, "message", post.Message)
time.Sleep(pendingPostIDsCacheTTL)
duplicatePost, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "message",
PendingPostId: pendingPostId,
}, session.Id, true)
require.Nil(t, err)
require.NotEqual(t, post.Id, duplicatePost.Id, "should have created new post id")
require.Equal(t, "message", duplicatePost.Message)
})
t.Run("Permissison to post required to resolve from pending post cache", func(t *testing.T) {
sessionBasicUser := &model.Session{
UserId: th.BasicUser.Id,
}
sessionBasicUser, err := th.App.CreateSession(th.Context, sessionBasicUser)
require.Nil(t, err)
sessionBasicUser2 := &model.Session{
UserId: th.BasicUser2.Id,
}
sessionBasicUser2, err = th.App.CreateSession(th.Context, sessionBasicUser2)
require.Nil(t, err)
pendingPostId := makePendingPostId(th.BasicUser)
privateChannel := th.CreatePrivateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, th.BasicUser, privateChannel)
post, _, err := th.App.CreatePostAsUser(th.Context.WithSession(sessionBasicUser), &model.Post{
UserId: th.BasicUser.Id,
ChannelId: privateChannel.Id,
Message: "message",
PendingPostId: pendingPostId,
}, sessionBasicUser.Id, true)
require.Nil(t, err)
require.Equal(t, "message", post.Message)
postAsDifferentUser, _, err := th.App.CreatePostAsUser(th.Context.WithSession(sessionBasicUser2), &model.Post{
UserId: th.BasicUser2.Id,
ChannelId: th.BasicChannel.Id,
Message: "message2",
PendingPostId: pendingPostId,
}, sessionBasicUser2.Id, true)
require.Nil(t, err)
require.NotEqual(t, post.Id, postAsDifferentUser.Id, "should have created new post id")
require.Equal(t, "message2", postAsDifferentUser.Message)
// Both posts should exist unchanged
actualPost, err := th.App.GetSinglePost(th.Context, post.Id, false)
require.Nil(t, err)
assert.Equal(t, "message", actualPost.Message)
assert.Equal(t, privateChannel.Id, actualPost.ChannelId)
actualPostAsDifferentUser, err := th.App.GetSinglePost(th.Context, postAsDifferentUser.Id, false)
require.Nil(t, err)
assert.Equal(t, "message2", actualPostAsDifferentUser.Message)
assert.Equal(t, th.BasicChannel.Id, actualPostAsDifferentUser.ChannelId)
})
}
func TestAttachFilesToPost(t *testing.T) {
mainHelper.Parallel(t)
t.Run("should attach files", func(t *testing.T) {
th := Setup(t).InitBasic(t)
info1, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
info2, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
post := th.BasicPost
post.FileIds = []string{info1.Id, info2.Id}
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
attachedFiles, appErr := th.App.attachFilesToPost(th.Context, post, post.FileIds)
assert.Nil(t, appErr)
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
assert.Len(t, attachedFiles, 2)
assert.Contains(t, attachedFiles, info1.Id)
assert.Contains(t, attachedFiles, info2.Id)
infos, _, appErr := th.App.GetFileInfosForPost(th.Context, post.Id, false, false)
assert.Nil(t, appErr)
assert.Len(t, infos, 2)
})
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
t.Run("should return only successfully attached files after failing to add files", func(t *testing.T) {
th := Setup(t).InitBasic(t)
info1, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
PostId: model.NewId(),
})
require.NoError(t, err)
info2, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
post := th.BasicPost
post.FileIds = []string{info1.Id, info2.Id}
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
attachedFiles, appErr := th.App.attachFilesToPost(th.Context, post, post.FileIds)
assert.Nil(t, appErr)
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
assert.Len(t, attachedFiles, 1)
assert.Contains(t, attachedFiles, info2.Id)
infos, _, appErr := th.App.GetFileInfosForPost(th.Context, post.Id, false, false)
assert.Nil(t, appErr)
assert.Len(t, infos, 1)
assert.Equal(t, info2.Id, infos[0].Id)
updated, appErr := th.App.GetSinglePost(th.Context, post.Id, false)
require.Nil(t, appErr)
assert.Len(t, updated.FileIds, 1)
assert.Contains(t, updated.FileIds, info2.Id)
})
}
func TestUpdatePostEditAt(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
post := th.BasicPost.Clone()
post.IsPinned = true
saved, isMemberForPreviews, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err)
assert.Equal(t, saved.EditAt, post.EditAt, "shouldn't have updated post.EditAt when pinning post")
assert.True(t, isMemberForPreviews)
[MM-21378] Add mutex to model.Post to guard against race conditions on Post.Props (#13884) * Add mutex to model.Post to guard against race conditions on Post.Props * Rename mutex * Add GetProp() method to Post * Fix more tests * Fix flaky test Benchmarks: BenchmarkPostPropsGet_indirect BenchmarkPostPropsGet_indirect-2 85026746 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-4 90273747 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-8 88324293 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-16 91427720 13.1 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct BenchmarkPostPropsGet_direct-2 1000000000 0.242 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-4 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-8 1000000000 0.240 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-16 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_indirect BenchmarkPostPropsAdd_indirect-2 5602224 203 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-4 5959496 206 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-8 5833999 205 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-16 5802493 225 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_direct BenchmarkPostPropsAdd_direct-2 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-4 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-8 100000000 11.6 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-16 99840794 11.4 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_indirect BenchmarkPostPropsDel_indirect-2 18824002 61.9 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-4 19470736 63.8 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-8 17640460 65.3 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-16 18692962 65.4 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_direct BenchmarkPostPropsDel_direct-2 516257440 2.34 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-4 514865216 2.43 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-8 511330477 2.37 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-16 499504010 2.38 ns/op 0 B/op 0 allocs/op
2020-03-13 16:12:20 -04:00
post = saved.Clone()
time.Sleep(time.Millisecond * 100)
post.Message = model.NewId()
saved, isMemberForPreviews, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err)
assert.NotEqual(t, saved.EditAt, post.EditAt, "should have updated post.EditAt when updating post message")
assert.True(t, isMemberForPreviews)
time.Sleep(time.Millisecond * 200)
}
func TestUpdatePostTimeLimit(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
post := th.BasicPost.Clone()
th.App.Srv().SetLicense(model.NewTestLicense())
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.PostEditTimeLimit = -1
})
_, _, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.PostEditTimeLimit = 1000000000
})
post.Message = model.NewId()
_, _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err, "should allow you to edit the post")
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.PostEditTimeLimit = 1
})
post.Message = model.NewId()
_, _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err, "should allow you to edit an old post because the time check is applied above in the call hierarchy")
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.PostEditTimeLimit = -1
})
}
func TestUpdatePostInArchivedChannel(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
archivedChannel := th.CreateChannel(t, th.BasicTeam)
post := th.CreatePost(t, archivedChannel)
appErr := th.App.DeleteChannel(th.Context, archivedChannel, "")
require.Nil(t, appErr)
_, _, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.NotNil(t, err)
require.Equal(t, "api.post.update_post.can_not_update_post_in_deleted.error", err.Id)
}
func TestPostReplyToPostWhereRootPosterLeftChannel(t *testing.T) {
mainHelper.Parallel(t)
// This test ensures that when replying to a root post made by a user who has since left the channel, the reply
// post completes successfully. This is a regression test for PLT-6523.
th := Setup(t).InitBasic(t)
channel := th.BasicChannel
userInChannel := th.BasicUser2
userNotInChannel := th.BasicUser
rootPost := th.BasicPost
_, err := th.App.AddUserToChannel(th.Context, userInChannel, channel, false)
require.Nil(t, err)
err = th.App.RemoveUserFromChannel(th.Context, userNotInChannel.Id, "", channel)
require.Nil(t, err)
replyPost := model.Post{
Message: "asd",
ChannelId: channel.Id,
RootId: rootPost.Id,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: userInChannel.Id,
CreateAt: 0,
}
_, _, err = th.App.CreatePostAsUser(th.Context, &replyPost, "", true)
require.Nil(t, err)
}
func TestPostAttachPostToChildPost(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
channel := th.BasicChannel
user := th.BasicUser
rootPost := th.BasicPost
replyPost1 := model.Post{
Message: "reply one",
ChannelId: channel.Id,
RootId: rootPost.Id,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: user.Id,
CreateAt: 0,
}
res1, _, err := th.App.CreatePostAsUser(th.Context, &replyPost1, "", true)
require.Nil(t, err)
replyPost2 := model.Post{
Message: "reply two",
ChannelId: channel.Id,
RootId: res1.Id,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: user.Id,
CreateAt: 0,
}
_, _, err = th.App.CreatePostAsUser(th.Context, &replyPost2, "", true)
assert.Equalf(t, err.StatusCode, http.StatusBadRequest, "Expected BadRequest error, got %v", err)
replyPost3 := model.Post{
Message: "reply three",
ChannelId: channel.Id,
RootId: rootPost.Id,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: user.Id,
CreateAt: 0,
}
_, _, err = th.App.CreatePostAsUser(th.Context, &replyPost3, "", true)
assert.Nil(t, err)
}
func TestUpdatePostPluginHooks(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
t.Run("Should stop processing at first reject", func(t *testing.T) {
setupMultiPluginAPITest(t, []string{
`
package main
import (
"github.com/mattermost/mattermost/server/public/plugin"
"github.com/mattermost/mattermost/server/public/model"
)
type MyPlugin struct {
plugin.MattermostPlugin
}
func (p *MyPlugin) MessageWillBeUpdated(c *plugin.Context, newPost, oldPost *model.Post) (*model.Post, string) {
return nil, "rejected"
}
func main() {
plugin.ClientMain(&MyPlugin{})
}
`,
`
package main
import (
"github.com/mattermost/mattermost/server/public/plugin"
"github.com/mattermost/mattermost/server/public/model"
)
type MyPlugin struct {
plugin.MattermostPlugin
}
func (p *MyPlugin) MessageWillBeUpdated(c *plugin.Context, newPost, oldPost *model.Post) (*model.Post, string) {
if (newPost == nil) {
return nil, "nil post"
}
newPost.Message = newPost.Message + "fromplugin"
return newPost, ""
}
func main() {
plugin.ClientMain(&MyPlugin{})
}
`,
}, []string{
`{"id": "testrejectfirstpost", "server": {"executable": "backend.exe"}}`,
`{"id": "testupdatepost", "server": {"executable": "backend.exe"}}`,
}, []string{
"testrejectfirstpost", "testupdatepost",
}, true, th.App, th.Context)
pendingPostId := makePendingPostId(th.BasicUser)
post, _, err := th.App.CreatePostAsUser(th.Context, &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "message",
PendingPostId: pendingPostId,
}, "", true)
require.Nil(t, err)
post.Message = "new message"
updatedPost, _, err := th.App.UpdatePost(th.Context, post, nil)
require.Nil(t, updatedPost)
require.NotNil(t, err)
require.Equal(t, "Post rejected by plugin. rejected", err.Id)
})
t.Run("Should update", func(t *testing.T) {
setupMultiPluginAPITest(t, []string{
`
package main
import (
"github.com/mattermost/mattermost/server/public/plugin"
"github.com/mattermost/mattermost/server/public/model"
)
type MyPlugin struct {
plugin.MattermostPlugin
}
func (p *MyPlugin) MessageWillBeUpdated(c *plugin.Context, newPost, oldPost *model.Post) (*model.Post, string) {
newPost.Message = newPost.Message + " 1"
return newPost, ""
}
func main() {
plugin.ClientMain(&MyPlugin{})
}
`,
`
package main
import (
"github.com/mattermost/mattermost/server/public/plugin"
"github.com/mattermost/mattermost/server/public/model"
)
type MyPlugin struct {
plugin.MattermostPlugin
}
func (p *MyPlugin) MessageWillBeUpdated(c *plugin.Context, newPost, oldPost *model.Post) (*model.Post, string) {
newPost.Message = "2 " + newPost.Message
return newPost, ""
}
func main() {
plugin.ClientMain(&MyPlugin{})
}
`,
}, []string{
`{"id": "testaddone", "server": {"executable": "backend.exe"}}`,
`{"id": "testaddtwo", "server": {"executable": "backend.exe"}}`,
}, []string{
"testaddone", "testaddtwo",
}, true, th.App, th.Context)
pendingPostId := makePendingPostId(th.BasicUser)
post, _, err := th.App.CreatePostAsUser(th.Context, &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "message",
PendingPostId: pendingPostId,
}, "", true)
require.Nil(t, err)
post.Message = "new message"
updatedPost, isMemberForPreviews, err := th.App.UpdatePost(th.Context, post, nil)
require.True(t, isMemberForPreviews)
require.Nil(t, err)
require.NotNil(t, updatedPost)
require.Equal(t, "2 new message 1", updatedPost.Message)
})
}
func TestPostChannelMentions(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
channel := th.BasicChannel
user := th.BasicUser
// Create context with session for the user to properly test sanitization
ctx := th.Context.WithSession(&model.Session{UserId: user.Id})
channelToMention, err := th.App.CreateChannel(th.Context, &model.Channel{
DisplayName: "Mention Test",
Name: "mention-test",
2021-07-12 14:05:36 -04:00
Type: model.ChannelTypeOpen,
TeamId: th.BasicTeam.Id,
}, false)
require.Nil(t, err)
defer func() {
appErr := th.App.PermanentDeleteChannel(th.Context, channelToMention)
require.Nil(t, appErr)
}()
channelToMention2, err := th.App.CreateChannel(th.Context, &model.Channel{
DisplayName: "Mention Test2",
Name: "mention-test2",
Type: model.ChannelTypeOpen,
TeamId: th.BasicTeam.Id,
}, false)
require.Nil(t, err)
defer func() {
appErr := th.App.PermanentDeleteChannel(th.Context, channelToMention2)
require.Nil(t, appErr)
}()
_, err = th.App.AddUserToChannel(th.Context, user, channel, false)
require.Nil(t, err)
post := &model.Post{
Message: fmt.Sprintf("hello, ~%v!", channelToMention.Name),
ChannelId: channel.Id,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: user.Id,
CreateAt: 0,
}
post, _, err = th.App.CreatePostAsUser(ctx, post, "", true)
require.Nil(t, err)
assert.Equal(t, map[string]any{
"mention-test": map[string]any{
"display_name": "Mention Test",
"team_name": th.BasicTeam.Name,
},
}, post.GetProp(model.PostPropsChannelMentions))
post.Message = fmt.Sprintf("goodbye, ~%v!", channelToMention2.Name)
result, isMemberForPreviews, err := th.App.UpdatePost(ctx, post, nil)
require.True(t, isMemberForPreviews)
require.Nil(t, err)
assert.Equal(t, map[string]any{
"mention-test2": map[string]any{
"display_name": "Mention Test2",
"team_name": th.BasicTeam.Name,
},
}, result.GetProp(model.PostPropsChannelMentions))
result.Message = "no more mentions!"
result, isMemberForPreviews, err = th.App.UpdatePost(ctx, result, nil)
require.True(t, isMemberForPreviews)
require.Nil(t, err)
assert.Nil(t, result.GetProp(model.PostPropsChannelMentions))
}
func TestImageProxy(t *testing.T) {
mainHelper.Parallel(t)
th := SetupWithStoreMock(t)
mockStore := th.App.Srv().Store().(*storemocks.Store)
mockUserStore := storemocks.UserStore{}
mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
mockPostStore := storemocks.PostStore{}
mockPostStore.On("GetMaxPostSize").Return(65535, nil)
mockSystemStore := storemocks.SystemStore{}
Adding Upgrade to Enterprise version feature (#14539) * Adding Upgrade to Enterprise version feature * Addressing PR review comments, and adding some minor improvements * Add tests file * Addressing PR comments * fix linter checks * Storing and exposing the upgraded from TE info * Fix showing errors on mac * A more appropiate status code for not-supported upgrade * Fixing tests * Handling permissions errors * More server logging around upgrade failures * Apply text changes suggested from code review Co-authored-by: Eric Sadur <57730300+esadur@users.noreply.github.com> * Address PR review comments * Only allow to restart the system after an upgrade * Verify file signature before upgrade * Adding limit to the downloaded file * Simplifying the upgrade binary process with backup in memory * Fixing backup/restore mechanism for the binary file * Improve file permissions handling * Askin the permissions for the right place (the parent directory) * Fixing tests * Addressing PR review comments * Fix license headers * Fixing retry layer * Making it work on windows builds * Adding license header * Fixing 2 tests * Fixing tests that need UpgradeFromTE System key mock * Extracting i18n translation * Apply suggestions from code review Co-authored-by: Eric Sadur <57730300+esadur@users.noreply.github.com> * Improving how the errors are written * Fixing another error text * Removing unneeded translation * Fixing upgrade status strings * Update i18n/en.json Co-authored-by: Eric Sadur <57730300+esadur@users.noreply.github.com> * Fixing tests Co-authored-by: Eric Sadur <57730300+esadur@users.noreply.github.com> Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
2020-08-21 14:23:04 -04:00
mockSystemStore.On("GetByName", "UpgradedFromTE").Return(&model.System{Name: "UpgradedFromTE", Value: "false"}, nil)
mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)
mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil)
mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log())
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905) * Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-04-08 15:49:43 -04:00
testHMACKey := model.NewTestPassword()
for name, tc := range map[string]struct {
ProxyType string
ProxyURL string
ProxyOptions string
ImageURL string
ProxiedImageURL string
ProxiedRemovedImageURL string
}{
"atmos/camo": {
2021-07-12 14:05:36 -04:00
ProxyType: model.ImageProxyTypeAtmosCamo,
ProxyURL: "https://127.0.0.1",
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905) * Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-04-08 15:49:43 -04:00
ProxyOptions: testHMACKey,
ImageURL: "http://mydomain.com/myimage",
ProxiedRemovedImageURL: "http://mydomain.com/myimage",
ProxiedImageURL: "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage",
},
2018-02-12 14:05:01 -05:00
"atmos/camo_SameSite": {
2021-07-12 14:05:36 -04:00
ProxyType: model.ImageProxyTypeAtmosCamo,
ProxyURL: "https://127.0.0.1",
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905) * Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-04-08 15:49:43 -04:00
ProxyOptions: testHMACKey,
ImageURL: "http://mymattermost.com/myimage",
ProxiedRemovedImageURL: "http://mymattermost.com/myimage",
ProxiedImageURL: "http://mymattermost.com/myimage",
},
2018-02-12 14:05:01 -05:00
"atmos/camo_PathOnly": {
2021-07-12 14:05:36 -04:00
ProxyType: model.ImageProxyTypeAtmosCamo,
ProxyURL: "https://127.0.0.1",
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905) * Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-04-08 15:49:43 -04:00
ProxyOptions: testHMACKey,
ImageURL: "/myimage",
ProxiedRemovedImageURL: "http://mymattermost.com/myimage",
ProxiedImageURL: "http://mymattermost.com/myimage",
2018-02-09 21:08:39 -05:00
},
2018-02-12 14:05:01 -05:00
"atmos/camo_EmptyImageURL": {
2021-07-12 14:05:36 -04:00
ProxyType: model.ImageProxyTypeAtmosCamo,
ProxyURL: "https://127.0.0.1",
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905) * Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-04-08 15:49:43 -04:00
ProxyOptions: testHMACKey,
ImageURL: "",
ProxiedRemovedImageURL: "",
ProxiedImageURL: "",
},
"local": {
2021-07-12 14:05:36 -04:00
ProxyType: model.ImageProxyTypeLocal,
ImageURL: "http://mydomain.com/myimage",
ProxiedRemovedImageURL: "http://mydomain.com/myimage",
ProxiedImageURL: "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage",
},
"local_SameSite": {
2021-07-12 14:05:36 -04:00
ProxyType: model.ImageProxyTypeLocal,
ImageURL: "http://mymattermost.com/myimage",
ProxiedRemovedImageURL: "http://mymattermost.com/myimage",
ProxiedImageURL: "http://mymattermost.com/myimage",
},
"local_PathOnly": {
2021-07-12 14:05:36 -04:00
ProxyType: model.ImageProxyTypeLocal,
ImageURL: "/myimage",
ProxiedRemovedImageURL: "http://mymattermost.com/myimage",
ProxiedImageURL: "http://mymattermost.com/myimage",
},
"local_EmptyImageURL": {
2021-07-12 14:05:36 -04:00
ProxyType: model.ImageProxyTypeLocal,
ImageURL: "",
ProxiedRemovedImageURL: "",
ProxiedImageURL: "",
},
} {
t.Run(name, func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.ImageProxySettings.Enable = model.NewPointer(true)
cfg.ImageProxySettings.ImageProxyType = model.NewPointer(tc.ProxyType)
cfg.ImageProxySettings.RemoteImageProxyOptions = model.NewPointer(tc.ProxyOptions)
cfg.ImageProxySettings.RemoteImageProxyURL = model.NewPointer(tc.ProxyURL)
})
post := &model.Post{
Id: model.NewId(),
Message: "![foo](" + tc.ImageURL + ")",
}
list := model.NewPostList()
list.Posts[post.Id] = post
assert.Equal(t, "![foo]("+tc.ProxiedImageURL+")", th.App.PostWithProxyAddedToImageURLs(post).Message)
assert.Equal(t, "![foo]("+tc.ImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
post.Message = "![foo](" + tc.ProxiedImageURL + ")"
assert.Equal(t, "![foo]("+tc.ProxiedRemovedImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
if tc.ImageURL != "" {
post.Message = "![foo](" + tc.ImageURL + " =500x200)"
assert.Equal(t, "![foo]("+tc.ProxiedImageURL+" =500x200)", th.App.PostWithProxyAddedToImageURLs(post).Message)
assert.Equal(t, "![foo]("+tc.ImageURL+" =500x200)", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
post.Message = "![foo](" + tc.ProxiedImageURL + " =500x200)"
assert.Equal(t, "![foo]("+tc.ProxiedRemovedImageURL+" =500x200)", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
}
})
}
}
func TestDeletePostWithFileAttachments(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
// Create a post with a file attachment.
teamID := th.BasicTeam.Id
channelID := th.BasicChannel.Id
userID := th.BasicUser.Id
filename := "test"
data := []byte("abcd")
info1, err := th.App.DoUploadFile(th.Context, time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamID, channelID, userID, filename, data, true)
require.Nil(t, err)
defer func() {
err := th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, info1.Id)
require.NoError(t, err)
appErr := th.App.RemoveFile(info1.Path)
require.Nil(t, appErr)
}()
post := &model.Post{
Message: "asd",
ChannelId: channelID,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: userID,
CreateAt: 0,
FileIds: []string{info1.Id},
}
post, _, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
assert.Nil(t, err)
// Delete the post.
_, err = th.App.DeletePost(th.Context, post.Id, userID)
assert.Nil(t, err)
// Wait for the cleanup routine to finish.
time.Sleep(time.Millisecond * 100)
// Check that the file can no longer be reached.
_, err = th.App.GetFileInfo(th.Context, info1.Id)
assert.NotNil(t, err)
}
func TestDeletePostWithRestrictedDM(t *testing.T) {
mainHelper.Parallel(t)
t.Run("cannot delete post in restricted DM", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.RestrictDirectMessage = model.DirectMessageTeam
})
// Create a DM channel between two users who don't share a team
dmChannel := th.CreateDmChannel(t, th.BasicUser2)
// Ensure the two users do not share a team
teams, err := th.App.GetTeamsForUser(th.BasicUser.Id)
require.Nil(t, err)
for _, team := range teams {
teamErr := th.App.RemoveUserFromTeam(th.Context, team.Id, th.BasicUser.Id, th.SystemAdminUser.Id)
require.Nil(t, teamErr)
}
teams, err = th.App.GetTeamsForUser(th.BasicUser2.Id)
require.Nil(t, err)
for _, team := range teams {
teamErr := th.App.RemoveUserFromTeam(th.Context, team.Id, th.BasicUser2.Id, th.SystemAdminUser.Id)
require.Nil(t, teamErr)
}
// Create separate teams for each user
team1 := th.CreateTeam(t)
team2 := th.CreateTeam(t)
th.LinkUserToTeam(t, th.BasicUser, team1)
th.LinkUserToTeam(t, th.BasicUser2, team2)
// Create a post in the DM channel
post := &model.Post{
UserId: th.BasicUser.Id,
ChannelId: dmChannel.Id,
Message: "test post",
}
post, _, err = th.App.CreatePost(th.Context, post, dmChannel, model.CreatePostFlags{})
require.Nil(t, err)
// Try to delete the post
_, appErr := th.App.DeletePost(th.Context, post.Id, th.BasicUser.Id)
require.NotNil(t, appErr)
require.Equal(t, "api.post.delete_post.can_not_delete_from_restricted_dm.error", appErr.Id)
require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
// Reset config
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.RestrictDirectMessage = model.DirectMessageAny
})
})
}
func TestDeletePostInArchivedChannel(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
archivedChannel := th.CreateChannel(t, th.BasicTeam)
post := th.CreatePost(t, archivedChannel)
appErr := th.App.DeleteChannel(th.Context, archivedChannel, "")
require.Nil(t, appErr)
_, err := th.App.DeletePost(th.Context, post.Id, "")
require.NotNil(t, err)
require.Equal(t, "api.post.delete_post.can_not_delete_post_in_deleted.error", err.Id)
}
func TestCreatePost(t *testing.T) {
mainHelper.Parallel(t)
t.Run("call PreparePostForClient before returning", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
*cfg.ImageProxySettings.Enable = true
*cfg.ImageProxySettings.ImageProxyType = "atmos/camo"
*cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1"
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905) * Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-04-08 15:49:43 -04:00
*cfg.ImageProxySettings.RemoteImageProxyOptions = model.NewTestPassword()
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log())
imageURL := "http://mydomain.com/myimage"
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "![image](" + imageURL + ")",
UserId: th.BasicUser.Id,
}
rpost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message)
})
t.Run("Sets prop MENTION_HIGHLIGHT_DISABLED when it should", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.AddUserToChannel(t, th.BasicUser, th.BasicChannel)
t.Run("Does not set prop when user has USE_CHANNEL_MENTIONS", func(t *testing.T) {
postWithNoMention := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "This post does not have mentions",
UserId: th.BasicUser.Id,
}
rpost, _, err := th.App.CreatePost(th.Context, postWithNoMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
[MM-21378] Add mutex to model.Post to guard against race conditions on Post.Props (#13884) * Add mutex to model.Post to guard against race conditions on Post.Props * Rename mutex * Add GetProp() method to Post * Fix more tests * Fix flaky test Benchmarks: BenchmarkPostPropsGet_indirect BenchmarkPostPropsGet_indirect-2 85026746 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-4 90273747 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-8 88324293 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-16 91427720 13.1 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct BenchmarkPostPropsGet_direct-2 1000000000 0.242 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-4 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-8 1000000000 0.240 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-16 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_indirect BenchmarkPostPropsAdd_indirect-2 5602224 203 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-4 5959496 206 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-8 5833999 205 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-16 5802493 225 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_direct BenchmarkPostPropsAdd_direct-2 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-4 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-8 100000000 11.6 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-16 99840794 11.4 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_indirect BenchmarkPostPropsDel_indirect-2 18824002 61.9 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-4 19470736 63.8 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-8 17640460 65.3 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-16 18692962 65.4 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_direct BenchmarkPostPropsDel_direct-2 516257440 2.34 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-4 514865216 2.43 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-8 511330477 2.37 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-16 499504010 2.38 ns/op 0 B/op 0 allocs/op
2020-03-13 16:12:20 -04:00
assert.Equal(t, rpost.GetProps(), model.StringInterface{})
postWithMention := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "This post has @here mention @all",
UserId: th.BasicUser.Id,
}
rpost, _, err = th.App.CreatePost(th.Context, postWithMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
[MM-21378] Add mutex to model.Post to guard against race conditions on Post.Props (#13884) * Add mutex to model.Post to guard against race conditions on Post.Props * Rename mutex * Add GetProp() method to Post * Fix more tests * Fix flaky test Benchmarks: BenchmarkPostPropsGet_indirect BenchmarkPostPropsGet_indirect-2 85026746 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-4 90273747 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-8 88324293 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-16 91427720 13.1 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct BenchmarkPostPropsGet_direct-2 1000000000 0.242 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-4 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-8 1000000000 0.240 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-16 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_indirect BenchmarkPostPropsAdd_indirect-2 5602224 203 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-4 5959496 206 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-8 5833999 205 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-16 5802493 225 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_direct BenchmarkPostPropsAdd_direct-2 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-4 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-8 100000000 11.6 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-16 99840794 11.4 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_indirect BenchmarkPostPropsDel_indirect-2 18824002 61.9 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-4 19470736 63.8 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-8 17640460 65.3 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-16 18692962 65.4 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_direct BenchmarkPostPropsDel_direct-2 516257440 2.34 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-4 514865216 2.43 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-8 511330477 2.37 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-16 499504010 2.38 ns/op 0 B/op 0 allocs/op
2020-03-13 16:12:20 -04:00
assert.Equal(t, rpost.GetProps(), model.StringInterface{})
})
t.Run("Sets prop when post has mentions and user does not have USE_CHANNEL_MENTIONS", func(t *testing.T) {
th.RemovePermissionFromRole(t, model.PermissionUseChannelMentions.Id, model.ChannelUserRoleId)
th.RemovePermissionFromRole(t, model.PermissionUseChannelMentions.Id, model.ChannelAdminRoleId)
postWithNoMention := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "This post does not have mentions",
UserId: th.BasicUser.Id,
}
rpost, _, err := th.App.CreatePost(th.Context, postWithNoMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
[MM-21378] Add mutex to model.Post to guard against race conditions on Post.Props (#13884) * Add mutex to model.Post to guard against race conditions on Post.Props * Rename mutex * Add GetProp() method to Post * Fix more tests * Fix flaky test Benchmarks: BenchmarkPostPropsGet_indirect BenchmarkPostPropsGet_indirect-2 85026746 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-4 90273747 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-8 88324293 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-16 91427720 13.1 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct BenchmarkPostPropsGet_direct-2 1000000000 0.242 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-4 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-8 1000000000 0.240 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-16 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_indirect BenchmarkPostPropsAdd_indirect-2 5602224 203 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-4 5959496 206 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-8 5833999 205 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-16 5802493 225 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_direct BenchmarkPostPropsAdd_direct-2 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-4 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-8 100000000 11.6 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-16 99840794 11.4 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_indirect BenchmarkPostPropsDel_indirect-2 18824002 61.9 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-4 19470736 63.8 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-8 17640460 65.3 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-16 18692962 65.4 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_direct BenchmarkPostPropsDel_direct-2 516257440 2.34 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-4 514865216 2.43 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-8 511330477 2.37 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-16 499504010 2.38 ns/op 0 B/op 0 allocs/op
2020-03-13 16:12:20 -04:00
assert.Equal(t, rpost.GetProps(), model.StringInterface{})
postWithMention := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "This post has @here mention @all",
UserId: th.BasicUser.Id,
}
rpost, _, err = th.App.CreatePost(th.Context, postWithMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
2021-07-12 14:05:36 -04:00
assert.Equal(t, rpost.GetProp(model.PostPropsMentionHighlightDisabled), true)
th.AddPermissionToRole(t, model.PermissionUseChannelMentions.Id, model.ChannelUserRoleId)
th.AddPermissionToRole(t, model.PermissionUseChannelMentions.Id, model.ChannelAdminRoleId)
})
})
2021-08-09 11:33:21 -04:00
t.Run("Sets PostPropsPreviewedPost when a permalink is the first link", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.AddUserToChannel(t, th.BasicUser, th.BasicChannel)
2021-08-09 11:33:21 -04:00
referencedPost := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "hello world",
UserId: th.BasicUser.Id,
}
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
})
th.Context.Session().UserId = th.BasicUser.Id
referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{})
2021-08-09 11:33:21 -04:00
require.Nil(t, err)
permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id)
channelForPreview := th.CreateChannel(t, th.BasicTeam)
2021-08-09 11:33:21 -04:00
previewPost := &model.Post{
ChannelId: channelForPreview.Id,
Message: permalink,
UserId: th.BasicUser.Id,
}
previewPost, _, err = th.App.CreatePost(th.Context, previewPost, channelForPreview, model.CreatePostFlags{})
2021-08-09 11:33:21 -04:00
require.Nil(t, err)
assert.Equal(t, previewPost.GetProps(), model.StringInterface{"previewed_post": referencedPost.Id})
})
t.Run("creates a single record for a permalink preview post", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
channelForPreview := th.CreateChannel(t, th.BasicTeam)
referencedPost := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "hello world",
UserId: th.BasicUser.Id,
}
referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://foobar.com"
*cfg.ServiceSettings.EnablePermalinkPreviews = true
})
permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id)
previewPost := &model.Post{
ChannelId: channelForPreview.Id,
Message: permalink,
UserId: th.BasicUser.Id,
}
previewPost, _, err = th.App.CreatePost(th.Context, previewPost, channelForPreview, model.CreatePostFlags{})
require.Nil(t, err)
sqlStore := th.GetSqlStore()
sql := fmt.Sprintf("select count(*) from Posts where Id = '%[1]s' or OriginalId = '%[1]s';", previewPost.Id)
var val int64
err2 := sqlStore.GetMaster().Get(&val, sql)
require.NoError(t, err2)
require.EqualValues(t, int64(1), val)
})
t.Run("sanitizes post metadata appropriately", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
})
th.AddUserToChannel(t, th.BasicUser, th.BasicChannel)
user1 := th.CreateUser(t)
user2 := th.CreateUser(t)
directChannel, err := th.App.createDirectChannel(th.Context, user1.Id, user2.Id)
require.Nil(t, err)
th.Context.Session().UserId = th.BasicUser.Id
testCases := []struct {
Description string
Channel *model.Channel
Author string
Length int
}{
{
Description: "removes metadata from post for members who cannot read channel",
Channel: directChannel,
Author: user1.Id,
Length: 0,
},
{
Description: "does not remove metadata from post for members who can read channel",
Channel: th.BasicChannel,
Author: th.BasicUser.Id,
Length: 1,
},
}
for _, testCase := range testCases {
t.Run(testCase.Description, func(t *testing.T) {
referencedPost := &model.Post{
ChannelId: testCase.Channel.Id,
Message: "hello world",
UserId: testCase.Author,
}
referencedPost, _, err = th.App.CreatePost(th.Context, referencedPost, testCase.Channel, model.CreatePostFlags{})
require.Nil(t, err)
permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id)
previewPost := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: permalink,
UserId: th.BasicUser.Id,
}
previewPost, _, err = th.App.CreatePost(th.Context, previewPost, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
require.Len(t, previewPost.Metadata.Embeds, testCase.Length)
})
}
})
t.Run("Should not allow to create posts on shared DMs", func(t *testing.T) {
mainHelper.Parallel(t)
th := setupSharedChannels(t).InitBasic(t)
user1 := th.CreateUser(t)
user2 := th.CreateUser(t)
dm, appErr := th.App.createDirectChannel(th.Context, user1.Id, user2.Id)
require.Nil(t, appErr)
require.NotNil(t, dm)
// we can't create direct channels with remote users, so we
// have to force the channel to be shared through the store to
// simulate preexisting shared DMs
sc := &model.SharedChannel{
ChannelId: dm.Id,
Type: dm.Type,
Home: true,
ShareName: "shareddm",
CreatorId: user1.Id,
RemoteId: model.NewId(),
}
_, scErr := th.Server.Store().SharedChannel().Save(sc)
require.NoError(t, scErr)
// and we update the channel to mark it as shared
dm.Shared = model.NewPointer(true)
_, err := th.Server.Store().Channel().Update(th.Context, dm)
require.NoError(t, err)
newPost := &model.Post{
ChannelId: dm.Id,
Message: "hello world",
UserId: user1.Id,
}
createdPost, _, appErr := th.App.CreatePost(th.Context, newPost, dm, model.CreatePostFlags{})
require.NotNil(t, appErr)
require.Nil(t, createdPost)
})
t.Run("Should not allow to create posts on shared GMs", func(t *testing.T) {
mainHelper.Parallel(t)
th := setupSharedChannels(t).InitBasic(t)
user1 := th.CreateUser(t)
user2 := th.CreateUser(t)
user3 := th.CreateUser(t)
gm, appErr := th.App.createGroupChannel(th.Context, []string{user1.Id, user2.Id, user3.Id}, user1.Id)
require.Nil(t, appErr)
require.NotNil(t, gm)
// we can't create group channels with remote users, so we
// have to force the channel to be shared through the store to
// simulate preexisting shared GMs
sc := &model.SharedChannel{
ChannelId: gm.Id,
Type: gm.Type,
Home: true,
ShareName: "sharedgm",
CreatorId: user1.Id,
RemoteId: model.NewId(),
}
_, err := th.Server.Store().SharedChannel().Save(sc)
require.NoError(t, err)
// and we update the channel to mark it as shared
gm.Shared = model.NewPointer(true)
_, err = th.Server.Store().Channel().Update(th.Context, gm)
require.NoError(t, err)
newPost := &model.Post{
ChannelId: gm.Id,
Message: "hello world",
UserId: user1.Id,
}
createdPost, _, appErr := th.App.CreatePost(th.Context, newPost, gm, model.CreatePostFlags{})
require.NotNil(t, appErr)
require.Nil(t, createdPost)
})
t.Run("MM-40016 should not panic with `concurrent map read and map write`", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
channelForPreview := th.CreateChannel(t, th.BasicTeam)
for range 20 {
user := th.CreateUser(t)
th.LinkUserToTeam(t, user, th.BasicTeam)
th.AddUserToChannel(t, user, channelForPreview)
}
referencedPost := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "hello world",
UserId: th.BasicUser.Id,
}
referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://example.com"
*cfg.ServiceSettings.EnablePermalinkPreviews = true
})
permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id)
previewPost := &model.Post{
ChannelId: channelForPreview.Id,
Message: permalink,
UserId: th.BasicUser.Id,
}
previewPost, _, err = th.App.CreatePost(th.Context, previewPost, channelForPreview, model.CreatePostFlags{})
require.Nil(t, err)
n := 1000
var wg sync.WaitGroup
wg.Add(n)
for range n {
go func() {
defer wg.Done()
post := previewPost.Clone()
_, _, appErr := th.App.UpdatePost(th.Context, post, nil)
require.Nil(t, appErr)
}()
}
wg.Wait()
})
t.Run("should sanitize the force notifications prop if the flag is not set", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.AddUserToChannel(t, th.BasicUser, th.BasicChannel)
postToCreate := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "hello world",
UserId: th.BasicUser.Id,
}
postToCreate.AddProp(model.PostPropsForceNotification, model.NewId())
createdPost, _, err := th.App.CreatePost(th.Context, postToCreate, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
require.Empty(t, createdPost.GetProp(model.PostPropsForceNotification))
})
t.Run("should add the force notifications prop if the flag is set", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.AddUserToChannel(t, th.BasicUser, th.BasicChannel)
postToCreate := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "hello world",
UserId: th.BasicUser.Id,
}
createdPost, _, err := th.App.CreatePost(th.Context, postToCreate, th.BasicChannel, model.CreatePostFlags{ForceNotification: true})
require.Nil(t, err)
require.NotEmpty(t, createdPost.GetProp(model.PostPropsForceNotification))
})
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
Merge the Integrated Boards MVP feature branch (#35796) * Add CreatedBy and UpdatedBy to the properties fields and values (#34485) * Add CreatedBy and UpdatedBy to the properties fields and values * Fix types --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Adds ObjectType to the property fields table (#34908) Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Update ObjectType migration setting an empty value and marking the column as not null (#34915) Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Adds uniqueness mechanisms to the property fields (#35058) * Adds uniqueness mechanisms to the property fields After adding ObjectType, this commit ensures that both the PSAv1 and PSAv2 schemas are supported, and enforces property uniqueness through both database indexes and a logical check when creating new property fields. * Adds uniqueness check to property updates Updates are covered on this commit and we refactor as well the SQL code to use the squirrel builder and work better with the conditional addition of the `existingID` piece of the query. * Add translations to error messages * Fixing retrylayer mocks * Remove retrylayer duplication * Address review comments * Fix comment to avoid linter issues * Address PR comments * Update server/channels/db/migrations/postgres/000157_add_object_type_to_property_fields.down.sql Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> * Update server/channels/db/migrations/postgres/000157_add_object_type_to_property_fields.up.sql Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> * Update server/channels/db/migrations/postgres/000157_add_object_type_to_property_fields.up.sql Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> * Update field validation to check only for valid target types * Update migrations to avoid concurrent index creation within a transaction * Update migrations to make all index ops concurrent * Update tests to use valid PSAv2 property fields * Adds a helper for valid PSAv2 TargetTypes --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> * Fix property tests (#35388) Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Adds Integrated Boards feature flag (#35378) Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Adds Integrated Boards MVP API changes (#34822) This PR includes the necessary changes for channels and posts endpoints and adds a set of generic endpoints to retrieve and manage property fields and values following the new Property System approach. Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> Co-authored-by: Mattermost Build <build@mattermost.com> * Property System Architecture permissions for v2 (#35113) * Adds uniqueness mechanisms to the property fields After adding ObjectType, this commit ensures that both the PSAv1 and PSAv2 schemas are supported, and enforces property uniqueness through both database indexes and a logical check when creating new property fields. * Adds uniqueness check to property updates Updates are covered on this commit and we refactor as well the SQL code to use the squirrel builder and work better with the conditional addition of the `existingID` piece of the query. * Add translations to error messages * Add the permissions to the migrations, model and update the store calls * Adds the property field and property group app layer * Adds authorization helpers for property fields and values * Make sure that users cannot lock themselves out of property fields * Migrate permissions from a JSON column to three normalized columns * Remove the audit comment * Use target level constants in authorization * Log authorization membership failures * Rename admin to sysadmin * Fix i18n sorting --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Add Views store and app layer (#35361) * Add Views store and app layer for Integrated Boards Implements the View entity (model, SQL store, service, app) as described in the Integrated Boards tech spec. Views are channel-scoped board configurations with typed props (board, kanban subviews) and soft-delete. - public/model: View, ViewBoardProps, Subview, ViewPatch types with PreSave/PreUpdate/IsValid/Patch/Clone/Auditable - Migration 158: Views table with jsonb Props column and indexes - SqlViewStore: CRUD with nil-safe Props marshaling (AppendBinaryFlag) - ViewService: CreateView seeds default kanban subview and links the boards property field; caches boardPropertyFieldID at startup - App layer: CreateView/GetView/GetViewsForChannel/UpdateView/DeleteView with channel-membership permission checks and WebSocket events (view_created, view_updated, view_deleted) - doSetupBoardsPropertyField: registers the Boards property group and board field in NewServer() before ViewService construction - GetFieldByName now returns store.ErrNotFound instead of raw sql.ErrNoRows * Move permission checks out of App layer for views - Remove HasPermissionToChannel calls from all App view methods - Drop userID params from GetView, GetViewsForChannel, UpdateView, DeleteView - Fix doSetupBoardsPropertyField to include required TargetType for PSAv2 field * Make View service generic and enforce board validation in model - Remove board-specific auto-setup from service and server startup - Enforce that board views require Props, at least one subview, and at least one linked property in IsValid() - Move default subview seeding out of app layer; callers must provide valid props - Call PreSave on subviews during PreUpdate to assign IDs to new subviews - Update all tests to reflect the new validation requirements * Restore migrations files to match base branch * Distinguish ErrNotFound from other errors in view store Get * Use CONCURRENTLY and nontransactional for index operations in views migration * Split views index creation into separate nontransactional migrations * Update migrations.list * Update i18n translations for views * Fix makeView helper to include required Props for board view validation * Rename ctx parameter from c to rctx in OAuthProvider mock * Remove views service layer, call store directly from app * Return 500 for unexpected DB errors in GetView, 404 only for not-found * Harden View model: deep-copy Props, validate linked property IDs - Add ViewBoardProps.Clone() to deep-copy LinkedProperties and Subviews - Use it in View.Clone() and View.Patch() to prevent shared-slice aliasing - Iterate over LinkedProperties in View.IsValid() and reject invalid IDs with a dedicated i18n key - Register ViewStore in storetest AssertExpectations so mock expectations are enforced - Add tests covering all new behaviours * Restore autotranslation worker_stopped i18n translation * Fix view store test IDs and improve error handling in app layer - Use model.NewId() for linked property IDs in testUpdateView to fix validation failure (IsValid rejects non-UUID strings) - Fix import grouping in app/view.go (stdlib imports in one block) - Return 404 instead of 500 when Update/Delete store calls return ErrNotFound (e.g. concurrent deletion TOCTOU race) * Add View store mock to retrylayer test genStore helper The View store was added to the store interface but the genStore() helper in retrylayer_test.go was not updated, causing TestRetry to panic. Also removes the duplicate Recap mock registration. * Refactor view deletion and websocket event handling; update SQL store methods to use query builder * revert property field store * Remove useless migrations * Add cursor-based pagination to View store GetForChannel - Add ViewQueryCursor and ViewQueryOpts types with validation - Return (views, cursor, error) for caller-driven pagination - PerPage clamping: <=0 defaults to 20, >200 clamps to 200 - Support IncludeDeleted filter - Add comprehensive store tests for pagination, cursor edge cases, PerPage clamping, and invalid input rejection - Add app layer test for empty channelID → 400 - Update interface, retrylayer, timerlayer, and mock signatures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Refactor test loops in ViewStore tests for improved readability * change pagination to limit/offset * Add upper-bound limits on View Subviews and LinkedProperties Defense-in-depth validation: cap Subviews at 50 and LinkedProperties at 500 to prevent abuse below the 300KB payload limit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * MM-67388, MM-66528, MM-67750: Add View REST API endpoints, websocket events, and sort order (#35442) * Add Views store and app layer for Integrated Boards Implements the View entity (model, SQL store, service, app) as described in the Integrated Boards tech spec. Views are channel-scoped board configurations with typed props (board, kanban subviews) and soft-delete. - public/model: View, ViewBoardProps, Subview, ViewPatch types with PreSave/PreUpdate/IsValid/Patch/Clone/Auditable - Migration 158: Views table with jsonb Props column and indexes - SqlViewStore: CRUD with nil-safe Props marshaling (AppendBinaryFlag) - ViewService: CreateView seeds default kanban subview and links the boards property field; caches boardPropertyFieldID at startup - App layer: CreateView/GetView/GetViewsForChannel/UpdateView/DeleteView with channel-membership permission checks and WebSocket events (view_created, view_updated, view_deleted) - doSetupBoardsPropertyField: registers the Boards property group and board field in NewServer() before ViewService construction - GetFieldByName now returns store.ErrNotFound instead of raw sql.ErrNoRows * Move permission checks out of App layer for views - Remove HasPermissionToChannel calls from all App view methods - Drop userID params from GetView, GetViewsForChannel, UpdateView, DeleteView - Fix doSetupBoardsPropertyField to include required TargetType for PSAv2 field * Make View service generic and enforce board validation in model - Remove board-specific auto-setup from service and server startup - Enforce that board views require Props, at least one subview, and at least one linked property in IsValid() - Move default subview seeding out of app layer; callers must provide valid props - Call PreSave on subviews during PreUpdate to assign IDs to new subviews - Update all tests to reflect the new validation requirements * Restore migrations files to match base branch * Distinguish ErrNotFound from other errors in view store Get * Use CONCURRENTLY and nontransactional for index operations in views migration * Split views index creation into separate nontransactional migrations * Update migrations.list * Update i18n translations for views * Fix makeView helper to include required Props for board view validation * Rename ctx parameter from c to rctx in OAuthProvider mock * Remove views service layer, call store directly from app * Return 500 for unexpected DB errors in GetView, 404 only for not-found * Harden View model: deep-copy Props, validate linked property IDs - Add ViewBoardProps.Clone() to deep-copy LinkedProperties and Subviews - Use it in View.Clone() and View.Patch() to prevent shared-slice aliasing - Iterate over LinkedProperties in View.IsValid() and reject invalid IDs with a dedicated i18n key - Register ViewStore in storetest AssertExpectations so mock expectations are enforced - Add tests covering all new behaviours * Restore autotranslation worker_stopped i18n translation * Fix view store test IDs and improve error handling in app layer - Use model.NewId() for linked property IDs in testUpdateView to fix validation failure (IsValid rejects non-UUID strings) - Fix import grouping in app/view.go (stdlib imports in one block) - Return 404 instead of 500 when Update/Delete store calls return ErrNotFound (e.g. concurrent deletion TOCTOU race) * Add View store mock to retrylayer test genStore helper The View store was added to the store interface but the genStore() helper in retrylayer_test.go was not updated, causing TestRetry to panic. Also removes the duplicate Recap mock registration. * Refactor view deletion and websocket event handling; update SQL store methods to use query builder * revert property field store * Add View API endpoints with OpenAPI spec, client methods, and i18n Implement REST API for channel views (board-type) behind the IntegratedBoards feature flag. Adds CRUD endpoints under /api/v4/channels/{channel_id}/views with permission checks matching the channel bookmark pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove useless migrations * Add cursor-based pagination to View store GetForChannel - Add ViewQueryCursor and ViewQueryOpts types with validation - Return (views, cursor, error) for caller-driven pagination - PerPage clamping: <=0 defaults to 20, >200 clamps to 200 - Support IncludeDeleted filter - Add comprehensive store tests for pagination, cursor edge cases, PerPage clamping, and invalid input rejection - Add app layer test for empty channelID → 400 - Update interface, retrylayer, timerlayer, and mock signatures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add cursor-based pagination to View API for channel views * Enhance cursor handling in getViewsForChannel and update tests for pagination * Refactor test loops in ViewStore tests for improved readability * Refactor loop in TestGetViewsForChannel for improved readability * change pagination to limit/offset * switch to limit/offset pagination * Add upper-bound limits on View Subviews and LinkedProperties Defense-in-depth validation: cap Subviews at 50 and LinkedProperties at 500 to prevent abuse below the 300KB payload limit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add view sort order API endpoint Add POST /api/v4/channels/{channel_id}/views/{view_id}/sort_order endpoint following the channel bookmarks reorder pattern. Includes store, app, and API layers with full test coverage at each layer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add connectionId to view WebSocket events and sort_order API spec Thread connectionId from request header through all view handlers (create, update, delete, sort_order) to WebSocket events, matching the channel bookmarks pattern. Add sort_order endpoint to OpenAPI spec. Update minimum server version to 11.6. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove duplicate View/ViewPatch definitions from definitions.yaml The merge from integrated-boards-mvp introduced duplicate View and ViewPatch schema definitions that were already defined earlier in the file with more detail (including ViewBoardProps ref and enums). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update minimum server version to 11.6 in views API spec Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add missing translations for view sort order error messages Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Merge integrated-boards-mvp into ibmvp_api-views; remove spec files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix flaky TestViewStore timestamp test on CI Add sleep before UpdateSortOrder to ensure timestamps differ, preventing same-millisecond comparisons on fast CI machines. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * remove duplicate views.yaml imclude * Use c.boolString() for include_deleted query param in GetViewsForChannel Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix views.yaml sort order schema: use integer type and require body Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Refactor view sort order tests to use named IDs instead of array indices Extract idA/idB/idC from views slice and add BEFORE/AFTER comments to make stateful subtest ordering easier to follow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Return 404 instead of 403 for view operations on deleted channels Deleted channels should appear non-existent to callers rather than revealing their existence via a 403. Detailed error text explains the context for debugging. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * add missing channel deleteat checks * Use c.Params.Page instead of manual page query param parsing in getViewsForChannel c.Params already validates and defaults page/per_page, so the manual parsing was redundant. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add support for total count in views retrieval * Add tests for handling deleted views in GetViewsForChannel and GetView * Short-circuit negative newIndex in UpdateSortOrder before opening transaction Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add per-channel limit on views to bound UpdateSortOrder cost Without a cap, unbounded view creation makes sort-order updates increasingly expensive (CASE WHEN per view, row locks). Adds MaxViewsPerChannel=50 constant and enforces it in the app layer before saving. Includes API and app layer tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove include_deleted support from views API Soft-deleted views are structural metadata with low risk, but no other similar endpoint (e.g. channel bookmarks) exposes deleted records without an admin gate. Rather than adding an admin-only permission check for consistency, remove the feature entirely since there is no current use case. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update view permissions to require `create_post` instead of channel management permissions * Remove obsolete view management error messages for direct and group messages --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat(migrations): add user tracking and object type to property fields - Introduced user tracking columns (CreatedBy, UpdatedBy) to PropertyFields and PropertyValues. - Added ObjectType column to PropertyFields with associated unique indexes for legacy and typed properties. - Created new migration scripts for adding and dropping these features, including necessary indexes for data integrity. - Established views for managing property fields with new attributes. This update enhances the schema to support better tracking and categorization of property fields. * Add Property System Architecture v2 API endpoints (#35583) * Adds uniqueness mechanisms to the property fields After adding ObjectType, this commit ensures that both the PSAv1 and PSAv2 schemas are supported, and enforces property uniqueness through both database indexes and a logical check when creating new property fields. * Adds uniqueness check to property updates Updates are covered on this commit and we refactor as well the SQL code to use the squirrel builder and work better with the conditional addition of the `existingID` piece of the query. * Add translations to error messages * Add the permissions to the migrations, model and update the store calls * Adds the property field and property group app layer * Adds authorization helpers for property fields and values * Make sure that users cannot lock themselves out of property fields * Migrate permissions from a JSON column to three normalized columns * Remove the audit comment * Use target level constants in authorization * Log authorization membership failures * Rename admin to sysadmin * Adds the Property System Architecture v2 API endpoints * Adds permission checks to the create field endpoint * Add target access checks to value endpoints * Add default branches for object_type and target_type and extra guards for cursor client4 methods * Fix vet API mismatch * Fix error checks * Fix linter * Add merge semantics for property patch logic and API endpoint * Fix i18n * Fix duplicated patch elements and early return on bad cursor * Update docs to use enums * Fix i18n sorting * Update app layer to return model.AppError * Adds a limit to the number of property values that can be patched in the same request * Require target_type filter when searching property fields * Add objectType validation as part of field.IsValid() * Fix linter * Fix test with bad objecttpye * Fix test grouping --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * MM-67968: Flatten view model — remove icon, subviews, typed board props (#35726) * feat(views): flatten view model by removing icon, subview, and board props Simplifies the View data model as part of MM-67968: removes Icon, Subview, and ViewBoardProps types; renames ViewTypeBoard to ViewTypeKanban; replaces typed Props with StringInterface (map[string]any); adds migration 000167 to drop the Icon column from the Views table. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * feat(api): update views OpenAPI spec to reflect flattened model Removes ViewBoardProps, Subview, and icon from the View and ViewPatch schemas. Changes type enum from board to kanban. Replaces typed props with a free-form StringInterface object. Aligns with MM-67968. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * refactor(views): simplify store by dropping dbView and marshalViewProps StringInterface already implements driver.Valuer and sql.Scanner, so the manual JSON marshal/unmarshal and the dbView intermediate struct were redundant. model.View now scans directly from the database. Also removes the dead ViewMaxLinkedProperties constant and wraps the Commit() error in UpdateSortOrder. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * fix(api): allow arbitrary JSON in view props OpenAPI schema The props field was restricted to string values via additionalProperties: { type: string }, conflicting with the Go model's StringInterface (map[string]any). Changed to additionalProperties: true in View, ViewPatch, and inline POST schemas. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * Adds basic implementation of the generic redux store for PSAv2 (#35512) * Adds basic implementation of the generic redux store for PSAv2 * Add created_by and updated_by to the test fixtures * Make target_id, target_type and object_type mandatory * Wrap getPropertyFieldsByIds and getPropertyValuesForTargetByFieldIds with createSelector * Address PR comments --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Adds websocket messages for the PSAv2 API events (#35696) * Adds uniqueness mechanisms to the property fields After adding ObjectType, this commit ensures that both the PSAv1 and PSAv2 schemas are supported, and enforces property uniqueness through both database indexes and a logical check when creating new property fields. * Adds uniqueness check to property updates Updates are covered on this commit and we refactor as well the SQL code to use the squirrel builder and work better with the conditional addition of the `existingID` piece of the query. * Add translations to error messages * Add the permissions to the migrations, model and update the store calls * Adds the property field and property group app layer * Adds authorization helpers for property fields and values * Make sure that users cannot lock themselves out of property fields * Migrate permissions from a JSON column to three normalized columns * Remove the audit comment * Use target level constants in authorization * Log authorization membership failures * Rename admin to sysadmin * Adds the Property System Architecture v2 API endpoints * Adds permission checks to the create field endpoint * Add target access checks to value endpoints * Add default branches for object_type and target_type and extra guards for cursor client4 methods * Fix vet API mismatch * Fix error checks * Fix linter * Add merge semantics for property patch logic and API endpoint * Fix i18n * Fix duplicated patch elements and early return on bad cursor * Update docs to use enums * Fix i18n sorting * Update app layer to return model.AppError * Adds a limit to the number of property values that can be patched in the same request * Adds websocket messages for the PSAv2 API events * Add IsPSAv2 helper to the property field for clarity * Add guard against nil returns on field deletion * Add docs to the websocket endpoints --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * migrations: consolidate views migrations and reorder after master - Merged 000165 (create Views) with 000167 (drop Icon) since Icon was never needed - Renumbered branch migrations 159-166 → 160-167 so master's 000159 (deduplicate_policy_names) runs first - Regenerated migrations.list Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add API endpoint to retrieve posts for a specific view (#35604) Automatic Merge * Apply fixes after merge * Return a more specific error from getting multiple fields * Prevent getting broadcast params on field deletion if not needed * Remove duplicated migration code * Update property conflict code to always use master * Adds nil guard when iterating on property fields * Check that permission level is valid before getting rejected by the database * Validate correctness on TargetID for PSAv2 fields * Avoid PSAv1 using permissions or protected * Fix test data after validation change * Fix flaky search test * Adds more posts for filter use cases to properly test exclusions --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> Co-authored-by: Julien Tant <julien@craftyx.fr> Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Julien Tant <785518+JulienTant@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 05:36:35 -04:00
t.Run("creates post with type card", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "card post",
Type: model.PostTypeCard,
}
rpost, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, appErr)
require.NotNil(t, rpost)
assert.Equal(t, model.PostTypeCard, rpost.Type)
assert.Equal(t, "card post", rpost.Message)
})
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
t.Run("Should remove post file IDs for burn on read posts", func(t *testing.T) {
th := Setup(t).InitBasic(t)
ci: enable fullyparallel mode for server tests (#35816) * ci: enable fullyparallel mode for server tests Replace os.Setenv, os.Chdir, and global state mutations with parallel-safe alternatives (t.Setenv, t.Chdir, test hooks) across 37 files. Refactor GetLogRootPath and MM_INSTALL_TYPE to use package-level test hooks instead of environment variables. This enables gotestsum --fullparallel, allowing all test packages to run with maximum parallelism within each shard. Co-authored-by: Claude <claude@anthropic.com> * ci: split fullyparallel from continue-on-error in workflow template - Add new boolean input 'allow-failure' separate from 'fullyparallel' - Change continue-on-error to use allow-failure instead of fullyparallel - Update server-ci.yml to pass allow-failure: true for test coverage job - Allows independent control of parallel execution and failure tolerance Co-authored-by: Claude <claude@anthropic.com> * fix: protect TestOverrideLogRootPath with sync.Mutex for parallel tests - Replace global var TestOverrideLogRootPath with mutex-protected functions - Add SetTestOverrideLogRootPath() and getTestOverrideLogRootPath() functions - Update GetLogRootPath() to use thread-safe getter - Update all test files to use SetTestOverrideLogRootPath() with t.Cleanup() - Fixes race condition when running tests with t.Parallel() Co-authored-by: Claude <claude@anthropic.com> * fix: configure audit settings before server setup in tests - Move ExperimentalAuditSettings from UpdateConfig() to config defaults - Pass audit config via app.Config() option in SetupWithServerOptions() - Fixes audit test setup ordering to configure BEFORE server initialization - Resolves CodeRabbit's audit config timing issue in api4 tests Co-authored-by: Claude <claude@anthropic.com> * fix: implement SetTestOverrideLogRootPath mutex in logger.go The previous commit updated test callers to use SetTestOverrideLogRootPath() but didn't actually create the function in config/logger.go, causing build failures across all CI shards. This commit: - Replaces the exported var TestOverrideLogRootPath with mutex-protected unexported state (testOverrideLogRootPath + testOverrideLogRootMu) - Adds exported SetTestOverrideLogRootPath() setter - Adds unexported getTestOverrideLogRootPath() getter - Updates GetLogRootPath() to use the thread-safe getter - Fixes log_test.go callers that were missed in the previous commit Co-authored-by: Claude <claude@anthropic.com> * fix(test): use SetupConfig for access_control feature flag registration InitAccessControlPolicy() checks FeatureFlags.AttributeBasedAccessControl at route registration time during server startup. Setting the flag via UpdateConfig after Setup() is too late — routes are never registered and API calls return 404. Use SetupConfig() to pass the feature flag in the initial config before server startup, ensuring routes are properly registered. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore BurnOnRead flag state in TestRevealPost subtest The 'feature not enabled' subtest disables BurnOnRead without restoring it via t.Cleanup. Subsequent subtests inherit the disabled state, which can cause 501 errors when they expect the feature to be available. Add t.Cleanup to restore FeatureFlags.BurnOnRead = true after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore EnableSharedChannelsMemberSync flag via t.Cleanup The test disables EnableSharedChannelsMemberSync without restoring it. If the subtest exits early (e.g., require failure), later sibling subtests inherit a disabled flag and become flaky. Add t.Cleanup to restore the flag after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * Fix test parallelism: use instance-scoped overrides and init-time audit config Replace package-level test globals (TestOverrideInstallType, SetTestOverrideLogRootPath) with fields on PlatformService so each test gets its own instance without process-wide mutation. Fix three audit tests (TestUserLoginAudit, TestLogoutAuditAuthStatus, TestUpdatePasswordAudit) that configured the audit logger after server init — the audit logger only reads config at startup, so pass audit settings via app.Config() at init time instead. Also revert the Go 1.24.13 downgrade and bump mattermost-govet to v2.0.2 for Go 1.25.8 compatibility. * Fix audit unit tests * Fix MMCLOUDURL unit tests * Fixed unit tests using MM_NOTIFY_ADMIN_COOL_OFF_DAYS * Make app migrations idempotent for parallel test safety Change System().Save() to System().SaveOrUpdate() in all migration completion markers. When two parallel tests share a database pool entry, both may race through the check-then-insert migration pattern. Save() causes a duplicate key fatal crash; SaveOrUpdate() makes the second write a harmless no-op. * test: address review feedback on fullyparallel PR - Use SetLogRootPathOverride() setter instead of direct field access in platform/support_packet_test.go and platform/log_test.go (pvev) - Restore TestGetLogRootPath in config/logger_test.go to keep MM_LOG_PATH env var coverage; test uses t.Setenv so it runs serially which is fine (pvev) - Fix misleading comment in config_test.go: code uses t.Setenv, not os.Setenv (jgheithcock) Co-authored-by: Claude <claude@anthropic.com> * fix: add missing os import in post_test.go The os import was dropped during a merge conflict resolution while burn-on-read shared channel tests from master still use os.Setenv. Co-authored-by: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: wiggin77 <wiggin77@warpmail.net> Co-authored-by: Mattermost Build <build@mattermost.com>
2026-04-08 20:48:36 -04:00
// Enable BurnOnRead feature flag
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.BurnOnRead = true })
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
enableBoRFeature(th)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "hello world",
Type: model.PostTypeBurnOnRead,
FileIds: []string{model.NewId()},
}
createdPost, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{})
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
require.Nil(t, appErr)
require.Empty(t, createdPost.FileIds)
})
t.Run("should reject burn-on-read posts in shared channels", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_BURNONREAD", "true")
t.Cleanup(func() {
os.Unsetenv("MM_FEATUREFLAGS_BURNONREAD")
})
th := setupSharedChannels(t).InitBasic(t)
enableBoRFeature(th)
channel := th.CreateChannel(t, th.BasicTeam)
sc := &model.SharedChannel{
ChannelId: channel.Id,
TeamId: th.BasicTeam.Id,
Type: channel.Type,
Home: true,
ShareName: "shared-bor-test",
CreatorId: th.BasicUser.Id,
RemoteId: model.NewId(),
}
_, scErr := th.Server.Store().SharedChannel().Save(sc)
require.NoError(t, scErr)
channel.Shared = model.NewPointer(true)
_, err := th.Server.Store().Channel().Update(th.Context, channel)
require.NoError(t, err)
post := &model.Post{
ChannelId: channel.Id,
UserId: th.BasicUser.Id,
Message: "burn-on-read in shared channel",
Type: model.PostTypeBurnOnRead,
}
createdPost, _, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{SetOnline: true})
require.NotNil(t, appErr)
require.Nil(t, createdPost)
require.Equal(t, "api.post.fill_in_post_props.burn_on_read.shared_channel.app_error", appErr.Id)
require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
})
t.Run("should allow burn-on-read posts in non-shared channels", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_BURNONREAD", "true")
t.Cleanup(func() {
os.Unsetenv("MM_FEATUREFLAGS_BURNONREAD")
})
th := Setup(t).InitBasic(t)
enableBoRFeature(th)
channel := th.CreateChannel(t, th.BasicTeam)
require.False(t, channel.IsShared())
post := &model.Post{
ChannelId: channel.Id,
UserId: th.BasicUser.Id,
Message: "burn-on-read in non-shared channel",
Type: model.PostTypeBurnOnRead,
}
createdPost, _, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, appErr)
require.NotNil(t, createdPost)
require.Equal(t, model.PostTypeBurnOnRead, createdPost.Type)
})
}
func TestPatchPost(t *testing.T) {
mainHelper.Parallel(t)
t.Run("call PreparePostForClient before returning", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
*cfg.ImageProxySettings.Enable = true
*cfg.ImageProxySettings.ImageProxyType = "atmos/camo"
*cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1"
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905) * Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-04-08 15:49:43 -04:00
*cfg.ImageProxySettings.RemoteImageProxyOptions = model.NewTestPassword()
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log())
imageURL := "http://mydomain.com/myimage"
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "![image](http://mydomain/anotherimage)",
UserId: th.BasicUser.Id,
}
rpost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
assert.NotEqual(t, "![image]("+proxiedImageURL+")", rpost.Message)
patch := &model.PostPatch{
Message: model.NewPointer("![image](" + imageURL + ")"),
}
rpost, _, err = th.App.PatchPost(th.Context, rpost.Id, patch, nil)
require.Nil(t, err)
assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message)
})
t.Run("Sets Prop MENTION_HIGHLIGHT_DISABLED when it should", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.AddUserToChannel(t, th.BasicUser, th.BasicChannel)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "This post does not have mentions",
UserId: th.BasicUser.Id,
}
rpost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
t.Run("Does not set prop when user has USE_CHANNEL_MENTIONS", func(t *testing.T) {
patchWithNoMention := &model.PostPatch{Message: model.NewPointer("This patch has no channel mention")}
rpost, _, err = th.App.PatchPost(th.Context, rpost.Id, patchWithNoMention, nil)
require.Nil(t, err)
[MM-21378] Add mutex to model.Post to guard against race conditions on Post.Props (#13884) * Add mutex to model.Post to guard against race conditions on Post.Props * Rename mutex * Add GetProp() method to Post * Fix more tests * Fix flaky test Benchmarks: BenchmarkPostPropsGet_indirect BenchmarkPostPropsGet_indirect-2 85026746 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-4 90273747 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-8 88324293 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-16 91427720 13.1 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct BenchmarkPostPropsGet_direct-2 1000000000 0.242 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-4 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-8 1000000000 0.240 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-16 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_indirect BenchmarkPostPropsAdd_indirect-2 5602224 203 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-4 5959496 206 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-8 5833999 205 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-16 5802493 225 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_direct BenchmarkPostPropsAdd_direct-2 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-4 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-8 100000000 11.6 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-16 99840794 11.4 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_indirect BenchmarkPostPropsDel_indirect-2 18824002 61.9 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-4 19470736 63.8 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-8 17640460 65.3 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-16 18692962 65.4 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_direct BenchmarkPostPropsDel_direct-2 516257440 2.34 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-4 514865216 2.43 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-8 511330477 2.37 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-16 499504010 2.38 ns/op 0 B/op 0 allocs/op
2020-03-13 16:12:20 -04:00
assert.Equal(t, rpost.GetProps(), model.StringInterface{})
patchWithMention := &model.PostPatch{Message: model.NewPointer("This patch has a mention now @here")}
rpost, _, err = th.App.PatchPost(th.Context, rpost.Id, patchWithMention, nil)
require.Nil(t, err)
[MM-21378] Add mutex to model.Post to guard against race conditions on Post.Props (#13884) * Add mutex to model.Post to guard against race conditions on Post.Props * Rename mutex * Add GetProp() method to Post * Fix more tests * Fix flaky test Benchmarks: BenchmarkPostPropsGet_indirect BenchmarkPostPropsGet_indirect-2 85026746 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-4 90273747 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-8 88324293 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-16 91427720 13.1 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct BenchmarkPostPropsGet_direct-2 1000000000 0.242 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-4 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-8 1000000000 0.240 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-16 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_indirect BenchmarkPostPropsAdd_indirect-2 5602224 203 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-4 5959496 206 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-8 5833999 205 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-16 5802493 225 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_direct BenchmarkPostPropsAdd_direct-2 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-4 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-8 100000000 11.6 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-16 99840794 11.4 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_indirect BenchmarkPostPropsDel_indirect-2 18824002 61.9 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-4 19470736 63.8 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-8 17640460 65.3 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-16 18692962 65.4 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_direct BenchmarkPostPropsDel_direct-2 516257440 2.34 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-4 514865216 2.43 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-8 511330477 2.37 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-16 499504010 2.38 ns/op 0 B/op 0 allocs/op
2020-03-13 16:12:20 -04:00
assert.Equal(t, rpost.GetProps(), model.StringInterface{})
})
t.Run("Sets prop when user does not have USE_CHANNEL_MENTIONS", func(t *testing.T) {
th.RemovePermissionFromRole(t, model.PermissionUseChannelMentions.Id, model.ChannelUserRoleId)
th.RemovePermissionFromRole(t, model.PermissionUseChannelMentions.Id, model.ChannelAdminRoleId)
patchWithNoMention := &model.PostPatch{Message: model.NewPointer("This patch still does not have a mention")}
rpost, _, err = th.App.PatchPost(th.Context, rpost.Id, patchWithNoMention, nil)
require.Nil(t, err)
[MM-21378] Add mutex to model.Post to guard against race conditions on Post.Props (#13884) * Add mutex to model.Post to guard against race conditions on Post.Props * Rename mutex * Add GetProp() method to Post * Fix more tests * Fix flaky test Benchmarks: BenchmarkPostPropsGet_indirect BenchmarkPostPropsGet_indirect-2 85026746 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-4 90273747 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-8 88324293 13.0 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_indirect-16 91427720 13.1 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct BenchmarkPostPropsGet_direct-2 1000000000 0.242 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-4 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-8 1000000000 0.240 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsGet_direct-16 1000000000 0.241 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_indirect BenchmarkPostPropsAdd_indirect-2 5602224 203 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-4 5959496 206 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-8 5833999 205 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_indirect-16 5802493 225 ns/op 336 B/op 2 allocs/op BenchmarkPostPropsAdd_direct BenchmarkPostPropsAdd_direct-2 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-4 100000000 11.3 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-8 100000000 11.6 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsAdd_direct-16 99840794 11.4 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_indirect BenchmarkPostPropsDel_indirect-2 18824002 61.9 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-4 19470736 63.8 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-8 17640460 65.3 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_indirect-16 18692962 65.4 ns/op 48 B/op 1 allocs/op BenchmarkPostPropsDel_direct BenchmarkPostPropsDel_direct-2 516257440 2.34 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-4 514865216 2.43 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-8 511330477 2.37 ns/op 0 B/op 0 allocs/op BenchmarkPostPropsDel_direct-16 499504010 2.38 ns/op 0 B/op 0 allocs/op
2020-03-13 16:12:20 -04:00
assert.Equal(t, rpost.GetProps(), model.StringInterface{})
patchWithMention := &model.PostPatch{Message: model.NewPointer("This patch has a mention now @here")}
rpost, _, err = th.App.PatchPost(th.Context, rpost.Id, patchWithMention, nil)
require.Nil(t, err)
2021-07-12 14:05:36 -04:00
assert.Equal(t, rpost.GetProp(model.PostPropsMentionHighlightDisabled), true)
th.AddPermissionToRole(t, model.PermissionUseChannelMentions.Id, model.ChannelUserRoleId)
th.AddPermissionToRole(t, model.PermissionUseChannelMentions.Id, model.ChannelAdminRoleId)
})
})
t.Run("cannot patch post in restricted DM", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.RestrictDirectMessage = model.DirectMessageTeam
})
// Create a DM channel between two users who don't share a team
dmChannel := th.CreateDmChannel(t, th.BasicUser2)
// Ensure the two users do not share a team
teams, err := th.App.GetTeamsForUser(th.BasicUser.Id)
require.Nil(t, err)
for _, team := range teams {
teamErr := th.App.RemoveUserFromTeam(th.Context, team.Id, th.BasicUser.Id, th.SystemAdminUser.Id)
require.Nil(t, teamErr)
}
teams, err = th.App.GetTeamsForUser(th.BasicUser2.Id)
require.Nil(t, err)
for _, team := range teams {
teamErr := th.App.RemoveUserFromTeam(th.Context, team.Id, th.BasicUser2.Id, th.SystemAdminUser.Id)
require.Nil(t, teamErr)
}
// Create separate teams for each user
team1 := th.CreateTeam(t)
team2 := th.CreateTeam(t)
th.LinkUserToTeam(t, th.BasicUser, team1)
th.LinkUserToTeam(t, th.BasicUser2, team2)
// Create a post in the DM channel
post := &model.Post{
UserId: th.BasicUser.Id,
ChannelId: dmChannel.Id,
Message: "test post",
}
post, _, err = th.App.CreatePost(th.Context, post, dmChannel, model.CreatePostFlags{})
require.Nil(t, err)
// Try to patch the post
patch := &model.PostPatch{
Message: model.NewPointer("updated message"),
}
_, _, appErr := th.App.PatchPost(th.Context, post.Id, patch, model.DefaultUpdatePostOptions())
require.NotNil(t, appErr)
require.Equal(t, "api.post.patch_post.can_not_update_post_in_restricted_dm.error", appErr.Id)
require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
// Reset config
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.RestrictDirectMessage = model.DirectMessageAny
})
})
}
func TestCreatePostAsUser(t *testing.T) {
mainHelper.Parallel(t)
t.Run("marks channel as viewed for regular user", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "test",
UserId: th.BasicUser.Id,
}
channelMemberBefore, err := th.App.Srv().Store().Channel().GetMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
_, _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true)
require.Nil(t, appErr)
channelMemberAfter, err := th.App.Srv().Store().Channel().GetMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
require.NoError(t, err)
require.Greater(t, channelMemberAfter.LastViewedAt, channelMemberBefore.LastViewedAt)
})
t.Run("does not mark channel as viewed for webhook from user", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "test",
UserId: th.BasicUser.Id,
}
post.AddProp(model.PostPropsFromWebhook, "true")
channelMemberBefore, err := th.App.Srv().Store().Channel().GetMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
_, _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true)
require.Nil(t, appErr)
channelMemberAfter, err := th.App.Srv().Store().Channel().GetMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
require.NoError(t, err)
require.Equal(t, channelMemberAfter.LastViewedAt, channelMemberBefore.LastViewedAt)
})
t.Run("does not mark channel as viewed for bot user in channel", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
bot := th.CreateBot(t)
botUser, appErr := th.App.GetUser(bot.UserId)
require.Nil(t, appErr)
th.LinkUserToTeam(t, botUser, th.BasicTeam)
th.AddUserToChannel(t, botUser, th.BasicChannel)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "test",
UserId: bot.UserId,
}
channelMemberBefore, err := th.App.Srv().Store().Channel().GetMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
_, _, appErr = th.App.CreatePostAsUser(th.Context, post, "", true)
require.Nil(t, appErr)
channelMemberAfter, err := th.App.Srv().Store().Channel().GetMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
require.NoError(t, err)
require.Equal(t, channelMemberAfter.LastViewedAt, channelMemberBefore.LastViewedAt)
})
t.Run("does not log warning for bot user not in channel", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
bot := th.CreateBot(t)
botUser, appErr := th.App.GetUser(bot.UserId)
require.Nil(t, appErr)
th.LinkUserToTeam(t, botUser, th.BasicTeam)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "test",
UserId: bot.UserId,
}
_, _, appErr = th.App.CreatePostAsUser(th.Context, post, "", true)
require.Nil(t, appErr)
require.NoError(t, th.TestLogger.Flush())
testlib.AssertNoLog(t, th.LogBuffer, mlog.LvlWarn.Name, "Failed to get membership")
})
t.Run("marks channel as viewed for reply post when CRT is off", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOff
})
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "test",
UserId: th.BasicUser2.Id,
}
rootPost, _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true)
require.Nil(t, appErr)
channelMemberBefore, err := th.App.Srv().Store().Channel().GetMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
replyPost := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "test reply",
UserId: th.BasicUser.Id,
RootId: rootPost.Id,
}
_, _, appErr = th.App.CreatePostAsUser(th.Context, replyPost, "", true)
require.Nil(t, appErr)
channelMemberAfter, err := th.App.Srv().Store().Channel().GetMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
require.NoError(t, err)
require.NotEqual(t, channelMemberAfter.LastViewedAt, channelMemberBefore.LastViewedAt)
})
t.Run("does not mark channel as viewed for reply post when CRT is on", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true
*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
})
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "test",
UserId: th.BasicUser2.Id,
}
rootPost, _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true)
require.Nil(t, appErr)
channelMemberBefore, err := th.App.Srv().Store().Channel().GetMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
require.NoError(t, err)
time.Sleep(1 * time.Millisecond)
replyPost := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "test reply",
UserId: th.BasicUser.Id,
RootId: rootPost.Id,
}
_, _, appErr = th.App.CreatePostAsUser(th.Context, replyPost, "", true)
require.Nil(t, appErr)
channelMemberAfter, err := th.App.Srv().Store().Channel().GetMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
require.NoError(t, err)
require.Equal(t, channelMemberAfter.LastViewedAt, channelMemberBefore.LastViewedAt)
})
t.Run("cannot create post as user in restricted DM", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.RestrictDirectMessage = model.DirectMessageTeam
})
// Create a DM channel between two users who don't share a team
dmChannel := th.CreateDmChannel(t, th.BasicUser2)
// Ensure the two users do not share a team
teams, err := th.App.GetTeamsForUser(th.BasicUser.Id)
require.Nil(t, err)
for _, team := range teams {
teamErr := th.App.RemoveUserFromTeam(th.Context, team.Id, th.BasicUser.Id, th.SystemAdminUser.Id)
require.Nil(t, teamErr)
}
teams, err = th.App.GetTeamsForUser(th.BasicUser2.Id)
require.Nil(t, err)
for _, team := range teams {
teamErr := th.App.RemoveUserFromTeam(th.Context, team.Id, th.BasicUser2.Id, th.SystemAdminUser.Id)
require.Nil(t, teamErr)
}
// Create separate teams for each user
team1 := th.CreateTeam(t)
team2 := th.CreateTeam(t)
th.LinkUserToTeam(t, th.BasicUser, team1)
th.LinkUserToTeam(t, th.BasicUser2, team2)
post := &model.Post{
UserId: th.BasicUser.Id,
ChannelId: dmChannel.Id,
Message: "test post",
}
_, _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true)
require.NotNil(t, appErr)
require.Equal(t, "api.post.create_post.can_not_post_in_restricted_dm.error", appErr.Id)
require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
// Reset config
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.RestrictDirectMessage = model.DirectMessageAny
})
})
}
func TestPatchPostInArchivedChannel(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
archivedChannel := th.CreateChannel(t, th.BasicTeam)
post := th.CreatePost(t, archivedChannel)
appErr := th.App.DeleteChannel(th.Context, archivedChannel, "")
require.Nil(t, appErr)
_, _, err := th.App.PatchPost(th.Context, post.Id, &model.PostPatch{IsPinned: model.NewPointer(true)}, nil)
require.NotNil(t, err)
require.Equal(t, "api.post.patch_post.can_not_update_post_in_deleted.error", err.Id)
}
func TestUpdateEphemeralPost(t *testing.T) {
mainHelper.Parallel(t)
t.Run("Post contains preview if the user has permissions", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.AddUserToChannel(t, th.BasicUser, th.BasicChannel)
referencedPost := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "hello world",
UserId: th.BasicUser.Id,
}
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
})
th.Context.Session().UserId = th.BasicUser.Id
referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id)
testPost := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: permalink,
UserId: th.BasicUser.Id,
}
testPost, _ = th.App.UpdateEphemeralPost(th.Context, th.BasicUser.Id, testPost)
require.NotNil(t, testPost.Metadata)
require.Len(t, testPost.Metadata.Embeds, 1)
require.Equal(t, model.PostEmbedPermalink, testPost.Metadata.Embeds[0].Type)
})
t.Run("Post does not contain preview if the user has no permissions", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
privateChannel := th.CreatePrivateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, th.BasicUser, privateChannel)
th.AddUserToChannel(t, th.BasicUser2, th.BasicChannel)
referencedPost := &model.Post{
ChannelId: privateChannel.Id,
Message: "hello world",
UserId: th.BasicUser.Id,
}
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
})
th.Context.Session().UserId = th.BasicUser.Id
referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id)
testPost := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: permalink,
UserId: th.BasicUser2.Id,
}
testPost, _ = th.App.UpdateEphemeralPost(th.Context, th.BasicUser2.Id, testPost)
require.Nil(t, testPost.Metadata.Embeds)
})
}
func TestUpdatePost(t *testing.T) {
mainHelper.Parallel(t)
t.Run("call PreparePostForClient before returning", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
*cfg.ImageProxySettings.Enable = true
*cfg.ImageProxySettings.ImageProxyType = "atmos/camo"
*cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1"
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905) * Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-04-08 15:49:43 -04:00
*cfg.ImageProxySettings.RemoteImageProxyOptions = model.NewTestPassword()
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log())
imageURL := "http://mydomain.com/myimage"
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "![image](http://mydomain/anotherimage)",
UserId: th.BasicUser.Id,
}
rpost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
assert.NotEqual(t, "![image]("+proxiedImageURL+")", rpost.Message)
post.Id = rpost.Id
post.Message = "![image](" + imageURL + ")"
rpost, isMemberForPreviews, err := th.App.UpdatePost(th.Context, post, nil)
require.True(t, isMemberForPreviews)
require.Nil(t, err)
assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message)
})
2021-08-09 11:33:21 -04:00
t.Run("Sets PostPropsPreviewedPost when a post is updated to have a permalink as the first link", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.AddUserToChannel(t, th.BasicUser, th.BasicChannel)
2021-08-09 11:33:21 -04:00
referencedPost := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "hello world",
UserId: th.BasicUser.Id,
}
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
})
th.Context.Session().UserId = th.BasicUser.Id
referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{})
2021-08-09 11:33:21 -04:00
require.Nil(t, err)
permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id)
channelForTestPost := th.CreateChannel(t, th.BasicTeam)
2021-08-09 11:33:21 -04:00
testPost := &model.Post{
ChannelId: channelForTestPost.Id,
Message: "hello world",
UserId: th.BasicUser.Id,
}
testPost, _, err = th.App.CreatePost(th.Context, testPost, channelForTestPost, model.CreatePostFlags{})
2021-08-09 11:33:21 -04:00
require.Nil(t, err)
assert.Equal(t, model.StringInterface{}, testPost.GetProps())
2021-08-09 11:33:21 -04:00
testPost.Message = permalink
testPost, isMemberForPreviews, err := th.App.UpdatePost(th.Context, testPost, nil)
require.True(t, isMemberForPreviews)
2021-08-09 11:33:21 -04:00
require.Nil(t, err)
assert.Equal(t, model.StringInterface{model.PostPropsPreviewedPost: referencedPost.Id}, testPost.GetProps())
2021-08-09 11:33:21 -04:00
})
2023-12-01 04:50:57 -05:00
t.Run("sanitizes post metadata appropriately", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
2023-12-01 04:50:57 -05:00
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
})
th.AddUserToChannel(t, th.BasicUser, th.BasicChannel)
2023-12-01 04:50:57 -05:00
user1 := th.CreateUser(t)
user2 := th.CreateUser(t)
2023-12-01 04:50:57 -05:00
directChannel, err := th.App.createDirectChannel(th.Context, user1.Id, user2.Id)
require.Nil(t, err)
th.Context.Session().UserId = th.BasicUser.Id
testCases := []struct {
Description string
Channel *model.Channel
Author string
Length int
}{
{
Description: "removes metadata from post for members who cannot read channel",
Channel: directChannel,
Author: user1.Id,
Length: 0,
},
{
Description: "does not remove metadata from post for members who can read channel",
Channel: th.BasicChannel,
Author: th.BasicUser.Id,
Length: 1,
},
}
for _, testCase := range testCases {
t.Run(testCase.Description, func(t *testing.T) {
referencedPost := &model.Post{
ChannelId: testCase.Channel.Id,
Message: "hello world",
UserId: testCase.Author,
}
_, _, err = th.App.CreatePost(th.Context, referencedPost, testCase.Channel, model.CreatePostFlags{})
2023-12-01 04:50:57 -05:00
require.Nil(t, err)
previewPost := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
}
previewPost, _, err = th.App.CreatePost(th.Context, previewPost, th.BasicChannel, model.CreatePostFlags{})
2023-12-01 04:50:57 -05:00
require.Nil(t, err)
permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id)
previewPost.Message = permalink
previewPost, isMemberForPreviews, err := th.App.UpdatePost(th.Context, previewPost, nil)
require.True(t, isMemberForPreviews)
2023-12-01 04:50:57 -05:00
require.Nil(t, err)
require.Len(t, previewPost.Metadata.Embeds, testCase.Length)
})
}
})
t.Run("should strip client-supplied embeds", func(t *testing.T) {
// MM-67055: Verify that client-supplied metadata.embeds are stripped.
// This prevents WebSocket message spoofing via permalink embeds.
//
// Note: Priority and Acknowledgements are stored in separate database tables,
// not in post metadata. Shared Channels handles them separately via
// syncRemotePriorityMetadata and syncRemoteAcknowledgementsMetadata after
// calling UpdatePost. See sync_recv.go::upsertSyncPost
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.AddUserToChannel(t, th.BasicUser, th.BasicChannel)
th.Context.Session().UserId = th.BasicUser.Id
// Create a basic post
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "original message",
UserId: th.BasicUser.Id,
}
createdPost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
// Try to update with spoofed embeds (the attack vector)
updatePost := &model.Post{
Id: createdPost.Id,
ChannelId: th.BasicChannel.Id,
Message: "updated message",
UserId: th.BasicUser.Id,
Metadata: &model.PostMetadata{
Embeds: []*model.PostEmbed{
{
Type: model.PostEmbedPermalink,
Data: &model.PreviewPost{
PostID: "spoofed-post-id",
Post: &model.Post{
Id: "spoofed-post-id",
UserId: th.BasicUser2.Id,
Message: "Spoofed message from another user!",
},
},
},
},
},
}
updatedPost, _, err := th.App.UpdatePost(th.Context, updatePost, nil)
require.Nil(t, err)
require.NotNil(t, updatedPost.Metadata)
// Verify embeds were stripped
assert.Empty(t, updatedPost.Metadata.Embeds, "spoofed embeds should be stripped")
})
t.Run("cannot update post in restricted DM", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.RestrictDirectMessage = model.DirectMessageTeam
})
// Create a DM channel between two users who don't share a team
dmChannel := th.CreateDmChannel(t, th.BasicUser2)
// Ensure the two users do not share a team
teams, err := th.App.GetTeamsForUser(th.BasicUser.Id)
require.Nil(t, err)
for _, team := range teams {
teamErr := th.App.RemoveUserFromTeam(th.Context, team.Id, th.BasicUser.Id, th.SystemAdminUser.Id)
require.Nil(t, teamErr)
}
teams, err = th.App.GetTeamsForUser(th.BasicUser2.Id)
require.Nil(t, err)
for _, team := range teams {
teamErr := th.App.RemoveUserFromTeam(th.Context, team.Id, th.BasicUser2.Id, th.SystemAdminUser.Id)
require.Nil(t, teamErr)
}
// Create separate teams for each user
team1 := th.CreateTeam(t)
team2 := th.CreateTeam(t)
th.LinkUserToTeam(t, th.BasicUser, team1)
th.LinkUserToTeam(t, th.BasicUser2, team2)
// Create a post in the DM channel
post := &model.Post{
UserId: th.BasicUser.Id,
ChannelId: dmChannel.Id,
Message: "test post",
}
post, _, err = th.App.CreatePost(th.Context, post, dmChannel, model.CreatePostFlags{})
require.Nil(t, err)
// Try to update the post
post.Message = "updated message"
_, _, appErr := th.App.UpdatePost(th.Context, post, model.DefaultUpdatePostOptions())
require.NotNil(t, appErr)
require.Equal(t, "api.post.update_post.can_not_update_post_in_restricted_dm.error", appErr.Id)
require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
// Reset config
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.RestrictDirectMessage = model.DirectMessageAny
})
})
}
func TestSearchPostsForUser(t *testing.T) {
mainHelper.Parallel(t)
perPage := 5
searchTerm := "searchTerm"
setup := func(t *testing.T, enableElasticsearch bool) (*TestHelper, []*model.Post) {
th := Setup(t).InitBasic(t)
posts := make([]*model.Post, 7)
for i := 0; i < cap(posts); i++ {
post, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: searchTerm,
}, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
posts[i] = post
}
if enableElasticsearch {
th.App.Srv().SetLicense(model.NewTestLicense("elastic_search"))
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ElasticsearchSettings.EnableIndexing = true
*cfg.ElasticsearchSettings.EnableSearching = true
})
} else {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ElasticsearchSettings.EnableSearching = false
})
}
return th, posts
}
t.Run("should return everything as first page of posts from database", func(t *testing.T) {
mainHelper.Parallel(t)
th, posts := setup(t, false)
page := 0
results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
assert.Nil(t, err)
assert.Equal(t, []string{
posts[6].Id,
posts[5].Id,
posts[4].Id,
posts[3].Id,
posts[2].Id,
posts[1].Id,
posts[0].Id,
}, results.Order)
assert.True(t, allPostHaveMembership)
})
t.Run("should not return later pages of posts from database", func(t *testing.T) {
mainHelper.Parallel(t)
th, _ := setup(t, false)
page := 1
results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
assert.Nil(t, err)
assert.Equal(t, []string{}, results.Order)
assert.True(t, allPostHaveMembership)
})
t.Run("should return first page of posts from ElasticSearch", func(t *testing.T) {
mainHelper.Parallel(t)
th, posts := setup(t, true)
page := 0
resultsPage := []string{
posts[6].Id,
posts[5].Id,
posts[4].Id,
posts[3].Id,
posts[2].Id,
}
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
es := &mocks.SearchEngineInterface{}
es.On("SearchPosts", mock.Anything, mock.Anything, page, perPage).Return(resultsPage, nil, nil)
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
es.On("Start").Return(nil).Maybe()
es.On("IsActive").Return(true)
es.On("IsSearchEnabled").Return(true)
th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = es
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
defer func() {
th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
}()
results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
assert.Nil(t, err)
assert.Equal(t, resultsPage, results.Order)
assert.True(t, allPostHaveMembership)
es.AssertExpectations(t)
})
t.Run("should return later pages of posts from ElasticSearch", func(t *testing.T) {
mainHelper.Parallel(t)
th, posts := setup(t, true)
page := 1
resultsPage := []string{
posts[1].Id,
posts[0].Id,
}
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
es := &mocks.SearchEngineInterface{}
es.On("SearchPosts", mock.Anything, mock.Anything, page, perPage).Return(resultsPage, nil, nil)
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
es.On("Start").Return(nil).Maybe()
es.On("IsActive").Return(true)
es.On("IsSearchEnabled").Return(true)
th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = es
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
defer func() {
th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
}()
results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
assert.Nil(t, err)
assert.Equal(t, resultsPage, results.Order)
assert.True(t, allPostHaveMembership)
es.AssertExpectations(t)
})
t.Run("should fall back to database if ElasticSearch fails on first page", func(t *testing.T) {
mainHelper.Parallel(t)
th, posts := setup(t, true)
page := 0
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
es := &mocks.SearchEngineInterface{}
es.On("SearchPosts", mock.Anything, mock.Anything, page, perPage).Return(nil, nil, &model.AppError{})
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
es.On("GetName").Return("mock")
es.On("Start").Return(nil).Maybe()
es.On("IsActive").Return(true)
es.On("IsSearchEnabled").Return(true)
th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = es
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
defer func() {
th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
}()
results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
assert.Nil(t, err)
assert.Equal(t, []string{
posts[6].Id,
posts[5].Id,
posts[4].Id,
posts[3].Id,
posts[2].Id,
posts[1].Id,
posts[0].Id,
}, results.Order)
assert.True(t, allPostHaveMembership)
es.AssertExpectations(t)
})
t.Run("should return nothing if ElasticSearch fails on later pages", func(t *testing.T) {
mainHelper.Parallel(t)
th, _ := setup(t, true)
page := 1
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
es := &mocks.SearchEngineInterface{}
es.On("SearchPosts", mock.Anything, mock.Anything, page, perPage).Return(nil, nil, &model.AppError{})
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
es.On("GetName").Return("mock")
es.On("Start").Return(nil).Maybe()
es.On("IsActive").Return(true)
es.On("IsSearchEnabled").Return(true)
th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = es
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
defer func() {
th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil
Adding the new search engine abstraction (#13304) * WIP * Adding bleve to go modules * WIP * Adding missing files from searchengine implementation * WIP * WIP * WIP * WIP * WIP * WIP * User and channel indexing and searches implemented * Make bleve tests run with in-memory indexes * Implement post index and deletion tests * Initial commits for the search layer * Removing unnecesary indexing * WIP * WIP * More fixes for tests * Adding the search layer * Finishing the migration of searchers to the layer * Removing unnecesary code * Allowing multiple engines active at the same time * WIP * Add simple post search * Print information when using bleve * Adding some debugging to understand better how the searches are working * Making more dynamic config of search engines * Add post search basics * Adding the Purge API endpoint * Fixing bleve config updates * Adding missed file * Regenerating search engine mocks * Adding missed v5 to modules imports * fixing i18n * Fixing some test around search engine * Removing all bleve traces * Cleaning up the vendors directory and go.mod/go.sum files * Regenerating timer layer * Adding properly the license * Fixing govet shadow error * Fixing some tests * Fixing TestSearchPostsFromUser * Fixing another test * Fixing more tests * Fixing more tests * Removing SearchEngine redundant text from searchengine module code * Fixing some reindexing problems in members updates * Fixing tests * Addressing PR comments * Reverting go.mod and go.sum * Addressing PR comments * Fixing tests compilation * Fixing govet * Adding search engine stop method * Being more explicit on where we use includeDeleted * Adding GetSqlSupplier test helper method * Mocking elasticsearch start function * Fixing tests Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-03-13 10:33:18 -04:00
}()
results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
assert.Nil(t, err)
assert.Equal(t, []string{}, results.Order)
assert.True(t, allPostHaveMembership)
es.AssertExpectations(t)
})
t.Run("should return the same results if there is a tilde in the channel name", func(t *testing.T) {
mainHelper.Parallel(t)
th, _ := setup(t, false)
page := 0
searchQueryWithPrefix := fmt.Sprintf("in:~%s %s", th.BasicChannel.Name, searchTerm)
resultsWithPrefix, _, err := th.App.SearchPostsForUser(th.Context, searchQueryWithPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
assert.Nil(t, err)
assert.Greater(t, len(resultsWithPrefix.PostList.Posts), 0, "searching using a tilde in front of a channel should return results")
searchQueryWithoutPrefix := fmt.Sprintf("in:%s %s", th.BasicChannel.Name, searchTerm)
resultsWithoutPrefix, _, err := th.App.SearchPostsForUser(th.Context, searchQueryWithoutPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
assert.Nil(t, err)
assert.Equal(t, len(resultsWithPrefix.Posts), len(resultsWithoutPrefix.Posts), "searching using a tilde in front of a channel should return the same number of results")
for k, v := range resultsWithPrefix.Posts {
assert.Equal(t, v, resultsWithoutPrefix.Posts[k], "post at %s was different", k)
}
})
t.Run("should return the same results if there is an 'at' in the user", func(t *testing.T) {
mainHelper.Parallel(t)
th, _ := setup(t, false)
page := 0
searchQueryWithPrefix := fmt.Sprintf("from:@%s %s", th.BasicUser.Username, searchTerm)
resultsWithPrefix, _, err := th.App.SearchPostsForUser(th.Context, searchQueryWithPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
assert.Nil(t, err)
assert.Greater(t, len(resultsWithPrefix.PostList.Posts), 0, "searching using a 'at' symbol in front of a channel should return results")
searchQueryWithoutPrefix := fmt.Sprintf("from:@%s %s", th.BasicUser.Username, searchTerm)
resultsWithoutPrefix, _, err := th.App.SearchPostsForUser(th.Context, searchQueryWithoutPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
assert.Nil(t, err)
assert.Equal(t, len(resultsWithPrefix.Posts), len(resultsWithoutPrefix.Posts), "searching using an 'at' symbol in front of a channel should return the same number of results")
for k, v := range resultsWithPrefix.Posts {
assert.Equal(t, v, resultsWithoutPrefix.Posts[k], "post at %s was different", k)
}
})
}
func TestCountMentionsFromPost(t *testing.T) {
mainHelper.Parallel(t)
t.Run("should not count posts without mentions", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test2",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test3",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 0, count)
})
t.Run("should count keyword mentions", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
2021-07-12 14:05:36 -04:00
user2.NotifyProps[model.MentionKeysNotifyProp] = "apple"
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test2",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "apple",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// post1 and post3 should mention the user
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 2, count)
})
t.Run("should count channel-wide mentions when enabled", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
2021-07-12 14:05:36 -04:00
user2.NotifyProps[model.ChannelMentionsNotifyProp] = "true"
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "@channel",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "@all",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// post2 and post3 should mention the user
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 2, count)
})
t.Run("should not count channel-wide mentions when disabled for user", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
2021-07-12 14:05:36 -04:00
user2.NotifyProps[model.ChannelMentionsNotifyProp] = "false"
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "@channel",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "@all",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 0, count)
})
t.Run("should not count channel-wide mentions when disabled for channel", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
2021-07-12 14:05:36 -04:00
user2.NotifyProps[model.ChannelMentionsNotifyProp] = "true"
_, err := th.App.UpdateChannelMemberNotifyProps(th.Context, map[string]string{
2021-07-12 14:05:36 -04:00
model.IgnoreChannelMentionsNotifyProp: model.IgnoreChannelMentionsOn,
}, channel.Id, user2.Id)
require.Nil(t, err)
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "@channel",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "@all",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 0, count)
})
t.Run("should count comment mentions when using COMMENTS_NOTIFY_ROOT", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
2021-07-12 14:05:36 -04:00
user2.NotifyProps[model.CommentsNotifyProp] = model.CommentsNotifyRoot
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user2.Id,
ChannelId: channel.Id,
Message: "test",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
RootId: post1.Id,
Message: "test2",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
post3, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test3",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user2.Id,
ChannelId: channel.Id,
RootId: post3.Id,
Message: "test4",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
RootId: post3.Id,
Message: "test5",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// post2 should mention the user
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 1, count)
})
t.Run("should count comment mentions when using COMMENTS_NOTIFY_ANY", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
2021-07-12 14:05:36 -04:00
user2.NotifyProps[model.CommentsNotifyProp] = model.CommentsNotifyAny
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user2.Id,
ChannelId: channel.Id,
Message: "test",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
RootId: post1.Id,
Message: "test2",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
post3, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test3",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user2.Id,
ChannelId: channel.Id,
RootId: post3.Id,
Message: "test4",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
RootId: post3.Id,
Message: "test5",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// post2 and post5 should mention the user
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 2, count)
})
t.Run("should count mentions caused by being added to the channel", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test",
2021-07-12 14:05:36 -04:00
Type: model.PostTypeAddToChannel,
Props: map[string]any{
2021-07-12 14:05:36 -04:00
model.PostPropsAddedUserId: model.NewId(),
},
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test2",
2021-07-12 14:05:36 -04:00
Type: model.PostTypeAddToChannel,
Props: map[string]any{
2021-07-12 14:05:36 -04:00
model.PostPropsAddedUserId: user2.Id,
},
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test3",
2021-07-12 14:05:36 -04:00
Type: model.PostTypeAddToChannel,
Props: map[string]any{
2021-07-12 14:05:36 -04:00
model.PostPropsAddedUserId: user2.Id,
},
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// should be mentioned by post2 and post3
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 2, count)
})
t.Run("should return the number of posts made by the other user for a direct channel", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel, err := th.App.createDirectChannel(th.Context, user1.Id, user2.Id)
require.Nil(t, err)
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test",
}, channel, model.CreatePostFlags{SetOnline: true})
2019-11-19 10:05:04 -05:00
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test2",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 2, count)
count, _, _, err = th.App.countMentionsFromPost(th.Context, user1, post1)
assert.Nil(t, err)
assert.Equal(t, 0, count)
})
t.Run("should return the number of posts made by the other user for a group message", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
user3 := th.SystemAdminUser
channel, err := th.App.createGroupChannel(th.Context, []string{user1.Id, user2.Id, user3.Id}, user1.Id)
require.Nil(t, err)
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test2",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user3.Id,
ChannelId: channel.Id,
Message: "test3",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 3, count)
count, _, _, err = th.App.countMentionsFromPost(th.Context, user1, post1)
assert.Nil(t, err)
assert.Equal(t, 1, count)
})
t.Run("should not count mentions from the before the given post", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
_, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
post2, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test2",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// post1 and post3 should mention the user, but we only count post3
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post2)
assert.Nil(t, err)
assert.Equal(t, 1, count)
})
t.Run("should not count mentions from the user's own posts", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user2.Id,
ChannelId: channel.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// post2 should mention the user
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 1, count)
})
t.Run("should include comments made before the given post when counting comment mentions", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
2021-07-12 14:05:36 -04:00
user2.NotifyProps[model.CommentsNotifyProp] = model.CommentsNotifyAny
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test1",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user2.Id,
ChannelId: channel.Id,
RootId: post1.Id,
Message: "test2",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
post3, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test3",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
RootId: post1.Id,
Message: "test4",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// post4 should mention the user
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post3)
assert.Nil(t, err)
assert.Equal(t, 1, count)
})
t.Run("should not include comments made before the given post when rootPost is inaccessible", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
// Create an Entry license with post history limits
license := model.NewTestLicenseSKU(model.LicenseShortSkuMattermostEntry)
license.Limits = &model.LicenseLimits{
PostHistory: 10000, // Set some post history limit to enable filtering
}
th.App.Srv().SetLicense(license)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
user2.NotifyProps[model.CommentsNotifyProp] = model.CommentsNotifyAny
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test1",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user2.Id,
ChannelId: channel.Id,
RootId: post1.Id,
Message: "test2",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
time.Sleep(time.Millisecond * 2)
post3, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test3",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
RootId: post1.Id,
Message: "test4",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// Make posts created before post3 inaccessible
e := th.App.Srv().Store().System().SaveOrUpdate(&model.System{
Name: model.SystemLastAccessiblePostTime,
Value: strconv.FormatInt(post3.CreateAt, 10),
})
require.NoError(t, e)
// post4 should mention the user, but since post2 is inaccessible due to the cloud plan's limit,
// post4 does not notify the user.
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post3)
assert.Nil(t, err)
assert.Zero(t, count)
})
t.Run("should count mentions from the user's webhook posts", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test1",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user2.Id,
ChannelId: channel.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user2.Id,
ChannelId: channel.Id,
Message: fmt.Sprintf("@%s", user2.Username),
Props: map[string]any{
model.PostPropsFromWebhook: "true",
},
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// post3 should mention the user
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 1, count)
})
t.Run("should count multiple pages of mentions", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
numPosts := 215
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
for i := 0; i < numPosts-1; i++ {
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
}
// Every post should mention the user
count, _, _, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, numPosts, count)
})
t.Run("should count urgent mentions", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.PostPriority = true
})
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
user2.NotifyProps[model.MentionKeysNotifyProp] = "apple"
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: fmt.Sprintf("@%s", user2.Username),
Metadata: &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewPointer(model.PostPriorityUrgent),
},
},
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "apple",
Metadata: &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewPointer(model.PostPriorityUrgent),
},
},
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// all posts mention the user but only post1, post3 are urgent
_, _, count, err := th.App.countMentionsFromPost(th.Context, user2, post1)
assert.Nil(t, err)
assert.Equal(t, 2, count)
})
}
func TestFillInPostProps(t *testing.T) {
mainHelper.Parallel(t)
t.Run("should not add disable group highlight to post props for user with group mention permissions", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.Srv().SetLicense(model.NewTestLicense("ldap"))
user1 := th.BasicUser
channel := th.CreateChannel(t, th.BasicTeam)
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test123123 @group1 @group2 blah blah blah",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
err = th.App.FillInPostProps(th.Context, post1, channel)
assert.Nil(t, err)
assert.Equal(t, post1.Props, model.StringInterface{})
})
t.Run("should not add disable group highlight to post props for app without license", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
id := model.NewId()
guest := &model.User{
Email: "success+" + id + "@simulator.amazonses.com",
Username: "un_" + id,
Nickname: "nn_" + id,
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905) * Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-04-08 15:49:43 -04:00
Password: model.NewTestPassword(),
EmailVerified: true,
}
guest, err := th.App.CreateGuest(th.Context, guest)
require.Nil(t, err)
th.LinkUserToTeam(t, guest, th.BasicTeam)
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, guest, channel)
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: guest.Id,
ChannelId: channel.Id,
Message: "test123123 @group1 @group2 blah blah blah",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
err = th.App.FillInPostProps(th.Context, post1, channel)
assert.Nil(t, err)
assert.Equal(t, post1.Props, model.StringInterface{})
})
t.Run("should add disable group highlight to post props for guest user", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.Srv().SetLicense(model.NewTestLicense("ldap"))
id := model.NewId()
guest := &model.User{
Email: "success+" + id + "@simulator.amazonses.com",
Username: "un_" + id,
Nickname: "nn_" + id,
Generate instead of hard-coding test passwords, enforce new minimum for FIPS, shard CI, fix FIPS builds (#35905) * Replace hardcoded test passwords with model.NewTestPassword() Add model.NewTestPassword() utility that generates 14+ character passwords meeting complexity requirements for FIPS compliance. Replace all short hardcoded test passwords across the test suite with calls to this function. * Enforce FIPS compliance for passwords and HMAC keys FIPS OpenSSL requires HMAC keys to be at least 14 bytes. PBKDF2 uses the password as the HMAC key internally, so short passwords cause PKCS5_PBKDF2_HMAC to fail. - Add FIPSEnabled and PasswordFIPSMinimumLength build-tag constants - Raise the password minimum length floor to 14 when compiled with requirefips, applied in SetDefaults only when unset and validated independently in IsValid - Return ErrMismatchedHashAndPassword for too-short passwords in PBKDF2 CompareHashAndPassword rather than a cryptic OpenSSL error - Validate atmos/camo HMAC key length under FIPS and lengthen test keys accordingly - Adjust password validation tests to use PasswordFIPSMinimumLength so they work under both FIPS and non-FIPS builds * CI: shard FIPS test suite and extract merge template Run FIPS tests on PRs that touch go.mod or have 'fips' in the branch name. Shard FIPS tests across 4 runners matching the normal Postgres suite. Extract the test result merge logic into a reusable workflow template to deduplicate the normal and FIPS merge jobs. * more * Fix email test helper to respect FIPS minimum password length * Fix test helpers to respect FIPS minimum password length * Remove unnecessary "disable strict password requirements" blocks from test helpers * Fix CodeRabbit review comments on PR #35905 - Add server-test-merge-template.yml to server-ci.yml pull_request.paths so changes to the reusable merge workflow trigger Server CI validation - Skip merge-postgres-fips-test-results job when test-postgres-normal-fips was skipped, preventing failures due to missing artifacts - Set guest.Password on returned guest in CreateGuestAndClient helper to keep contract consistent with CreateUserWithClient - Use shared LowercaseLetters/UppercaseLetters/NUMBERS/PasswordFIPSMinimumLength constants in NewTestPassword() to avoid drift if FIPS floor changes https://claude.ai/code/session_01HmE9QkZM3cAoXn2J7XrK2f * Rename FIPS test artifact to match server-ci-report pattern The server-ci-report job searches for artifacts matching "*-test-logs", so rename from postgres-server-test-logs-fips to postgres-server-fips-test-logs to be included in the report. --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-04-08 15:49:43 -04:00
Password: model.NewTestPassword(),
EmailVerified: true,
}
guest, err := th.App.CreateGuest(th.Context, guest)
require.Nil(t, err)
th.LinkUserToTeam(t, guest, th.BasicTeam)
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, guest, channel)
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: guest.Id,
ChannelId: channel.Id,
Message: "test123123 @group1 @group2 blah blah blah",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
err = th.App.FillInPostProps(th.Context, post1, channel)
assert.Nil(t, err)
assert.Equal(t, post1.Props, model.StringInterface{"disable_group_highlight": true})
})
t.Run("should set AI-generated username when user ID is post creator", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
channel := th.CreateChannel(t, th.BasicTeam)
post1 := &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test post",
}
post1.AddProp(model.PostPropsAIGeneratedByUserID, user1.Id)
err := th.App.FillInPostProps(th.Context, post1, channel)
assert.Nil(t, err)
assert.Equal(t, user1.Id, post1.GetProp(model.PostPropsAIGeneratedByUserID))
assert.Equal(t, user1.Username, post1.GetProp(model.PostPropsAIGeneratedByUsername))
})
t.Run("should set AI-generated username when user ID is a bot", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
channel := th.CreateChannel(t, th.BasicTeam)
// Create a bot
bot, appErr := th.App.CreateBot(th.Context, &model.Bot{
Username: "testbot",
Description: "test bot",
OwnerId: user1.Id,
})
require.Nil(t, appErr)
post1 := &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test post generated by bot",
}
post1.AddProp(model.PostPropsAIGeneratedByUserID, bot.UserId)
err := th.App.FillInPostProps(th.Context, post1, channel)
assert.Nil(t, err)
assert.Equal(t, bot.UserId, post1.GetProp(model.PostPropsAIGeneratedByUserID))
assert.Equal(t, bot.Username, post1.GetProp(model.PostPropsAIGeneratedByUsername))
})
t.Run("should return error when user ID is a different non-bot user", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
post1 := &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test post",
}
// Try to set AI-generated user ID to a different user (not post creator, not bot)
post1.AddProp(model.PostPropsAIGeneratedByUserID, user2.Id)
err := th.App.FillInPostProps(th.Context, post1, channel)
// Should return an error since user2 is neither the post creator nor a bot
assert.NotNil(t, err)
assert.Equal(t, "FillInPostProps", err.Where)
assert.Equal(t, http.StatusBadRequest, err.StatusCode)
})
t.Run("should remove AI-generated prop when user ID does not exist", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
channel := th.CreateChannel(t, th.BasicTeam)
post1 := &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "test post",
}
// Set AI-generated user ID to a non-existent user
post1.AddProp(model.PostPropsAIGeneratedByUserID, model.NewId())
err := th.App.FillInPostProps(th.Context, post1, channel)
assert.Nil(t, err)
// The property should be removed since the user doesn't exist
assert.Nil(t, post1.GetProp(model.PostPropsAIGeneratedByUserID))
assert.Nil(t, post1.GetProp(model.PostPropsAIGeneratedByUsername))
})
t.Run("should not populate channel mentions for channels in teams where the user is not a member", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
team2 := th.CreateTeam(t)
th.LinkUserToTeam(t, user2, team2)
// Create a channel in team2 which user1 is not a member of
channel2, err := th.App.CreateChannel(th.Context, &model.Channel{
DisplayName: "Channel in Team 2",
Name: "channel-in-team-2",
Type: model.ChannelTypeOpen,
TeamId: team2.Id,
CreatorId: user2.Id,
}, false)
require.Nil(t, err)
dmChannelBetweenUser1AndUser2 := th.CreateDmChannel(t, user2)
th.Context.Session().UserId = user1.Id
post, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: dmChannelBetweenUser1AndUser2.Id,
Message: "Testing out i should not be able to mention channel2 from team2? ~" + channel2.Name,
}, dmChannelBetweenUser1AndUser2, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
err = th.App.FillInPostProps(th.Context, post, dmChannelBetweenUser1AndUser2)
require.Nil(t, err)
mentions := post.GetProp(model.PostPropsChannelMentions)
require.Nil(t, mentions)
})
t.Run("should populate channel mentions for channels in teams where the user is a member", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
dmChannel := th.CreateDmChannel(t, user2)
th.Context.Session().UserId = user1.Id
post, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: dmChannel.Id,
Message: "Check out ~" + channel.Name,
}, dmChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
mentions := post.GetProp(model.PostPropsChannelMentions)
require.NotNil(t, mentions)
mentionsMap, ok := mentions.(map[string]any)
require.True(t, ok)
require.Contains(t, mentionsMap, channel.Name)
})
}
func TestThreadMembership(t *testing.T) {
mainHelper.Parallel(t)
t.Run("should update memberships for conversation participants", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true
*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
})
user1 := th.BasicUser
user2 := th.BasicUser2
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
postRoot, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "root post",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
RootId: postRoot.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// first user should now be part of the thread since they replied to a post
memberships, err2 := th.App.GetThreadMembershipsForUser(user1.Id, th.BasicTeam.Id)
require.NoError(t, err2)
require.Len(t, memberships, 1)
// second user should also be part of a thread since they were mentioned
memberships, err2 = th.App.GetThreadMembershipsForUser(user2.Id, th.BasicTeam.Id)
require.NoError(t, err2)
require.Len(t, memberships, 1)
post2, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user2.Id,
ChannelId: channel.Id,
Message: "second post",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
UserId: user2.Id,
ChannelId: channel.Id,
RootId: post2.Id,
Message: fmt.Sprintf("@%s", user1.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// first user should now be part of two threads
memberships, err2 = th.App.GetThreadMembershipsForUser(user1.Id, th.BasicTeam.Id)
require.NoError(t, err2)
require.Len(t, memberships, 2)
})
}
func TestFollowThreadSkipsParticipants(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true
2021-07-12 14:05:36 -04:00
*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
})
channel := th.BasicChannel
user := th.BasicUser
user2 := th.BasicUser2
sysadmin := th.SystemAdminUser
appErr := th.App.JoinChannel(th.Context, channel, user.Id)
require.Nil(t, appErr)
appErr = th.App.JoinChannel(th.Context, channel, user2.Id)
require.Nil(t, appErr)
_, appErr = th.App.JoinUserToTeam(th.Context, th.BasicTeam, sysadmin, sysadmin.Id)
require.Nil(t, appErr)
appErr = th.App.JoinChannel(th.Context, channel, sysadmin.Id)
require.Nil(t, appErr)
p1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + sysadmin.Username}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
threadMembership, appErr := th.App.GetThreadMembershipForUser(user.Id, p1.Id)
require.Nil(t, appErr)
thread, appErr := th.App.GetThreadForUser(th.Context, threadMembership, false)
require.Nil(t, appErr)
require.Len(t, thread.Participants, 1) // length should be 1, the original poster, since sysadmin was just mentioned but didn't post
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: sysadmin.Id, ChannelId: channel.Id, Message: "sysadmin reply"}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
threadMembership, appErr = th.App.GetThreadMembershipForUser(user.Id, p1.Id)
require.Nil(t, appErr)
thread, appErr = th.App.GetThreadForUser(th.Context, threadMembership, false)
require.Nil(t, appErr)
require.Len(t, thread.Participants, 2) // length should be 2, the original poster and sysadmin, since sysadmin participated now
// another user follows the thread
appErr = th.App.UpdateThreadFollowForUser(user2.Id, th.BasicTeam.Id, p1.Id, true)
require.Nil(t, appErr)
threadMembership, appErr = th.App.GetThreadMembershipForUser(user2.Id, p1.Id)
require.Nil(t, appErr)
thread, appErr = th.App.GetThreadForUser(th.Context, threadMembership, false)
require.Nil(t, appErr)
require.Len(t, thread.Participants, 2) // length should be 2, since follow shouldn't update participant list, only user1 and sysadmin are participants
for _, p := range thread.Participants {
require.True(t, p.Id == sysadmin.Id || p.Id == user.Id)
}
oldID := threadMembership.PostId
threadMembership.PostId = "notfound"
_, appErr = th.App.GetThreadForUser(th.Context, threadMembership, false)
require.NotNil(t, appErr)
assert.Equal(t, http.StatusNotFound, appErr.StatusCode)
threadMembership.Following = false
threadMembership.PostId = oldID
_, appErr = th.App.GetThreadForUser(th.Context, threadMembership, false)
require.NotNil(t, appErr)
assert.Equal(t, http.StatusNotFound, appErr.StatusCode)
}
func TestAutofollowBasedOnRootPost(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true
2021-07-12 14:05:36 -04:00
*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
})
channel := th.BasicChannel
user := th.BasicUser
user2 := th.BasicUser2
appErr := th.App.JoinChannel(th.Context, channel, user.Id)
require.Nil(t, appErr)
appErr = th.App.JoinChannel(th.Context, channel, user2.Id)
require.Nil(t, appErr)
p1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
m, err := th.App.GetThreadMembershipsForUser(user2.Id, th.BasicTeam.Id)
require.NoError(t, err)
require.Len(t, m, 0)
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
m, err = th.App.GetThreadMembershipsForUser(user2.Id, th.BasicTeam.Id)
require.NoError(t, err)
require.Len(t, m, 1)
}
func TestViewChannelShouldNotUpdateThreads(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true
2021-07-12 14:05:36 -04:00
*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
})
channel := th.BasicChannel
user := th.BasicUser
user2 := th.BasicUser2
appErr := th.App.JoinChannel(th.Context, channel, user.Id)
require.Nil(t, appErr)
appErr = th.App.JoinChannel(th.Context, channel, user2.Id)
require.Nil(t, appErr)
p1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
m, err := th.App.GetThreadMembershipsForUser(user2.Id, th.BasicTeam.Id)
require.NoError(t, err)
_, appErr = th.App.ViewChannel(th.Context, &model.ChannelView{
ChannelId: channel.Id,
PrevChannelId: "",
}, user2.Id, "", true)
require.Nil(t, appErr)
m1, err := th.App.GetThreadMembershipsForUser(user2.Id, th.BasicTeam.Id)
require.NoError(t, err)
require.Equal(t, m[0].LastViewed, m1[0].LastViewed) // opening the channel shouldn't update threads
}
func TestCollapsedThreadFetch(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true
2021-07-12 14:05:36 -04:00
*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
})
user1 := th.BasicUser
user2 := th.BasicUser2
t.Run("should only return root posts, enriched", func(t *testing.T) {
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
defer func() {
appErr := th.App.DeleteChannel(th.Context, channel, user1.Id)
require.Nil(t, appErr)
}()
postRoot, _, appErr := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "root post",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, appErr)
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
RootId: postRoot.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, appErr)
thread, err := th.App.Srv().Store().Thread().Get(postRoot.Id)
require.NoError(t, err)
require.Len(t, thread.Participants, 1)
_, appErr = th.App.MarkChannelAsUnreadFromPost(th.Context, postRoot.Id, user1.Id, true)
require.Nil(t, appErr)
l, appErr := th.App.GetPostsForChannelAroundLastUnread(th.Context, channel.Id, user1.Id, 10, 10, true, true, false)
require.Nil(t, appErr)
require.Len(t, l.Order, 1)
require.EqualValues(t, 1, l.Posts[postRoot.Id].ReplyCount)
require.EqualValues(t, []string{user1.Id}, []string{l.Posts[postRoot.Id].Participants[0].Id})
require.Empty(t, l.Posts[postRoot.Id].Participants[0].Email)
require.NotZero(t, l.Posts[postRoot.Id].LastReplyAt)
require.True(t, *l.Posts[postRoot.Id].IsFollowing)
// try extended fetch
l, appErr = th.App.GetPostsForChannelAroundLastUnread(th.Context, channel.Id, user1.Id, 10, 10, true, true, true)
require.Nil(t, appErr)
require.Len(t, l.Order, 1)
require.NotEmpty(t, l.Posts[postRoot.Id].Participants[0].Email)
})
t.Run("Should not panic on unexpected db error", func(t *testing.T) {
channel := th.CreateChannel(t, th.BasicTeam)
th.AddUserToChannel(t, user2, channel)
defer func() {
appErr := th.App.DeleteChannel(th.Context, channel, user1.Id)
require.Nil(t, appErr)
}()
postRoot, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "root post",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// we introduce a race to trigger an unexpected error from the db side.
var wg sync.WaitGroup
wg.Go(func() {
err := th.Server.Store().Post().PermanentDeleteByUser(th.Context, user1.Id)
require.NoError(t, err)
})
require.NotPanics(t, func() {
// We're only testing that this doesn't panic, not checking the error
// #nosec G104 - purposely not checking error as we're in a NotPanics block
_, _, _ = th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
RootId: postRoot.Id,
Message: fmt.Sprintf("@%s", user2.Username),
}, channel, model.CreatePostFlags{SetOnline: true})
})
wg.Wait()
})
t.Run("should sanitize participant data", func(t *testing.T) {
id := model.NewId()
user3, appErr := th.App.CreateUser(th.Context, &model.User{
Email: "success+" + id + "@simulator.amazonses.com",
Username: "un_" + id,
Nickname: "nn_" + id,
AuthData: model.NewPointer("bobbytables"),
AuthService: "saml",
EmailVerified: true,
})
require.Nil(t, appErr)
defer func() {
appErr = th.App.PermanentDeleteUser(th.Context, user3)
require.Nil(t, appErr)
}()
channel := th.CreateChannel(t, th.BasicTeam)
defer func() {
appErr = th.App.DeleteChannel(th.Context, channel, user1.Id)
require.Nil(t, appErr)
}()
th.LinkUserToTeam(t, user3, th.BasicTeam)
th.AddUserToChannel(t, user3, channel)
postRoot, _, appErr := th.App.CreatePost(th.Context, &model.Post{
UserId: user1.Id,
ChannelId: channel.Id,
Message: "root post",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, appErr)
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{
UserId: user3.Id,
ChannelId: channel.Id,
RootId: postRoot.Id,
Message: "reply",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, appErr)
thread, err := th.App.Srv().Store().Thread().Get(postRoot.Id)
require.NoError(t, err)
require.Len(t, thread.Participants, 1)
// extended fetch posts page
l, appErr := th.App.GetPostsPage(th.Context, model.GetPostsOptions{
UserId: user1.Id,
ChannelId: channel.Id,
PerPage: int(10),
SkipFetchThreads: false,
CollapsedThreads: true,
CollapsedThreadsExtended: true,
})
require.Nil(t, appErr)
require.Len(t, l.Order, 1)
require.NotEmpty(t, l.Posts[postRoot.Id].Participants[0].Email)
require.Empty(t, l.Posts[postRoot.Id].Participants[0].AuthData)
_, appErr = th.App.MarkChannelAsUnreadFromPost(th.Context, postRoot.Id, user1.Id, true)
require.Nil(t, appErr)
// extended fetch posts around
l, appErr = th.App.GetPostsForChannelAroundLastUnread(th.Context, channel.Id, user1.Id, 10, 10, true, true, true)
require.Nil(t, appErr)
require.Len(t, l.Order, 1)
require.NotEmpty(t, l.Posts[postRoot.Id].Participants[0].Email)
require.Empty(t, l.Posts[postRoot.Id].Participants[0].AuthData)
// extended fetch post thread
opts := model.GetPostsOptions{
SkipFetchThreads: false,
CollapsedThreads: true,
CollapsedThreadsExtended: true,
}
l, appErr = th.App.GetPostThread(th.Context, postRoot.Id, opts, user1.Id)
require.Nil(t, appErr)
require.Len(t, l.Order, 2)
require.NotEmpty(t, l.Posts[postRoot.Id].Participants[0].Email)
require.Empty(t, l.Posts[postRoot.Id].Participants[0].AuthData)
})
}
[MM-31094] Replication Lag (#16888) * MM-31094: Adds tooling to develop and test using a MySQL instance with replication lag. Adds some lazy lookups to fallback to master if results are not found. * MM-31094: Removes mysql-read-replica from default docker services. * MM-31094: Switches (store..SessionStore).Get and (store.TeamStore).GetMember to using context.Context. * MM-31094: Updates (store.UsersStore).Get to use context. * MM-31094: Updates (store.PostStore).Get to use context. * MM-31094: Removes feature flag and config setting. * MM-31094: Rolls back some master reads. * MM-31094: Rolls a non-cache read. * MM-31094: Removes feature flag from the store. * MM-31094: Removes unused constant and struct field. * MM-31094: Removes some old feature flag references. * MM-31094: Fixes some tests. * MM-31094: App layers fix. * MM-31094: Fixes mocks. * MM-31094: Don't reparse flag. * MM-31094: No reparse. * MM-31094: Removed unused FeatureFlags field. * MM-31094: Removes unnecessary feature flags variable declarations. * MM-31094: Fixes copy-paste error. * MM-31094: Fixes logical error. * MM-30194: Removes test method from store. * Revert "MM-30194: Removes test method from store." This reverts commit d5a6e8529bd5f4d993824c828e239d009b05e567. * MM-31094: Conforming to make's strange syntax. * MM-31094: Configures helper for read replica with option. * MM-31094: Adds some missing ctx's. * MM-31094: WIP * MM-31094: Updates test names. * MM-31094: WIP * MM-31094: Removes unnecessary master reads. * MM-31094: ID case changes out of scope. * MM-31094: Removes unused context. * MM-31094: Switches to a helper. Removes some var naming changes. Fixes a merge error. * MM-31094: Removes SQLITE db driver ref. * MM-31094: Layer generate fix. * MM-31094: Removes unnecessary changes. * MM-31094: Moves test method. * MM-31094: Re-add previous fix. * MM-31094: Removes make command for dev. * MM-31094: Fix for login. Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
2021-03-12 12:37:30 -05:00
func TestSharedChannelSyncForPostActions(t *testing.T) {
mainHelper.Parallel(t)
t.Run("creating a post in a shared channel performs a content sync when sync service is running on that node", func(t *testing.T) {
th := setupSharedChannels(t).InitBasic(t)
sharedChannelService := NewMockSharedChannelService(th.Server.GetSharedChannelSyncService())
th.Server.SetSharedChannelSyncService(sharedChannelService)
testCluster := &testlib.FakeClusterInterface{}
th.Server.Platform().SetCluster(testCluster)
user := th.BasicUser
channel := th.CreateChannel(t, th.BasicTeam, WithShared(true))
_, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user.Id,
ChannelId: channel.Id,
Message: "Hello folks",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err, "Creating a post should not error")
require.Len(t, sharedChannelService.channelNotifications, 1)
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[0])
})
t.Run("updating a post in a shared channel performs a content sync when sync service is running on that node", func(t *testing.T) {
th := setupSharedChannels(t).InitBasic(t)
sharedChannelService := NewMockSharedChannelService(th.Server.GetSharedChannelSyncService())
th.Server.SetSharedChannelSyncService(sharedChannelService)
testCluster := &testlib.FakeClusterInterface{}
th.Server.Platform().SetCluster(testCluster)
user := th.BasicUser
channel := th.CreateChannel(t, th.BasicTeam, WithShared(true))
post, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user.Id,
ChannelId: channel.Id,
Message: "Hello folks",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err, "Creating a post should not error")
_, _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err, "Updating a post should not error")
require.Len(t, sharedChannelService.channelNotifications, 2)
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[0])
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[1])
})
t.Run("deleting a post in a shared channel performs a content sync when sync service is running on that node", func(t *testing.T) {
th := setupSharedChannels(t).InitBasic(t)
sharedChannelService := NewMockSharedChannelService(th.Server.GetSharedChannelSyncService())
th.Server.SetSharedChannelSyncService(sharedChannelService)
testCluster := &testlib.FakeClusterInterface{}
th.Server.Platform().SetCluster(testCluster)
user := th.BasicUser
channel := th.CreateChannel(t, th.BasicTeam, WithShared(true))
post, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: user.Id,
ChannelId: channel.Id,
Message: "Hello folks",
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err, "Creating a post should not error")
_, err = th.App.DeletePost(th.Context, post.Id, user.Id)
require.Nil(t, err, "Deleting a post should not error")
// one creation and two deletes
require.Len(t, sharedChannelService.channelNotifications, 3)
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[0])
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[1])
assert.Equal(t, channel.Id, sharedChannelService.channelNotifications[2])
})
}
func TestAutofollowOnPostingAfterUnfollow(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true
2021-07-12 14:05:36 -04:00
*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
})
channel := th.BasicChannel
user := th.BasicUser
user2 := th.BasicUser2
appErr := th.App.JoinChannel(th.Context, channel, user.Id)
require.Nil(t, appErr)
appErr = th.App.JoinChannel(th.Context, channel, user2.Id)
require.Nil(t, appErr)
p1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user2.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "reply"}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
// unfollow thread
m, err := th.App.Srv().Store().Thread().MaintainMembership(user.Id, p1.Id, store.ThreadMembershipOpts{
Following: false,
UpdateFollowing: true,
})
require.NoError(t, err)
require.False(t, m.Following)
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "another reply"}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
// User should be following thread after posting in it, even after previously
// unfollowing it, if ThreadAutoFollow is true
m, appErr = th.App.GetThreadMembershipForUser(user.Id, p1.Id)
require.Nil(t, appErr)
require.True(t, m.Following)
}
func TestGetPostIfAuthorized(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
t.Run("Private channel", func(t *testing.T) {
privateChannel := th.CreatePrivateChannel(t, th.BasicTeam)
post, _, err := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: privateChannel.Id, Message: "Hello"}, privateChannel, model.CreatePostFlags{})
require.Nil(t, err)
require.NotNil(t, post)
session1, err := th.App.CreateSession(th.Context, &model.Session{UserId: th.BasicUser.Id, Props: model.StringMap{}})
require.Nil(t, err)
require.NotNil(t, session1)
session2, err := th.App.CreateSession(th.Context, &model.Session{UserId: th.BasicUser2.Id, Props: model.StringMap{}})
require.Nil(t, err)
require.NotNil(t, session2)
// User is not authorized to get post
_, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session2, false)
require.NotNil(t, err)
// User is authorized to get post
_, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session1, false)
require.Nil(t, err)
})
t.Run("Public channel", func(t *testing.T) {
publicChannel := th.CreateChannel(t, th.BasicTeam)
post, _, err := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: publicChannel.Id, Message: "Hello"}, publicChannel, model.CreatePostFlags{})
require.Nil(t, err)
require.NotNil(t, post)
session1, err := th.App.CreateSession(th.Context, &model.Session{UserId: th.BasicUser.Id, Props: model.StringMap{}})
require.Nil(t, err)
require.NotNil(t, session1)
session2, err := th.App.CreateSession(th.Context, &model.Session{UserId: th.BasicUser2.Id, Props: model.StringMap{}})
require.Nil(t, err)
require.NotNil(t, session2)
// User is authorized to get post
_, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session2, false)
require.Nil(t, err)
// User is authorized to get post
_, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session1, false)
require.Nil(t, err)
th.App.UpdateConfig(func(c *model.Config) {
b := true
c.ComplianceSettings.Enable = &b
})
// User is not authorized to get post
_, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session2, false)
require.NotNil(t, err)
// User is authorized to get post
_, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session1, false)
require.Nil(t, err)
})
}
// MM-68140: thread context for rewrite must not be built from posts in channels the session cannot read.
func TestBuildThreadContextForRewriteRequiresChannelReadAccess(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
t.Run("direct message between other users", func(t *testing.T) {
secretToken := "MM68140_SECRET_DM_THREAD_" + model.NewId()
dm := th.CreateDmChannel(t, th.BasicUser2)
root, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: th.BasicUser.Id,
ChannelId: dm.Id,
Message: secretToken,
}, dm, model.CreatePostFlags{})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
RootId: root.Id,
UserId: th.BasicUser2.Id,
ChannelId: dm.Id,
Message: "reply only visible to DM participants",
}, dm, model.CreatePostFlags{})
require.Nil(t, err)
attacker := th.CreateUser(t)
session, err := th.App.CreateSession(th.Context, &model.Session{UserId: attacker.Id, Props: model.StringMap{}})
require.Nil(t, err)
ctx := th.Context.WithSession(session)
contextStr, appErr := th.App.buildThreadContextForRewrite(ctx, root.Id)
require.NotNil(t, appErr, "expected permission error when root_id is in a channel the user cannot read, got nil")
assert.Equal(t, http.StatusForbidden, appErr.StatusCode)
assert.NotContains(t, contextStr, secretToken)
})
t.Run("private channel the user is not a member of", func(t *testing.T) {
secretToken := "MM68140_SECRET_PRIVATE_THREAD_" + model.NewId()
privateCh := th.CreatePrivateChannel(t, th.BasicTeam)
root, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: th.BasicUser.Id,
ChannelId: privateCh.Id,
Message: secretToken,
}, privateCh, model.CreatePostFlags{})
require.Nil(t, err)
session, err := th.App.CreateSession(th.Context, &model.Session{UserId: th.BasicUser2.Id, Props: model.StringMap{}})
require.Nil(t, err)
ctx := th.Context.WithSession(session)
contextStr, appErr := th.App.buildThreadContextForRewrite(ctx, root.Id)
require.NotNil(t, appErr)
assert.Equal(t, http.StatusForbidden, appErr.StatusCode)
assert.NotContains(t, contextStr, secretToken)
})
}
// MM-68140: additional edge cases for thread context authorization and anchor resolution.
func TestBuildThreadContextForRewriteEdgeCasesMM68140(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
t.Run("reply post id as root_id resolves thread and includes root message", func(t *testing.T) {
_, appErr := th.App.AddUserToChannel(th.Context, th.BasicUser2, th.BasicChannel, false)
require.Nil(t, appErr)
rootSecret := "MM68140_ROOT_VIA_REPLY_ANCHOR_" + model.NewId()
root, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: rootSecret,
}, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
reply, _, err := th.App.CreatePost(th.Context, &model.Post{
RootId: root.Id,
UserId: th.BasicUser2.Id,
ChannelId: th.BasicChannel.Id,
Message: "reply anchor",
}, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
session, err := th.App.CreateSession(th.Context, &model.Session{UserId: th.BasicUser2.Id, Props: model.StringMap{}})
require.Nil(t, err)
ctx := th.Context.WithSession(session)
contextStr, appErr := th.App.buildThreadContextForRewrite(ctx, reply.Id)
require.Nil(t, appErr)
assert.Contains(t, contextStr, rootSecret)
assert.Contains(t, contextStr, "reply anchor")
})
t.Run("nonexistent post id returns not found", func(t *testing.T) {
session, err := th.App.CreateSession(th.Context, &model.Session{UserId: th.BasicUser.Id, Props: model.StringMap{}})
require.Nil(t, err)
ctx := th.Context.WithSession(session)
_, appErr := th.App.buildThreadContextForRewrite(ctx, model.NewId())
require.NotNil(t, appErr)
assert.Equal(t, http.StatusNotFound, appErr.StatusCode)
})
t.Run("soft-deleted anchor post returns not found", func(t *testing.T) {
root, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: th.BasicUser.Id,
ChannelId: th.BasicChannel.Id,
Message: "to be deleted",
}, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
_, err = th.App.DeletePost(th.Context, root.Id, th.BasicUser.Id)
require.Nil(t, err)
session, err := th.App.CreateSession(th.Context, &model.Session{UserId: th.BasicUser.Id, Props: model.StringMap{}})
require.Nil(t, err)
ctx := th.Context.WithSession(session)
_, appErr := th.App.buildThreadContextForRewrite(ctx, root.Id)
require.NotNil(t, appErr)
assert.Equal(t, http.StatusNotFound, appErr.StatusCode)
})
t.Run("guest on team cannot use root_id for private channel they are not in", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.GuestAccountsSettings.Enable = true
})
guest := th.CreateGuest(t)
_, _, appErr := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, guest.Id, "")
require.Nil(t, appErr)
privateCh := th.CreatePrivateChannel(t, th.BasicTeam)
secretToken := "MM68140_GUEST_PRIVATE_" + model.NewId()
root, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: th.BasicUser.Id,
ChannelId: privateCh.Id,
Message: secretToken,
}, privateCh, model.CreatePostFlags{})
require.Nil(t, err)
session, err := th.App.CreateSession(th.Context, &model.Session{UserId: guest.Id, Props: model.StringMap{}})
require.Nil(t, err)
ctx := th.Context.WithSession(session)
contextStr, appErr := th.App.buildThreadContextForRewrite(ctx, root.Id)
require.NotNil(t, appErr)
assert.Equal(t, http.StatusForbidden, appErr.StatusCode)
assert.NotContains(t, contextStr, secretToken)
})
t.Run("system admin may read thread context for DM they do not participate in", func(t *testing.T) {
dm := th.CreateDmChannel(t, th.BasicUser2)
secretToken := "MM68140_ADMIN_DM_THREAD_" + model.NewId()
root, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: th.BasicUser.Id,
ChannelId: dm.Id,
Message: secretToken,
}, dm, model.CreatePostFlags{})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
RootId: root.Id,
UserId: th.BasicUser2.Id,
ChannelId: dm.Id,
Message: "dm reply",
}, dm, model.CreatePostFlags{})
require.Nil(t, err)
session, err := th.App.CreateSession(th.Context, &model.Session{UserId: th.SystemAdminUser.Id, Props: model.StringMap{}})
require.Nil(t, err)
ctx := th.Context.WithSession(session)
contextStr, appErr := th.App.buildThreadContextForRewrite(ctx, root.Id)
require.Nil(t, appErr)
assert.Contains(t, contextStr, secretToken)
})
t.Run("member can build context after channel is archived", func(t *testing.T) {
ch := th.CreateChannel(t, th.BasicTeam)
root, _, err := th.App.CreatePost(th.Context, &model.Post{
UserId: th.BasicUser.Id,
ChannelId: ch.Id,
Message: "MM68140_ARCHIVED_ROOT",
}, ch, model.CreatePostFlags{})
require.Nil(t, err)
_, _, err = th.App.CreatePost(th.Context, &model.Post{
RootId: root.Id,
UserId: th.BasicUser.Id,
ChannelId: ch.Id,
Message: "reply in archived",
}, ch, model.CreatePostFlags{})
require.Nil(t, err)
appErr := th.App.DeleteChannel(th.Context, ch, th.SystemAdminUser.Id)
require.Nil(t, appErr)
session, err := th.App.CreateSession(th.Context, &model.Session{UserId: th.BasicUser.Id, Props: model.StringMap{}})
require.Nil(t, err)
ctx := th.Context.WithSession(session)
contextStr, appErr := th.App.buildThreadContextForRewrite(ctx, root.Id)
require.Nil(t, appErr)
assert.Contains(t, contextStr, "MM68140_ARCHIVED_ROOT")
})
}
func TestShouldNotRefollowOnOthersReply(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true
*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
})
channel := th.BasicChannel
user := th.BasicUser
user2 := th.BasicUser2
appErr := th.App.JoinChannel(th.Context, channel, user.Id)
require.Nil(t, appErr)
appErr = th.App.JoinChannel(th.Context, channel, user2.Id)
require.Nil(t, appErr)
p1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user2.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
// User2 unfollows thread
m, err := th.App.Srv().Store().Thread().MaintainMembership(user2.Id, p1.Id, store.ThreadMembershipOpts{
Following: false,
UpdateFollowing: true,
})
require.NoError(t, err)
require.False(t, m.Following)
// user posts in the thread
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "another reply"}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
// User2 should still not be following the thread because they manually
// unfollowed the thread
m, appErr = th.App.GetThreadMembershipForUser(user2.Id, p1.Id)
require.Nil(t, appErr)
require.False(t, m.Following)
// user posts in the thread mentioning user2
_, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "reply with mention @" + user2.Username}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
// User2 should now be following the thread because they were explicitly mentioned
m, appErr = th.App.GetThreadMembershipForUser(user2.Id, p1.Id)
require.Nil(t, appErr)
require.True(t, m.Following)
}
func TestGetLastAccessiblePostTime(t *testing.T) {
mainHelper.Parallel(t)
th := SetupWithStoreMock(t)
// Setup store mocks needed for GetServerLimits
mockStore := th.App.Srv().Store().(*storemocks.Store)
mockUserStore := storemocks.UserStore{}
mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
mockStore.On("User").Return(&mockUserStore)
// Test with no license - should return 0
r, err := th.App.GetLastAccessiblePostTime()
assert.Nil(t, err)
assert.Equal(t, int64(0), r)
// Test with Entry license but no limits configured - should return 0
entryLicenseNoLimits := model.NewTestLicenseSKU(model.LicenseShortSkuMattermostEntry)
entryLicenseNoLimits.Limits = nil // No limits configured
th.App.Srv().SetLicense(entryLicenseNoLimits)
r, err = th.App.GetLastAccessiblePostTime()
assert.Nil(t, err)
assert.Equal(t, int64(0), r, "Entry license with no limits should return 0")
// Test with Entry license with zero post history limit - should return 0
entryLicenseZeroLimit := model.NewTestLicenseSKU(model.LicenseShortSkuMattermostEntry)
entryLicenseZeroLimit.Limits = &model.LicenseLimits{PostHistory: 0} // Zero limit
th.App.Srv().SetLicense(entryLicenseZeroLimit)
r, err = th.App.GetLastAccessiblePostTime()
assert.Nil(t, err)
assert.Equal(t, int64(0), r, "Entry license with zero post history limit should return 0")
// Test with Entry license that has post history limits
entryLicenseWithLimits := model.NewTestLicenseSKU(model.LicenseShortSkuMattermostEntry)
entryLicenseWithLimits.Limits = &model.LicenseLimits{PostHistory: 1000} // Actual limit
th.App.Srv().SetLicense(entryLicenseWithLimits)
// Test case 1: No system value found (ErrNotFound) - should return 0
mockSystemStore := storemocks.SystemStore{}
mockStore.On("System").Return(&mockSystemStore)
mockSystemStore.On("GetByName", mock.Anything).Return(nil, store.NewErrNotFound("", ""))
r, err = th.App.GetLastAccessiblePostTime()
assert.Nil(t, err)
assert.Equal(t, int64(0), r)
// Test case 2: Database error - should return error
mockSystemStore = storemocks.SystemStore{}
mockStore.On("System").Return(&mockSystemStore)
mockSystemStore.On("GetByName", mock.Anything).Return(nil, errors.New("database error"))
_, err = th.App.GetLastAccessiblePostTime()
assert.NotNil(t, err)
// Test case 3: Valid system value found - should return parsed timestamp
mockSystemStore = storemocks.SystemStore{}
mockStore.On("System").Return(&mockSystemStore)
mockSystemStore.On("GetByName", mock.Anything).Return(&model.System{Name: model.SystemLastAccessiblePostTime, Value: "1234567890"}, nil)
r, err = th.App.GetLastAccessiblePostTime()
assert.Nil(t, err)
assert.Equal(t, int64(1234567890), r)
}
func TestComputeLastAccessiblePostTime(t *testing.T) {
mainHelper.Parallel(t)
t.Run("Updates the time, if Entry license limit is applicable", func(t *testing.T) {
2022-10-31 09:56:04 -04:00
th := SetupWithStoreMock(t)
// Set Entry license with post history limit of 100 messages
entryLicensePostsLimit := model.NewTestLicenseSKU(model.LicenseShortSkuMattermostEntry)
entryLicensePostsLimit.Limits = &model.LicenseLimits{PostHistory: 100}
th.App.Srv().SetLicense(entryLicensePostsLimit)
2022-10-31 09:56:04 -04:00
mockStore := th.App.Srv().Store().(*storemocks.Store)
mockPostStore := storemocks.PostStore{}
mockPostStore.On("GetNthRecentPostTime", int64(100)).Return(int64(1234567890), nil)
2022-10-31 09:56:04 -04:00
mockSystemStore := storemocks.SystemStore{}
mockSystemStore.On("SaveOrUpdate", mock.Anything).Return(nil)
mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore)
2022-10-31 09:56:04 -04:00
err := th.App.ComputeLastAccessiblePostTime()
assert.NoError(t, err)
// Verify that the system value was saved with the calculated timestamp
mockSystemStore.AssertCalled(t, "SaveOrUpdate", &model.System{
Name: model.SystemLastAccessiblePostTime,
Value: "1234567890",
})
2022-10-31 09:56:04 -04:00
})
t.Run("Remove the time if license limit is NOT applicable", func(t *testing.T) {
2022-10-31 09:56:04 -04:00
th := SetupWithStoreMock(t)
// Set license without post history limits (using test license without limits)
license := model.NewTestLicense()
license.Limits = nil // No limits
th.App.Srv().SetLicense(license)
2022-10-31 09:56:04 -04:00
mockStore := th.App.Srv().Store().(*storemocks.Store)
mockSystemStore := storemocks.SystemStore{}
mockSystemStore.On("GetByName", model.SystemLastAccessiblePostTime).Return(&model.System{Name: model.SystemLastAccessiblePostTime, Value: "1234567890"}, nil)
mockSystemStore.On("PermanentDeleteByName", model.SystemLastAccessiblePostTime).Return(nil, nil)
2022-10-31 09:56:04 -04:00
mockStore.On("System").Return(&mockSystemStore)
err := th.App.ComputeLastAccessiblePostTime()
assert.NoError(t, err)
// Verify that SaveOrUpdate was not called (no new timestamp calculated)
2022-10-31 09:56:04 -04:00
mockSystemStore.AssertNotCalled(t, "SaveOrUpdate", mock.Anything)
// Verify that the previous value was deleted
mockSystemStore.AssertCalled(t, "PermanentDeleteByName", model.SystemLastAccessiblePostTime)
2022-10-31 09:56:04 -04:00
})
}
func TestGetEditHistoryForPost(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "new message",
UserId: th.BasicUser.Id,
}
rpost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
// update the post message
patch := &model.PostPatch{
Message: model.NewPointer("new message edited"),
}
_, _, err1 := th.App.PatchPost(th.Context, rpost.Id, patch, nil)
require.Nil(t, err1)
// update the post message again
patch = &model.PostPatch{
Message: model.NewPointer("new message edited again"),
}
_, _, err2 := th.App.PatchPost(th.Context, rpost.Id, patch, nil)
require.Nil(t, err2)
t.Run("should return the edit history", func(t *testing.T) {
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
edits, err := th.App.GetEditHistoryForPost(post.Id)
require.Nil(t, err)
require.Len(t, edits, 2)
require.Equal(t, "new message edited", edits[0].Message)
require.Equal(t, "new message", edits[1].Message)
})
t.Run("should return an error if the post is not found", func(t *testing.T) {
edits, err := th.App.GetEditHistoryForPost("invalid-post-id")
require.NotNil(t, err)
require.Empty(t, edits)
})
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
t.Run("edit history should contain file metadata", func(t *testing.T) {
fileBytes := []byte("file contents")
fileInfo, err := th.App.UploadFile(th.Context, fileBytes, th.BasicChannel.Id, "file.txt")
require.Nil(t, err)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "new message",
UserId: th.BasicUser.Id,
FileIds: model.StringArray{fileInfo.Id},
}
_, _, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
require.Nil(t, err)
patch := &model.PostPatch{
Message: model.NewPointer("new message edited"),
}
_, _, appErr := th.App.PatchPost(th.Context, post.Id, patch, nil)
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
require.Nil(t, appErr)
patch = &model.PostPatch{
Message: model.NewPointer("new message edited 2"),
}
_, _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil)
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
require.Nil(t, appErr)
patch = &model.PostPatch{
Message: model.NewPointer("new message edited 3"),
}
_, _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil)
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
require.Nil(t, appErr)
edits, err := th.App.GetEditHistoryForPost(post.Id)
require.Nil(t, err)
require.Len(t, edits, 3)
for _, edit := range edits {
require.Len(t, edit.FileIds, 1)
require.Equal(t, fileInfo.Id, edit.FileIds[0])
require.Len(t, edit.Metadata.Files, 1)
require.Equal(t, fileInfo.Id, edit.Metadata.Files[0].Id)
}
})
t.Run("edit history should contain file metadata even if the file info is deleted", func(t *testing.T) {
fileBytes := []byte("file contents")
fileInfo, appErr := th.App.UploadFile(th.Context, fileBytes, th.BasicChannel.Id, "file.txt")
require.Nil(t, appErr)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "new message",
UserId: th.BasicUser.Id,
FileIds: model.StringArray{fileInfo.Id},
}
_, _, appErr = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
require.Nil(t, appErr)
patch := &model.PostPatch{
Message: model.NewPointer("new message edited"),
}
_, _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil)
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
require.Nil(t, appErr)
patch = &model.PostPatch{
Message: model.NewPointer("new message edited 2"),
}
_, _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil)
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
require.Nil(t, appErr)
patch = &model.PostPatch{
Message: model.NewPointer("new message edited 3"),
}
_, _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil)
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
require.Nil(t, appErr)
// now delete the file info, and it should still be include in edit history metadata
_, err := th.App.Srv().Store().FileInfo().DeleteForPost(th.Context, post.Id)
require.NoError(t, err)
edits, appErr := th.App.GetEditHistoryForPost(post.Id)
require.Nil(t, appErr)
require.Len(t, edits, 3)
for _, edit := range edits {
require.Len(t, edit.FileIds, 1)
require.Equal(t, fileInfo.Id, edit.FileIds[0])
require.Len(t, edit.Metadata.Files, 1)
require.Equal(t, fileInfo.Id, edit.Metadata.Files[0].Id)
require.Greater(t, edit.Metadata.Files[0].DeleteAt, int64(0))
}
})
}
Feature: Wrangler (#23602) * Migrate feature/wrangler to mono-repo * Add wrangler files * Fix linters, types, etc * Fix snapshots * Fix playwright * Fix pipelines * Fix more pipeline * Fixes for pipelines * More changes for pipeline * Fix types * Add support for a feature flag, but leave it defaulted on for spinwick usage for now * Update snapshot * fix js error when removing last value of multiselect, support CSV marshaling to string array for textsetting * Fix linter * Remove TODO * Remove another TODO * fix tests * Fix i18n * Add server tests * Fix linter * Fix linter * Use proper icon for dot menu * Update snapshot * Add Cypress UI tests for various entrypoints to move thread modal, split SCSS out from forward post into its own thing * clean up * fix linter * More cleanup * Revert files to master * Fix linter for e2e tests * Make ForwardPostChannelSelect channel types configurable with a prop * Add missing return * Fixes from PR feedback * First batch of PR Feedback * Another batch of PR changes * Fix linter * Update snapshots * Wrangler system messages are translated to each user's locale * Initially translate Wrangler into system locale rather than initiating user * More fixes for PR Feedback * Fix some server tests * More updates with master. Fixes around pipelines. Enforce Enterprise license on front/back end * Add tests for dot_menu * More pipeline fixes * Fix e2etests prettier * Update cypress tests, change occurrences of 'Wrangler' with 'Move Thread' * Fix linter * Remove enterprise lock * A couple more occurrences of wrangler strings, and one more enterprise lock * Fix server tests * Fix i18n * Fix e2e linter * Feature flag shouldn't be on by default * Enable move threads feature in smoke tests (#25657) * enable move threads feature * add @prod tag * Fix move_thread_from_public_channel e2e test * Fix e2e style --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
2023-12-11 15:27:34 -05:00
func TestCopyWranglerPostlist(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
Feature: Wrangler (#23602) * Migrate feature/wrangler to mono-repo * Add wrangler files * Fix linters, types, etc * Fix snapshots * Fix playwright * Fix pipelines * Fix more pipeline * Fixes for pipelines * More changes for pipeline * Fix types * Add support for a feature flag, but leave it defaulted on for spinwick usage for now * Update snapshot * fix js error when removing last value of multiselect, support CSV marshaling to string array for textsetting * Fix linter * Remove TODO * Remove another TODO * fix tests * Fix i18n * Add server tests * Fix linter * Fix linter * Use proper icon for dot menu * Update snapshot * Add Cypress UI tests for various entrypoints to move thread modal, split SCSS out from forward post into its own thing * clean up * fix linter * More cleanup * Revert files to master * Fix linter for e2e tests * Make ForwardPostChannelSelect channel types configurable with a prop * Add missing return * Fixes from PR feedback * First batch of PR Feedback * Another batch of PR changes * Fix linter * Update snapshots * Wrangler system messages are translated to each user's locale * Initially translate Wrangler into system locale rather than initiating user * More fixes for PR Feedback * Fix some server tests * More updates with master. Fixes around pipelines. Enforce Enterprise license on front/back end * Add tests for dot_menu * More pipeline fixes * Fix e2etests prettier * Update cypress tests, change occurrences of 'Wrangler' with 'Move Thread' * Fix linter * Remove enterprise lock * A couple more occurrences of wrangler strings, and one more enterprise lock * Fix server tests * Fix i18n * Fix e2e linter * Feature flag shouldn't be on by default * Enable move threads feature in smoke tests (#25657) * enable move threads feature * add @prod tag * Fix move_thread_from_public_channel e2e test * Fix e2e style --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
2023-12-11 15:27:34 -05:00
// Create a post with a file attachment
fileBytes := []byte("file contents")
fileInfo, err := th.App.UploadFile(th.Context, fileBytes, th.BasicChannel.Id, "file.txt")
require.Nil(t, err)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "test message",
UserId: th.BasicUser.Id,
FileIds: []string{fileInfo.Id},
}
rootPost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
Feature: Wrangler (#23602) * Migrate feature/wrangler to mono-repo * Add wrangler files * Fix linters, types, etc * Fix snapshots * Fix playwright * Fix pipelines * Fix more pipeline * Fixes for pipelines * More changes for pipeline * Fix types * Add support for a feature flag, but leave it defaulted on for spinwick usage for now * Update snapshot * fix js error when removing last value of multiselect, support CSV marshaling to string array for textsetting * Fix linter * Remove TODO * Remove another TODO * fix tests * Fix i18n * Add server tests * Fix linter * Fix linter * Use proper icon for dot menu * Update snapshot * Add Cypress UI tests for various entrypoints to move thread modal, split SCSS out from forward post into its own thing * clean up * fix linter * More cleanup * Revert files to master * Fix linter for e2e tests * Make ForwardPostChannelSelect channel types configurable with a prop * Add missing return * Fixes from PR feedback * First batch of PR Feedback * Another batch of PR changes * Fix linter * Update snapshots * Wrangler system messages are translated to each user's locale * Initially translate Wrangler into system locale rather than initiating user * More fixes for PR Feedback * Fix some server tests * More updates with master. Fixes around pipelines. Enforce Enterprise license on front/back end * Add tests for dot_menu * More pipeline fixes * Fix e2etests prettier * Update cypress tests, change occurrences of 'Wrangler' with 'Move Thread' * Fix linter * Remove enterprise lock * A couple more occurrences of wrangler strings, and one more enterprise lock * Fix server tests * Fix i18n * Fix e2e linter * Feature flag shouldn't be on by default * Enable move threads feature in smoke tests (#25657) * enable move threads feature * add @prod tag * Fix move_thread_from_public_channel e2e test * Fix e2e style --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
2023-12-11 15:27:34 -05:00
require.Nil(t, err)
// Add a reaction to the post
reaction := &model.Reaction{
UserId: th.BasicUser.Id,
PostId: rootPost.Id,
EmojiName: "smile",
}
_, err = th.App.SaveReactionForPost(th.Context, reaction)
require.Nil(t, err)
// Copy the post to a new channel
targetChannel := &model.Channel{
TeamId: th.BasicTeam.Id,
Name: "test-channel",
Type: model.ChannelTypeOpen,
}
targetChannel, err = th.App.CreateChannel(th.Context, targetChannel, false)
require.Nil(t, err)
wpl := &model.WranglerPostList{
Posts: []*model.Post{rootPost},
FileAttachmentCount: 1,
}
newRootPost, _, err := th.App.CopyWranglerPostlist(th.Context, wpl, targetChannel)
Feature: Wrangler (#23602) * Migrate feature/wrangler to mono-repo * Add wrangler files * Fix linters, types, etc * Fix snapshots * Fix playwright * Fix pipelines * Fix more pipeline * Fixes for pipelines * More changes for pipeline * Fix types * Add support for a feature flag, but leave it defaulted on for spinwick usage for now * Update snapshot * fix js error when removing last value of multiselect, support CSV marshaling to string array for textsetting * Fix linter * Remove TODO * Remove another TODO * fix tests * Fix i18n * Add server tests * Fix linter * Fix linter * Use proper icon for dot menu * Update snapshot * Add Cypress UI tests for various entrypoints to move thread modal, split SCSS out from forward post into its own thing * clean up * fix linter * More cleanup * Revert files to master * Fix linter for e2e tests * Make ForwardPostChannelSelect channel types configurable with a prop * Add missing return * Fixes from PR feedback * First batch of PR Feedback * Another batch of PR changes * Fix linter * Update snapshots * Wrangler system messages are translated to each user's locale * Initially translate Wrangler into system locale rather than initiating user * More fixes for PR Feedback * Fix some server tests * More updates with master. Fixes around pipelines. Enforce Enterprise license on front/back end * Add tests for dot_menu * More pipeline fixes * Fix e2etests prettier * Update cypress tests, change occurrences of 'Wrangler' with 'Move Thread' * Fix linter * Remove enterprise lock * A couple more occurrences of wrangler strings, and one more enterprise lock * Fix server tests * Fix i18n * Fix e2e linter * Feature flag shouldn't be on by default * Enable move threads feature in smoke tests (#25657) * enable move threads feature * add @prod tag * Fix move_thread_from_public_channel e2e test * Fix e2e style --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
2023-12-11 15:27:34 -05:00
require.Nil(t, err)
// Check that the new post has the same message and file attachment
require.Equal(t, rootPost.Message, newRootPost.Message)
require.Len(t, newRootPost.FileIds, 1)
// Check that the new post has the same reaction
reactions, err := th.App.GetReactionsForPost(newRootPost.Id)
require.Nil(t, err)
require.Len(t, reactions, 1)
require.Equal(t, reaction.EmojiName, reactions[0].EmojiName)
}
func TestValidateMoveOrCopy(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
Feature: Wrangler (#23602) * Migrate feature/wrangler to mono-repo * Add wrangler files * Fix linters, types, etc * Fix snapshots * Fix playwright * Fix pipelines * Fix more pipeline * Fixes for pipelines * More changes for pipeline * Fix types * Add support for a feature flag, but leave it defaulted on for spinwick usage for now * Update snapshot * fix js error when removing last value of multiselect, support CSV marshaling to string array for textsetting * Fix linter * Remove TODO * Remove another TODO * fix tests * Fix i18n * Add server tests * Fix linter * Fix linter * Use proper icon for dot menu * Update snapshot * Add Cypress UI tests for various entrypoints to move thread modal, split SCSS out from forward post into its own thing * clean up * fix linter * More cleanup * Revert files to master * Fix linter for e2e tests * Make ForwardPostChannelSelect channel types configurable with a prop * Add missing return * Fixes from PR feedback * First batch of PR Feedback * Another batch of PR changes * Fix linter * Update snapshots * Wrangler system messages are translated to each user's locale * Initially translate Wrangler into system locale rather than initiating user * More fixes for PR Feedback * Fix some server tests * More updates with master. Fixes around pipelines. Enforce Enterprise license on front/back end * Add tests for dot_menu * More pipeline fixes * Fix e2etests prettier * Update cypress tests, change occurrences of 'Wrangler' with 'Move Thread' * Fix linter * Remove enterprise lock * A couple more occurrences of wrangler strings, and one more enterprise lock * Fix server tests * Fix i18n * Fix e2e linter * Feature flag shouldn't be on by default * Enable move threads feature in smoke tests (#25657) * enable move threads feature * add @prod tag * Fix move_thread_from_public_channel e2e test * Fix e2e style --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
2023-12-11 15:27:34 -05:00
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.WranglerSettings.MoveThreadFromPrivateChannelEnable = model.NewPointer(true)
cfg.WranglerSettings.MoveThreadFromDirectMessageChannelEnable = model.NewPointer(true)
cfg.WranglerSettings.MoveThreadFromGroupMessageChannelEnable = model.NewPointer(true)
cfg.WranglerSettings.MoveThreadToAnotherTeamEnable = model.NewPointer(true)
cfg.WranglerSettings.MoveThreadMaxCount = model.NewPointer(int64(100))
Feature: Wrangler (#23602) * Migrate feature/wrangler to mono-repo * Add wrangler files * Fix linters, types, etc * Fix snapshots * Fix playwright * Fix pipelines * Fix more pipeline * Fixes for pipelines * More changes for pipeline * Fix types * Add support for a feature flag, but leave it defaulted on for spinwick usage for now * Update snapshot * fix js error when removing last value of multiselect, support CSV marshaling to string array for textsetting * Fix linter * Remove TODO * Remove another TODO * fix tests * Fix i18n * Add server tests * Fix linter * Fix linter * Use proper icon for dot menu * Update snapshot * Add Cypress UI tests for various entrypoints to move thread modal, split SCSS out from forward post into its own thing * clean up * fix linter * More cleanup * Revert files to master * Fix linter for e2e tests * Make ForwardPostChannelSelect channel types configurable with a prop * Add missing return * Fixes from PR feedback * First batch of PR Feedback * Another batch of PR changes * Fix linter * Update snapshots * Wrangler system messages are translated to each user's locale * Initially translate Wrangler into system locale rather than initiating user * More fixes for PR Feedback * Fix some server tests * More updates with master. Fixes around pipelines. Enforce Enterprise license on front/back end * Add tests for dot_menu * More pipeline fixes * Fix e2etests prettier * Update cypress tests, change occurrences of 'Wrangler' with 'Move Thread' * Fix linter * Remove enterprise lock * A couple more occurrences of wrangler strings, and one more enterprise lock * Fix server tests * Fix i18n * Fix e2e linter * Feature flag shouldn't be on by default * Enable move threads feature in smoke tests (#25657) * enable move threads feature * add @prod tag * Fix move_thread_from_public_channel e2e test * Fix e2e style --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
2023-12-11 15:27:34 -05:00
})
t.Run("empty post list", func(t *testing.T) {
err := th.App.ValidateMoveOrCopy(th.Context, &model.WranglerPostList{}, th.BasicChannel, th.BasicChannel, th.BasicUser)
require.Error(t, err)
require.Equal(t, "The wrangler post list contains no posts", err.Error())
})
t.Run("moving from private channel with MoveThreadFromPrivateChannelEnable disabled", func(t *testing.T) {
privateChannel := &model.Channel{
TeamId: th.BasicTeam.Id,
Name: "private-channel",
Type: model.ChannelTypePrivate,
}
privateChannel, err := th.App.CreateChannel(th.Context, privateChannel, false)
require.Nil(t, err)
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.WranglerSettings.MoveThreadFromPrivateChannelEnable = model.NewPointer(false)
Feature: Wrangler (#23602) * Migrate feature/wrangler to mono-repo * Add wrangler files * Fix linters, types, etc * Fix snapshots * Fix playwright * Fix pipelines * Fix more pipeline * Fixes for pipelines * More changes for pipeline * Fix types * Add support for a feature flag, but leave it defaulted on for spinwick usage for now * Update snapshot * fix js error when removing last value of multiselect, support CSV marshaling to string array for textsetting * Fix linter * Remove TODO * Remove another TODO * fix tests * Fix i18n * Add server tests * Fix linter * Fix linter * Use proper icon for dot menu * Update snapshot * Add Cypress UI tests for various entrypoints to move thread modal, split SCSS out from forward post into its own thing * clean up * fix linter * More cleanup * Revert files to master * Fix linter for e2e tests * Make ForwardPostChannelSelect channel types configurable with a prop * Add missing return * Fixes from PR feedback * First batch of PR Feedback * Another batch of PR changes * Fix linter * Update snapshots * Wrangler system messages are translated to each user's locale * Initially translate Wrangler into system locale rather than initiating user * More fixes for PR Feedback * Fix some server tests * More updates with master. Fixes around pipelines. Enforce Enterprise license on front/back end * Add tests for dot_menu * More pipeline fixes * Fix e2etests prettier * Update cypress tests, change occurrences of 'Wrangler' with 'Move Thread' * Fix linter * Remove enterprise lock * A couple more occurrences of wrangler strings, and one more enterprise lock * Fix server tests * Fix i18n * Fix e2e linter * Feature flag shouldn't be on by default * Enable move threads feature in smoke tests (#25657) * enable move threads feature * add @prod tag * Fix move_thread_from_public_channel e2e test * Fix e2e style --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
2023-12-11 15:27:34 -05:00
})
e := th.App.ValidateMoveOrCopy(th.Context, &model.WranglerPostList{Posts: []*model.Post{{ChannelId: privateChannel.Id}}}, privateChannel, th.BasicChannel, th.BasicUser)
require.Error(t, e)
require.Equal(t, "Wrangler is currently configured to not allow moving posts from private channels", e.Error())
})
t.Run("moving from direct channel with MoveThreadFromDirectMessageChannelEnable disabled", func(t *testing.T) {
directChannel, err := th.App.createDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id)
require.Nil(t, err)
require.NotNil(t, directChannel)
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.WranglerSettings.MoveThreadFromDirectMessageChannelEnable = model.NewPointer(false)
Feature: Wrangler (#23602) * Migrate feature/wrangler to mono-repo * Add wrangler files * Fix linters, types, etc * Fix snapshots * Fix playwright * Fix pipelines * Fix more pipeline * Fixes for pipelines * More changes for pipeline * Fix types * Add support for a feature flag, but leave it defaulted on for spinwick usage for now * Update snapshot * fix js error when removing last value of multiselect, support CSV marshaling to string array for textsetting * Fix linter * Remove TODO * Remove another TODO * fix tests * Fix i18n * Add server tests * Fix linter * Fix linter * Use proper icon for dot menu * Update snapshot * Add Cypress UI tests for various entrypoints to move thread modal, split SCSS out from forward post into its own thing * clean up * fix linter * More cleanup * Revert files to master * Fix linter for e2e tests * Make ForwardPostChannelSelect channel types configurable with a prop * Add missing return * Fixes from PR feedback * First batch of PR Feedback * Another batch of PR changes * Fix linter * Update snapshots * Wrangler system messages are translated to each user's locale * Initially translate Wrangler into system locale rather than initiating user * More fixes for PR Feedback * Fix some server tests * More updates with master. Fixes around pipelines. Enforce Enterprise license on front/back end * Add tests for dot_menu * More pipeline fixes * Fix e2etests prettier * Update cypress tests, change occurrences of 'Wrangler' with 'Move Thread' * Fix linter * Remove enterprise lock * A couple more occurrences of wrangler strings, and one more enterprise lock * Fix server tests * Fix i18n * Fix e2e linter * Feature flag shouldn't be on by default * Enable move threads feature in smoke tests (#25657) * enable move threads feature * add @prod tag * Fix move_thread_from_public_channel e2e test * Fix e2e style --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
2023-12-11 15:27:34 -05:00
})
e := th.App.ValidateMoveOrCopy(th.Context, &model.WranglerPostList{Posts: []*model.Post{{ChannelId: directChannel.Id}}}, directChannel, th.BasicChannel, th.BasicUser)
require.Error(t, e)
require.Equal(t, "Wrangler is currently configured to not allow moving posts from direct message channels", e.Error())
})
t.Run("moving from group channel with MoveThreadFromGroupMessageChannelEnable disabled", func(t *testing.T) {
groupChannel := &model.Channel{
TeamId: th.BasicTeam.Id,
Name: "group-channel",
Type: model.ChannelTypeGroup,
}
groupChannel, err := th.App.CreateChannel(th.Context, groupChannel, false)
require.Nil(t, err)
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.WranglerSettings.MoveThreadFromGroupMessageChannelEnable = model.NewPointer(false)
Feature: Wrangler (#23602) * Migrate feature/wrangler to mono-repo * Add wrangler files * Fix linters, types, etc * Fix snapshots * Fix playwright * Fix pipelines * Fix more pipeline * Fixes for pipelines * More changes for pipeline * Fix types * Add support for a feature flag, but leave it defaulted on for spinwick usage for now * Update snapshot * fix js error when removing last value of multiselect, support CSV marshaling to string array for textsetting * Fix linter * Remove TODO * Remove another TODO * fix tests * Fix i18n * Add server tests * Fix linter * Fix linter * Use proper icon for dot menu * Update snapshot * Add Cypress UI tests for various entrypoints to move thread modal, split SCSS out from forward post into its own thing * clean up * fix linter * More cleanup * Revert files to master * Fix linter for e2e tests * Make ForwardPostChannelSelect channel types configurable with a prop * Add missing return * Fixes from PR feedback * First batch of PR Feedback * Another batch of PR changes * Fix linter * Update snapshots * Wrangler system messages are translated to each user's locale * Initially translate Wrangler into system locale rather than initiating user * More fixes for PR Feedback * Fix some server tests * More updates with master. Fixes around pipelines. Enforce Enterprise license on front/back end * Add tests for dot_menu * More pipeline fixes * Fix e2etests prettier * Update cypress tests, change occurrences of 'Wrangler' with 'Move Thread' * Fix linter * Remove enterprise lock * A couple more occurrences of wrangler strings, and one more enterprise lock * Fix server tests * Fix i18n * Fix e2e linter * Feature flag shouldn't be on by default * Enable move threads feature in smoke tests (#25657) * enable move threads feature * add @prod tag * Fix move_thread_from_public_channel e2e test * Fix e2e style --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
2023-12-11 15:27:34 -05:00
})
e := th.App.ValidateMoveOrCopy(th.Context, &model.WranglerPostList{Posts: []*model.Post{{ChannelId: groupChannel.Id}}}, groupChannel, th.BasicChannel, th.BasicUser)
require.Error(t, e)
require.Equal(t, "Wrangler is currently configured to not allow moving posts from group message channels", e.Error())
})
t.Run("moving to different team with MoveThreadToAnotherTeamEnable disabled", func(t *testing.T) {
team := &model.Team{
Name: "testteam",
DisplayName: "testteam",
Type: model.TeamOpen,
}
targetTeam, err := th.App.CreateTeam(th.Context, team)
require.Nil(t, err)
require.NotNil(t, targetTeam)
targetChannel := &model.Channel{
TeamId: targetTeam.Id,
Name: "test-channel",
Type: model.ChannelTypeOpen,
}
targetChannel, err = th.App.CreateChannel(th.Context, targetChannel, false)
require.Nil(t, err)
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.WranglerSettings.MoveThreadToAnotherTeamEnable = model.NewPointer(false)
Feature: Wrangler (#23602) * Migrate feature/wrangler to mono-repo * Add wrangler files * Fix linters, types, etc * Fix snapshots * Fix playwright * Fix pipelines * Fix more pipeline * Fixes for pipelines * More changes for pipeline * Fix types * Add support for a feature flag, but leave it defaulted on for spinwick usage for now * Update snapshot * fix js error when removing last value of multiselect, support CSV marshaling to string array for textsetting * Fix linter * Remove TODO * Remove another TODO * fix tests * Fix i18n * Add server tests * Fix linter * Fix linter * Use proper icon for dot menu * Update snapshot * Add Cypress UI tests for various entrypoints to move thread modal, split SCSS out from forward post into its own thing * clean up * fix linter * More cleanup * Revert files to master * Fix linter for e2e tests * Make ForwardPostChannelSelect channel types configurable with a prop * Add missing return * Fixes from PR feedback * First batch of PR Feedback * Another batch of PR changes * Fix linter * Update snapshots * Wrangler system messages are translated to each user's locale * Initially translate Wrangler into system locale rather than initiating user * More fixes for PR Feedback * Fix some server tests * More updates with master. Fixes around pipelines. Enforce Enterprise license on front/back end * Add tests for dot_menu * More pipeline fixes * Fix e2etests prettier * Update cypress tests, change occurrences of 'Wrangler' with 'Move Thread' * Fix linter * Remove enterprise lock * A couple more occurrences of wrangler strings, and one more enterprise lock * Fix server tests * Fix i18n * Fix e2e linter * Feature flag shouldn't be on by default * Enable move threads feature in smoke tests (#25657) * enable move threads feature * add @prod tag * Fix move_thread_from_public_channel e2e test * Fix e2e style --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
2023-12-11 15:27:34 -05:00
})
e := th.App.ValidateMoveOrCopy(th.Context, &model.WranglerPostList{Posts: []*model.Post{{ChannelId: th.BasicChannel.Id}}}, th.BasicChannel, targetChannel, th.BasicUser)
require.Error(t, e)
require.Equal(t, "Wrangler is currently configured to not allow moving messages to different teams", e.Error())
})
t.Run("moving to channel user is not a member of", func(t *testing.T) {
targetChannel := &model.Channel{
TeamId: th.BasicTeam.Id,
Name: "test-channel",
Type: model.ChannelTypePrivate,
}
targetChannel, err := th.App.CreateChannel(th.Context, targetChannel, false)
require.Nil(t, err)
err = th.App.RemoveUserFromChannel(th.Context, th.BasicUser.Id, th.SystemAdminUser.Id, th.BasicChannel)
require.Nil(t, err)
e := th.App.ValidateMoveOrCopy(th.Context, &model.WranglerPostList{Posts: []*model.Post{{ChannelId: th.BasicChannel.Id}}}, th.BasicChannel, targetChannel, th.BasicUser)
require.Error(t, e)
require.Equal(t, fmt.Sprintf("channel with ID %s doesn't exist or you are not a member", targetChannel.Id), e.Error())
})
t.Run("moving thread longer than MoveThreadMaxCount", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.WranglerSettings.MoveThreadMaxCount = 1
})
e := th.App.ValidateMoveOrCopy(th.Context, &model.WranglerPostList{Posts: []*model.Post{{ChannelId: th.BasicChannel.Id}, {ChannelId: th.BasicChannel.Id}}}, th.BasicChannel, th.BasicChannel, th.BasicUser)
require.Error(t, e)
require.Equal(t, "the thread is 2 posts long, but this command is configured to only move threads of up to 1 posts", e.Error())
})
}
func TestPermanentDeletePost(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
ci: enable fullyparallel mode for server tests (#35816) * ci: enable fullyparallel mode for server tests Replace os.Setenv, os.Chdir, and global state mutations with parallel-safe alternatives (t.Setenv, t.Chdir, test hooks) across 37 files. Refactor GetLogRootPath and MM_INSTALL_TYPE to use package-level test hooks instead of environment variables. This enables gotestsum --fullparallel, allowing all test packages to run with maximum parallelism within each shard. Co-authored-by: Claude <claude@anthropic.com> * ci: split fullyparallel from continue-on-error in workflow template - Add new boolean input 'allow-failure' separate from 'fullyparallel' - Change continue-on-error to use allow-failure instead of fullyparallel - Update server-ci.yml to pass allow-failure: true for test coverage job - Allows independent control of parallel execution and failure tolerance Co-authored-by: Claude <claude@anthropic.com> * fix: protect TestOverrideLogRootPath with sync.Mutex for parallel tests - Replace global var TestOverrideLogRootPath with mutex-protected functions - Add SetTestOverrideLogRootPath() and getTestOverrideLogRootPath() functions - Update GetLogRootPath() to use thread-safe getter - Update all test files to use SetTestOverrideLogRootPath() with t.Cleanup() - Fixes race condition when running tests with t.Parallel() Co-authored-by: Claude <claude@anthropic.com> * fix: configure audit settings before server setup in tests - Move ExperimentalAuditSettings from UpdateConfig() to config defaults - Pass audit config via app.Config() option in SetupWithServerOptions() - Fixes audit test setup ordering to configure BEFORE server initialization - Resolves CodeRabbit's audit config timing issue in api4 tests Co-authored-by: Claude <claude@anthropic.com> * fix: implement SetTestOverrideLogRootPath mutex in logger.go The previous commit updated test callers to use SetTestOverrideLogRootPath() but didn't actually create the function in config/logger.go, causing build failures across all CI shards. This commit: - Replaces the exported var TestOverrideLogRootPath with mutex-protected unexported state (testOverrideLogRootPath + testOverrideLogRootMu) - Adds exported SetTestOverrideLogRootPath() setter - Adds unexported getTestOverrideLogRootPath() getter - Updates GetLogRootPath() to use the thread-safe getter - Fixes log_test.go callers that were missed in the previous commit Co-authored-by: Claude <claude@anthropic.com> * fix(test): use SetupConfig for access_control feature flag registration InitAccessControlPolicy() checks FeatureFlags.AttributeBasedAccessControl at route registration time during server startup. Setting the flag via UpdateConfig after Setup() is too late — routes are never registered and API calls return 404. Use SetupConfig() to pass the feature flag in the initial config before server startup, ensuring routes are properly registered. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore BurnOnRead flag state in TestRevealPost subtest The 'feature not enabled' subtest disables BurnOnRead without restoring it via t.Cleanup. Subsequent subtests inherit the disabled state, which can cause 501 errors when they expect the feature to be available. Add t.Cleanup to restore FeatureFlags.BurnOnRead = true after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore EnableSharedChannelsMemberSync flag via t.Cleanup The test disables EnableSharedChannelsMemberSync without restoring it. If the subtest exits early (e.g., require failure), later sibling subtests inherit a disabled flag and become flaky. Add t.Cleanup to restore the flag after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * Fix test parallelism: use instance-scoped overrides and init-time audit config Replace package-level test globals (TestOverrideInstallType, SetTestOverrideLogRootPath) with fields on PlatformService so each test gets its own instance without process-wide mutation. Fix three audit tests (TestUserLoginAudit, TestLogoutAuditAuthStatus, TestUpdatePasswordAudit) that configured the audit logger after server init — the audit logger only reads config at startup, so pass audit settings via app.Config() at init time instead. Also revert the Go 1.24.13 downgrade and bump mattermost-govet to v2.0.2 for Go 1.25.8 compatibility. * Fix audit unit tests * Fix MMCLOUDURL unit tests * Fixed unit tests using MM_NOTIFY_ADMIN_COOL_OFF_DAYS * Make app migrations idempotent for parallel test safety Change System().Save() to System().SaveOrUpdate() in all migration completion markers. When two parallel tests share a database pool entry, both may race through the check-then-insert migration pattern. Save() causes a duplicate key fatal crash; SaveOrUpdate() makes the second write a harmless no-op. * test: address review feedback on fullyparallel PR - Use SetLogRootPathOverride() setter instead of direct field access in platform/support_packet_test.go and platform/log_test.go (pvev) - Restore TestGetLogRootPath in config/logger_test.go to keep MM_LOG_PATH env var coverage; test uses t.Setenv so it runs serially which is fine (pvev) - Fix misleading comment in config_test.go: code uses t.Setenv, not os.Setenv (jgheithcock) Co-authored-by: Claude <claude@anthropic.com> * fix: add missing os import in post_test.go The os import was dropped during a merge conflict resolution while burn-on-read shared channel tests from master still use os.Setenv. Co-authored-by: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: wiggin77 <wiggin77@warpmail.net> Co-authored-by: Mattermost Build <build@mattermost.com>
2026-04-08 20:48:36 -04:00
// Enable BurnOnRead feature flag
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.BurnOnRead = true })
t.Run("should permanently delete a post and its file attachment", func(t *testing.T) {
// Create a post with a file attachment.
teamID := th.BasicTeam.Id
channelID := th.BasicChannel.Id
userID := th.BasicUser.Id
filename := "test"
data := []byte("abcd")
info1, err := th.App.DoUploadFile(th.Context, time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamID, channelID, userID, filename, data, true)
assert.Nil(t, err)
post := &model.Post{
Message: "asd",
ChannelId: channelID,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: userID,
CreateAt: 0,
FileIds: []string{info1.Id},
}
post, _, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
assert.Nil(t, err)
// Delete the post.
err = th.App.PermanentDeletePost(th.Context, post.Id, userID)
assert.Nil(t, err)
// Wait for the cleanup routine to finish.
time.Sleep(time.Millisecond * 100)
// Check that the post can no longer be reached.
_, err = th.App.GetSinglePost(th.Context, post.Id, true)
assert.NotNil(t, err)
// Check that the file can no longer be reached.
_, err = th.App.GetFileInfo(th.Context, info1.Id)
assert.NotNil(t, err)
})
t.Run("should permanently delete a post that is soft deleted", func(t *testing.T) {
// Create a post with a file attachment.
teamID := th.BasicTeam.Id
channelID := th.BasicChannel.Id
userID := th.BasicUser.Id
filename := "test"
data := []byte("abcd")
info1, appErr := th.App.DoUploadFile(th.Context, time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamID, channelID, userID, filename, data, true)
require.Nil(t, appErr)
post := &model.Post{
Message: "asd",
ChannelId: channelID,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: userID,
CreateAt: 0,
FileIds: []string{info1.Id},
}
post, _, appErr = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
assert.Nil(t, appErr)
infos, err := th.App.Srv().Store().FileInfo().GetForPost(post.Id, true, true, false)
require.NoError(t, err)
assert.Len(t, infos, 1)
// Soft delete the post.
_, appErr = th.App.DeletePost(th.Context, post.Id, userID)
assert.Nil(t, appErr)
// Wait for the cleanup routine to finish.
time.Sleep(time.Millisecond * 100)
// Delete the post.
appErr = th.App.PermanentDeletePost(th.Context, post.Id, userID)
assert.Nil(t, appErr)
// Check that the post can no longer be reached.
_, appErr = th.App.GetSinglePost(th.Context, post.Id, true)
assert.NotNil(t, appErr)
infos, err = th.App.Srv().Store().FileInfo().GetForPost(post.Id, true, true, false)
require.NoError(t, err)
assert.Len(t, infos, 0)
})
t.Run("should permanently delete a burn-on-read post and its file attachments", func(t *testing.T) {
// Enable feature with license
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.ServiceSettings.EnableBurnOnRead = model.NewPointer(true)
})
// Create a burn-on-read post with a file attachment
teamID := th.BasicTeam.Id
channelID := th.BasicChannel.Id
userID := th.BasicUser.Id
filename := "burn_on_read_file"
data := []byte("burn on read file content")
info1, err := th.App.DoUploadFile(th.Context, time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamID, channelID, userID, filename, data, true)
require.Nil(t, err)
post := &model.Post{
Message: "burn on read message with file",
ChannelId: channelID,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: userID,
CreateAt: 0,
FileIds: []string{info1.Id},
Type: model.PostTypeBurnOnRead,
}
post.AddProp(model.PostPropsExpireAt, model.GetMillis()+int64(model.DefaultExpirySeconds*1000))
post, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, appErr)
require.Equal(t, model.PostTypeBurnOnRead, post.Type)
// Verify that the post has empty message and file IDs (stored in TemporaryPosts)
assert.Empty(t, post.Message)
assert.Empty(t, post.FileIds)
// Verify that TemporaryPost exists with original content
tmpPost, tmpErr := th.App.Srv().Store().TemporaryPost().Get(th.Context, post.Id, true)
require.NoError(t, tmpErr)
require.NotNil(t, tmpPost)
assert.Equal(t, "burn on read message with file", tmpPost.Message)
assert.Equal(t, model.StringArray{info1.Id}, tmpPost.FileIDs)
// Verify file info exists before deletion
_, err = th.App.GetFileInfo(th.Context, info1.Id)
require.Nil(t, err)
// Permanently delete the post
appErr = th.App.PermanentDeletePost(th.Context, post.Id, userID)
require.Nil(t, appErr)
// Check that the post can no longer be reached
_, err = th.App.GetSinglePost(th.Context, post.Id, true)
assert.NotNil(t, err)
// Check that the file also deleted
_, err = th.App.GetFileInfo(th.Context, info1.Id)
assert.NotNil(t, err)
// Verify TemporaryPost is also deleted
_, tmpErr = th.App.Srv().Store().TemporaryPost().Get(th.Context, post.Id, true)
assert.Error(t, tmpErr)
assert.True(t, store.IsErrNotFound(tmpErr))
})
t.Run("should send unrevealed post in websocket broadcast", func(t *testing.T) {
// Enable feature with license
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.ServiceSettings.EnableBurnOnRead = model.NewPointer(true)
})
// Create a burn-on-read post
//teamID := th.BasicTeam.Id
channelID := th.BasicChannel.Id
userID := th.BasicUser.Id
wsMessages, closeWS := connectFakeWebSocket(t, th, userID, "", []model.WebsocketEventType{model.WebsocketEventPostDeleted})
defer closeWS()
post := &model.Post{
Message: "burn on read message with file",
ChannelId: channelID,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: userID,
CreateAt: 0,
Type: model.PostTypeBurnOnRead,
}
post.AddProp(model.PostPropsExpireAt, model.GetMillis()+int64(model.DefaultExpirySeconds*1000))
post, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, appErr)
require.Equal(t, model.PostTypeBurnOnRead, post.Type)
appErr = th.App.PermanentDeletePost(th.Context, post.Id, userID)
require.Nil(t, appErr)
var received *model.WebSocketEvent
select {
case received = <-wsMessages:
// the post sent in websocket payload shouldn't contain message or file IDs
data := received.GetData()
postJSON, ok := data["post"].(string)
require.True(t, ok)
var receivedPost model.Post
err := json.Unmarshal([]byte(postJSON), &receivedPost)
require.NoError(t, err)
require.Equal(t, post.Id, receivedPost.Id)
require.Equal(t, "", receivedPost.Message)
require.Equal(t, 0, len(receivedPost.FileIds))
case <-time.After(10 * time.Second):
require.Fail(t, "Did not receive websocket message in time")
}
})
}
func TestSendTestMessage(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
t.Run("Should create the post with the correct prop", func(t *testing.T) {
post, result := th.App.SendTestMessage(th.Context, th.BasicUser.Id)
assert.Nil(t, result)
assert.NotEmpty(t, post.GetProp(model.PostPropsForceNotification))
})
}
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
func TestPopulateEditHistoryFileMetadata(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
t.Run("should populate file metadata for all posts", func(t *testing.T) {
fileInfo1, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
fileInfo2, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
post1 := th.CreatePost(t, th.BasicChannel, func(post *model.Post) {
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
post.FileIds = model.StringArray{fileInfo1.Id}
})
post2 := th.CreatePost(t, th.BasicChannel, func(post *model.Post) {
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
post.FileIds = model.StringArray{fileInfo2.Id}
})
appErr := th.App.populateEditHistoryFileMetadata([]*model.Post{post1, post2})
require.Nil(t, appErr)
require.Len(t, post1.Metadata.Files, 1)
require.Equal(t, fileInfo1.Id, post1.Metadata.Files[0].Id)
require.Len(t, post2.Metadata.Files, 1)
require.Equal(t, fileInfo2.Id, post2.Metadata.Files[0].Id)
})
t.Run("should populate file metadata even for deleted posts", func(t *testing.T) {
fileInfo1, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
fileInfo2, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
post1 := th.CreatePost(t, th.BasicChannel, func(post *model.Post) {
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
post.FileIds = model.StringArray{fileInfo1.Id}
})
post2 := th.CreatePost(t, th.BasicChannel, func(post *model.Post) {
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
post.FileIds = model.StringArray{fileInfo2.Id}
})
_, appErr := th.App.DeletePost(th.Context, post1.Id, th.BasicUser.Id)
require.Nil(t, appErr)
_, appErr = th.App.DeletePost(th.Context, post2.Id, th.BasicUser.Id)
require.Nil(t, appErr)
appErr = th.App.populateEditHistoryFileMetadata([]*model.Post{post1, post2})
require.Nil(t, appErr)
require.Len(t, post1.Metadata.Files, 1)
require.Equal(t, fileInfo1.Id, post1.Metadata.Files[0].Id)
require.Len(t, post2.Metadata.Files, 1)
require.Equal(t, fileInfo2.Id, post2.Metadata.Files[0].Id)
})
t.Run("should populate file metadata even for deleted fileInfos", func(t *testing.T) {
fileInfo1, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
fileInfo2, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
post1 := th.CreatePost(t, th.BasicChannel, func(post *model.Post) {
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
post.FileIds = model.StringArray{fileInfo1.Id}
})
post2 := th.CreatePost(t, th.BasicChannel, func(post *model.Post) {
Feature edit attachments (#29769) * Updated patch/update post API to allow file modification (#29447) * WIP * WIP * Atatched new files ton post * WIP: deleting removed files * Deleted removed files and invalidated file metadata cache * removed file ignore logif from update post API * Added TestFindExclusives * Added tests for DeleteForPostByIds * Added app layer tests * Added tests * Added API level tests * test enhancements * Fixed a test * Edit history include file metadata (#29505) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * removed commented code * Improved test helper * Show attachments in edit history RHS (#29519) * Send file metadata in edit history metadata * Added app tests * Added store tests * Added tests for populateEditHistoryFileMetadata{ * Added cache to avoid repetitigve DB calls for edits with only message changes * Added API tests * i18m fix * WIUP: displa files in edit * removed commented code * Displayed file in edit history * Handled file icon * Fixed closing history component on clicking on file * Simplified selector * Simplified selector * Improved test helper * Disabled action menu on edit history file * Added tests * Improved selector * Updated snapshot * review Fixes * restructured componnets * Updated test * Updated test * Restore post api (#29643) * Restore post version API WIP * Undelete files WIP * Added store tests * Created post restore API * Updated updatepost safeUpdate signature * review fixex and improvements * Fixed an app test * Added API laer tests * Added API tests and OpenAPI specs * Fixed a typo * Allow editing files when editing posts (#29709) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * CI * Added doc * Restore post api integration (#29719) * WIP - basic view files when editing post * Cleanup * bg color * Added text editor tests for files * WIP * WIP * removed debug log * Allowed admin to add and remove files on someone else's post * Handled drafts and scheduled posts * linter fixes * Updated snapshot * server test fix * Used new API to restore post * handled edut limit and undo * lint fix * added comments * Fixed edit post item tests * Fixed buttons * Aded snapshots * fix test * Updated snapshot * Minor fixes * fixed snapshot * Edit file dnd area (#29763) * dnd wip * DND continued * Supported multiple unbind dragster funcs * lint fixes * Got center channel file drop working when editing a post * file dnd working with center channel and rhs * file dnd working with center channel and rhs * removed unneeded stopPropogation calls * cleanup * DND overlay fix * Lint fix * Advanced text editor test updates for file upload overlay * fixed use upload hook tests * Updated some more snapshots * minor cleanup * Updated i18n * removed need of array for dragster unbind events * lint fixes * edit history cursor * Fixed bugu causing faliure to delete empty posts (#29778) * Files in restore confirmation (#29781) * Added files to restore post confirmation dialog * Fixed post restore toast colors * Fixed restore bug * Fixed restore confirmation toast tests * a11y improvement and modal width fix * Edit attachment misc fixes (#29808) * Removed single image actions in restore post confirmation dialog * Fixed file drop overlay size and position * Made edit indiator accessible * Lint fix * Added bunch of more tests * ANother test migrated from enzyme to react testing library * More test enhancements * More test enhancements * More test enhancements * lint fixes * Fixed a test * Added missing snapshots * Test fixes
2025-01-13 07:46:56 -05:00
post.FileIds = model.StringArray{fileInfo2.Id}
})
_, err = th.App.Srv().Store().FileInfo().DeleteForPost(th.Context, post1.Id)
require.NoError(t, err)
_, err = th.App.Srv().Store().FileInfo().DeleteForPost(th.Context, post2.Id)
require.NoError(t, err)
appErr := th.App.populateEditHistoryFileMetadata([]*model.Post{post1, post2})
require.Nil(t, appErr)
require.Len(t, post1.Metadata.Files, 1)
require.Equal(t, fileInfo1.Id, post1.Metadata.Files[0].Id)
require.Greater(t, post1.Metadata.Files[0].DeleteAt, int64(0))
require.Len(t, post2.Metadata.Files, 1)
require.Equal(t, fileInfo2.Id, post2.Metadata.Files[0].Id)
require.Greater(t, post2.Metadata.Files[0].DeleteAt, int64(0))
})
}
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
func TestFilterPostsByChannelPermissions(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.GuestAccountsSettings.Enable = true
})
guestUser := th.CreateGuest(t)
_, _, appErr := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, guestUser.Id, "")
require.Nil(t, appErr)
privateChannel := th.CreatePrivateChannel(t, th.BasicTeam)
_, appErr = th.App.AddUserToChannel(th.Context, guestUser, privateChannel, false)
require.Nil(t, appErr)
_, appErr = th.App.AddUserToChannel(th.Context, guestUser, th.BasicChannel, false)
require.Nil(t, appErr)
post1 := th.CreatePost(t, th.BasicChannel)
post2 := th.CreatePost(t, privateChannel)
post3 := th.CreatePost(t, th.BasicChannel)
t.Run("should filter posts when user has read_channel_content permission", func(t *testing.T) {
postList := model.NewPostList()
postList.Posts[post1.Id] = post1
postList.Posts[post2.Id] = post2
postList.Posts[post3.Id] = post3
postList.Order = []string{post1.Id, post2.Id, post3.Id}
allPostHaveMembership, appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id)
require.Nil(t, appErr)
require.Len(t, postList.Posts, 3)
require.Len(t, postList.Order, 3)
require.True(t, allPostHaveMembership)
})
t.Run("should filter posts when guest has read_channel_content permission", func(t *testing.T) {
postList := model.NewPostList()
postList.Posts[post1.Id] = post1
postList.Posts[post2.Id] = post2
postList.Posts[post3.Id] = post3
postList.Order = []string{post1.Id, post2.Id, post3.Id}
allPostHaveMembership, appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, guestUser.Id)
require.Nil(t, appErr)
require.Len(t, postList.Posts, 3)
require.Len(t, postList.Order, 3)
require.True(t, allPostHaveMembership)
})
t.Run("should filter posts when guest does not have read_channel_content permission", func(t *testing.T) {
channelGuestRole, appErr := th.App.GetRoleByName(th.Context, model.ChannelGuestRoleId)
require.Nil(t, appErr)
originalPermissions := make([]string, len(channelGuestRole.Permissions))
copy(originalPermissions, channelGuestRole.Permissions)
newPermissions := []string{}
for _, perm := range channelGuestRole.Permissions {
if perm != model.PermissionReadChannelContent.Id && perm != model.PermissionReadChannel.Id {
newPermissions = append(newPermissions, perm)
}
}
_, appErr = th.App.PatchRole(channelGuestRole, &model.RolePatch{
Permissions: &newPermissions,
})
require.Nil(t, appErr)
defer func() {
_, err := th.App.PatchRole(channelGuestRole, &model.RolePatch{
Permissions: &originalPermissions,
})
require.Nil(t, err)
}()
postList := model.NewPostList()
postList.Posts[post1.Id] = post1
postList.Posts[post2.Id] = post2
postList.Posts[post3.Id] = post3
postList.Order = []string{post1.Id, post2.Id, post3.Id}
allPostHaveMembership, appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, guestUser.Id)
require.Nil(t, appErr)
require.Len(t, postList.Posts, 0)
require.Len(t, postList.Order, 0)
require.True(t, allPostHaveMembership)
})
t.Run("should handle empty post list", func(t *testing.T) {
postList := model.NewPostList()
allPostHaveMembership, appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id)
require.Nil(t, appErr)
require.Len(t, postList.Posts, 0)
require.Len(t, postList.Order, 0)
require.True(t, allPostHaveMembership)
})
t.Run("should handle nil post list", func(t *testing.T) {
_, appErr := th.App.FilterPostsByChannelPermissions(th.Context, nil, th.BasicUser.Id)
require.Nil(t, appErr)
})
t.Run("should handle posts with empty channel IDs", func(t *testing.T) {
postList := model.NewPostList()
postWithoutChannel := &model.Post{
Id: model.NewId(),
ChannelId: "",
Message: "test",
}
postList.Posts[postWithoutChannel.Id] = postWithoutChannel
postList.Order = []string{postWithoutChannel.Id}
allPostHaveMembership, appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id)
require.Nil(t, appErr)
require.Len(t, postList.Posts, 0)
require.Len(t, postList.Order, 0)
require.True(t, allPostHaveMembership)
})
t.Run("should handle posts from non-existent channels", func(t *testing.T) {
postList := model.NewPostList()
postWithInvalidChannel := &model.Post{
Id: model.NewId(),
ChannelId: model.NewId(),
Message: "test",
}
postList.Posts[postWithInvalidChannel.Id] = postWithInvalidChannel
postList.Order = []string{postWithInvalidChannel.Id}
allPostHaveMembership, appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id)
require.Nil(t, appErr)
require.Len(t, postList.Posts, 0)
require.Len(t, postList.Order, 0)
require.True(t, allPostHaveMembership)
})
}
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
func TestRevealPost(t *testing.T) {
th := Setup(t).InitBasic(t)
ci: enable fullyparallel mode for server tests (#35816) * ci: enable fullyparallel mode for server tests Replace os.Setenv, os.Chdir, and global state mutations with parallel-safe alternatives (t.Setenv, t.Chdir, test hooks) across 37 files. Refactor GetLogRootPath and MM_INSTALL_TYPE to use package-level test hooks instead of environment variables. This enables gotestsum --fullparallel, allowing all test packages to run with maximum parallelism within each shard. Co-authored-by: Claude <claude@anthropic.com> * ci: split fullyparallel from continue-on-error in workflow template - Add new boolean input 'allow-failure' separate from 'fullyparallel' - Change continue-on-error to use allow-failure instead of fullyparallel - Update server-ci.yml to pass allow-failure: true for test coverage job - Allows independent control of parallel execution and failure tolerance Co-authored-by: Claude <claude@anthropic.com> * fix: protect TestOverrideLogRootPath with sync.Mutex for parallel tests - Replace global var TestOverrideLogRootPath with mutex-protected functions - Add SetTestOverrideLogRootPath() and getTestOverrideLogRootPath() functions - Update GetLogRootPath() to use thread-safe getter - Update all test files to use SetTestOverrideLogRootPath() with t.Cleanup() - Fixes race condition when running tests with t.Parallel() Co-authored-by: Claude <claude@anthropic.com> * fix: configure audit settings before server setup in tests - Move ExperimentalAuditSettings from UpdateConfig() to config defaults - Pass audit config via app.Config() option in SetupWithServerOptions() - Fixes audit test setup ordering to configure BEFORE server initialization - Resolves CodeRabbit's audit config timing issue in api4 tests Co-authored-by: Claude <claude@anthropic.com> * fix: implement SetTestOverrideLogRootPath mutex in logger.go The previous commit updated test callers to use SetTestOverrideLogRootPath() but didn't actually create the function in config/logger.go, causing build failures across all CI shards. This commit: - Replaces the exported var TestOverrideLogRootPath with mutex-protected unexported state (testOverrideLogRootPath + testOverrideLogRootMu) - Adds exported SetTestOverrideLogRootPath() setter - Adds unexported getTestOverrideLogRootPath() getter - Updates GetLogRootPath() to use the thread-safe getter - Fixes log_test.go callers that were missed in the previous commit Co-authored-by: Claude <claude@anthropic.com> * fix(test): use SetupConfig for access_control feature flag registration InitAccessControlPolicy() checks FeatureFlags.AttributeBasedAccessControl at route registration time during server startup. Setting the flag via UpdateConfig after Setup() is too late — routes are never registered and API calls return 404. Use SetupConfig() to pass the feature flag in the initial config before server startup, ensuring routes are properly registered. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore BurnOnRead flag state in TestRevealPost subtest The 'feature not enabled' subtest disables BurnOnRead without restoring it via t.Cleanup. Subsequent subtests inherit the disabled state, which can cause 501 errors when they expect the feature to be available. Add t.Cleanup to restore FeatureFlags.BurnOnRead = true after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore EnableSharedChannelsMemberSync flag via t.Cleanup The test disables EnableSharedChannelsMemberSync without restoring it. If the subtest exits early (e.g., require failure), later sibling subtests inherit a disabled flag and become flaky. Add t.Cleanup to restore the flag after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * Fix test parallelism: use instance-scoped overrides and init-time audit config Replace package-level test globals (TestOverrideInstallType, SetTestOverrideLogRootPath) with fields on PlatformService so each test gets its own instance without process-wide mutation. Fix three audit tests (TestUserLoginAudit, TestLogoutAuditAuthStatus, TestUpdatePasswordAudit) that configured the audit logger after server init — the audit logger only reads config at startup, so pass audit settings via app.Config() at init time instead. Also revert the Go 1.24.13 downgrade and bump mattermost-govet to v2.0.2 for Go 1.25.8 compatibility. * Fix audit unit tests * Fix MMCLOUDURL unit tests * Fixed unit tests using MM_NOTIFY_ADMIN_COOL_OFF_DAYS * Make app migrations idempotent for parallel test safety Change System().Save() to System().SaveOrUpdate() in all migration completion markers. When two parallel tests share a database pool entry, both may race through the check-then-insert migration pattern. Save() causes a duplicate key fatal crash; SaveOrUpdate() makes the second write a harmless no-op. * test: address review feedback on fullyparallel PR - Use SetLogRootPathOverride() setter instead of direct field access in platform/support_packet_test.go and platform/log_test.go (pvev) - Restore TestGetLogRootPath in config/logger_test.go to keep MM_LOG_PATH env var coverage; test uses t.Setenv so it runs serially which is fine (pvev) - Fix misleading comment in config_test.go: code uses t.Setenv, not os.Setenv (jgheithcock) Co-authored-by: Claude <claude@anthropic.com> * fix: add missing os import in post_test.go The os import was dropped during a merge conflict resolution while burn-on-read shared channel tests from master still use os.Setenv. Co-authored-by: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: wiggin77 <wiggin77@warpmail.net> Co-authored-by: Mattermost Build <build@mattermost.com>
2026-04-08 20:48:36 -04:00
// Enable BurnOnRead feature flag
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.BurnOnRead = true })
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
// Helper to create a burn-on-read post
createBurnOnReadPost := func() *model.Post {
post := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "burn on read message",
Type: model.PostTypeBurnOnRead,
}
post.AddProp(model.PostPropsExpireAt, model.GetMillis()+int64(model.DefaultExpirySeconds*1000))
createdPost, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{})
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
require.Nil(t, appErr)
require.NotNil(t, createdPost)
return createdPost
}
// Helper to create a regular post
createRegularPost := func() *model.Post {
post := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "regular message",
}
createdPost, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{})
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
require.Nil(t, appErr)
require.NotNil(t, createdPost)
return createdPost
}
// Create a second user for testing
user2 := th.CreateUser(t)
th.LinkUserToTeam(t, user2, th.BasicTeam)
th.AddUserToChannel(t, user2, th.BasicChannel)
t.Run("post type non burn on read", func(t *testing.T) {
regularPost := createRegularPost()
revealedPost, appErr := th.App.RevealPost(th.Context, regularPost, user2.Id, "")
require.NotNil(t, appErr)
require.Nil(t, revealedPost)
require.Equal(t, "app.reveal_post.not_burn_on_read.app_error", appErr.Id)
require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
})
t.Run("post doesn't have required prop", func(t *testing.T) {
enableBoRFeature(th)
// Create a burn-on-read post without expire_at prop
post := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "burn on read message",
Type: model.PostTypeBurnOnRead,
}
// First save the post normally (which will add expire_at automatically)
createdPost, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{})
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
require.Nil(t, appErr)
// Now manually remove the expire_at prop to test missing prop scenario
createdPost.SetProps(make(model.StringInterface))
revealedPost, appErr := th.App.RevealPost(th.Context, createdPost, user2.Id, "")
require.NotNil(t, appErr)
require.Nil(t, revealedPost)
require.Equal(t, "app.reveal_post.missing_expire_at.app_error", appErr.Id)
require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
})
t.Run("post with invalid expire_at prop type", func(t *testing.T) {
enableBoRFeature(th)
post := createBurnOnReadPost()
// Manually set invalid expire_at type
post.SetProps(make(model.StringInterface))
post.AddProp(model.PostPropsExpireAt, "invalid_string")
revealedPost, appErr := th.App.RevealPost(th.Context, post, user2.Id, "")
require.NotNil(t, appErr)
require.Nil(t, revealedPost)
require.Equal(t, "app.reveal_post.missing_expire_at.app_error", appErr.Id)
})
t.Run("post with zero expire_at", func(t *testing.T) {
enableBoRFeature(th)
post := createBurnOnReadPost()
// Manually set zero expire_at
post.SetProps(make(model.StringInterface))
post.AddProp(model.PostPropsExpireAt, float64(0))
revealedPost, appErr := th.App.RevealPost(th.Context, post, user2.Id, "")
require.NotNil(t, appErr)
require.Nil(t, revealedPost)
require.Equal(t, "app.reveal_post.missing_expire_at.app_error", appErr.Id)
})
t.Run("read receipt does not exist", func(t *testing.T) {
enableBoRFeature(th)
post := createBurnOnReadPost()
revealedPost, appErr := th.App.RevealPost(th.Context, post, user2.Id, "")
require.Nil(t, appErr)
require.NotNil(t, revealedPost)
require.Equal(t, "burn on read message", revealedPost.Message)
require.NotNil(t, revealedPost.Metadata)
require.NotZero(t, revealedPost.Metadata.ExpireAt)
// Verify read receipt was created
receipt, err := th.App.Srv().Store().ReadReceipt().Get(th.Context, post.Id, user2.Id)
require.NoError(t, err)
require.NotNil(t, receipt)
require.Equal(t, post.Id, receipt.PostID)
require.Equal(t, user2.Id, receipt.UserID)
require.Equal(t, revealedPost.Metadata.ExpireAt, receipt.ExpireAt)
})
t.Run("read receipt exists and not expired", func(t *testing.T) {
enableBoRFeature(th)
post := createBurnOnReadPost()
// First reveal to create receipt
revealedPost1, appErr := th.App.RevealPost(th.Context, post, user2.Id, "")
require.Nil(t, appErr)
require.NotNil(t, revealedPost1)
require.NotZero(t, revealedPost1.Metadata.ExpireAt)
// Reveal again - should succeed and return the same post
revealedPost2, appErr := th.App.RevealPost(th.Context, post, user2.Id, "")
require.Nil(t, appErr)
require.NotNil(t, revealedPost2)
require.Equal(t, "burn on read message", revealedPost2.Message)
require.NotNil(t, revealedPost2.Metadata)
require.Equal(t, revealedPost1.Metadata.ExpireAt, revealedPost2.Metadata.ExpireAt)
})
t.Run("read receipt exists but expired", func(t *testing.T) {
enableBoRFeature(th)
post := createBurnOnReadPost()
// Create an expired read receipt
expiredReceipt := &model.ReadReceipt{
UserID: user2.Id,
PostID: post.Id,
ExpireAt: model.GetMillis() - 1000, // Expired 1 second ago
}
_, err := th.App.Srv().Store().ReadReceipt().Save(th.Context, expiredReceipt)
require.NoError(t, err)
revealedPost, appErr := th.App.RevealPost(th.Context, post, user2.Id, "")
require.NotNil(t, appErr)
require.Nil(t, revealedPost)
require.Equal(t, "app.reveal_post.read_receipt_expired.error", appErr.Id)
require.Equal(t, http.StatusForbidden, appErr.StatusCode)
})
t.Run("post expired", func(t *testing.T) {
post := createBurnOnReadPost()
// Manually set expired expire_at
post.SetProps(make(model.StringInterface))
post.AddProp(model.PostPropsExpireAt, model.GetMillis()-1000)
revealedPost, appErr := th.App.RevealPost(th.Context, post, user2.Id, "")
require.NotNil(t, appErr)
require.Nil(t, revealedPost)
require.Equal(t, "app.reveal_post.post_expired.app_error", appErr.Id)
require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
})
t.Run("revealed post preserves existing metadata", func(t *testing.T) {
enableBoRFeature(th)
fileBytes := []byte("test")
fileInfo, err := th.App.UploadFile(th.Context, fileBytes, th.BasicChannel.Id, "file.txt")
require.Nil(t, err)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "burn on read message",
Type: model.PostTypeBurnOnRead,
FileIds: model.StringArray{fileInfo.Id},
}
post.AddProp(model.PostPropsExpireAt, model.GetMillis()+int64(model.DefaultExpirySeconds*1000))
createdPost, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{})
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
require.Nil(t, appErr)
require.NotNil(t, createdPost)
revealedPost, appErr := th.App.RevealPost(th.Context, createdPost, user2.Id, "")
require.Nil(t, appErr)
require.NotNil(t, revealedPost)
require.NotNil(t, revealedPost.Metadata)
require.NotZero(t, revealedPost.Metadata.ExpireAt)
require.Len(t, revealedPost.Metadata.Files, 1)
})
t.Run("updateTemporaryPostIfAllRead bypasses cache to get fresh data", func(t *testing.T) {
enableBoRFeature(th)
// Create a third user to have multiple recipients
user3 := th.CreateUser(t)
th.LinkUserToTeam(t, user3, th.BasicTeam)
th.AddUserToChannel(t, user3, th.BasicChannel)
post := createBurnOnReadPost()
// Get initial TemporaryPost ExpireAt (should be max TTL - 24 hours)
tmpPostInitial, err := th.App.Srv().Store().TemporaryPost().Get(th.Context, post.Id, true)
require.NoError(t, err)
initialExpireAt := tmpPostInitial.ExpireAt
// First reveal by user2 creates a read receipt
_, appErr := th.App.RevealPost(th.Context, post, user2.Id, "")
require.Nil(t, appErr)
// Get the read receipt's ExpireAt (should be 5 minutes)
receipt, err := th.App.Srv().Store().ReadReceipt().Get(th.Context, post.Id, user2.Id)
require.NoError(t, err)
// Verify receipt ExpireAt is less than initial tmpPost ExpireAt (5 minutes vs 24 hours)
require.Less(t, receipt.ExpireAt, initialExpireAt)
// At this point, user3 hasn't revealed yet, so TemporaryPost shouldn't be updated
tmpPostAfterFirstReveal, err := th.App.Srv().Store().TemporaryPost().Get(th.Context, post.Id, false)
require.NoError(t, err)
require.Equal(t, initialExpireAt, tmpPostAfterFirstReveal.ExpireAt)
// Now user3 reveals - this should trigger updateTemporaryPostIfAllRead
_, appErr = th.App.RevealPost(th.Context, post, user3.Id, "")
require.Nil(t, appErr)
// Fetch the TemporaryPost with cache bypass to verify it was updated
tmpPostAfterAllReveal, err := th.App.Srv().Store().TemporaryPost().Get(th.Context, post.Id, false)
require.NoError(t, err)
// Verify the ExpireAt was updated to match the shortest receipt ExpireAt
require.Less(t, tmpPostAfterAllReveal.ExpireAt, initialExpireAt)
// Should be close to the first recipient's ExpireAt (within a few ms due to timing)
require.InDelta(t, receipt.ExpireAt, tmpPostAfterAllReveal.ExpireAt, 10000) // 10 second tolerance
})
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
}
func TestBurnPost(t *testing.T) {
th := Setup(t).InitBasic(t)
ci: enable fullyparallel mode for server tests (#35816) * ci: enable fullyparallel mode for server tests Replace os.Setenv, os.Chdir, and global state mutations with parallel-safe alternatives (t.Setenv, t.Chdir, test hooks) across 37 files. Refactor GetLogRootPath and MM_INSTALL_TYPE to use package-level test hooks instead of environment variables. This enables gotestsum --fullparallel, allowing all test packages to run with maximum parallelism within each shard. Co-authored-by: Claude <claude@anthropic.com> * ci: split fullyparallel from continue-on-error in workflow template - Add new boolean input 'allow-failure' separate from 'fullyparallel' - Change continue-on-error to use allow-failure instead of fullyparallel - Update server-ci.yml to pass allow-failure: true for test coverage job - Allows independent control of parallel execution and failure tolerance Co-authored-by: Claude <claude@anthropic.com> * fix: protect TestOverrideLogRootPath with sync.Mutex for parallel tests - Replace global var TestOverrideLogRootPath with mutex-protected functions - Add SetTestOverrideLogRootPath() and getTestOverrideLogRootPath() functions - Update GetLogRootPath() to use thread-safe getter - Update all test files to use SetTestOverrideLogRootPath() with t.Cleanup() - Fixes race condition when running tests with t.Parallel() Co-authored-by: Claude <claude@anthropic.com> * fix: configure audit settings before server setup in tests - Move ExperimentalAuditSettings from UpdateConfig() to config defaults - Pass audit config via app.Config() option in SetupWithServerOptions() - Fixes audit test setup ordering to configure BEFORE server initialization - Resolves CodeRabbit's audit config timing issue in api4 tests Co-authored-by: Claude <claude@anthropic.com> * fix: implement SetTestOverrideLogRootPath mutex in logger.go The previous commit updated test callers to use SetTestOverrideLogRootPath() but didn't actually create the function in config/logger.go, causing build failures across all CI shards. This commit: - Replaces the exported var TestOverrideLogRootPath with mutex-protected unexported state (testOverrideLogRootPath + testOverrideLogRootMu) - Adds exported SetTestOverrideLogRootPath() setter - Adds unexported getTestOverrideLogRootPath() getter - Updates GetLogRootPath() to use the thread-safe getter - Fixes log_test.go callers that were missed in the previous commit Co-authored-by: Claude <claude@anthropic.com> * fix(test): use SetupConfig for access_control feature flag registration InitAccessControlPolicy() checks FeatureFlags.AttributeBasedAccessControl at route registration time during server startup. Setting the flag via UpdateConfig after Setup() is too late — routes are never registered and API calls return 404. Use SetupConfig() to pass the feature flag in the initial config before server startup, ensuring routes are properly registered. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore BurnOnRead flag state in TestRevealPost subtest The 'feature not enabled' subtest disables BurnOnRead without restoring it via t.Cleanup. Subsequent subtests inherit the disabled state, which can cause 501 errors when they expect the feature to be available. Add t.Cleanup to restore FeatureFlags.BurnOnRead = true after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore EnableSharedChannelsMemberSync flag via t.Cleanup The test disables EnableSharedChannelsMemberSync without restoring it. If the subtest exits early (e.g., require failure), later sibling subtests inherit a disabled flag and become flaky. Add t.Cleanup to restore the flag after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * Fix test parallelism: use instance-scoped overrides and init-time audit config Replace package-level test globals (TestOverrideInstallType, SetTestOverrideLogRootPath) with fields on PlatformService so each test gets its own instance without process-wide mutation. Fix three audit tests (TestUserLoginAudit, TestLogoutAuditAuthStatus, TestUpdatePasswordAudit) that configured the audit logger after server init — the audit logger only reads config at startup, so pass audit settings via app.Config() at init time instead. Also revert the Go 1.24.13 downgrade and bump mattermost-govet to v2.0.2 for Go 1.25.8 compatibility. * Fix audit unit tests * Fix MMCLOUDURL unit tests * Fixed unit tests using MM_NOTIFY_ADMIN_COOL_OFF_DAYS * Make app migrations idempotent for parallel test safety Change System().Save() to System().SaveOrUpdate() in all migration completion markers. When two parallel tests share a database pool entry, both may race through the check-then-insert migration pattern. Save() causes a duplicate key fatal crash; SaveOrUpdate() makes the second write a harmless no-op. * test: address review feedback on fullyparallel PR - Use SetLogRootPathOverride() setter instead of direct field access in platform/support_packet_test.go and platform/log_test.go (pvev) - Restore TestGetLogRootPath in config/logger_test.go to keep MM_LOG_PATH env var coverage; test uses t.Setenv so it runs serially which is fine (pvev) - Fix misleading comment in config_test.go: code uses t.Setenv, not os.Setenv (jgheithcock) Co-authored-by: Claude <claude@anthropic.com> * fix: add missing os import in post_test.go The os import was dropped during a merge conflict resolution while burn-on-read shared channel tests from master still use os.Setenv. Co-authored-by: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: wiggin77 <wiggin77@warpmail.net> Co-authored-by: Mattermost Build <build@mattermost.com>
2026-04-08 20:48:36 -04:00
// Enable BurnOnRead feature flag
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.BurnOnRead = true })
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
// feature flag, configuration and license is not checked for this feature
// so we set these to enable the feature to create a burn on read post
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.ServiceSettings.EnableBurnOnRead = model.NewPointer(true)
})
th.AddUserToChannel(t, th.BasicUser, th.BasicChannel) // author of the post
th.AddUserToChannel(t, th.BasicUser2, th.BasicChannel) // recipient of the post
// Helper to create a burn-on-read post
createBurnOnReadPost := func() *model.Post {
post := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "burn on read message",
Type: model.PostTypeBurnOnRead,
}
post.AddProp(model.PostPropsExpireAt, model.GetMillis()+int64(model.DefaultExpirySeconds*1000))
createdPost, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{})
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
require.Nil(t, appErr)
require.NotNil(t, createdPost)
return createdPost
}
// Helper to create a regular post
createRegularPost := func() *model.Post {
post := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "regular message",
}
createdPost, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{})
[MM-61758] Burn on read feature (#34703) * Add read receipt store for burn on read message types * update mocks * fix invalidation target * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * add translations * Added logic to associate files of BoR post with the post * Added test * fixes * disable pinning posts and review comments * MM-66594 - Burn on read UI integration (#34647) * MM-66244 - add BoR visual components to message editor * MM-66246 - BoR visual indicator for sender and receiver * MM-66607 - bor - add timer countdown and autodeletion * add the system console max time to live config * use the max expire at and create global scheduler to register bor messages * use seconds for BoR config values in BE * implement the read by text shown in the tooltip logic * unestack the posts from same receiver and BoR and fix styling * avoid opening reply RHS * remove unused dispatchers * persis the BoR label in the drafts * move expiration value to metadata * adjust unit tests to metadata insted of props * code clean up and some performance improvements; add period grace for deletion too * adjust migration serie number * hide bor messages when config is off * performance improvements on post component and code clean up * keep bor existing post functionality if config is disabled * Add read receipt store for burn on read message types * Add temporary posts table * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * avoid reacting to unrevealed bor messages * adjust migration number * Add read receipt store for burn on read message types * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * adjust post reveal and type with backend changes * use real config values, adjust icon usage and style * adjust the delete from from sender and receiver * improve self deleting logic by placing in badge, use burn endpoint * adjust websocket events handling for the read by sender label information * adjust styling for concealed and error state * update burn-on-read post event handling for improved recipient tracking and multi-device sync * replace burn_on_read with type in database migrations and model * remove burn_on_read metadata from PostMetadata and related structures * Added logic to associate files of BoR post with the post * Added test * adjust migration name and fix linter * Add read receipt store for burn on read message types * update mocks * have consistent case on index creation * Add temporary posts table * add mock * add transaction support * reflect review comments * wip: Add reveal endpoint * user check error id instead * wip: Add ws events and cleanup for burn on read posts * add burn endpoint for explicitly burning messages * Added logic to associate files of BoR post with the post * Added test * disable pinning posts and review comments * show attachment on bor reveal * remove unused translation * Enhance burn-on-read post handling and refine previous post ID retrieval logic * adjust the returning chunk to work with bor messages * read temp post from master db * read from master * show the copy link button to the sender * revert unnecessary check * restore correct json tag * remove unused error handling and clarify burn-on-read comment * improve type safety and use proper selectors * eliminate code duplication in deletion handler * optimize performance and add documentation * delete bor message for sender once all receivers reveal it * add burn on read to scheduled posts * add feature enable check * use master to avoid all read recipients race condition --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> * squash migrations into single file * add configuration for the scheduler * don't run messagehasbeenposted hook * remove parallel tests on burn on read * add clean up for closing opened modals from previous tests * simplify delete menu item rendering * add cleanup step to close open modals after each test to prevent pollution * streamline delete button visibility logic for Burn on Read posts * improve reliability of closing post menu and modals by using body ESC key --------- Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Pablo Vélez <pablovv2012@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
2025-12-11 01:59:50 -05:00
require.Nil(t, appErr)
require.NotNil(t, createdPost)
return createdPost
}
t.Run("burn on read post", func(t *testing.T) {
post := createBurnOnReadPost()
appErr := th.App.BurnPost(th.Context, post, th.BasicUser.Id, "")
require.Nil(t, appErr)
// Verify post is deleted
post, err := th.App.Srv().Store().Post().GetSingle(th.Context, post.Id, false)
require.Error(t, err)
require.Nil(t, post)
require.True(t, store.IsErrNotFound(err))
})
t.Run("regular post", func(t *testing.T) {
post := createRegularPost()
appErr := th.App.BurnPost(th.Context, post, th.BasicUser.Id, "")
require.NotNil(t, appErr)
require.Equal(t, "app.burn_post.not_burn_on_read.app_error", appErr.Id)
})
t.Run("read receipt does not exist", func(t *testing.T) {
post := createBurnOnReadPost()
appErr := th.App.BurnPost(th.Context, post, th.BasicUser2.Id, "")
require.NotNil(t, appErr)
require.Equal(t, "app.burn_post.not_revealed.app_error", appErr.Id)
})
t.Run("read receipt exists but expired", func(t *testing.T) {
post := createBurnOnReadPost()
// Create an expired read receipt
expiredTime := model.GetMillis() - 1000 // Expired 1 second ago
expiredReceipt := &model.ReadReceipt{
UserID: th.BasicUser2.Id,
PostID: post.Id,
ExpireAt: expiredTime,
}
_, err := th.App.Srv().Store().ReadReceipt().Save(th.Context, expiredReceipt)
require.NoError(t, err)
appErr := th.App.BurnPost(th.Context, post, th.BasicUser2.Id, "")
require.Nil(t, appErr) // this is a no-op
// Verify receipt ExpireAt is unchanged (no-op)
receipt, err := th.App.Srv().Store().ReadReceipt().Get(th.Context, post.Id, th.BasicUser2.Id)
require.NoError(t, err)
require.Equal(t, expiredTime, receipt.ExpireAt)
})
t.Run("read receipt exists and not expired", func(t *testing.T) {
post := createBurnOnReadPost()
// Create a read receipt that is not expired
notExpiredReceipt := &model.ReadReceipt{
UserID: th.BasicUser2.Id,
PostID: post.Id,
ExpireAt: model.GetMillis() + 10000, // Not expired 10 seconds from now
}
_, err := th.App.Srv().Store().ReadReceipt().Save(th.Context, notExpiredReceipt)
require.NoError(t, err)
appErr := th.App.BurnPost(th.Context, post, th.BasicUser2.Id, "")
require.Nil(t, appErr)
// Verify receipt ExpireAt is updated to current time
receipt, err := th.App.Srv().Store().ReadReceipt().Get(th.Context, post.Id, th.BasicUser2.Id)
require.NoError(t, err)
require.LessOrEqual(t, receipt.ExpireAt, model.GetMillis())
})
}
func TestGetFlaggedPostsWithExpiredBurnOnRead(t *testing.T) {
th := Setup(t).InitBasic(t)
ci: enable fullyparallel mode for server tests (#35816) * ci: enable fullyparallel mode for server tests Replace os.Setenv, os.Chdir, and global state mutations with parallel-safe alternatives (t.Setenv, t.Chdir, test hooks) across 37 files. Refactor GetLogRootPath and MM_INSTALL_TYPE to use package-level test hooks instead of environment variables. This enables gotestsum --fullparallel, allowing all test packages to run with maximum parallelism within each shard. Co-authored-by: Claude <claude@anthropic.com> * ci: split fullyparallel from continue-on-error in workflow template - Add new boolean input 'allow-failure' separate from 'fullyparallel' - Change continue-on-error to use allow-failure instead of fullyparallel - Update server-ci.yml to pass allow-failure: true for test coverage job - Allows independent control of parallel execution and failure tolerance Co-authored-by: Claude <claude@anthropic.com> * fix: protect TestOverrideLogRootPath with sync.Mutex for parallel tests - Replace global var TestOverrideLogRootPath with mutex-protected functions - Add SetTestOverrideLogRootPath() and getTestOverrideLogRootPath() functions - Update GetLogRootPath() to use thread-safe getter - Update all test files to use SetTestOverrideLogRootPath() with t.Cleanup() - Fixes race condition when running tests with t.Parallel() Co-authored-by: Claude <claude@anthropic.com> * fix: configure audit settings before server setup in tests - Move ExperimentalAuditSettings from UpdateConfig() to config defaults - Pass audit config via app.Config() option in SetupWithServerOptions() - Fixes audit test setup ordering to configure BEFORE server initialization - Resolves CodeRabbit's audit config timing issue in api4 tests Co-authored-by: Claude <claude@anthropic.com> * fix: implement SetTestOverrideLogRootPath mutex in logger.go The previous commit updated test callers to use SetTestOverrideLogRootPath() but didn't actually create the function in config/logger.go, causing build failures across all CI shards. This commit: - Replaces the exported var TestOverrideLogRootPath with mutex-protected unexported state (testOverrideLogRootPath + testOverrideLogRootMu) - Adds exported SetTestOverrideLogRootPath() setter - Adds unexported getTestOverrideLogRootPath() getter - Updates GetLogRootPath() to use the thread-safe getter - Fixes log_test.go callers that were missed in the previous commit Co-authored-by: Claude <claude@anthropic.com> * fix(test): use SetupConfig for access_control feature flag registration InitAccessControlPolicy() checks FeatureFlags.AttributeBasedAccessControl at route registration time during server startup. Setting the flag via UpdateConfig after Setup() is too late — routes are never registered and API calls return 404. Use SetupConfig() to pass the feature flag in the initial config before server startup, ensuring routes are properly registered. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore BurnOnRead flag state in TestRevealPost subtest The 'feature not enabled' subtest disables BurnOnRead without restoring it via t.Cleanup. Subsequent subtests inherit the disabled state, which can cause 501 errors when they expect the feature to be available. Add t.Cleanup to restore FeatureFlags.BurnOnRead = true after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore EnableSharedChannelsMemberSync flag via t.Cleanup The test disables EnableSharedChannelsMemberSync without restoring it. If the subtest exits early (e.g., require failure), later sibling subtests inherit a disabled flag and become flaky. Add t.Cleanup to restore the flag after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * Fix test parallelism: use instance-scoped overrides and init-time audit config Replace package-level test globals (TestOverrideInstallType, SetTestOverrideLogRootPath) with fields on PlatformService so each test gets its own instance without process-wide mutation. Fix three audit tests (TestUserLoginAudit, TestLogoutAuditAuthStatus, TestUpdatePasswordAudit) that configured the audit logger after server init — the audit logger only reads config at startup, so pass audit settings via app.Config() at init time instead. Also revert the Go 1.24.13 downgrade and bump mattermost-govet to v2.0.2 for Go 1.25.8 compatibility. * Fix audit unit tests * Fix MMCLOUDURL unit tests * Fixed unit tests using MM_NOTIFY_ADMIN_COOL_OFF_DAYS * Make app migrations idempotent for parallel test safety Change System().Save() to System().SaveOrUpdate() in all migration completion markers. When two parallel tests share a database pool entry, both may race through the check-then-insert migration pattern. Save() causes a duplicate key fatal crash; SaveOrUpdate() makes the second write a harmless no-op. * test: address review feedback on fullyparallel PR - Use SetLogRootPathOverride() setter instead of direct field access in platform/support_packet_test.go and platform/log_test.go (pvev) - Restore TestGetLogRootPath in config/logger_test.go to keep MM_LOG_PATH env var coverage; test uses t.Setenv so it runs serially which is fine (pvev) - Fix misleading comment in config_test.go: code uses t.Setenv, not os.Setenv (jgheithcock) Co-authored-by: Claude <claude@anthropic.com> * fix: add missing os import in post_test.go The os import was dropped during a merge conflict resolution while burn-on-read shared channel tests from master still use os.Setenv. Co-authored-by: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: wiggin77 <wiggin77@warpmail.net> Co-authored-by: Mattermost Build <build@mattermost.com>
2026-04-08 20:48:36 -04:00
// Enable BurnOnRead feature flag
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.BurnOnRead = true })
// Create a second user for testing
user2 := th.CreateUser(t)
th.LinkUserToTeam(t, user2, th.BasicTeam)
th.AddUserToChannel(t, user2, th.BasicChannel)
t.Run("expired burn-on-read post should not be returned in flagged posts", func(t *testing.T) {
enableBoRFeature(th)
// Create a burn-on-read post
borPost := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "burn on read message",
Type: model.PostTypeBurnOnRead,
}
borPost.AddProp(model.PostPropsExpireAt, model.GetMillis()+int64(10*1000)) // 10 seconds
createdPost, _, appErr := th.App.CreatePost(th.Context, borPost, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, appErr)
require.NotNil(t, createdPost)
// User2 reveals the post
revealedPost, appErr := th.App.RevealPost(th.Context, createdPost, user2.Id, "")
require.Nil(t, appErr)
require.NotNil(t, revealedPost)
// User2 saves/flags the post
preference := model.Preference{
UserId: user2.Id,
Category: model.PreferenceCategoryFlaggedPost,
Name: createdPost.Id,
Value: "true",
}
err := th.App.Srv().Store().Preference().Save(model.Preferences{preference})
require.NoError(t, err)
// Verify post appears in flagged posts before expiration
flaggedPosts, appErr := th.App.GetFlaggedPosts(th.Context, user2.Id, 0, 10)
require.Nil(t, appErr)
require.NotNil(t, flaggedPosts)
require.Contains(t, flaggedPosts.Order, createdPost.Id)
require.NotNil(t, flaggedPosts.Posts[createdPost.Id])
// Simulate expiration by updating the receipt's ExpireAt to the past
receipt, err := th.App.Srv().Store().ReadReceipt().Get(th.Context, createdPost.Id, user2.Id)
require.NoError(t, err)
require.NotNil(t, receipt)
receipt.ExpireAt = model.GetMillis() - 1000 // 1 second in the past
_, err = th.App.Srv().Store().ReadReceipt().Update(th.Context, receipt)
require.NoError(t, err)
// Get flagged posts again - expired post should be filtered out
flaggedPosts, appErr = th.App.GetFlaggedPosts(th.Context, user2.Id, 0, 10)
require.Nil(t, appErr)
require.NotNil(t, flaggedPosts)
require.NotContains(t, flaggedPosts.Order, createdPost.Id, "Expired burn-on-read post should not be in flagged posts")
require.Nil(t, flaggedPosts.Posts[createdPost.Id], "Expired burn-on-read post should not be in posts map")
})
t.Run("expired burn-on-read post should not be returned in flagged posts for team", func(t *testing.T) {
enableBoRFeature(th)
// Create a burn-on-read post
borPost := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "burn on read team message",
Type: model.PostTypeBurnOnRead,
}
borPost.AddProp(model.PostPropsExpireAt, model.GetMillis()+int64(10*1000))
createdPost, _, appErr := th.App.CreatePost(th.Context, borPost, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, appErr)
// User2 reveals and flags the post
revealedPost, appErr := th.App.RevealPost(th.Context, createdPost, user2.Id, "")
require.Nil(t, appErr)
require.NotNil(t, revealedPost)
preference := model.Preference{
UserId: user2.Id,
Category: model.PreferenceCategoryFlaggedPost,
Name: createdPost.Id,
Value: "true",
}
err := th.App.Srv().Store().Preference().Save(model.Preferences{preference})
require.NoError(t, err)
// Expire the receipt
receipt, err := th.App.Srv().Store().ReadReceipt().Get(th.Context, createdPost.Id, user2.Id)
require.NoError(t, err)
receipt.ExpireAt = model.GetMillis() - 1000
_, err = th.App.Srv().Store().ReadReceipt().Update(th.Context, receipt)
require.NoError(t, err)
// Get flagged posts for team
flaggedPosts, appErr := th.App.GetFlaggedPostsForTeam(th.Context, user2.Id, th.BasicTeam.Id, 0, 10)
require.Nil(t, appErr)
require.NotContains(t, flaggedPosts.Order, createdPost.Id)
})
t.Run("expired burn-on-read post should not be returned in flagged posts for channel", func(t *testing.T) {
enableBoRFeature(th)
// Create a burn-on-read post
borPost := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "burn on read channel message",
Type: model.PostTypeBurnOnRead,
}
borPost.AddProp(model.PostPropsExpireAt, model.GetMillis()+int64(10*1000))
createdPost, _, appErr := th.App.CreatePost(th.Context, borPost, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, appErr)
// User2 reveals and flags the post
revealedPost, appErr := th.App.RevealPost(th.Context, createdPost, user2.Id, "")
require.Nil(t, appErr)
require.NotNil(t, revealedPost)
preference := model.Preference{
UserId: user2.Id,
Category: model.PreferenceCategoryFlaggedPost,
Name: createdPost.Id,
Value: "true",
}
err := th.App.Srv().Store().Preference().Save(model.Preferences{preference})
require.NoError(t, err)
// Expire the receipt
receipt, err := th.App.Srv().Store().ReadReceipt().Get(th.Context, createdPost.Id, user2.Id)
require.NoError(t, err)
receipt.ExpireAt = model.GetMillis() - 1000
_, err = th.App.Srv().Store().ReadReceipt().Update(th.Context, receipt)
require.NoError(t, err)
// Get flagged posts for channel
flaggedPosts, appErr := th.App.GetFlaggedPostsForChannel(th.Context, user2.Id, th.BasicChannel.Id, 0, 10)
require.Nil(t, appErr)
require.NotContains(t, flaggedPosts.Order, createdPost.Id)
})
t.Run("non-expired burn-on-read post should appear in flagged posts", func(t *testing.T) {
enableBoRFeature(th)
// Create a burn-on-read post with long expiration
borPost := &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "burn on read message still valid",
Type: model.PostTypeBurnOnRead,
}
borPost.AddProp(model.PostPropsExpireAt, model.GetMillis()+int64(3600*1000)) // 1 hour
createdPost, _, appErr := th.App.CreatePost(th.Context, borPost, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, appErr)
// User2 reveals and flags the post
revealedPost, appErr := th.App.RevealPost(th.Context, createdPost, user2.Id, "")
require.Nil(t, appErr)
require.NotNil(t, revealedPost)
preference := model.Preference{
UserId: user2.Id,
Category: model.PreferenceCategoryFlaggedPost,
Name: createdPost.Id,
Value: "true",
}
err := th.App.Srv().Store().Preference().Save(model.Preferences{preference})
require.NoError(t, err)
// Get flagged posts - post should be present
flaggedPosts, appErr := th.App.GetFlaggedPosts(th.Context, user2.Id, 0, 10)
require.Nil(t, appErr)
require.Contains(t, flaggedPosts.Order, createdPost.Id, "Non-expired burn-on-read post should be in flagged posts")
require.NotNil(t, flaggedPosts.Posts[createdPost.Id])
// Verify metadata is populated correctly
post := flaggedPosts.Posts[createdPost.Id]
require.NotNil(t, post.Metadata)
require.NotZero(t, post.Metadata.ExpireAt)
require.Greater(t, post.Metadata.ExpireAt, model.GetMillis())
})
}
func TestBurnOnReadRestrictionsForDMsAndBots(t *testing.T) {
th := Setup(t).InitBasic(t)
ci: enable fullyparallel mode for server tests (#35816) * ci: enable fullyparallel mode for server tests Replace os.Setenv, os.Chdir, and global state mutations with parallel-safe alternatives (t.Setenv, t.Chdir, test hooks) across 37 files. Refactor GetLogRootPath and MM_INSTALL_TYPE to use package-level test hooks instead of environment variables. This enables gotestsum --fullparallel, allowing all test packages to run with maximum parallelism within each shard. Co-authored-by: Claude <claude@anthropic.com> * ci: split fullyparallel from continue-on-error in workflow template - Add new boolean input 'allow-failure' separate from 'fullyparallel' - Change continue-on-error to use allow-failure instead of fullyparallel - Update server-ci.yml to pass allow-failure: true for test coverage job - Allows independent control of parallel execution and failure tolerance Co-authored-by: Claude <claude@anthropic.com> * fix: protect TestOverrideLogRootPath with sync.Mutex for parallel tests - Replace global var TestOverrideLogRootPath with mutex-protected functions - Add SetTestOverrideLogRootPath() and getTestOverrideLogRootPath() functions - Update GetLogRootPath() to use thread-safe getter - Update all test files to use SetTestOverrideLogRootPath() with t.Cleanup() - Fixes race condition when running tests with t.Parallel() Co-authored-by: Claude <claude@anthropic.com> * fix: configure audit settings before server setup in tests - Move ExperimentalAuditSettings from UpdateConfig() to config defaults - Pass audit config via app.Config() option in SetupWithServerOptions() - Fixes audit test setup ordering to configure BEFORE server initialization - Resolves CodeRabbit's audit config timing issue in api4 tests Co-authored-by: Claude <claude@anthropic.com> * fix: implement SetTestOverrideLogRootPath mutex in logger.go The previous commit updated test callers to use SetTestOverrideLogRootPath() but didn't actually create the function in config/logger.go, causing build failures across all CI shards. This commit: - Replaces the exported var TestOverrideLogRootPath with mutex-protected unexported state (testOverrideLogRootPath + testOverrideLogRootMu) - Adds exported SetTestOverrideLogRootPath() setter - Adds unexported getTestOverrideLogRootPath() getter - Updates GetLogRootPath() to use the thread-safe getter - Fixes log_test.go callers that were missed in the previous commit Co-authored-by: Claude <claude@anthropic.com> * fix(test): use SetupConfig for access_control feature flag registration InitAccessControlPolicy() checks FeatureFlags.AttributeBasedAccessControl at route registration time during server startup. Setting the flag via UpdateConfig after Setup() is too late — routes are never registered and API calls return 404. Use SetupConfig() to pass the feature flag in the initial config before server startup, ensuring routes are properly registered. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore BurnOnRead flag state in TestRevealPost subtest The 'feature not enabled' subtest disables BurnOnRead without restoring it via t.Cleanup. Subsequent subtests inherit the disabled state, which can cause 501 errors when they expect the feature to be available. Add t.Cleanup to restore FeatureFlags.BurnOnRead = true after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * fix(test): restore EnableSharedChannelsMemberSync flag via t.Cleanup The test disables EnableSharedChannelsMemberSync without restoring it. If the subtest exits early (e.g., require failure), later sibling subtests inherit a disabled flag and become flaky. Add t.Cleanup to restore the flag after the subtest completes. Co-authored-by: Claude <claude@anthropic.com> * Fix test parallelism: use instance-scoped overrides and init-time audit config Replace package-level test globals (TestOverrideInstallType, SetTestOverrideLogRootPath) with fields on PlatformService so each test gets its own instance without process-wide mutation. Fix three audit tests (TestUserLoginAudit, TestLogoutAuditAuthStatus, TestUpdatePasswordAudit) that configured the audit logger after server init — the audit logger only reads config at startup, so pass audit settings via app.Config() at init time instead. Also revert the Go 1.24.13 downgrade and bump mattermost-govet to v2.0.2 for Go 1.25.8 compatibility. * Fix audit unit tests * Fix MMCLOUDURL unit tests * Fixed unit tests using MM_NOTIFY_ADMIN_COOL_OFF_DAYS * Make app migrations idempotent for parallel test safety Change System().Save() to System().SaveOrUpdate() in all migration completion markers. When two parallel tests share a database pool entry, both may race through the check-then-insert migration pattern. Save() causes a duplicate key fatal crash; SaveOrUpdate() makes the second write a harmless no-op. * test: address review feedback on fullyparallel PR - Use SetLogRootPathOverride() setter instead of direct field access in platform/support_packet_test.go and platform/log_test.go (pvev) - Restore TestGetLogRootPath in config/logger_test.go to keep MM_LOG_PATH env var coverage; test uses t.Setenv so it runs serially which is fine (pvev) - Fix misleading comment in config_test.go: code uses t.Setenv, not os.Setenv (jgheithcock) Co-authored-by: Claude <claude@anthropic.com> * fix: add missing os import in post_test.go The os import was dropped during a merge conflict resolution while burn-on-read shared channel tests from master still use os.Setenv. Co-authored-by: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: wiggin77 <wiggin77@warpmail.net> Co-authored-by: Mattermost Build <build@mattermost.com>
2026-04-08 20:48:36 -04:00
// Enable BurnOnRead feature flag
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.BurnOnRead = true })
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.ServiceSettings.EnableBurnOnRead = model.NewPointer(true)
cfg.ServiceSettings.BurnOnReadMaximumTimeToLiveSeconds = model.NewPointer(600)
cfg.ServiceSettings.BurnOnReadDurationSeconds = model.NewPointer(600)
})
t.Run("should allow burn-on-read posts in direct messages with another user", func(t *testing.T) {
// Create a direct message channel between two different users
dmChannel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id)
require.Nil(t, appErr)
require.Equal(t, model.ChannelTypeDirect, dmChannel.Type)
post := &model.Post{
ChannelId: dmChannel.Id,
Message: "This is a burn-on-read message in DM",
UserId: th.BasicUser.Id,
Type: model.PostTypeBurnOnRead,
}
createdPost, _, err := th.App.CreatePost(th.Context, post, dmChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
require.NotNil(t, createdPost)
require.Equal(t, model.PostTypeBurnOnRead, createdPost.Type)
})
t.Run("should allow burn-on-read posts in group messages", func(t *testing.T) {
// Create a group message channel with at least 3 users
user3 := th.CreateUser(t)
th.LinkUserToTeam(t, user3, th.BasicTeam)
gmChannel := th.CreateGroupChannel(t, th.BasicUser2, user3)
require.Equal(t, model.ChannelTypeGroup, gmChannel.Type)
// This should succeed - group messages allow BoR
post := &model.Post{
ChannelId: gmChannel.Id,
Message: "This is a burn-on-read message in GM",
UserId: th.BasicUser.Id,
Type: model.PostTypeBurnOnRead,
}
createdPost, _, err := th.App.CreatePost(th.Context, post, gmChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
require.NotNil(t, createdPost)
require.Equal(t, model.PostTypeBurnOnRead, createdPost.Type)
})
t.Run("should allow burn-on-read posts from bot users", func(t *testing.T) {
// Create a bot user
bot := &model.Bot{
Username: "testbot",
DisplayName: "Test Bot",
Description: "Test Bot for burn-on-read (bots can send BoR for OTP, integrations, etc.)",
OwnerId: th.BasicUser.Id,
}
createdBot, appErr := th.App.CreateBot(th.Context, bot)
require.Nil(t, appErr)
// Get the bot user
botUser, appErr := th.App.GetUser(createdBot.UserId)
require.Nil(t, appErr)
require.True(t, botUser.IsBot)
// Create a burn-on-read post as bot (should succeed - bots can send BoR)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "This is a burn-on-read message from bot",
UserId: botUser.Id,
Type: model.PostTypeBurnOnRead,
}
createdPost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
require.NotNil(t, createdPost)
require.Equal(t, model.PostTypeBurnOnRead, createdPost.Type)
})
t.Run("should reject burn-on-read posts in self DMs", func(t *testing.T) {
// Create a self DM channel (user messaging themselves)
selfDMChannel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser.Id)
require.Nil(t, appErr)
require.Equal(t, model.ChannelTypeDirect, selfDMChannel.Type)
// Try to create a burn-on-read post in self DM
post := &model.Post{
ChannelId: selfDMChannel.Id,
Message: "This is a burn-on-read message to myself",
UserId: th.BasicUser.Id,
Type: model.PostTypeBurnOnRead,
}
_, _, err := th.App.CreatePost(th.Context, post, selfDMChannel, model.CreatePostFlags{SetOnline: true})
require.NotNil(t, err)
require.Equal(t, "api.post.fill_in_post_props.burn_on_read.self_dm.app_error", err.Id)
})
t.Run("should reject burn-on-read posts in DMs with bots/AI agents", func(t *testing.T) {
// Create a bot user
bot := &model.Bot{
Username: "aiagent",
DisplayName: "AI Agent",
Description: "Test AI Agent for burn-on-read restrictions",
OwnerId: th.BasicUser.Id,
}
createdBot, appErr := th.App.CreateBot(th.Context, bot)
require.Nil(t, appErr)
// Get the bot user
botUser, appErr := th.App.GetUser(createdBot.UserId)
require.Nil(t, appErr)
require.True(t, botUser.IsBot)
// Create a DM channel between the regular user and the bot
dmWithBotChannel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, botUser.Id)
require.Nil(t, appErr)
require.Equal(t, model.ChannelTypeDirect, dmWithBotChannel.Type)
// Try to create a burn-on-read post in DM with bot (regular user sending)
post := &model.Post{
ChannelId: dmWithBotChannel.Id,
Message: "This is a burn-on-read message to AI agent",
UserId: th.BasicUser.Id,
Type: model.PostTypeBurnOnRead,
}
_, _, err := th.App.CreatePost(th.Context, post, dmWithBotChannel, model.CreatePostFlags{SetOnline: true})
require.NotNil(t, err)
require.Equal(t, "api.post.fill_in_post_props.burn_on_read.bot_dm.app_error", err.Id)
})
t.Run("should reject burn-on-read posts in DMs with deleted users", func(t *testing.T) {
// Create a user that we'll delete
userToDelete := th.CreateUser(t)
th.LinkUserToTeam(t, userToDelete, th.BasicTeam)
// Create a DM channel between the regular user and the user we'll delete
dmChannel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, userToDelete.Id)
require.Nil(t, appErr)
require.Equal(t, model.ChannelTypeDirect, dmChannel.Type)
// Delete the user
appErr = th.App.PermanentDeleteUser(th.Context, userToDelete)
require.Nil(t, appErr)
// Try to create a burn-on-read post in DM with deleted user
post := &model.Post{
ChannelId: dmChannel.Id,
Message: "This is a burn-on-read message to deleted user",
UserId: th.BasicUser.Id,
Type: model.PostTypeBurnOnRead,
}
// This should fail because we can't validate the other user (deleted)
_, _, err := th.App.CreatePost(th.Context, post, dmChannel, model.CreatePostFlags{SetOnline: true})
require.NotNil(t, err)
require.Equal(t, "api.post.fill_in_post_props.burn_on_read.user.app_error", err.Id)
})
t.Run("should allow burn-on-read posts in public channels", func(t *testing.T) {
// This should succeed - public channel, regular user
require.Equal(t, model.ChannelTypeOpen, th.BasicChannel.Type)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "This is a burn-on-read message in public channel",
UserId: th.BasicUser.Id,
Type: model.PostTypeBurnOnRead,
}
createdPost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
require.NotNil(t, createdPost)
require.Equal(t, model.PostTypeBurnOnRead, createdPost.Type)
})
t.Run("should allow burn-on-read posts in private channels", func(t *testing.T) {
// Create a private channel using helper
createdPrivateChannel := th.CreatePrivateChannel(t, th.BasicTeam)
require.Equal(t, model.ChannelTypePrivate, createdPrivateChannel.Type)
// This should succeed - private channel, regular user
post := &model.Post{
ChannelId: createdPrivateChannel.Id,
Message: "This is a burn-on-read message in private channel",
UserId: th.BasicUser.Id,
Type: model.PostTypeBurnOnRead,
}
createdPost, _, err := th.App.CreatePost(th.Context, post, createdPrivateChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
require.NotNil(t, createdPost)
require.Equal(t, model.PostTypeBurnOnRead, createdPost.Type)
})
}
func TestGetBurnOnReadPost(t *testing.T) {
t.Run("success - temporary post found", func(t *testing.T) {
th := Setup(t).InitBasic(t)
post := &model.Post{
Id: model.NewId(),
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "placeholder message",
FileIds: model.StringArray{"file1"},
Type: model.PostTypeBurnOnRead,
}
temporaryPost := &model.TemporaryPost{
ID: post.Id,
Type: model.PostTypeBurnOnRead,
ExpireAt: model.GetMillis() + 3600000,
Message: "actual secret message",
FileIDs: model.StringArray{"file2", "file3"},
}
_, err := th.App.Srv().Store().TemporaryPost().Save(th.Context, temporaryPost)
require.NoError(t, err)
resultPost, appErr := th.App.getBurnOnReadPost(th.Context, post)
require.Nil(t, appErr)
require.NotNil(t, resultPost)
assert.Equal(t, temporaryPost.Message, resultPost.Message)
assert.Equal(t, temporaryPost.FileIDs, resultPost.FileIds)
// Ensure original post is not modified
assert.Equal(t, "placeholder message", post.Message)
assert.Equal(t, model.StringArray{"file1"}, post.FileIds)
})
t.Run("temporary post not found - returns app error", func(t *testing.T) {
th := Setup(t).InitBasic(t)
post := &model.Post{
Id: model.NewId(),
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "placeholder message",
Type: model.PostTypeBurnOnRead,
}
resultPost, appErr := th.App.getBurnOnReadPost(th.Context, post)
require.NotNil(t, appErr)
require.Nil(t, resultPost)
assert.Equal(t, "app.post.get_post.app_error", appErr.Id)
assert.Equal(t, http.StatusInternalServerError, appErr.StatusCode)
})
}
func TestPostChannelMentionsWithPrivateChannels(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
channel := th.BasicChannel
user := th.BasicUser
// Create context with session for the user to properly test sanitization
ctx := th.Context.WithSession(&model.Session{UserId: user.Id})
// Create a private channel where user IS a member
privateChannelMember, err := th.App.CreateChannel(th.Context, &model.Channel{
DisplayName: "Private Member",
Name: "private-member",
Type: model.ChannelTypePrivate,
TeamId: th.BasicTeam.Id,
}, false)
require.Nil(t, err)
th.AddUserToChannel(t, user, privateChannelMember)
// Create a private channel where user is NOT a member
privateChannelNonMember, err := th.App.CreateChannel(th.Context, &model.Channel{
DisplayName: "Private Non-Member",
Name: "private-non-member",
Type: model.ChannelTypePrivate,
TeamId: th.BasicTeam.Id,
}, false)
require.Nil(t, err)
// Create a public channel where user is NOT a member
publicChannel, err := th.App.CreateChannel(th.Context, &model.Channel{
DisplayName: "Public Channel",
Name: "public-channel",
Type: model.ChannelTypeOpen,
TeamId: th.BasicTeam.Id,
}, false)
require.Nil(t, err)
post := &model.Post{
Message: fmt.Sprintf("~%v and ~%v and ~%v", privateChannelMember.Name, privateChannelNonMember.Name, publicChannel.Name),
ChannelId: channel.Id,
PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
UserId: user.Id,
CreateAt: 0,
}
post, _, err = th.App.CreatePostAsUser(ctx, post, "", true)
require.Nil(t, err)
mentions := post.GetProp(model.PostPropsChannelMentions)
require.NotNil(t, mentions)
mentionsMap, ok := mentions.(map[string]any)
require.True(t, ok)
// Should include private channel where user IS a member
assert.Contains(t, mentionsMap, privateChannelMember.Name)
// Should NOT include private channel where user is NOT a member
assert.NotContains(t, mentionsMap, privateChannelNonMember.Name)
// Should include public channel (user has team access)
assert.Contains(t, mentionsMap, publicChannel.Name)
}
Merge the Integrated Boards MVP feature branch (#35796) * Add CreatedBy and UpdatedBy to the properties fields and values (#34485) * Add CreatedBy and UpdatedBy to the properties fields and values * Fix types --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Adds ObjectType to the property fields table (#34908) Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Update ObjectType migration setting an empty value and marking the column as not null (#34915) Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Adds uniqueness mechanisms to the property fields (#35058) * Adds uniqueness mechanisms to the property fields After adding ObjectType, this commit ensures that both the PSAv1 and PSAv2 schemas are supported, and enforces property uniqueness through both database indexes and a logical check when creating new property fields. * Adds uniqueness check to property updates Updates are covered on this commit and we refactor as well the SQL code to use the squirrel builder and work better with the conditional addition of the `existingID` piece of the query. * Add translations to error messages * Fixing retrylayer mocks * Remove retrylayer duplication * Address review comments * Fix comment to avoid linter issues * Address PR comments * Update server/channels/db/migrations/postgres/000157_add_object_type_to_property_fields.down.sql Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> * Update server/channels/db/migrations/postgres/000157_add_object_type_to_property_fields.up.sql Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> * Update server/channels/db/migrations/postgres/000157_add_object_type_to_property_fields.up.sql Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> * Update field validation to check only for valid target types * Update migrations to avoid concurrent index creation within a transaction * Update migrations to make all index ops concurrent * Update tests to use valid PSAv2 property fields * Adds a helper for valid PSAv2 TargetTypes --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> * Fix property tests (#35388) Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Adds Integrated Boards feature flag (#35378) Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Adds Integrated Boards MVP API changes (#34822) This PR includes the necessary changes for channels and posts endpoints and adds a set of generic endpoints to retrieve and manage property fields and values following the new Property System approach. Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> Co-authored-by: Mattermost Build <build@mattermost.com> * Property System Architecture permissions for v2 (#35113) * Adds uniqueness mechanisms to the property fields After adding ObjectType, this commit ensures that both the PSAv1 and PSAv2 schemas are supported, and enforces property uniqueness through both database indexes and a logical check when creating new property fields. * Adds uniqueness check to property updates Updates are covered on this commit and we refactor as well the SQL code to use the squirrel builder and work better with the conditional addition of the `existingID` piece of the query. * Add translations to error messages * Add the permissions to the migrations, model and update the store calls * Adds the property field and property group app layer * Adds authorization helpers for property fields and values * Make sure that users cannot lock themselves out of property fields * Migrate permissions from a JSON column to three normalized columns * Remove the audit comment * Use target level constants in authorization * Log authorization membership failures * Rename admin to sysadmin * Fix i18n sorting --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Add Views store and app layer (#35361) * Add Views store and app layer for Integrated Boards Implements the View entity (model, SQL store, service, app) as described in the Integrated Boards tech spec. Views are channel-scoped board configurations with typed props (board, kanban subviews) and soft-delete. - public/model: View, ViewBoardProps, Subview, ViewPatch types with PreSave/PreUpdate/IsValid/Patch/Clone/Auditable - Migration 158: Views table with jsonb Props column and indexes - SqlViewStore: CRUD with nil-safe Props marshaling (AppendBinaryFlag) - ViewService: CreateView seeds default kanban subview and links the boards property field; caches boardPropertyFieldID at startup - App layer: CreateView/GetView/GetViewsForChannel/UpdateView/DeleteView with channel-membership permission checks and WebSocket events (view_created, view_updated, view_deleted) - doSetupBoardsPropertyField: registers the Boards property group and board field in NewServer() before ViewService construction - GetFieldByName now returns store.ErrNotFound instead of raw sql.ErrNoRows * Move permission checks out of App layer for views - Remove HasPermissionToChannel calls from all App view methods - Drop userID params from GetView, GetViewsForChannel, UpdateView, DeleteView - Fix doSetupBoardsPropertyField to include required TargetType for PSAv2 field * Make View service generic and enforce board validation in model - Remove board-specific auto-setup from service and server startup - Enforce that board views require Props, at least one subview, and at least one linked property in IsValid() - Move default subview seeding out of app layer; callers must provide valid props - Call PreSave on subviews during PreUpdate to assign IDs to new subviews - Update all tests to reflect the new validation requirements * Restore migrations files to match base branch * Distinguish ErrNotFound from other errors in view store Get * Use CONCURRENTLY and nontransactional for index operations in views migration * Split views index creation into separate nontransactional migrations * Update migrations.list * Update i18n translations for views * Fix makeView helper to include required Props for board view validation * Rename ctx parameter from c to rctx in OAuthProvider mock * Remove views service layer, call store directly from app * Return 500 for unexpected DB errors in GetView, 404 only for not-found * Harden View model: deep-copy Props, validate linked property IDs - Add ViewBoardProps.Clone() to deep-copy LinkedProperties and Subviews - Use it in View.Clone() and View.Patch() to prevent shared-slice aliasing - Iterate over LinkedProperties in View.IsValid() and reject invalid IDs with a dedicated i18n key - Register ViewStore in storetest AssertExpectations so mock expectations are enforced - Add tests covering all new behaviours * Restore autotranslation worker_stopped i18n translation * Fix view store test IDs and improve error handling in app layer - Use model.NewId() for linked property IDs in testUpdateView to fix validation failure (IsValid rejects non-UUID strings) - Fix import grouping in app/view.go (stdlib imports in one block) - Return 404 instead of 500 when Update/Delete store calls return ErrNotFound (e.g. concurrent deletion TOCTOU race) * Add View store mock to retrylayer test genStore helper The View store was added to the store interface but the genStore() helper in retrylayer_test.go was not updated, causing TestRetry to panic. Also removes the duplicate Recap mock registration. * Refactor view deletion and websocket event handling; update SQL store methods to use query builder * revert property field store * Remove useless migrations * Add cursor-based pagination to View store GetForChannel - Add ViewQueryCursor and ViewQueryOpts types with validation - Return (views, cursor, error) for caller-driven pagination - PerPage clamping: <=0 defaults to 20, >200 clamps to 200 - Support IncludeDeleted filter - Add comprehensive store tests for pagination, cursor edge cases, PerPage clamping, and invalid input rejection - Add app layer test for empty channelID → 400 - Update interface, retrylayer, timerlayer, and mock signatures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Refactor test loops in ViewStore tests for improved readability * change pagination to limit/offset * Add upper-bound limits on View Subviews and LinkedProperties Defense-in-depth validation: cap Subviews at 50 and LinkedProperties at 500 to prevent abuse below the 300KB payload limit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * MM-67388, MM-66528, MM-67750: Add View REST API endpoints, websocket events, and sort order (#35442) * Add Views store and app layer for Integrated Boards Implements the View entity (model, SQL store, service, app) as described in the Integrated Boards tech spec. Views are channel-scoped board configurations with typed props (board, kanban subviews) and soft-delete. - public/model: View, ViewBoardProps, Subview, ViewPatch types with PreSave/PreUpdate/IsValid/Patch/Clone/Auditable - Migration 158: Views table with jsonb Props column and indexes - SqlViewStore: CRUD with nil-safe Props marshaling (AppendBinaryFlag) - ViewService: CreateView seeds default kanban subview and links the boards property field; caches boardPropertyFieldID at startup - App layer: CreateView/GetView/GetViewsForChannel/UpdateView/DeleteView with channel-membership permission checks and WebSocket events (view_created, view_updated, view_deleted) - doSetupBoardsPropertyField: registers the Boards property group and board field in NewServer() before ViewService construction - GetFieldByName now returns store.ErrNotFound instead of raw sql.ErrNoRows * Move permission checks out of App layer for views - Remove HasPermissionToChannel calls from all App view methods - Drop userID params from GetView, GetViewsForChannel, UpdateView, DeleteView - Fix doSetupBoardsPropertyField to include required TargetType for PSAv2 field * Make View service generic and enforce board validation in model - Remove board-specific auto-setup from service and server startup - Enforce that board views require Props, at least one subview, and at least one linked property in IsValid() - Move default subview seeding out of app layer; callers must provide valid props - Call PreSave on subviews during PreUpdate to assign IDs to new subviews - Update all tests to reflect the new validation requirements * Restore migrations files to match base branch * Distinguish ErrNotFound from other errors in view store Get * Use CONCURRENTLY and nontransactional for index operations in views migration * Split views index creation into separate nontransactional migrations * Update migrations.list * Update i18n translations for views * Fix makeView helper to include required Props for board view validation * Rename ctx parameter from c to rctx in OAuthProvider mock * Remove views service layer, call store directly from app * Return 500 for unexpected DB errors in GetView, 404 only for not-found * Harden View model: deep-copy Props, validate linked property IDs - Add ViewBoardProps.Clone() to deep-copy LinkedProperties and Subviews - Use it in View.Clone() and View.Patch() to prevent shared-slice aliasing - Iterate over LinkedProperties in View.IsValid() and reject invalid IDs with a dedicated i18n key - Register ViewStore in storetest AssertExpectations so mock expectations are enforced - Add tests covering all new behaviours * Restore autotranslation worker_stopped i18n translation * Fix view store test IDs and improve error handling in app layer - Use model.NewId() for linked property IDs in testUpdateView to fix validation failure (IsValid rejects non-UUID strings) - Fix import grouping in app/view.go (stdlib imports in one block) - Return 404 instead of 500 when Update/Delete store calls return ErrNotFound (e.g. concurrent deletion TOCTOU race) * Add View store mock to retrylayer test genStore helper The View store was added to the store interface but the genStore() helper in retrylayer_test.go was not updated, causing TestRetry to panic. Also removes the duplicate Recap mock registration. * Refactor view deletion and websocket event handling; update SQL store methods to use query builder * revert property field store * Add View API endpoints with OpenAPI spec, client methods, and i18n Implement REST API for channel views (board-type) behind the IntegratedBoards feature flag. Adds CRUD endpoints under /api/v4/channels/{channel_id}/views with permission checks matching the channel bookmark pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove useless migrations * Add cursor-based pagination to View store GetForChannel - Add ViewQueryCursor and ViewQueryOpts types with validation - Return (views, cursor, error) for caller-driven pagination - PerPage clamping: <=0 defaults to 20, >200 clamps to 200 - Support IncludeDeleted filter - Add comprehensive store tests for pagination, cursor edge cases, PerPage clamping, and invalid input rejection - Add app layer test for empty channelID → 400 - Update interface, retrylayer, timerlayer, and mock signatures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add cursor-based pagination to View API for channel views * Enhance cursor handling in getViewsForChannel and update tests for pagination * Refactor test loops in ViewStore tests for improved readability * Refactor loop in TestGetViewsForChannel for improved readability * change pagination to limit/offset * switch to limit/offset pagination * Add upper-bound limits on View Subviews and LinkedProperties Defense-in-depth validation: cap Subviews at 50 and LinkedProperties at 500 to prevent abuse below the 300KB payload limit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add view sort order API endpoint Add POST /api/v4/channels/{channel_id}/views/{view_id}/sort_order endpoint following the channel bookmarks reorder pattern. Includes store, app, and API layers with full test coverage at each layer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add connectionId to view WebSocket events and sort_order API spec Thread connectionId from request header through all view handlers (create, update, delete, sort_order) to WebSocket events, matching the channel bookmarks pattern. Add sort_order endpoint to OpenAPI spec. Update minimum server version to 11.6. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove duplicate View/ViewPatch definitions from definitions.yaml The merge from integrated-boards-mvp introduced duplicate View and ViewPatch schema definitions that were already defined earlier in the file with more detail (including ViewBoardProps ref and enums). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update minimum server version to 11.6 in views API spec Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add missing translations for view sort order error messages Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Merge integrated-boards-mvp into ibmvp_api-views; remove spec files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix flaky TestViewStore timestamp test on CI Add sleep before UpdateSortOrder to ensure timestamps differ, preventing same-millisecond comparisons on fast CI machines. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * remove duplicate views.yaml imclude * Use c.boolString() for include_deleted query param in GetViewsForChannel Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix views.yaml sort order schema: use integer type and require body Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Refactor view sort order tests to use named IDs instead of array indices Extract idA/idB/idC from views slice and add BEFORE/AFTER comments to make stateful subtest ordering easier to follow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Return 404 instead of 403 for view operations on deleted channels Deleted channels should appear non-existent to callers rather than revealing their existence via a 403. Detailed error text explains the context for debugging. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * add missing channel deleteat checks * Use c.Params.Page instead of manual page query param parsing in getViewsForChannel c.Params already validates and defaults page/per_page, so the manual parsing was redundant. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add support for total count in views retrieval * Add tests for handling deleted views in GetViewsForChannel and GetView * Short-circuit negative newIndex in UpdateSortOrder before opening transaction Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add per-channel limit on views to bound UpdateSortOrder cost Without a cap, unbounded view creation makes sort-order updates increasingly expensive (CASE WHEN per view, row locks). Adds MaxViewsPerChannel=50 constant and enforces it in the app layer before saving. Includes API and app layer tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove include_deleted support from views API Soft-deleted views are structural metadata with low risk, but no other similar endpoint (e.g. channel bookmarks) exposes deleted records without an admin gate. Rather than adding an admin-only permission check for consistency, remove the feature entirely since there is no current use case. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update view permissions to require `create_post` instead of channel management permissions * Remove obsolete view management error messages for direct and group messages --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat(migrations): add user tracking and object type to property fields - Introduced user tracking columns (CreatedBy, UpdatedBy) to PropertyFields and PropertyValues. - Added ObjectType column to PropertyFields with associated unique indexes for legacy and typed properties. - Created new migration scripts for adding and dropping these features, including necessary indexes for data integrity. - Established views for managing property fields with new attributes. This update enhances the schema to support better tracking and categorization of property fields. * Add Property System Architecture v2 API endpoints (#35583) * Adds uniqueness mechanisms to the property fields After adding ObjectType, this commit ensures that both the PSAv1 and PSAv2 schemas are supported, and enforces property uniqueness through both database indexes and a logical check when creating new property fields. * Adds uniqueness check to property updates Updates are covered on this commit and we refactor as well the SQL code to use the squirrel builder and work better with the conditional addition of the `existingID` piece of the query. * Add translations to error messages * Add the permissions to the migrations, model and update the store calls * Adds the property field and property group app layer * Adds authorization helpers for property fields and values * Make sure that users cannot lock themselves out of property fields * Migrate permissions from a JSON column to three normalized columns * Remove the audit comment * Use target level constants in authorization * Log authorization membership failures * Rename admin to sysadmin * Adds the Property System Architecture v2 API endpoints * Adds permission checks to the create field endpoint * Add target access checks to value endpoints * Add default branches for object_type and target_type and extra guards for cursor client4 methods * Fix vet API mismatch * Fix error checks * Fix linter * Add merge semantics for property patch logic and API endpoint * Fix i18n * Fix duplicated patch elements and early return on bad cursor * Update docs to use enums * Fix i18n sorting * Update app layer to return model.AppError * Adds a limit to the number of property values that can be patched in the same request * Require target_type filter when searching property fields * Add objectType validation as part of field.IsValid() * Fix linter * Fix test with bad objecttpye * Fix test grouping --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * MM-67968: Flatten view model — remove icon, subviews, typed board props (#35726) * feat(views): flatten view model by removing icon, subview, and board props Simplifies the View data model as part of MM-67968: removes Icon, Subview, and ViewBoardProps types; renames ViewTypeBoard to ViewTypeKanban; replaces typed Props with StringInterface (map[string]any); adds migration 000167 to drop the Icon column from the Views table. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * feat(api): update views OpenAPI spec to reflect flattened model Removes ViewBoardProps, Subview, and icon from the View and ViewPatch schemas. Changes type enum from board to kanban. Replaces typed props with a free-form StringInterface object. Aligns with MM-67968. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * refactor(views): simplify store by dropping dbView and marshalViewProps StringInterface already implements driver.Valuer and sql.Scanner, so the manual JSON marshal/unmarshal and the dbView intermediate struct were redundant. model.View now scans directly from the database. Also removes the dead ViewMaxLinkedProperties constant and wraps the Commit() error in UpdateSortOrder. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * fix(api): allow arbitrary JSON in view props OpenAPI schema The props field was restricted to string values via additionalProperties: { type: string }, conflicting with the Go model's StringInterface (map[string]any). Changed to additionalProperties: true in View, ViewPatch, and inline POST schemas. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * Adds basic implementation of the generic redux store for PSAv2 (#35512) * Adds basic implementation of the generic redux store for PSAv2 * Add created_by and updated_by to the test fixtures * Make target_id, target_type and object_type mandatory * Wrap getPropertyFieldsByIds and getPropertyValuesForTargetByFieldIds with createSelector * Address PR comments --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * Adds websocket messages for the PSAv2 API events (#35696) * Adds uniqueness mechanisms to the property fields After adding ObjectType, this commit ensures that both the PSAv1 and PSAv2 schemas are supported, and enforces property uniqueness through both database indexes and a logical check when creating new property fields. * Adds uniqueness check to property updates Updates are covered on this commit and we refactor as well the SQL code to use the squirrel builder and work better with the conditional addition of the `existingID` piece of the query. * Add translations to error messages * Add the permissions to the migrations, model and update the store calls * Adds the property field and property group app layer * Adds authorization helpers for property fields and values * Make sure that users cannot lock themselves out of property fields * Migrate permissions from a JSON column to three normalized columns * Remove the audit comment * Use target level constants in authorization * Log authorization membership failures * Rename admin to sysadmin * Adds the Property System Architecture v2 API endpoints * Adds permission checks to the create field endpoint * Add target access checks to value endpoints * Add default branches for object_type and target_type and extra guards for cursor client4 methods * Fix vet API mismatch * Fix error checks * Fix linter * Add merge semantics for property patch logic and API endpoint * Fix i18n * Fix duplicated patch elements and early return on bad cursor * Update docs to use enums * Fix i18n sorting * Update app layer to return model.AppError * Adds a limit to the number of property values that can be patched in the same request * Adds websocket messages for the PSAv2 API events * Add IsPSAv2 helper to the property field for clarity * Add guard against nil returns on field deletion * Add docs to the websocket endpoints --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> * migrations: consolidate views migrations and reorder after master - Merged 000165 (create Views) with 000167 (drop Icon) since Icon was never needed - Renumbered branch migrations 159-166 → 160-167 so master's 000159 (deduplicate_policy_names) runs first - Regenerated migrations.list Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add API endpoint to retrieve posts for a specific view (#35604) Automatic Merge * Apply fixes after merge * Return a more specific error from getting multiple fields * Prevent getting broadcast params on field deletion if not needed * Remove duplicated migration code * Update property conflict code to always use master * Adds nil guard when iterating on property fields * Check that permission level is valid before getting rejected by the database * Validate correctness on TargetID for PSAv2 fields * Avoid PSAv1 using permissions or protected * Fix test data after validation change * Fix flaky search test * Adds more posts for filter use cases to properly test exclusions --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es> Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> Co-authored-by: Julien Tant <julien@craftyx.fr> Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Julien Tant <785518+JulienTant@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 05:36:35 -04:00
func TestGetPostsForView(t *testing.T) {
mainHelper.Parallel(t)
t.Run("returns posts for channel", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
// Create a few posts
post1, _, err := th.App.CreatePost(th.Context, &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "post 1",
}, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
post2, _, err := th.App.CreatePost(th.Context, &model.Post{
ChannelId: th.BasicChannel.Id,
UserId: th.BasicUser.Id,
Message: "post 2",
}, th.BasicChannel, model.CreatePostFlags{})
require.Nil(t, err)
options := model.GetPostsOptions{
ChannelId: th.BasicChannel.Id,
Page: 0,
PerPage: 10,
UserId: th.BasicUser.Id,
}
postList, appErr := th.App.GetPostsForView(th.Context, options)
require.Nil(t, appErr)
require.NotNil(t, postList)
assert.Contains(t, postList.Posts, post1.Id)
assert.Contains(t, postList.Posts, post2.Id)
})
t.Run("returns empty list for channel with no posts", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
channel := th.CreateChannel(t, th.BasicTeam)
options := model.GetPostsOptions{
ChannelId: channel.Id,
Page: 0,
PerPage: 10,
UserId: th.BasicUser.Id,
}
postList, appErr := th.App.GetPostsForView(th.Context, options)
require.Nil(t, appErr)
require.NotNil(t, postList)
assert.Empty(t, postList.Posts)
})
t.Run("respects pagination", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
channel := th.CreateChannel(t, th.BasicTeam)
for i := range 5 {
_, _, err := th.App.CreatePost(th.Context, &model.Post{
ChannelId: channel.Id,
UserId: th.BasicUser.Id,
Message: fmt.Sprintf("post %d", i),
}, channel, model.CreatePostFlags{})
require.Nil(t, err)
}
options := model.GetPostsOptions{
ChannelId: channel.Id,
Page: 0,
PerPage: 2,
UserId: th.BasicUser.Id,
}
postList, appErr := th.App.GetPostsForView(th.Context, options)
require.Nil(t, appErr)
require.NotNil(t, postList)
assert.Len(t, postList.Posts, 2)
})
t.Run("invalid channel id returns error", func(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
options := model.GetPostsOptions{
ChannelId: model.NewId(),
Page: 0,
PerPage: 10,
UserId: th.BasicUser.Id,
}
postList, appErr := th.App.GetPostsForView(th.Context, options)
require.Nil(t, appErr)
require.NotNil(t, postList)
assert.Empty(t, postList.Posts)
})
}