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 }