github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/app/post_test.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/mattermost/mattermost-server/v5/mlog"
    17  	"github.com/mattermost/mattermost-server/v5/model"
    18  	"github.com/mattermost/mattermost-server/v5/plugin/plugintest/mock"
    19  	"github.com/mattermost/mattermost-server/v5/services/imageproxy"
    20  	"github.com/mattermost/mattermost-server/v5/services/searchengine/mocks"
    21  	"github.com/mattermost/mattermost-server/v5/store/storetest"
    22  	storemocks "github.com/mattermost/mattermost-server/v5/store/storetest/mocks"
    23  	"github.com/mattermost/mattermost-server/v5/testlib"
    24  )
    25  
    26  func TestCreatePostDeduplicate(t *testing.T) {
    27  	th := Setup(t).InitBasic()
    28  	defer th.TearDown()
    29  
    30  	t.Run("duplicate create post is idempotent", func(t *testing.T) {
    31  		pendingPostId := model.NewId()
    32  		post, err := th.App.CreatePostAsUser(&model.Post{
    33  			UserId:        th.BasicUser.Id,
    34  			ChannelId:     th.BasicChannel.Id,
    35  			Message:       "message",
    36  			PendingPostId: pendingPostId,
    37  		}, "", true)
    38  		require.Nil(t, err)
    39  		require.Equal(t, "message", post.Message)
    40  
    41  		duplicatePost, err := th.App.CreatePostAsUser(&model.Post{
    42  			UserId:        th.BasicUser.Id,
    43  			ChannelId:     th.BasicChannel.Id,
    44  			Message:       "message",
    45  			PendingPostId: pendingPostId,
    46  		}, "", true)
    47  		require.Nil(t, err)
    48  		require.Equal(t, post.Id, duplicatePost.Id, "should have returned previously created post id")
    49  		require.Equal(t, "message", duplicatePost.Message)
    50  	})
    51  
    52  	t.Run("post rejected by plugin leaves cache ready for non-deduplicated try", func(t *testing.T) {
    53  		setupPluginApiTest(t, `
    54  			package main
    55  
    56  			import (
    57  				"github.com/mattermost/mattermost-server/v5/plugin"
    58  				"github.com/mattermost/mattermost-server/v5/model"
    59  			)
    60  
    61  			type MyPlugin struct {
    62  				plugin.MattermostPlugin
    63  				allow bool
    64  			}
    65  
    66  			func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
    67  				if !p.allow {
    68  					p.allow = true
    69  					return nil, "rejected"
    70  				}
    71  
    72  				return nil, ""
    73  			}
    74  
    75  			func main() {
    76  				plugin.ClientMain(&MyPlugin{})
    77  			}
    78  		`, `{"id": "testrejectfirstpost", "backend": {"executable": "backend.exe"}}`, "testrejectfirstpost", th.App)
    79  
    80  		pendingPostId := model.NewId()
    81  		post, err := th.App.CreatePostAsUser(&model.Post{
    82  			UserId:        th.BasicUser.Id,
    83  			ChannelId:     th.BasicChannel.Id,
    84  			Message:       "message",
    85  			PendingPostId: pendingPostId,
    86  		}, "", true)
    87  		require.NotNil(t, err)
    88  		require.Equal(t, "Post rejected by plugin. rejected", err.Id)
    89  		require.Nil(t, post)
    90  
    91  		duplicatePost, err := th.App.CreatePostAsUser(&model.Post{
    92  			UserId:        th.BasicUser.Id,
    93  			ChannelId:     th.BasicChannel.Id,
    94  			Message:       "message",
    95  			PendingPostId: pendingPostId,
    96  		}, "", true)
    97  		require.Nil(t, err)
    98  		require.Equal(t, "message", duplicatePost.Message)
    99  	})
   100  
   101  	t.Run("slow posting after cache entry blocks duplicate request", func(t *testing.T) {
   102  		setupPluginApiTest(t, `
   103  			package main
   104  
   105  			import (
   106  				"github.com/mattermost/mattermost-server/v5/plugin"
   107  				"github.com/mattermost/mattermost-server/v5/model"
   108  				"time"
   109  			)
   110  
   111  			type MyPlugin struct {
   112  				plugin.MattermostPlugin
   113  				instant bool
   114  			}
   115  
   116  			func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
   117  				if !p.instant {
   118  					p.instant = true
   119  					time.Sleep(3 * time.Second)
   120  				}
   121  
   122  				return nil, ""
   123  			}
   124  
   125  			func main() {
   126  				plugin.ClientMain(&MyPlugin{})
   127  			}
   128  		`, `{"id": "testdelayfirstpost", "backend": {"executable": "backend.exe"}}`, "testdelayfirstpost", th.App)
   129  
   130  		var post *model.Post
   131  		pendingPostId := model.NewId()
   132  
   133  		wg := sync.WaitGroup{}
   134  
   135  		// Launch a goroutine to make the first CreatePost call that will get delayed
   136  		// by the plugin above.
   137  		wg.Add(1)
   138  		go func() {
   139  			defer wg.Done()
   140  			var appErr *model.AppError
   141  			post, appErr = th.App.CreatePostAsUser(&model.Post{
   142  				UserId:        th.BasicUser.Id,
   143  				ChannelId:     th.BasicChannel.Id,
   144  				Message:       "plugin delayed",
   145  				PendingPostId: pendingPostId,
   146  			}, "", true)
   147  			require.Nil(t, appErr)
   148  			require.Equal(t, post.Message, "plugin delayed")
   149  		}()
   150  
   151  		// Give the goroutine above a chance to start and get delayed by the plugin.
   152  		time.Sleep(2 * time.Second)
   153  
   154  		// Try creating a duplicate post
   155  		duplicatePost, err := th.App.CreatePostAsUser(&model.Post{
   156  			UserId:        th.BasicUser.Id,
   157  			ChannelId:     th.BasicChannel.Id,
   158  			Message:       "plugin delayed",
   159  			PendingPostId: pendingPostId,
   160  		}, "", true)
   161  		require.NotNil(t, err)
   162  		require.Equal(t, "api.post.deduplicate_create_post.pending", err.Id)
   163  		require.Nil(t, duplicatePost)
   164  
   165  		// Wait for the first CreatePost to finish to ensure assertions are made.
   166  		wg.Wait()
   167  	})
   168  
   169  	t.Run("duplicate create post after cache expires is not idempotent", func(t *testing.T) {
   170  		pendingPostId := model.NewId()
   171  		post, err := th.App.CreatePostAsUser(&model.Post{
   172  			UserId:        th.BasicUser.Id,
   173  			ChannelId:     th.BasicChannel.Id,
   174  			Message:       "message",
   175  			PendingPostId: pendingPostId,
   176  		}, "", true)
   177  		require.Nil(t, err)
   178  		require.Equal(t, "message", post.Message)
   179  
   180  		time.Sleep(PendingPostIDsCacheTTL)
   181  
   182  		duplicatePost, err := th.App.CreatePostAsUser(&model.Post{
   183  			UserId:        th.BasicUser.Id,
   184  			ChannelId:     th.BasicChannel.Id,
   185  			Message:       "message",
   186  			PendingPostId: pendingPostId,
   187  		}, "", true)
   188  		require.Nil(t, err)
   189  		require.NotEqual(t, post.Id, duplicatePost.Id, "should have created new post id")
   190  		require.Equal(t, "message", duplicatePost.Message)
   191  	})
   192  }
   193  
   194  func TestAttachFilesToPost(t *testing.T) {
   195  	t.Run("should attach files", func(t *testing.T) {
   196  		th := Setup(t).InitBasic()
   197  		defer th.TearDown()
   198  
   199  		info1, err := th.App.Srv().Store.FileInfo().Save(&model.FileInfo{
   200  			CreatorId: th.BasicUser.Id,
   201  			Path:      "path.txt",
   202  		})
   203  		require.NoError(t, err)
   204  
   205  		info2, err := th.App.Srv().Store.FileInfo().Save(&model.FileInfo{
   206  			CreatorId: th.BasicUser.Id,
   207  			Path:      "path.txt",
   208  		})
   209  		require.NoError(t, err)
   210  
   211  		post := th.BasicPost
   212  		post.FileIds = []string{info1.Id, info2.Id}
   213  
   214  		appErr := th.App.attachFilesToPost(post)
   215  		assert.Nil(t, appErr)
   216  
   217  		infos, appErr := th.App.GetFileInfosForPost(post.Id, false)
   218  		assert.Nil(t, appErr)
   219  		assert.Len(t, infos, 2)
   220  	})
   221  
   222  	t.Run("should update File.PostIds after failing to add files", func(t *testing.T) {
   223  		th := Setup(t).InitBasic()
   224  		defer th.TearDown()
   225  
   226  		info1, err := th.App.Srv().Store.FileInfo().Save(&model.FileInfo{
   227  			CreatorId: th.BasicUser.Id,
   228  			Path:      "path.txt",
   229  			PostId:    model.NewId(),
   230  		})
   231  		require.NoError(t, err)
   232  
   233  		info2, err := th.App.Srv().Store.FileInfo().Save(&model.FileInfo{
   234  			CreatorId: th.BasicUser.Id,
   235  			Path:      "path.txt",
   236  		})
   237  		require.NoError(t, err)
   238  
   239  		post := th.BasicPost
   240  		post.FileIds = []string{info1.Id, info2.Id}
   241  
   242  		appErr := th.App.attachFilesToPost(post)
   243  		assert.Nil(t, appErr)
   244  
   245  		infos, appErr := th.App.GetFileInfosForPost(post.Id, false)
   246  		assert.Nil(t, appErr)
   247  		assert.Len(t, infos, 1)
   248  		assert.Equal(t, info2.Id, infos[0].Id)
   249  
   250  		updated, appErr := th.App.GetSinglePost(post.Id)
   251  		require.Nil(t, appErr)
   252  		assert.Len(t, updated.FileIds, 1)
   253  		assert.Contains(t, updated.FileIds, info2.Id)
   254  	})
   255  }
   256  
   257  func TestUpdatePostEditAt(t *testing.T) {
   258  	th := Setup(t).InitBasic()
   259  	defer th.TearDown()
   260  
   261  	post := &model.Post{}
   262  	post = th.BasicPost.Clone()
   263  
   264  	post.IsPinned = true
   265  	saved, err := th.App.UpdatePost(post, true)
   266  	require.Nil(t, err)
   267  	assert.Equal(t, saved.EditAt, post.EditAt, "shouldn't have updated post.EditAt when pinning post")
   268  	post = saved.Clone()
   269  
   270  	time.Sleep(time.Millisecond * 100)
   271  
   272  	post.Message = model.NewId()
   273  	saved, err = th.App.UpdatePost(post, true)
   274  	require.Nil(t, err)
   275  	assert.NotEqual(t, saved.EditAt, post.EditAt, "should have updated post.EditAt when updating post message")
   276  
   277  	time.Sleep(time.Millisecond * 200)
   278  }
   279  
   280  func TestUpdatePostTimeLimit(t *testing.T) {
   281  	th := Setup(t).InitBasic()
   282  	defer th.TearDown()
   283  
   284  	post := &model.Post{}
   285  	post = th.BasicPost.Clone()
   286  
   287  	th.App.Srv().SetLicense(model.NewTestLicense())
   288  
   289  	th.App.UpdateConfig(func(cfg *model.Config) {
   290  		*cfg.ServiceSettings.PostEditTimeLimit = -1
   291  	})
   292  	_, err := th.App.UpdatePost(post, true)
   293  	require.Nil(t, err)
   294  
   295  	th.App.UpdateConfig(func(cfg *model.Config) {
   296  		*cfg.ServiceSettings.PostEditTimeLimit = 1000000000
   297  	})
   298  	post.Message = model.NewId()
   299  
   300  	_, err = th.App.UpdatePost(post, true)
   301  	require.Nil(t, err, "should allow you to edit the post")
   302  
   303  	th.App.UpdateConfig(func(cfg *model.Config) {
   304  		*cfg.ServiceSettings.PostEditTimeLimit = 1
   305  	})
   306  	post.Message = model.NewId()
   307  	_, err = th.App.UpdatePost(post, true)
   308  	require.NotNil(t, err, "should fail on update old post")
   309  
   310  	th.App.UpdateConfig(func(cfg *model.Config) {
   311  		*cfg.ServiceSettings.PostEditTimeLimit = -1
   312  	})
   313  }
   314  
   315  func TestUpdatePostInArchivedChannel(t *testing.T) {
   316  	th := Setup(t).InitBasic()
   317  	defer th.TearDown()
   318  
   319  	archivedChannel := th.CreateChannel(th.BasicTeam)
   320  	post := th.CreatePost(archivedChannel)
   321  	th.App.DeleteChannel(archivedChannel, "")
   322  
   323  	_, err := th.App.UpdatePost(post, true)
   324  	require.NotNil(t, err)
   325  	require.Equal(t, "api.post.update_post.can_not_update_post_in_deleted.error", err.Id)
   326  }
   327  
   328  func TestPostReplyToPostWhereRootPosterLeftChannel(t *testing.T) {
   329  	// This test ensures that when replying to a root post made by a user who has since left the channel, the reply
   330  	// post completes successfully. This is a regression test for PLT-6523.
   331  	th := Setup(t).InitBasic()
   332  	defer th.TearDown()
   333  
   334  	channel := th.BasicChannel
   335  	userInChannel := th.BasicUser2
   336  	userNotInChannel := th.BasicUser
   337  	rootPost := th.BasicPost
   338  
   339  	_, err := th.App.AddUserToChannel(userInChannel, channel)
   340  	require.Nil(t, err)
   341  
   342  	err = th.App.RemoveUserFromChannel(userNotInChannel.Id, "", channel)
   343  	require.Nil(t, err)
   344  	replyPost := model.Post{
   345  		Message:       "asd",
   346  		ChannelId:     channel.Id,
   347  		RootId:        rootPost.Id,
   348  		ParentId:      rootPost.Id,
   349  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   350  		UserId:        userInChannel.Id,
   351  		CreateAt:      0,
   352  	}
   353  
   354  	_, err = th.App.CreatePostAsUser(&replyPost, "", true)
   355  	require.Nil(t, err)
   356  }
   357  
   358  func TestPostAttachPostToChildPost(t *testing.T) {
   359  	th := Setup(t).InitBasic()
   360  	defer th.TearDown()
   361  
   362  	channel := th.BasicChannel
   363  	user := th.BasicUser
   364  	rootPost := th.BasicPost
   365  
   366  	replyPost1 := model.Post{
   367  		Message:       "reply one",
   368  		ChannelId:     channel.Id,
   369  		RootId:        rootPost.Id,
   370  		ParentId:      rootPost.Id,
   371  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   372  		UserId:        user.Id,
   373  		CreateAt:      0,
   374  	}
   375  
   376  	res1, err := th.App.CreatePostAsUser(&replyPost1, "", true)
   377  	require.Nil(t, err)
   378  
   379  	replyPost2 := model.Post{
   380  		Message:       "reply two",
   381  		ChannelId:     channel.Id,
   382  		RootId:        res1.Id,
   383  		ParentId:      res1.Id,
   384  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   385  		UserId:        user.Id,
   386  		CreateAt:      0,
   387  	}
   388  
   389  	_, err = th.App.CreatePostAsUser(&replyPost2, "", true)
   390  	assert.Equalf(t, err.StatusCode, http.StatusBadRequest, "Expected BadRequest error, got %v", err)
   391  
   392  	replyPost3 := model.Post{
   393  		Message:       "reply three",
   394  		ChannelId:     channel.Id,
   395  		RootId:        rootPost.Id,
   396  		ParentId:      rootPost.Id,
   397  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   398  		UserId:        user.Id,
   399  		CreateAt:      0,
   400  	}
   401  
   402  	_, err = th.App.CreatePostAsUser(&replyPost3, "", true)
   403  	assert.Nil(t, err)
   404  }
   405  
   406  func TestPostChannelMentions(t *testing.T) {
   407  	th := Setup(t).InitBasic()
   408  	defer th.TearDown()
   409  
   410  	channel := th.BasicChannel
   411  	user := th.BasicUser
   412  
   413  	channelToMention, err := th.App.CreateChannel(&model.Channel{
   414  		DisplayName: "Mention Test",
   415  		Name:        "mention-test",
   416  		Type:        model.CHANNEL_OPEN,
   417  		TeamId:      th.BasicTeam.Id,
   418  	}, false)
   419  	require.Nil(t, err)
   420  	defer th.App.PermanentDeleteChannel(channelToMention)
   421  
   422  	_, err = th.App.AddUserToChannel(user, channel)
   423  	require.Nil(t, err)
   424  
   425  	post := &model.Post{
   426  		Message:       fmt.Sprintf("hello, ~%v!", channelToMention.Name),
   427  		ChannelId:     channel.Id,
   428  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   429  		UserId:        user.Id,
   430  		CreateAt:      0,
   431  	}
   432  
   433  	result, err := th.App.CreatePostAsUser(post, "", true)
   434  	require.Nil(t, err)
   435  	assert.Equal(t, map[string]interface{}{
   436  		"mention-test": map[string]interface{}{
   437  			"display_name": "Mention Test",
   438  			"team_name":    th.BasicTeam.Name,
   439  		},
   440  	}, result.GetProp("channel_mentions"))
   441  
   442  	post.Message = fmt.Sprintf("goodbye, ~%v!", channelToMention.Name)
   443  	result, err = th.App.UpdatePost(post, false)
   444  	require.Nil(t, err)
   445  	assert.Equal(t, map[string]interface{}{
   446  		"mention-test": map[string]interface{}{
   447  			"display_name": "Mention Test",
   448  			"team_name":    th.BasicTeam.Name,
   449  		},
   450  	}, result.GetProp("channel_mentions"))
   451  }
   452  
   453  func TestImageProxy(t *testing.T) {
   454  	th := SetupWithStoreMock(t)
   455  	defer th.TearDown()
   456  
   457  	mockStore := th.App.Srv().Store.(*storemocks.Store)
   458  	mockUserStore := storemocks.UserStore{}
   459  	mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
   460  	mockPostStore := storemocks.PostStore{}
   461  	mockPostStore.On("GetMaxPostSize").Return(65535, nil)
   462  	mockSystemStore := storemocks.SystemStore{}
   463  	mockSystemStore.On("GetByName", "UpgradedFromTE").Return(&model.System{Name: "UpgradedFromTE", Value: "false"}, nil)
   464  	mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)
   465  	mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil)
   466  
   467  	mockStore.On("User").Return(&mockUserStore)
   468  	mockStore.On("Post").Return(&mockPostStore)
   469  	mockStore.On("System").Return(&mockSystemStore)
   470  
   471  	th.App.UpdateConfig(func(cfg *model.Config) {
   472  		*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
   473  	})
   474  
   475  	th.Server.ImageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService, th.Server.Log)
   476  
   477  	for name, tc := range map[string]struct {
   478  		ProxyType              string
   479  		ProxyURL               string
   480  		ProxyOptions           string
   481  		ImageURL               string
   482  		ProxiedImageURL        string
   483  		ProxiedRemovedImageURL string
   484  	}{
   485  		"atmos/camo": {
   486  			ProxyType:              model.IMAGE_PROXY_TYPE_ATMOS_CAMO,
   487  			ProxyURL:               "https://127.0.0.1",
   488  			ProxyOptions:           "foo",
   489  			ImageURL:               "http://mydomain.com/myimage",
   490  			ProxiedRemovedImageURL: "http://mydomain.com/myimage",
   491  			ProxiedImageURL:        "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage",
   492  		},
   493  		"atmos/camo_SameSite": {
   494  			ProxyType:              model.IMAGE_PROXY_TYPE_ATMOS_CAMO,
   495  			ProxyURL:               "https://127.0.0.1",
   496  			ProxyOptions:           "foo",
   497  			ImageURL:               "http://mymattermost.com/myimage",
   498  			ProxiedRemovedImageURL: "http://mymattermost.com/myimage",
   499  			ProxiedImageURL:        "http://mymattermost.com/myimage",
   500  		},
   501  		"atmos/camo_PathOnly": {
   502  			ProxyType:              model.IMAGE_PROXY_TYPE_ATMOS_CAMO,
   503  			ProxyURL:               "https://127.0.0.1",
   504  			ProxyOptions:           "foo",
   505  			ImageURL:               "/myimage",
   506  			ProxiedRemovedImageURL: "http://mymattermost.com/myimage",
   507  			ProxiedImageURL:        "http://mymattermost.com/myimage",
   508  		},
   509  		"atmos/camo_EmptyImageURL": {
   510  			ProxyType:              model.IMAGE_PROXY_TYPE_ATMOS_CAMO,
   511  			ProxyURL:               "https://127.0.0.1",
   512  			ProxyOptions:           "foo",
   513  			ImageURL:               "",
   514  			ProxiedRemovedImageURL: "",
   515  			ProxiedImageURL:        "",
   516  		},
   517  		"local": {
   518  			ProxyType:              model.IMAGE_PROXY_TYPE_LOCAL,
   519  			ImageURL:               "http://mydomain.com/myimage",
   520  			ProxiedRemovedImageURL: "http://mydomain.com/myimage",
   521  			ProxiedImageURL:        "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage",
   522  		},
   523  		"local_SameSite": {
   524  			ProxyType:              model.IMAGE_PROXY_TYPE_LOCAL,
   525  			ImageURL:               "http://mymattermost.com/myimage",
   526  			ProxiedRemovedImageURL: "http://mymattermost.com/myimage",
   527  			ProxiedImageURL:        "http://mymattermost.com/myimage",
   528  		},
   529  		"local_PathOnly": {
   530  			ProxyType:              model.IMAGE_PROXY_TYPE_LOCAL,
   531  			ImageURL:               "/myimage",
   532  			ProxiedRemovedImageURL: "http://mymattermost.com/myimage",
   533  			ProxiedImageURL:        "http://mymattermost.com/myimage",
   534  		},
   535  		"local_EmptyImageURL": {
   536  			ProxyType:              model.IMAGE_PROXY_TYPE_LOCAL,
   537  			ImageURL:               "",
   538  			ProxiedRemovedImageURL: "",
   539  			ProxiedImageURL:        "",
   540  		},
   541  	} {
   542  		t.Run(name, func(t *testing.T) {
   543  			th.App.UpdateConfig(func(cfg *model.Config) {
   544  				cfg.ImageProxySettings.Enable = model.NewBool(true)
   545  				cfg.ImageProxySettings.ImageProxyType = model.NewString(tc.ProxyType)
   546  				cfg.ImageProxySettings.RemoteImageProxyOptions = model.NewString(tc.ProxyOptions)
   547  				cfg.ImageProxySettings.RemoteImageProxyURL = model.NewString(tc.ProxyURL)
   548  			})
   549  
   550  			post := &model.Post{
   551  				Id:      model.NewId(),
   552  				Message: "![foo](" + tc.ImageURL + ")",
   553  			}
   554  
   555  			list := model.NewPostList()
   556  			list.Posts[post.Id] = post
   557  
   558  			assert.Equal(t, "![foo]("+tc.ProxiedImageURL+")", th.App.PostWithProxyAddedToImageURLs(post).Message)
   559  
   560  			assert.Equal(t, "![foo]("+tc.ImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   561  			post.Message = "![foo](" + tc.ProxiedImageURL + ")"
   562  			assert.Equal(t, "![foo]("+tc.ProxiedRemovedImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   563  
   564  			if tc.ImageURL != "" {
   565  				post.Message = "![foo](" + tc.ImageURL + " =500x200)"
   566  				assert.Equal(t, "![foo]("+tc.ProxiedImageURL+" =500x200)", th.App.PostWithProxyAddedToImageURLs(post).Message)
   567  				assert.Equal(t, "![foo]("+tc.ImageURL+" =500x200)", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   568  				post.Message = "![foo](" + tc.ProxiedImageURL + " =500x200)"
   569  				assert.Equal(t, "![foo]("+tc.ProxiedRemovedImageURL+" =500x200)", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
   570  			}
   571  		})
   572  	}
   573  }
   574  
   575  func TestMaxPostSize(t *testing.T) {
   576  	t.Skip("TODO: fix flaky test")
   577  	t.Parallel()
   578  
   579  	testCases := []struct {
   580  		Description         string
   581  		StoreMaxPostSize    int
   582  		ExpectedMaxPostSize int
   583  	}{
   584  		{
   585  			"Max post size less than model.model.POST_MESSAGE_MAX_RUNES_V1 ",
   586  			0,
   587  			model.POST_MESSAGE_MAX_RUNES_V1,
   588  		},
   589  		{
   590  			"4000 rune limit",
   591  			4000,
   592  			4000,
   593  		},
   594  		{
   595  			"16383 rune limit",
   596  			16383,
   597  			16383,
   598  		},
   599  	}
   600  
   601  	for _, testCase := range testCases {
   602  		testCase := testCase
   603  		t.Run(testCase.Description, func(t *testing.T) {
   604  			t.Parallel()
   605  
   606  			mockStore := &storetest.Store{}
   607  			defer mockStore.AssertExpectations(t)
   608  
   609  			mockStore.PostStore.On("GetMaxPostSize").Return(testCase.StoreMaxPostSize)
   610  
   611  			app := App{
   612  				srv: &Server{
   613  					Store: mockStore,
   614  				},
   615  			}
   616  
   617  			assert.Equal(t, testCase.ExpectedMaxPostSize, app.MaxPostSize())
   618  		})
   619  	}
   620  }
   621  
   622  func TestDeletePostWithFileAttachments(t *testing.T) {
   623  	th := Setup(t).InitBasic()
   624  	defer th.TearDown()
   625  
   626  	// Create a post with a file attachment.
   627  	teamID := th.BasicTeam.Id
   628  	channelId := th.BasicChannel.Id
   629  	userID := th.BasicUser.Id
   630  	filename := "test"
   631  	data := []byte("abcd")
   632  
   633  	info1, err := th.App.DoUploadFile(time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamID, channelId, userID, filename, data)
   634  	require.Nil(t, err)
   635  	defer func() {
   636  		th.App.Srv().Store.FileInfo().PermanentDelete(info1.Id)
   637  		th.App.RemoveFile(info1.Path)
   638  	}()
   639  
   640  	post := &model.Post{
   641  		Message:       "asd",
   642  		ChannelId:     channelId,
   643  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   644  		UserId:        userID,
   645  		CreateAt:      0,
   646  		FileIds:       []string{info1.Id},
   647  	}
   648  
   649  	post, err = th.App.CreatePost(post, th.BasicChannel, false, true)
   650  	assert.Nil(t, err)
   651  
   652  	// Delete the post.
   653  	post, err = th.App.DeletePost(post.Id, userID)
   654  	assert.Nil(t, err)
   655  
   656  	// Wait for the cleanup routine to finish.
   657  	time.Sleep(time.Millisecond * 100)
   658  
   659  	// Check that the file can no longer be reached.
   660  	_, err = th.App.GetFileInfo(info1.Id)
   661  	assert.NotNil(t, err)
   662  }
   663  
   664  func TestDeletePostInArchivedChannel(t *testing.T) {
   665  	th := Setup(t).InitBasic()
   666  	defer th.TearDown()
   667  
   668  	archivedChannel := th.CreateChannel(th.BasicTeam)
   669  	post := th.CreatePost(archivedChannel)
   670  	th.App.DeleteChannel(archivedChannel, "")
   671  
   672  	_, err := th.App.DeletePost(post.Id, "")
   673  	require.NotNil(t, err)
   674  	require.Equal(t, "api.post.delete_post.can_not_delete_post_in_deleted.error", err.Id)
   675  }
   676  
   677  func TestCreatePost(t *testing.T) {
   678  	t.Run("call PreparePostForClient before returning", func(t *testing.T) {
   679  		th := Setup(t).InitBasic()
   680  		defer th.TearDown()
   681  
   682  		th.App.UpdateConfig(func(cfg *model.Config) {
   683  			*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
   684  			*cfg.ImageProxySettings.Enable = true
   685  			*cfg.ImageProxySettings.ImageProxyType = "atmos/camo"
   686  			*cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1"
   687  			*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
   688  		})
   689  
   690  		th.Server.ImageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService, th.Server.Log)
   691  
   692  		imageURL := "http://mydomain.com/myimage"
   693  		proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
   694  
   695  		post := &model.Post{
   696  			ChannelId: th.BasicChannel.Id,
   697  			Message:   "![image](" + imageURL + ")",
   698  			UserId:    th.BasicUser.Id,
   699  		}
   700  
   701  		rpost, err := th.App.CreatePost(post, th.BasicChannel, false, true)
   702  		require.Nil(t, err)
   703  		assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message)
   704  	})
   705  
   706  	t.Run("Sets prop MENTION_HIGHLIGHT_DISABLED when it should", func(t *testing.T) {
   707  		th := Setup(t).InitBasic()
   708  		defer th.TearDown()
   709  		th.AddUserToChannel(th.BasicUser, th.BasicChannel)
   710  
   711  		t.Run("Does not set prop when user has USE_CHANNEL_MENTIONS", func(t *testing.T) {
   712  			postWithNoMention := &model.Post{
   713  				ChannelId: th.BasicChannel.Id,
   714  				Message:   "This post does not have mentions",
   715  				UserId:    th.BasicUser.Id,
   716  			}
   717  			rpost, err := th.App.CreatePost(postWithNoMention, th.BasicChannel, false, true)
   718  			require.Nil(t, err)
   719  			assert.Equal(t, rpost.GetProps(), model.StringInterface{})
   720  
   721  			postWithMention := &model.Post{
   722  				ChannelId: th.BasicChannel.Id,
   723  				Message:   "This post has @here mention @all",
   724  				UserId:    th.BasicUser.Id,
   725  			}
   726  			rpost, err = th.App.CreatePost(postWithMention, th.BasicChannel, false, true)
   727  			require.Nil(t, err)
   728  			assert.Equal(t, rpost.GetProps(), model.StringInterface{})
   729  		})
   730  
   731  		t.Run("Sets prop when post has mentions and user does not have USE_CHANNEL_MENTIONS", func(t *testing.T) {
   732  			th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
   733  			th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
   734  
   735  			postWithNoMention := &model.Post{
   736  				ChannelId: th.BasicChannel.Id,
   737  				Message:   "This post does not have mentions",
   738  				UserId:    th.BasicUser.Id,
   739  			}
   740  			rpost, err := th.App.CreatePost(postWithNoMention, th.BasicChannel, false, true)
   741  			require.Nil(t, err)
   742  			assert.Equal(t, rpost.GetProps(), model.StringInterface{})
   743  
   744  			postWithMention := &model.Post{
   745  				ChannelId: th.BasicChannel.Id,
   746  				Message:   "This post has @here mention @all",
   747  				UserId:    th.BasicUser.Id,
   748  			}
   749  			rpost, err = th.App.CreatePost(postWithMention, th.BasicChannel, false, true)
   750  			require.Nil(t, err)
   751  			assert.Equal(t, rpost.GetProp(model.POST_PROPS_MENTION_HIGHLIGHT_DISABLED), true)
   752  
   753  			th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
   754  			th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
   755  		})
   756  	})
   757  }
   758  
   759  func TestPatchPost(t *testing.T) {
   760  	t.Run("call PreparePostForClient before returning", func(t *testing.T) {
   761  		th := Setup(t).InitBasic()
   762  		defer th.TearDown()
   763  
   764  		th.App.UpdateConfig(func(cfg *model.Config) {
   765  			*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
   766  			*cfg.ImageProxySettings.Enable = true
   767  			*cfg.ImageProxySettings.ImageProxyType = "atmos/camo"
   768  			*cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1"
   769  			*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
   770  		})
   771  
   772  		th.Server.ImageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService, th.Server.Log)
   773  
   774  		imageURL := "http://mydomain.com/myimage"
   775  		proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
   776  
   777  		post := &model.Post{
   778  			ChannelId: th.BasicChannel.Id,
   779  			Message:   "![image](http://mydomain/anotherimage)",
   780  			UserId:    th.BasicUser.Id,
   781  		}
   782  
   783  		rpost, err := th.App.CreatePost(post, th.BasicChannel, false, true)
   784  		require.Nil(t, err)
   785  		assert.NotEqual(t, "![image]("+proxiedImageURL+")", rpost.Message)
   786  
   787  		patch := &model.PostPatch{
   788  			Message: model.NewString("![image](" + imageURL + ")"),
   789  		}
   790  
   791  		rpost, err = th.App.PatchPost(rpost.Id, patch)
   792  		require.Nil(t, err)
   793  		assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message)
   794  	})
   795  
   796  	t.Run("Sets Prop MENTION_HIGHLIGHT_DISABLED when it should", func(t *testing.T) {
   797  		th := Setup(t).InitBasic()
   798  		defer th.TearDown()
   799  
   800  		th.AddUserToChannel(th.BasicUser, th.BasicChannel)
   801  
   802  		post := &model.Post{
   803  			ChannelId: th.BasicChannel.Id,
   804  			Message:   "This post does not have mentions",
   805  			UserId:    th.BasicUser.Id,
   806  		}
   807  
   808  		rpost, err := th.App.CreatePost(post, th.BasicChannel, false, true)
   809  		require.Nil(t, err)
   810  
   811  		t.Run("Does not set prop when user has USE_CHANNEL_MENTIONS", func(t *testing.T) {
   812  			patchWithNoMention := &model.PostPatch{Message: model.NewString("This patch has no channel mention")}
   813  
   814  			rpost, err = th.App.PatchPost(rpost.Id, patchWithNoMention)
   815  			require.Nil(t, err)
   816  			assert.Equal(t, rpost.GetProps(), model.StringInterface{})
   817  
   818  			patchWithMention := &model.PostPatch{Message: model.NewString("This patch has a mention now @here")}
   819  
   820  			rpost, err = th.App.PatchPost(rpost.Id, patchWithMention)
   821  			require.Nil(t, err)
   822  			assert.Equal(t, rpost.GetProps(), model.StringInterface{})
   823  		})
   824  
   825  		t.Run("Sets prop when user does not have USE_CHANNEL_MENTIONS", func(t *testing.T) {
   826  			th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
   827  			th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
   828  
   829  			patchWithNoMention := &model.PostPatch{Message: model.NewString("This patch still does not have a mention")}
   830  			rpost, err = th.App.PatchPost(rpost.Id, patchWithNoMention)
   831  			require.Nil(t, err)
   832  			assert.Equal(t, rpost.GetProps(), model.StringInterface{})
   833  
   834  			patchWithMention := &model.PostPatch{Message: model.NewString("This patch has a mention now @here")}
   835  
   836  			rpost, err = th.App.PatchPost(rpost.Id, patchWithMention)
   837  			require.Nil(t, err)
   838  			assert.Equal(t, rpost.GetProp(model.POST_PROPS_MENTION_HIGHLIGHT_DISABLED), true)
   839  
   840  			th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
   841  			th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
   842  		})
   843  	})
   844  }
   845  
   846  func TestCreatePostAsUser(t *testing.T) {
   847  	t.Run("marks channel as viewed for regular user", func(t *testing.T) {
   848  		th := Setup(t).InitBasic()
   849  		defer th.TearDown()
   850  
   851  		post := &model.Post{
   852  			ChannelId: th.BasicChannel.Id,
   853  			Message:   "test",
   854  			UserId:    th.BasicUser.Id,
   855  		}
   856  
   857  		channelMemberBefore, err := th.App.Srv().Store.Channel().GetMember(th.BasicChannel.Id, th.BasicUser.Id)
   858  		require.NoError(t, err)
   859  
   860  		time.Sleep(1 * time.Millisecond)
   861  		_, appErr := th.App.CreatePostAsUser(post, "", true)
   862  		require.Nil(t, appErr)
   863  
   864  		channelMemberAfter, err := th.App.Srv().Store.Channel().GetMember(th.BasicChannel.Id, th.BasicUser.Id)
   865  		require.NoError(t, err)
   866  
   867  		require.Greater(t, channelMemberAfter.LastViewedAt, channelMemberBefore.LastViewedAt)
   868  	})
   869  
   870  	t.Run("does not mark channel as viewed for webhook from user", func(t *testing.T) {
   871  		th := Setup(t).InitBasic()
   872  		defer th.TearDown()
   873  
   874  		post := &model.Post{
   875  			ChannelId: th.BasicChannel.Id,
   876  			Message:   "test",
   877  			UserId:    th.BasicUser.Id,
   878  		}
   879  		post.AddProp("from_webhook", "true")
   880  
   881  		channelMemberBefore, err := th.App.Srv().Store.Channel().GetMember(th.BasicChannel.Id, th.BasicUser.Id)
   882  		require.NoError(t, err)
   883  
   884  		time.Sleep(1 * time.Millisecond)
   885  		_, appErr := th.App.CreatePostAsUser(post, "", true)
   886  		require.Nil(t, appErr)
   887  
   888  		channelMemberAfter, err := th.App.Srv().Store.Channel().GetMember(th.BasicChannel.Id, th.BasicUser.Id)
   889  		require.NoError(t, err)
   890  
   891  		require.Equal(t, channelMemberAfter.LastViewedAt, channelMemberBefore.LastViewedAt)
   892  	})
   893  
   894  	t.Run("does not mark channel as viewed for bot user in channel", func(t *testing.T) {
   895  		th := Setup(t).InitBasic()
   896  		defer th.TearDown()
   897  
   898  		bot := th.CreateBot()
   899  
   900  		botUser, appErr := th.App.GetUser(bot.UserId)
   901  		require.Nil(t, appErr)
   902  
   903  		th.LinkUserToTeam(botUser, th.BasicTeam)
   904  		th.AddUserToChannel(botUser, th.BasicChannel)
   905  
   906  		post := &model.Post{
   907  			ChannelId: th.BasicChannel.Id,
   908  			Message:   "test",
   909  			UserId:    bot.UserId,
   910  		}
   911  
   912  		channelMemberBefore, nErr := th.App.Srv().Store.Channel().GetMember(th.BasicChannel.Id, th.BasicUser.Id)
   913  		require.NoError(t, nErr)
   914  
   915  		time.Sleep(1 * time.Millisecond)
   916  		_, appErr = th.App.CreatePostAsUser(post, "", true)
   917  		require.Nil(t, appErr)
   918  
   919  		channelMemberAfter, nErr := th.App.Srv().Store.Channel().GetMember(th.BasicChannel.Id, th.BasicUser.Id)
   920  		require.NoError(t, nErr)
   921  
   922  		require.Equal(t, channelMemberAfter.LastViewedAt, channelMemberBefore.LastViewedAt)
   923  	})
   924  
   925  	t.Run("logs warning for user not in channel", func(t *testing.T) {
   926  		th := Setup(t).InitBasic()
   927  		defer th.TearDown()
   928  
   929  		user := th.CreateUser()
   930  		th.LinkUserToTeam(user, th.BasicTeam)
   931  
   932  		post := &model.Post{
   933  			ChannelId: th.BasicChannel.Id,
   934  			Message:   "test",
   935  			UserId:    user.Id,
   936  		}
   937  
   938  		_, appErr := th.App.CreatePostAsUser(post, "", true)
   939  		require.Nil(t, appErr)
   940  
   941  		testlib.AssertLog(t, th.LogBuffer, mlog.LevelWarn, "Failed to get membership")
   942  	})
   943  
   944  	t.Run("does not log warning for bot user not in channel", func(t *testing.T) {
   945  		th := Setup(t).InitBasic()
   946  		defer th.TearDown()
   947  
   948  		bot := th.CreateBot()
   949  
   950  		botUser, appErr := th.App.GetUser(bot.UserId)
   951  		require.Nil(t, appErr)
   952  
   953  		th.LinkUserToTeam(botUser, th.BasicTeam)
   954  
   955  		post := &model.Post{
   956  			ChannelId: th.BasicChannel.Id,
   957  			Message:   "test",
   958  			UserId:    bot.UserId,
   959  		}
   960  
   961  		_, appErr = th.App.CreatePostAsUser(post, "", true)
   962  		require.Nil(t, appErr)
   963  
   964  		testlib.AssertNoLog(t, th.LogBuffer, mlog.LevelWarn, "Failed to get membership")
   965  	})
   966  }
   967  
   968  func TestPatchPostInArchivedChannel(t *testing.T) {
   969  	th := Setup(t).InitBasic()
   970  	defer th.TearDown()
   971  
   972  	archivedChannel := th.CreateChannel(th.BasicTeam)
   973  	post := th.CreatePost(archivedChannel)
   974  	th.App.DeleteChannel(archivedChannel, "")
   975  
   976  	_, err := th.App.PatchPost(post.Id, &model.PostPatch{IsPinned: model.NewBool(true)})
   977  	require.NotNil(t, err)
   978  	require.Equal(t, "api.post.patch_post.can_not_update_post_in_deleted.error", err.Id)
   979  }
   980  
   981  func TestUpdatePost(t *testing.T) {
   982  	t.Run("call PreparePostForClient before returning", func(t *testing.T) {
   983  		th := Setup(t).InitBasic()
   984  		defer th.TearDown()
   985  
   986  		th.App.UpdateConfig(func(cfg *model.Config) {
   987  			*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
   988  			*cfg.ImageProxySettings.Enable = true
   989  			*cfg.ImageProxySettings.ImageProxyType = "atmos/camo"
   990  			*cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1"
   991  			*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
   992  		})
   993  
   994  		th.Server.ImageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService, th.Server.Log)
   995  
   996  		imageURL := "http://mydomain.com/myimage"
   997  		proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
   998  
   999  		post := &model.Post{
  1000  			ChannelId: th.BasicChannel.Id,
  1001  			Message:   "![image](http://mydomain/anotherimage)",
  1002  			UserId:    th.BasicUser.Id,
  1003  		}
  1004  
  1005  		rpost, err := th.App.CreatePost(post, th.BasicChannel, false, true)
  1006  		require.Nil(t, err)
  1007  		assert.NotEqual(t, "![image]("+proxiedImageURL+")", rpost.Message)
  1008  
  1009  		post.Id = rpost.Id
  1010  		post.Message = "![image](" + imageURL + ")"
  1011  
  1012  		rpost, err = th.App.UpdatePost(post, false)
  1013  		require.Nil(t, err)
  1014  		assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message)
  1015  	})
  1016  }
  1017  
  1018  func TestSearchPostsInTeamForUser(t *testing.T) {
  1019  	perPage := 5
  1020  	searchTerm := "searchTerm"
  1021  
  1022  	setup := func(t *testing.T, enableElasticsearch bool) (*TestHelper, []*model.Post) {
  1023  		th := Setup(t).InitBasic()
  1024  
  1025  		posts := make([]*model.Post, 7)
  1026  		for i := 0; i < cap(posts); i++ {
  1027  			post, err := th.App.CreatePost(&model.Post{
  1028  				UserId:    th.BasicUser.Id,
  1029  				ChannelId: th.BasicChannel.Id,
  1030  				Message:   searchTerm,
  1031  			}, th.BasicChannel, false, true)
  1032  
  1033  			require.Nil(t, err)
  1034  
  1035  			posts[i] = post
  1036  		}
  1037  
  1038  		if enableElasticsearch {
  1039  			th.App.Srv().SetLicense(model.NewTestLicense("elastic_search"))
  1040  
  1041  			th.App.UpdateConfig(func(cfg *model.Config) {
  1042  				*cfg.ElasticsearchSettings.EnableIndexing = true
  1043  				*cfg.ElasticsearchSettings.EnableSearching = true
  1044  			})
  1045  		} else {
  1046  			th.App.UpdateConfig(func(cfg *model.Config) {
  1047  				*cfg.ElasticsearchSettings.EnableSearching = false
  1048  			})
  1049  		}
  1050  
  1051  		return th, posts
  1052  	}
  1053  
  1054  	t.Run("should return everything as first page of posts from database", func(t *testing.T) {
  1055  		th, posts := setup(t, false)
  1056  		defer th.TearDown()
  1057  
  1058  		page := 0
  1059  
  1060  		results, err := th.App.SearchPostsInTeamForUser(searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
  1061  
  1062  		assert.Nil(t, err)
  1063  		assert.Equal(t, []string{
  1064  			posts[6].Id,
  1065  			posts[5].Id,
  1066  			posts[4].Id,
  1067  			posts[3].Id,
  1068  			posts[2].Id,
  1069  			posts[1].Id,
  1070  			posts[0].Id,
  1071  		}, results.Order)
  1072  	})
  1073  
  1074  	t.Run("should not return later pages of posts from database", func(t *testing.T) {
  1075  		th, _ := setup(t, false)
  1076  		defer th.TearDown()
  1077  
  1078  		page := 1
  1079  
  1080  		results, err := th.App.SearchPostsInTeamForUser(searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
  1081  
  1082  		assert.Nil(t, err)
  1083  		assert.Equal(t, []string{}, results.Order)
  1084  	})
  1085  
  1086  	t.Run("should return first page of posts from ElasticSearch", func(t *testing.T) {
  1087  		th, posts := setup(t, true)
  1088  		defer th.TearDown()
  1089  
  1090  		page := 0
  1091  		resultsPage := []string{
  1092  			posts[6].Id,
  1093  			posts[5].Id,
  1094  			posts[4].Id,
  1095  			posts[3].Id,
  1096  			posts[2].Id,
  1097  		}
  1098  
  1099  		es := &mocks.SearchEngineInterface{}
  1100  		es.On("SearchPosts", mock.Anything, mock.Anything, page, perPage).Return(resultsPage, nil, nil)
  1101  		es.On("GetName").Return("mock")
  1102  		es.On("Start").Return(nil).Maybe()
  1103  		es.On("IsActive").Return(true)
  1104  		es.On("IsSearchEnabled").Return(true)
  1105  		th.App.Srv().SearchEngine.ElasticsearchEngine = es
  1106  		defer func() {
  1107  			th.App.Srv().SearchEngine.ElasticsearchEngine = nil
  1108  		}()
  1109  
  1110  		results, err := th.App.SearchPostsInTeamForUser(searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
  1111  
  1112  		assert.Nil(t, err)
  1113  		assert.Equal(t, resultsPage, results.Order)
  1114  		es.AssertExpectations(t)
  1115  	})
  1116  
  1117  	t.Run("should return later pages of posts from ElasticSearch", func(t *testing.T) {
  1118  		th, posts := setup(t, true)
  1119  		defer th.TearDown()
  1120  
  1121  		page := 1
  1122  		resultsPage := []string{
  1123  			posts[1].Id,
  1124  			posts[0].Id,
  1125  		}
  1126  
  1127  		es := &mocks.SearchEngineInterface{}
  1128  		es.On("SearchPosts", mock.Anything, mock.Anything, page, perPage).Return(resultsPage, nil, nil)
  1129  		es.On("GetName").Return("mock")
  1130  		es.On("Start").Return(nil).Maybe()
  1131  		es.On("IsActive").Return(true)
  1132  		es.On("IsSearchEnabled").Return(true)
  1133  		th.App.Srv().SearchEngine.ElasticsearchEngine = es
  1134  		defer func() {
  1135  			th.App.Srv().SearchEngine.ElasticsearchEngine = nil
  1136  		}()
  1137  
  1138  		results, err := th.App.SearchPostsInTeamForUser(searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
  1139  
  1140  		assert.Nil(t, err)
  1141  		assert.Equal(t, resultsPage, results.Order)
  1142  		es.AssertExpectations(t)
  1143  	})
  1144  
  1145  	t.Run("should fall back to database if ElasticSearch fails on first page", func(t *testing.T) {
  1146  		th, posts := setup(t, true)
  1147  		defer th.TearDown()
  1148  
  1149  		page := 0
  1150  
  1151  		es := &mocks.SearchEngineInterface{}
  1152  		es.On("SearchPosts", mock.Anything, mock.Anything, page, perPage).Return(nil, nil, &model.AppError{})
  1153  		es.On("GetName").Return("mock")
  1154  		es.On("Start").Return(nil).Maybe()
  1155  		es.On("IsActive").Return(true)
  1156  		es.On("IsSearchEnabled").Return(true)
  1157  		th.App.Srv().SearchEngine.ElasticsearchEngine = es
  1158  		defer func() {
  1159  			th.App.Srv().SearchEngine.ElasticsearchEngine = nil
  1160  		}()
  1161  
  1162  		results, err := th.App.SearchPostsInTeamForUser(searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
  1163  
  1164  		assert.Nil(t, err)
  1165  		assert.Equal(t, []string{
  1166  			posts[6].Id,
  1167  			posts[5].Id,
  1168  			posts[4].Id,
  1169  			posts[3].Id,
  1170  			posts[2].Id,
  1171  			posts[1].Id,
  1172  			posts[0].Id,
  1173  		}, results.Order)
  1174  		es.AssertExpectations(t)
  1175  	})
  1176  
  1177  	t.Run("should return nothing if ElasticSearch fails on later pages", func(t *testing.T) {
  1178  		th, _ := setup(t, true)
  1179  		defer th.TearDown()
  1180  
  1181  		page := 1
  1182  
  1183  		es := &mocks.SearchEngineInterface{}
  1184  		es.On("SearchPosts", mock.Anything, mock.Anything, page, perPage).Return(nil, nil, &model.AppError{})
  1185  		es.On("GetName").Return("mock")
  1186  		es.On("Start").Return(nil).Maybe()
  1187  		es.On("IsActive").Return(true)
  1188  		es.On("IsSearchEnabled").Return(true)
  1189  		th.App.Srv().SearchEngine.ElasticsearchEngine = es
  1190  		defer func() {
  1191  			th.App.Srv().SearchEngine.ElasticsearchEngine = nil
  1192  		}()
  1193  
  1194  		results, err := th.App.SearchPostsInTeamForUser(searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage)
  1195  
  1196  		assert.Nil(t, err)
  1197  		assert.Equal(t, []string{}, results.Order)
  1198  		es.AssertExpectations(t)
  1199  	})
  1200  }
  1201  
  1202  func TestCountMentionsFromPost(t *testing.T) {
  1203  	t.Run("should not count posts without mentions", func(t *testing.T) {
  1204  		th := Setup(t).InitBasic()
  1205  		defer th.TearDown()
  1206  
  1207  		user1 := th.BasicUser
  1208  		user2 := th.BasicUser2
  1209  
  1210  		channel := th.CreateChannel(th.BasicTeam)
  1211  		th.AddUserToChannel(user2, channel)
  1212  
  1213  		post1, err := th.App.CreatePost(&model.Post{
  1214  			UserId:    user1.Id,
  1215  			ChannelId: channel.Id,
  1216  			Message:   "test",
  1217  		}, channel, false, true)
  1218  		require.Nil(t, err)
  1219  		_, err = th.App.CreatePost(&model.Post{
  1220  			UserId:    user1.Id,
  1221  			ChannelId: channel.Id,
  1222  			Message:   "test2",
  1223  		}, channel, false, true)
  1224  		require.Nil(t, err)
  1225  		_, err = th.App.CreatePost(&model.Post{
  1226  			UserId:    user1.Id,
  1227  			ChannelId: channel.Id,
  1228  			Message:   "test3",
  1229  		}, channel, false, true)
  1230  		require.Nil(t, err)
  1231  
  1232  		count, err := th.App.countMentionsFromPost(user2, post1)
  1233  
  1234  		assert.Nil(t, err)
  1235  		assert.Equal(t, 0, count)
  1236  	})
  1237  
  1238  	t.Run("should count keyword mentions", func(t *testing.T) {
  1239  		th := Setup(t).InitBasic()
  1240  		defer th.TearDown()
  1241  
  1242  		user1 := th.BasicUser
  1243  		user2 := th.BasicUser2
  1244  
  1245  		channel := th.CreateChannel(th.BasicTeam)
  1246  		th.AddUserToChannel(user2, channel)
  1247  
  1248  		user2.NotifyProps[model.MENTION_KEYS_NOTIFY_PROP] = "apple"
  1249  
  1250  		post1, err := th.App.CreatePost(&model.Post{
  1251  			UserId:    user1.Id,
  1252  			ChannelId: channel.Id,
  1253  			Message:   fmt.Sprintf("@%s", user2.Username),
  1254  		}, channel, false, true)
  1255  		require.Nil(t, err)
  1256  		_, err = th.App.CreatePost(&model.Post{
  1257  			UserId:    user1.Id,
  1258  			ChannelId: channel.Id,
  1259  			Message:   "test2",
  1260  		}, channel, false, true)
  1261  		require.Nil(t, err)
  1262  		_, err = th.App.CreatePost(&model.Post{
  1263  			UserId:    user1.Id,
  1264  			ChannelId: channel.Id,
  1265  			Message:   "apple",
  1266  		}, channel, false, true)
  1267  		require.Nil(t, err)
  1268  
  1269  		// post1 and post3 should mention the user
  1270  
  1271  		count, err := th.App.countMentionsFromPost(user2, post1)
  1272  
  1273  		assert.Nil(t, err)
  1274  		assert.Equal(t, 2, count)
  1275  	})
  1276  
  1277  	t.Run("should count channel-wide mentions when enabled", func(t *testing.T) {
  1278  		th := Setup(t).InitBasic()
  1279  		defer th.TearDown()
  1280  
  1281  		user1 := th.BasicUser
  1282  		user2 := th.BasicUser2
  1283  
  1284  		channel := th.CreateChannel(th.BasicTeam)
  1285  		th.AddUserToChannel(user2, channel)
  1286  
  1287  		user2.NotifyProps[model.CHANNEL_MENTIONS_NOTIFY_PROP] = "true"
  1288  
  1289  		post1, err := th.App.CreatePost(&model.Post{
  1290  			UserId:    user1.Id,
  1291  			ChannelId: channel.Id,
  1292  			Message:   "test",
  1293  		}, channel, false, true)
  1294  		require.Nil(t, err)
  1295  		_, err = th.App.CreatePost(&model.Post{
  1296  			UserId:    user1.Id,
  1297  			ChannelId: channel.Id,
  1298  			Message:   "@channel",
  1299  		}, channel, false, true)
  1300  		require.Nil(t, err)
  1301  		_, err = th.App.CreatePost(&model.Post{
  1302  			UserId:    user1.Id,
  1303  			ChannelId: channel.Id,
  1304  			Message:   "@all",
  1305  		}, channel, false, true)
  1306  		require.Nil(t, err)
  1307  
  1308  		// post2 and post3 should mention the user
  1309  
  1310  		count, err := th.App.countMentionsFromPost(user2, post1)
  1311  
  1312  		assert.Nil(t, err)
  1313  		assert.Equal(t, 2, count)
  1314  	})
  1315  
  1316  	t.Run("should not count channel-wide mentions when disabled for user", func(t *testing.T) {
  1317  		th := Setup(t).InitBasic()
  1318  		defer th.TearDown()
  1319  
  1320  		user1 := th.BasicUser
  1321  		user2 := th.BasicUser2
  1322  
  1323  		channel := th.CreateChannel(th.BasicTeam)
  1324  		th.AddUserToChannel(user2, channel)
  1325  
  1326  		user2.NotifyProps[model.CHANNEL_MENTIONS_NOTIFY_PROP] = "false"
  1327  
  1328  		post1, err := th.App.CreatePost(&model.Post{
  1329  			UserId:    user1.Id,
  1330  			ChannelId: channel.Id,
  1331  			Message:   "test",
  1332  		}, channel, false, true)
  1333  		require.Nil(t, err)
  1334  		_, err = th.App.CreatePost(&model.Post{
  1335  			UserId:    user1.Id,
  1336  			ChannelId: channel.Id,
  1337  			Message:   "@channel",
  1338  		}, channel, false, true)
  1339  		require.Nil(t, err)
  1340  		_, err = th.App.CreatePost(&model.Post{
  1341  			UserId:    user1.Id,
  1342  			ChannelId: channel.Id,
  1343  			Message:   "@all",
  1344  		}, channel, false, true)
  1345  		require.Nil(t, err)
  1346  
  1347  		count, err := th.App.countMentionsFromPost(user2, post1)
  1348  
  1349  		assert.Nil(t, err)
  1350  		assert.Equal(t, 0, count)
  1351  	})
  1352  
  1353  	t.Run("should not count channel-wide mentions when disabled for channel", func(t *testing.T) {
  1354  		th := Setup(t).InitBasic()
  1355  		defer th.TearDown()
  1356  
  1357  		user1 := th.BasicUser
  1358  		user2 := th.BasicUser2
  1359  
  1360  		channel := th.CreateChannel(th.BasicTeam)
  1361  		th.AddUserToChannel(user2, channel)
  1362  
  1363  		user2.NotifyProps[model.CHANNEL_MENTIONS_NOTIFY_PROP] = "true"
  1364  
  1365  		_, err := th.App.UpdateChannelMemberNotifyProps(map[string]string{
  1366  			model.IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP: model.IGNORE_CHANNEL_MENTIONS_ON,
  1367  		}, channel.Id, user2.Id)
  1368  		require.Nil(t, err)
  1369  
  1370  		post1, err := th.App.CreatePost(&model.Post{
  1371  			UserId:    user1.Id,
  1372  			ChannelId: channel.Id,
  1373  			Message:   "test",
  1374  		}, channel, false, true)
  1375  		require.Nil(t, err)
  1376  		_, err = th.App.CreatePost(&model.Post{
  1377  			UserId:    user1.Id,
  1378  			ChannelId: channel.Id,
  1379  			Message:   "@channel",
  1380  		}, channel, false, true)
  1381  		require.Nil(t, err)
  1382  		_, err = th.App.CreatePost(&model.Post{
  1383  			UserId:    user1.Id,
  1384  			ChannelId: channel.Id,
  1385  			Message:   "@all",
  1386  		}, channel, false, true)
  1387  		require.Nil(t, err)
  1388  
  1389  		count, err := th.App.countMentionsFromPost(user2, post1)
  1390  
  1391  		assert.Nil(t, err)
  1392  		assert.Equal(t, 0, count)
  1393  	})
  1394  
  1395  	t.Run("should count comment mentions when using COMMENTS_NOTIFY_ROOT", func(t *testing.T) {
  1396  		th := Setup(t).InitBasic()
  1397  		defer th.TearDown()
  1398  
  1399  		user1 := th.BasicUser
  1400  		user2 := th.BasicUser2
  1401  
  1402  		channel := th.CreateChannel(th.BasicTeam)
  1403  		th.AddUserToChannel(user2, channel)
  1404  
  1405  		user2.NotifyProps[model.COMMENTS_NOTIFY_PROP] = model.COMMENTS_NOTIFY_ROOT
  1406  
  1407  		post1, err := th.App.CreatePost(&model.Post{
  1408  			UserId:    user2.Id,
  1409  			ChannelId: channel.Id,
  1410  			Message:   "test",
  1411  		}, channel, false, true)
  1412  		require.Nil(t, err)
  1413  		_, err = th.App.CreatePost(&model.Post{
  1414  			UserId:    user1.Id,
  1415  			ChannelId: channel.Id,
  1416  			RootId:    post1.Id,
  1417  			Message:   "test2",
  1418  		}, channel, false, true)
  1419  		require.Nil(t, err)
  1420  		post3, err := th.App.CreatePost(&model.Post{
  1421  			UserId:    user1.Id,
  1422  			ChannelId: channel.Id,
  1423  			Message:   "test3",
  1424  		}, channel, false, true)
  1425  		require.Nil(t, err)
  1426  		_, err = th.App.CreatePost(&model.Post{
  1427  			UserId:    user2.Id,
  1428  			ChannelId: channel.Id,
  1429  			RootId:    post3.Id,
  1430  			Message:   "test4",
  1431  		}, channel, false, true)
  1432  		require.Nil(t, err)
  1433  		_, err = th.App.CreatePost(&model.Post{
  1434  			UserId:    user1.Id,
  1435  			ChannelId: channel.Id,
  1436  			RootId:    post3.Id,
  1437  			Message:   "test5",
  1438  		}, channel, false, true)
  1439  		require.Nil(t, err)
  1440  
  1441  		// post2 should mention the user
  1442  
  1443  		count, err := th.App.countMentionsFromPost(user2, post1)
  1444  
  1445  		assert.Nil(t, err)
  1446  		assert.Equal(t, 1, count)
  1447  	})
  1448  
  1449  	t.Run("should count comment mentions when using COMMENTS_NOTIFY_ANY", func(t *testing.T) {
  1450  		th := Setup(t).InitBasic()
  1451  		defer th.TearDown()
  1452  
  1453  		user1 := th.BasicUser
  1454  		user2 := th.BasicUser2
  1455  
  1456  		channel := th.CreateChannel(th.BasicTeam)
  1457  		th.AddUserToChannel(user2, channel)
  1458  
  1459  		user2.NotifyProps[model.COMMENTS_NOTIFY_PROP] = model.COMMENTS_NOTIFY_ANY
  1460  
  1461  		post1, err := th.App.CreatePost(&model.Post{
  1462  			UserId:    user2.Id,
  1463  			ChannelId: channel.Id,
  1464  			Message:   "test",
  1465  		}, channel, false, true)
  1466  		require.Nil(t, err)
  1467  		_, err = th.App.CreatePost(&model.Post{
  1468  			UserId:    user1.Id,
  1469  			ChannelId: channel.Id,
  1470  			RootId:    post1.Id,
  1471  			Message:   "test2",
  1472  		}, channel, false, true)
  1473  		require.Nil(t, err)
  1474  		post3, err := th.App.CreatePost(&model.Post{
  1475  			UserId:    user1.Id,
  1476  			ChannelId: channel.Id,
  1477  			Message:   "test3",
  1478  		}, channel, false, true)
  1479  		require.Nil(t, err)
  1480  		_, err = th.App.CreatePost(&model.Post{
  1481  			UserId:    user2.Id,
  1482  			ChannelId: channel.Id,
  1483  			RootId:    post3.Id,
  1484  			Message:   "test4",
  1485  		}, channel, false, true)
  1486  		require.Nil(t, err)
  1487  		_, err = th.App.CreatePost(&model.Post{
  1488  			UserId:    user1.Id,
  1489  			ChannelId: channel.Id,
  1490  			RootId:    post3.Id,
  1491  			Message:   "test5",
  1492  		}, channel, false, true)
  1493  		require.Nil(t, err)
  1494  
  1495  		// post2 and post5 should mention the user
  1496  
  1497  		count, err := th.App.countMentionsFromPost(user2, post1)
  1498  
  1499  		assert.Nil(t, err)
  1500  		assert.Equal(t, 2, count)
  1501  	})
  1502  
  1503  	t.Run("should count mentions caused by being added to the channel", func(t *testing.T) {
  1504  		th := Setup(t).InitBasic()
  1505  		defer th.TearDown()
  1506  
  1507  		user1 := th.BasicUser
  1508  		user2 := th.BasicUser2
  1509  
  1510  		channel := th.CreateChannel(th.BasicTeam)
  1511  		th.AddUserToChannel(user2, channel)
  1512  
  1513  		post1, err := th.App.CreatePost(&model.Post{
  1514  			UserId:    user1.Id,
  1515  			ChannelId: channel.Id,
  1516  			Message:   "test",
  1517  			Type:      model.POST_ADD_TO_CHANNEL,
  1518  			Props: map[string]interface{}{
  1519  				model.POST_PROPS_ADDED_USER_ID: model.NewId(),
  1520  			},
  1521  		}, channel, false, true)
  1522  		require.Nil(t, err)
  1523  		_, err = th.App.CreatePost(&model.Post{
  1524  			UserId:    user1.Id,
  1525  			ChannelId: channel.Id,
  1526  			Message:   "test2",
  1527  			Type:      model.POST_ADD_TO_CHANNEL,
  1528  			Props: map[string]interface{}{
  1529  				model.POST_PROPS_ADDED_USER_ID: user2.Id,
  1530  			},
  1531  		}, channel, false, true)
  1532  		require.Nil(t, err)
  1533  		_, err = th.App.CreatePost(&model.Post{
  1534  			UserId:    user1.Id,
  1535  			ChannelId: channel.Id,
  1536  			Message:   "test3",
  1537  			Type:      model.POST_ADD_TO_CHANNEL,
  1538  			Props: map[string]interface{}{
  1539  				model.POST_PROPS_ADDED_USER_ID: user2.Id,
  1540  			},
  1541  		}, channel, false, true)
  1542  		require.Nil(t, err)
  1543  
  1544  		// should be mentioned by post2 and post3
  1545  
  1546  		count, err := th.App.countMentionsFromPost(user2, post1)
  1547  
  1548  		assert.Nil(t, err)
  1549  		assert.Equal(t, 2, count)
  1550  	})
  1551  
  1552  	t.Run("should return the number of posts made by the other user for a direct channel", func(t *testing.T) {
  1553  		th := Setup(t).InitBasic()
  1554  		defer th.TearDown()
  1555  
  1556  		user1 := th.BasicUser
  1557  		user2 := th.BasicUser2
  1558  
  1559  		channel, err := th.App.createDirectChannel(user1.Id, user2.Id)
  1560  		require.Nil(t, err)
  1561  
  1562  		post1, err := th.App.CreatePost(&model.Post{
  1563  			UserId:    user1.Id,
  1564  			ChannelId: channel.Id,
  1565  			Message:   "test",
  1566  		}, channel, false, true)
  1567  		require.Nil(t, err)
  1568  
  1569  		_, err = th.App.CreatePost(&model.Post{
  1570  			UserId:    user1.Id,
  1571  			ChannelId: channel.Id,
  1572  			Message:   "test2",
  1573  		}, channel, false, true)
  1574  		require.Nil(t, err)
  1575  
  1576  		count, err := th.App.countMentionsFromPost(user2, post1)
  1577  
  1578  		assert.Nil(t, err)
  1579  		assert.Equal(t, 2, count)
  1580  
  1581  		count, err = th.App.countMentionsFromPost(user1, post1)
  1582  
  1583  		assert.Nil(t, err)
  1584  		assert.Equal(t, 0, count)
  1585  	})
  1586  
  1587  	t.Run("should not count mentions from the before the given post", func(t *testing.T) {
  1588  		th := Setup(t).InitBasic()
  1589  		defer th.TearDown()
  1590  
  1591  		user1 := th.BasicUser
  1592  		user2 := th.BasicUser2
  1593  
  1594  		channel := th.CreateChannel(th.BasicTeam)
  1595  		th.AddUserToChannel(user2, channel)
  1596  
  1597  		_, err := th.App.CreatePost(&model.Post{
  1598  			UserId:    user1.Id,
  1599  			ChannelId: channel.Id,
  1600  			Message:   fmt.Sprintf("@%s", user2.Username),
  1601  		}, channel, false, true)
  1602  		require.Nil(t, err)
  1603  		post2, err := th.App.CreatePost(&model.Post{
  1604  			UserId:    user1.Id,
  1605  			ChannelId: channel.Id,
  1606  			Message:   "test2",
  1607  		}, channel, false, true)
  1608  		require.Nil(t, err)
  1609  		_, err = th.App.CreatePost(&model.Post{
  1610  			UserId:    user1.Id,
  1611  			ChannelId: channel.Id,
  1612  			Message:   fmt.Sprintf("@%s", user2.Username),
  1613  		}, channel, false, true)
  1614  		require.Nil(t, err)
  1615  
  1616  		// post1 and post3 should mention the user, but we only count post3
  1617  
  1618  		count, err := th.App.countMentionsFromPost(user2, post2)
  1619  
  1620  		assert.Nil(t, err)
  1621  		assert.Equal(t, 1, count)
  1622  	})
  1623  
  1624  	t.Run("should not count mentions from the user's own posts", func(t *testing.T) {
  1625  		th := Setup(t).InitBasic()
  1626  		defer th.TearDown()
  1627  
  1628  		user1 := th.BasicUser
  1629  		user2 := th.BasicUser2
  1630  
  1631  		channel := th.CreateChannel(th.BasicTeam)
  1632  		th.AddUserToChannel(user2, channel)
  1633  
  1634  		post1, err := th.App.CreatePost(&model.Post{
  1635  			UserId:    user1.Id,
  1636  			ChannelId: channel.Id,
  1637  			Message:   fmt.Sprintf("@%s", user2.Username),
  1638  		}, channel, false, true)
  1639  		require.Nil(t, err)
  1640  		_, err = th.App.CreatePost(&model.Post{
  1641  			UserId:    user2.Id,
  1642  			ChannelId: channel.Id,
  1643  			Message:   fmt.Sprintf("@%s", user2.Username),
  1644  		}, channel, false, true)
  1645  		require.Nil(t, err)
  1646  
  1647  		// post2 should mention the user
  1648  
  1649  		count, err := th.App.countMentionsFromPost(user2, post1)
  1650  
  1651  		assert.Nil(t, err)
  1652  		assert.Equal(t, 1, count)
  1653  	})
  1654  
  1655  	t.Run("should include comments made before the given post when counting comment mentions", func(t *testing.T) {
  1656  		th := Setup(t).InitBasic()
  1657  		defer th.TearDown()
  1658  
  1659  		user1 := th.BasicUser
  1660  		user2 := th.BasicUser2
  1661  
  1662  		channel := th.CreateChannel(th.BasicTeam)
  1663  		th.AddUserToChannel(user2, channel)
  1664  
  1665  		user2.NotifyProps[model.COMMENTS_NOTIFY_PROP] = model.COMMENTS_NOTIFY_ANY
  1666  
  1667  		post1, err := th.App.CreatePost(&model.Post{
  1668  			UserId:    user1.Id,
  1669  			ChannelId: channel.Id,
  1670  			Message:   "test1",
  1671  		}, channel, false, true)
  1672  		require.Nil(t, err)
  1673  		_, err = th.App.CreatePost(&model.Post{
  1674  			UserId:    user2.Id,
  1675  			ChannelId: channel.Id,
  1676  			RootId:    post1.Id,
  1677  			Message:   "test2",
  1678  		}, channel, false, true)
  1679  		require.Nil(t, err)
  1680  		post3, err := th.App.CreatePost(&model.Post{
  1681  			UserId:    user1.Id,
  1682  			ChannelId: channel.Id,
  1683  			Message:   "test3",
  1684  		}, channel, false, true)
  1685  		require.Nil(t, err)
  1686  		_, err = th.App.CreatePost(&model.Post{
  1687  			UserId:    user1.Id,
  1688  			ChannelId: channel.Id,
  1689  			RootId:    post1.Id,
  1690  			Message:   "test4",
  1691  		}, channel, false, true)
  1692  		require.Nil(t, err)
  1693  
  1694  		// post4 should mention the user
  1695  
  1696  		count, err := th.App.countMentionsFromPost(user2, post3)
  1697  
  1698  		assert.Nil(t, err)
  1699  		assert.Equal(t, 1, count)
  1700  	})
  1701  
  1702  	t.Run("should count mentions from the user's webhook posts", func(t *testing.T) {
  1703  		th := Setup(t).InitBasic()
  1704  		defer th.TearDown()
  1705  
  1706  		user1 := th.BasicUser
  1707  		user2 := th.BasicUser2
  1708  
  1709  		channel := th.CreateChannel(th.BasicTeam)
  1710  		th.AddUserToChannel(user2, channel)
  1711  
  1712  		post1, err := th.App.CreatePost(&model.Post{
  1713  			UserId:    user1.Id,
  1714  			ChannelId: channel.Id,
  1715  			Message:   "test1",
  1716  		}, channel, false, true)
  1717  		require.Nil(t, err)
  1718  		_, err = th.App.CreatePost(&model.Post{
  1719  			UserId:    user2.Id,
  1720  			ChannelId: channel.Id,
  1721  			Message:   fmt.Sprintf("@%s", user2.Username),
  1722  		}, channel, false, true)
  1723  		require.Nil(t, err)
  1724  		_, err = th.App.CreatePost(&model.Post{
  1725  			UserId:    user2.Id,
  1726  			ChannelId: channel.Id,
  1727  			Message:   fmt.Sprintf("@%s", user2.Username),
  1728  			Props: map[string]interface{}{
  1729  				"from_webhook": "true",
  1730  			},
  1731  		}, channel, false, true)
  1732  		require.Nil(t, err)
  1733  
  1734  		// post3 should mention the user
  1735  
  1736  		count, err := th.App.countMentionsFromPost(user2, post1)
  1737  
  1738  		assert.Nil(t, err)
  1739  		assert.Equal(t, 1, count)
  1740  	})
  1741  
  1742  	t.Run("should count multiple pages of mentions", func(t *testing.T) {
  1743  		th := Setup(t).InitBasic()
  1744  		defer th.TearDown()
  1745  
  1746  		user1 := th.BasicUser
  1747  		user2 := th.BasicUser2
  1748  
  1749  		channel := th.CreateChannel(th.BasicTeam)
  1750  		th.AddUserToChannel(user2, channel)
  1751  
  1752  		numPosts := 215
  1753  
  1754  		post1, err := th.App.CreatePost(&model.Post{
  1755  			UserId:    user1.Id,
  1756  			ChannelId: channel.Id,
  1757  			Message:   fmt.Sprintf("@%s", user2.Username),
  1758  		}, channel, false, true)
  1759  		require.Nil(t, err)
  1760  
  1761  		for i := 0; i < numPosts-1; i++ {
  1762  			_, err = th.App.CreatePost(&model.Post{
  1763  				UserId:    user1.Id,
  1764  				ChannelId: channel.Id,
  1765  				Message:   fmt.Sprintf("@%s", user2.Username),
  1766  			}, channel, false, true)
  1767  			require.Nil(t, err)
  1768  		}
  1769  
  1770  		// Every post should mention the user
  1771  
  1772  		count, err := th.App.countMentionsFromPost(user2, post1)
  1773  
  1774  		assert.Nil(t, err)
  1775  		assert.Equal(t, numPosts, count)
  1776  	})
  1777  }
  1778  
  1779  func TestFillInPostProps(t *testing.T) {
  1780  	t.Run("should not add disable group highlight to post props for user with group mention permissions", func(t *testing.T) {
  1781  		th := Setup(t).InitBasic()
  1782  		defer th.TearDown()
  1783  		th.App.Srv().SetLicense(model.NewTestLicense("ldap"))
  1784  
  1785  		user1 := th.BasicUser
  1786  
  1787  		channel := th.CreateChannel(th.BasicTeam)
  1788  
  1789  		post1, err := th.App.CreatePost(&model.Post{
  1790  			UserId:    user1.Id,
  1791  			ChannelId: channel.Id,
  1792  			Message:   "test123123 @group1 @group2 blah blah blah",
  1793  		}, channel, false, true)
  1794  		require.Nil(t, err)
  1795  
  1796  		err = th.App.FillInPostProps(post1, channel)
  1797  
  1798  		assert.Nil(t, err)
  1799  		assert.Equal(t, post1.Props, model.StringInterface{})
  1800  	})
  1801  
  1802  	t.Run("should not add disable group highlight to post props for app without license", func(t *testing.T) {
  1803  		th := Setup(t).InitBasic()
  1804  		defer th.TearDown()
  1805  
  1806  		id := model.NewId()
  1807  		guest := &model.User{
  1808  			Email:         "success+" + id + "@simulator.amazonses.com",
  1809  			Username:      "un_" + id,
  1810  			Nickname:      "nn_" + id,
  1811  			Password:      "Password1",
  1812  			EmailVerified: true,
  1813  		}
  1814  		guest, err := th.App.CreateGuest(guest)
  1815  		require.Nil(t, err)
  1816  		th.LinkUserToTeam(guest, th.BasicTeam)
  1817  
  1818  		channel := th.CreateChannel(th.BasicTeam)
  1819  		th.AddUserToChannel(guest, channel)
  1820  
  1821  		post1, err := th.App.CreatePost(&model.Post{
  1822  			UserId:    guest.Id,
  1823  			ChannelId: channel.Id,
  1824  			Message:   "test123123 @group1 @group2 blah blah blah",
  1825  		}, channel, false, true)
  1826  		require.Nil(t, err)
  1827  
  1828  		err = th.App.FillInPostProps(post1, channel)
  1829  
  1830  		assert.Nil(t, err)
  1831  		assert.Equal(t, post1.Props, model.StringInterface{})
  1832  	})
  1833  
  1834  	t.Run("should add disable group highlight to post props for guest user", func(t *testing.T) {
  1835  		th := Setup(t).InitBasic()
  1836  		defer th.TearDown()
  1837  		th.App.Srv().SetLicense(model.NewTestLicense("ldap"))
  1838  
  1839  		id := model.NewId()
  1840  		guest := &model.User{
  1841  			Email:         "success+" + id + "@simulator.amazonses.com",
  1842  			Username:      "un_" + id,
  1843  			Nickname:      "nn_" + id,
  1844  			Password:      "Password1",
  1845  			EmailVerified: true,
  1846  		}
  1847  		guest, err := th.App.CreateGuest(guest)
  1848  		require.Nil(t, err)
  1849  		th.LinkUserToTeam(guest, th.BasicTeam)
  1850  
  1851  		channel := th.CreateChannel(th.BasicTeam)
  1852  		th.AddUserToChannel(guest, channel)
  1853  
  1854  		post1, err := th.App.CreatePost(&model.Post{
  1855  			UserId:    guest.Id,
  1856  			ChannelId: channel.Id,
  1857  			Message:   "test123123 @group1 @group2 blah blah blah",
  1858  		}, channel, false, true)
  1859  		require.Nil(t, err)
  1860  
  1861  		err = th.App.FillInPostProps(post1, channel)
  1862  
  1863  		assert.Nil(t, err)
  1864  		assert.Equal(t, post1.Props, model.StringInterface{"disable_group_highlight": true})
  1865  	})
  1866  }
  1867  
  1868  func TestThreadMembership(t *testing.T) {
  1869  	t.Run("should update memberships for conversation participants", func(t *testing.T) {
  1870  		th := Setup(t).InitBasic()
  1871  		defer th.TearDown()
  1872  
  1873  		user1 := th.BasicUser
  1874  		user2 := th.BasicUser2
  1875  
  1876  		channel := th.CreateChannel(th.BasicTeam)
  1877  		th.AddUserToChannel(user2, channel)
  1878  
  1879  		postRoot, err := th.App.CreatePost(&model.Post{
  1880  			UserId:    user1.Id,
  1881  			ChannelId: channel.Id,
  1882  			Message:   "root post",
  1883  		}, channel, false, true)
  1884  		require.Nil(t, err)
  1885  
  1886  		_, err = th.App.CreatePost(&model.Post{
  1887  			UserId:    user1.Id,
  1888  			ChannelId: channel.Id,
  1889  			RootId:    postRoot.Id,
  1890  			Message:   fmt.Sprintf("@%s", user2.Username),
  1891  		}, channel, false, true)
  1892  		require.Nil(t, err)
  1893  
  1894  		// first user should now be part of the thread since they replied to a post
  1895  		memberships, err2 := th.App.GetThreadMembershipsForUser(user1.Id, th.BasicTeam.Id)
  1896  		require.NoError(t, err2)
  1897  		require.Len(t, memberships, 1)
  1898  		// second user should also be part of a thread since they were mentioned
  1899  		memberships, err2 = th.App.GetThreadMembershipsForUser(user2.Id, th.BasicTeam.Id)
  1900  		require.NoError(t, err2)
  1901  		require.Len(t, memberships, 1)
  1902  
  1903  		post2, err := th.App.CreatePost(&model.Post{
  1904  			UserId:    user2.Id,
  1905  			ChannelId: channel.Id,
  1906  			Message:   "second post",
  1907  		}, channel, false, true)
  1908  		require.Nil(t, err)
  1909  
  1910  		_, err = th.App.CreatePost(&model.Post{
  1911  			UserId:    user2.Id,
  1912  			ChannelId: channel.Id,
  1913  			RootId:    post2.Id,
  1914  			Message:   fmt.Sprintf("@%s", user1.Username),
  1915  		}, channel, false, true)
  1916  		require.Nil(t, err)
  1917  
  1918  		// first user should now be part of two threads
  1919  		memberships, err2 = th.App.GetThreadMembershipsForUser(user1.Id, th.BasicTeam.Id)
  1920  		require.NoError(t, err2)
  1921  		require.Len(t, memberships, 2)
  1922  	})
  1923  }
  1924  
  1925  func TestCollapsedThreadFetch(t *testing.T) {
  1926  	th := Setup(t).InitBasic()
  1927  	defer th.TearDown()
  1928  	th.App.UpdateConfig(func(cfg *model.Config) {
  1929  		*cfg.ServiceSettings.ThreadAutoFollow = true
  1930  		*cfg.ServiceSettings.CollapsedThreads = model.COLLAPSED_THREADS_DEFAULT_ON
  1931  	})
  1932  	user1 := th.BasicUser
  1933  	user2 := th.BasicUser2
  1934  
  1935  	t.Run("should only return root posts, enriched", func(t *testing.T) {
  1936  		channel := th.CreateChannel(th.BasicTeam)
  1937  		th.AddUserToChannel(user2, channel)
  1938  		defer th.App.DeleteChannel(channel, user1.Id)
  1939  
  1940  		postRoot, err := th.App.CreatePost(&model.Post{
  1941  			UserId:    user1.Id,
  1942  			ChannelId: channel.Id,
  1943  			Message:   "root post",
  1944  		}, channel, false, true)
  1945  		require.Nil(t, err)
  1946  
  1947  		_, err = th.App.CreatePost(&model.Post{
  1948  			UserId:    user1.Id,
  1949  			ChannelId: channel.Id,
  1950  			RootId:    postRoot.Id,
  1951  			Message:   fmt.Sprintf("@%s", user2.Username),
  1952  		}, channel, false, true)
  1953  		require.Nil(t, err)
  1954  		thread, nErr := th.App.Srv().Store.Thread().Get(postRoot.Id)
  1955  		require.NoError(t, nErr)
  1956  		require.Len(t, thread.Participants, 2)
  1957  		th.App.MarkChannelAsUnreadFromPost(postRoot.Id, user1.Id)
  1958  		l, err := th.App.GetPostsForChannelAroundLastUnread(channel.Id, user1.Id, 10, 10, true, true, false)
  1959  		require.Nil(t, err)
  1960  		require.Len(t, l.Order, 1)
  1961  		require.EqualValues(t, 1, l.Posts[postRoot.Id].ReplyCount)
  1962  		require.EqualValues(t, []string{user1.Id, user2.Id}, []string{l.Posts[postRoot.Id].Participants[0].Id, l.Posts[postRoot.Id].Participants[1].Id})
  1963  		require.Empty(t, l.Posts[postRoot.Id].Participants[0].Email)
  1964  		require.NotZero(t, l.Posts[postRoot.Id].LastReplyAt)
  1965  
  1966  		// try extended fetch
  1967  		l, err = th.App.GetPostsForChannelAroundLastUnread(channel.Id, user1.Id, 10, 10, true, true, true)
  1968  		require.Nil(t, err)
  1969  		require.Len(t, l.Order, 1)
  1970  		require.NotEmpty(t, l.Posts[postRoot.Id].Participants[0].Email)
  1971  	})
  1972  }