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