github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/app/notification_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 "testing" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/mattermost/mattermost-server/v5/model" 14 "github.com/mattermost/mattermost-server/v5/utils" 15 ) 16 17 func TestSendNotifications(t *testing.T) { 18 th := Setup(t).InitBasic() 19 defer th.TearDown() 20 21 th.App.AddUserToChannel(th.BasicUser2, th.BasicChannel) 22 23 post1, appErr := th.App.CreatePostMissingChannel(&model.Post{ 24 UserId: th.BasicUser.Id, 25 ChannelId: th.BasicChannel.Id, 26 Message: "@" + th.BasicUser2.Username, 27 Type: model.POST_ADD_TO_CHANNEL, 28 Props: map[string]interface{}{model.POST_PROPS_ADDED_USER_ID: "junk"}, 29 }, true) 30 require.Nil(t, appErr) 31 32 mentions, err := th.App.SendNotifications(post1, th.BasicTeam, th.BasicChannel, th.BasicUser, nil, true) 33 require.NoError(t, err) 34 require.NotNil(t, mentions) 35 require.True(t, utils.StringInSlice(th.BasicUser2.Id, mentions), "mentions", mentions) 36 37 dm, appErr := th.App.GetOrCreateDirectChannel(th.BasicUser.Id, th.BasicUser2.Id) 38 require.Nil(t, appErr) 39 40 post2, appErr := th.App.CreatePostMissingChannel(&model.Post{ 41 UserId: th.BasicUser.Id, 42 ChannelId: dm.Id, 43 Message: "dm message", 44 }, true) 45 require.Nil(t, appErr) 46 47 mentions, err = th.App.SendNotifications(post2, th.BasicTeam, dm, th.BasicUser, nil, true) 48 require.NoError(t, err) 49 require.NotNil(t, mentions) 50 51 _, appErr = th.App.UpdateActive(th.BasicUser2, false) 52 require.Nil(t, appErr) 53 appErr = th.App.Srv().InvalidateAllCaches() 54 require.Nil(t, appErr) 55 56 post3, appErr := th.App.CreatePostMissingChannel(&model.Post{ 57 UserId: th.BasicUser.Id, 58 ChannelId: dm.Id, 59 Message: "dm message", 60 }, true) 61 require.Nil(t, appErr) 62 63 mentions, err = th.App.SendNotifications(post3, th.BasicTeam, dm, th.BasicUser, nil, true) 64 require.NoError(t, err) 65 require.NotNil(t, mentions) 66 67 th.BasicChannel.DeleteAt = 1 68 mentions, err = th.App.SendNotifications(post1, th.BasicTeam, th.BasicChannel, th.BasicUser, nil, true) 69 require.NoError(t, err) 70 require.Empty(t, mentions) 71 72 t.Run("replies to post created by OAuth bot should not notify user", func(t *testing.T) { 73 th := Setup(t).InitBasic() 74 defer th.TearDown() 75 testUserNotNotified := func(t *testing.T, user *model.User) { 76 rootPost := &model.Post{ 77 UserId: user.Id, 78 ChannelId: th.BasicChannel.Id, 79 Message: "a message", 80 Props: model.StringInterface{"from_webhook": "true", "override_username": "a bot"}, 81 } 82 83 rootPost, appErr = th.App.CreatePostMissingChannel(rootPost, false) 84 require.Nil(t, appErr) 85 86 childPost := &model.Post{ 87 UserId: th.BasicUser2.Id, 88 ChannelId: th.BasicChannel.Id, 89 RootId: rootPost.Id, 90 Message: "a reply", 91 } 92 childPost, appErr = th.App.CreatePostMissingChannel(childPost, false) 93 require.Nil(t, appErr) 94 95 postList := model.PostList{ 96 Order: []string{rootPost.Id, childPost.Id}, 97 Posts: map[string]*model.Post{rootPost.Id: rootPost, childPost.Id: childPost}, 98 } 99 mentions, err = th.App.SendNotifications(childPost, th.BasicTeam, th.BasicChannel, th.BasicUser2, &postList, true) 100 require.NoError(t, err) 101 require.False(t, utils.StringInSlice(user.Id, mentions)) 102 } 103 104 th.BasicUser.NotifyProps[model.COMMENTS_NOTIFY_PROP] = model.COMMENTS_NOTIFY_ANY 105 th.BasicUser, appErr = th.App.UpdateUser(th.BasicUser, false) 106 require.Nil(t, appErr) 107 t.Run("user wants notifications on all comments", func(t *testing.T) { 108 testUserNotNotified(t, th.BasicUser) 109 }) 110 111 th.BasicUser.NotifyProps[model.COMMENTS_NOTIFY_PROP] = model.COMMENTS_NOTIFY_ROOT 112 th.BasicUser, appErr = th.App.UpdateUser(th.BasicUser, false) 113 require.Nil(t, appErr) 114 t.Run("user wants notifications on root comment", func(t *testing.T) { 115 testUserNotNotified(t, th.BasicUser) 116 }) 117 }) 118 } 119 120 func TestSendNotificationsWithManyUsers(t *testing.T) { 121 th := Setup(t).InitBasic() 122 defer th.TearDown() 123 124 users := []*model.User{} 125 for i := 0; i < 10; i++ { 126 user := th.CreateUser() 127 th.LinkUserToTeam(user, th.BasicTeam) 128 th.App.AddUserToChannel(user, th.BasicChannel) 129 users = append(users, user) 130 } 131 132 _, appErr1 := th.App.CreatePostMissingChannel(&model.Post{ 133 UserId: th.BasicUser.Id, 134 ChannelId: th.BasicChannel.Id, 135 Message: "@channel", 136 Type: model.POST_ADD_TO_CHANNEL, 137 Props: map[string]interface{}{model.POST_PROPS_ADDED_USER_ID: "junk"}, 138 }, true) 139 require.Nil(t, appErr1) 140 141 // Each user should have a mention count of exactly 1 in the DB at this point. 142 t.Run("1-mention", func(t *testing.T) { 143 for i, user := range users { 144 t.Run(fmt.Sprintf("user-%d", i+1), func(t *testing.T) { 145 channelUnread, appErr2 := th.Server.Store.Channel().GetChannelUnread(th.BasicChannel.Id, user.Id) 146 require.NoError(t, appErr2) 147 assert.Equal(t, int64(1), channelUnread.MentionCount) 148 }) 149 } 150 }) 151 152 _, appErr1 = th.App.CreatePostMissingChannel(&model.Post{ 153 UserId: th.BasicUser.Id, 154 ChannelId: th.BasicChannel.Id, 155 Message: "@channel", 156 Type: model.POST_ADD_TO_CHANNEL, 157 Props: map[string]interface{}{model.POST_PROPS_ADDED_USER_ID: "junk"}, 158 }, true) 159 require.Nil(t, appErr1) 160 161 // Now each user should have a mention count of exactly 2 in the DB. 162 t.Run("2-mentions", func(t *testing.T) { 163 for i, user := range users { 164 t.Run(fmt.Sprintf("user-%d", i+1), func(t *testing.T) { 165 channelUnread, appErr2 := th.Server.Store.Channel().GetChannelUnread(th.BasicChannel.Id, user.Id) 166 require.NoError(t, appErr2) 167 assert.Equal(t, int64(2), channelUnread.MentionCount) 168 }) 169 } 170 }) 171 } 172 173 func TestSendOutOfChannelMentions(t *testing.T) { 174 th := Setup(t).InitBasic() 175 defer th.TearDown() 176 177 channel := th.BasicChannel 178 179 user1 := th.BasicUser 180 user2 := th.BasicUser2 181 182 t.Run("should send ephemeral post when there is an out of channel mention", func(t *testing.T) { 183 post := &model.Post{} 184 potentialMentions := []string{user2.Username} 185 186 sent, err := th.App.sendOutOfChannelMentions(user1, post, channel, potentialMentions) 187 188 assert.NoError(t, err) 189 assert.True(t, sent) 190 }) 191 192 t.Run("should not send ephemeral post when there are no out of channel mentions", func(t *testing.T) { 193 post := &model.Post{} 194 potentialMentions := []string{"not a user"} 195 196 sent, err := th.App.sendOutOfChannelMentions(user1, post, channel, potentialMentions) 197 198 assert.NoError(t, err) 199 assert.False(t, sent) 200 }) 201 } 202 203 func TestFilterOutOfChannelMentions(t *testing.T) { 204 th := Setup(t).InitBasic() 205 defer th.TearDown() 206 207 channel := th.BasicChannel 208 209 user1 := th.BasicUser 210 user2 := th.BasicUser2 211 user3 := th.CreateUser() 212 guest := th.CreateGuest() 213 user4 := th.CreateUser() 214 guestAndUser4Channel := th.CreateChannel(th.BasicTeam) 215 defer th.App.PermanentDeleteUser(guest) 216 th.LinkUserToTeam(user3, th.BasicTeam) 217 th.LinkUserToTeam(user4, th.BasicTeam) 218 th.LinkUserToTeam(guest, th.BasicTeam) 219 th.App.AddUserToChannel(guest, channel) 220 th.App.AddUserToChannel(user4, guestAndUser4Channel) 221 th.App.AddUserToChannel(guest, guestAndUser4Channel) 222 223 t.Run("should return users not in the channel", func(t *testing.T) { 224 post := &model.Post{} 225 potentialMentions := []string{user2.Username, user3.Username} 226 227 outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(user1, post, channel, potentialMentions) 228 229 assert.NoError(t, err) 230 assert.Len(t, outOfChannelUsers, 2) 231 assert.True(t, (outOfChannelUsers[0].Id == user2.Id || outOfChannelUsers[1].Id == user2.Id)) 232 assert.True(t, (outOfChannelUsers[0].Id == user3.Id || outOfChannelUsers[1].Id == user3.Id)) 233 assert.Nil(t, outOfGroupUsers) 234 }) 235 236 t.Run("should return only visible users not in the channel (for guests)", func(t *testing.T) { 237 post := &model.Post{} 238 potentialMentions := []string{user2.Username, user3.Username, user4.Username} 239 240 outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(guest, post, channel, potentialMentions) 241 242 require.NoError(t, err) 243 require.Len(t, outOfChannelUsers, 1) 244 assert.Equal(t, user4.Id, outOfChannelUsers[0].Id) 245 assert.Nil(t, outOfGroupUsers) 246 }) 247 248 t.Run("should not return results for a system message", func(t *testing.T) { 249 post := &model.Post{ 250 Type: model.POST_ADD_REMOVE, 251 } 252 potentialMentions := []string{user2.Username, user3.Username} 253 254 outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(user1, post, channel, potentialMentions) 255 256 assert.NoError(t, err) 257 assert.Nil(t, outOfChannelUsers) 258 assert.Nil(t, outOfGroupUsers) 259 }) 260 261 t.Run("should not return results for a direct message", func(t *testing.T) { 262 post := &model.Post{} 263 directChannel := &model.Channel{ 264 Type: model.CHANNEL_DIRECT, 265 } 266 potentialMentions := []string{user2.Username, user3.Username} 267 268 outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(user1, post, directChannel, potentialMentions) 269 270 assert.NoError(t, err) 271 assert.Nil(t, outOfChannelUsers) 272 assert.Nil(t, outOfGroupUsers) 273 }) 274 275 t.Run("should not return results for a group message", func(t *testing.T) { 276 post := &model.Post{} 277 groupChannel := &model.Channel{ 278 Type: model.CHANNEL_GROUP, 279 } 280 potentialMentions := []string{user2.Username, user3.Username} 281 282 outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(user1, post, groupChannel, potentialMentions) 283 284 assert.NoError(t, err) 285 assert.Nil(t, outOfChannelUsers) 286 assert.Nil(t, outOfGroupUsers) 287 }) 288 289 t.Run("should not return inactive users", func(t *testing.T) { 290 inactiveUser := th.CreateUser() 291 inactiveUser, appErr := th.App.UpdateActive(inactiveUser, false) 292 require.Nil(t, appErr) 293 294 post := &model.Post{} 295 potentialMentions := []string{inactiveUser.Username} 296 297 outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(user1, post, channel, potentialMentions) 298 299 assert.NoError(t, err) 300 assert.Nil(t, outOfChannelUsers) 301 assert.Nil(t, outOfGroupUsers) 302 }) 303 304 t.Run("should not return bot users", func(t *testing.T) { 305 botUser := th.CreateUser() 306 botUser.IsBot = true 307 308 post := &model.Post{} 309 potentialMentions := []string{botUser.Username} 310 311 outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(user1, post, channel, potentialMentions) 312 313 assert.NoError(t, err) 314 assert.Nil(t, outOfChannelUsers) 315 assert.Nil(t, outOfGroupUsers) 316 }) 317 318 t.Run("should not return results for non-existent users", func(t *testing.T) { 319 post := &model.Post{} 320 potentialMentions := []string{"foo", "bar"} 321 322 outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(user1, post, channel, potentialMentions) 323 324 assert.NoError(t, err) 325 assert.Nil(t, outOfChannelUsers) 326 assert.Nil(t, outOfGroupUsers) 327 }) 328 329 t.Run("should separate users not in the channel from users not in the group", func(t *testing.T) { 330 nonChannelMember := th.CreateUser() 331 th.LinkUserToTeam(nonChannelMember, th.BasicTeam) 332 nonGroupMember := th.CreateUser() 333 th.LinkUserToTeam(nonGroupMember, th.BasicTeam) 334 335 group := th.CreateGroup() 336 _, appErr := th.App.UpsertGroupMember(group.Id, th.BasicUser.Id) 337 require.Nil(t, appErr) 338 _, appErr = th.App.UpsertGroupMember(group.Id, nonChannelMember.Id) 339 require.Nil(t, appErr) 340 341 constrainedChannel := th.CreateChannel(th.BasicTeam) 342 constrainedChannel.GroupConstrained = model.NewBool(true) 343 constrainedChannel, appErr = th.App.UpdateChannel(constrainedChannel) 344 require.Nil(t, appErr) 345 346 _, appErr = th.App.UpsertGroupSyncable(&model.GroupSyncable{ 347 GroupId: group.Id, 348 Type: model.GroupSyncableTypeChannel, 349 SyncableId: constrainedChannel.Id, 350 }) 351 require.Nil(t, appErr) 352 353 post := &model.Post{} 354 potentialMentions := []string{nonChannelMember.Username, nonGroupMember.Username} 355 356 outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(user1, post, constrainedChannel, potentialMentions) 357 358 assert.NoError(t, err) 359 assert.Len(t, outOfChannelUsers, 1) 360 assert.Equal(t, nonChannelMember.Id, outOfChannelUsers[0].Id) 361 assert.Len(t, outOfGroupUsers, 1) 362 assert.Equal(t, nonGroupMember.Id, outOfGroupUsers[0].Id) 363 }) 364 } 365 366 func TestGetExplicitMentions(t *testing.T) { 367 id1 := model.NewId() 368 id2 := model.NewId() 369 id3 := model.NewId() 370 371 for name, tc := range map[string]struct { 372 Message string 373 Attachments []*model.SlackAttachment 374 Keywords map[string][]string 375 Groups map[string]*model.Group 376 Expected *ExplicitMentions 377 }{ 378 "Nobody": { 379 Message: "this is a message", 380 Keywords: map[string][]string{}, 381 Expected: &ExplicitMentions{}, 382 }, 383 "NonexistentUser": { 384 Message: "this is a message for @user", 385 Expected: &ExplicitMentions{ 386 OtherPotentialMentions: []string{"user"}, 387 }, 388 }, 389 "OnePerson": { 390 Message: "this is a message for @user", 391 Keywords: map[string][]string{"@user": {id1}}, 392 Expected: &ExplicitMentions{ 393 Mentions: map[string]MentionType{ 394 id1: KeywordMention, 395 }, 396 }, 397 }, 398 "OnePersonWithPeriodAtEndOfUsername": { 399 Message: "this is a message for @user.name.", 400 Keywords: map[string][]string{"@user.name.": {id1}}, 401 Expected: &ExplicitMentions{ 402 Mentions: map[string]MentionType{ 403 id1: KeywordMention, 404 }, 405 }, 406 }, 407 "OnePersonWithPeriodAtEndOfUsernameButNotSimilarName": { 408 Message: "this is a message for @user.name.", 409 Keywords: map[string][]string{"@user.name.": {id1}, "@user.name": {id2}}, 410 Expected: &ExplicitMentions{ 411 Mentions: map[string]MentionType{ 412 id1: KeywordMention, 413 }, 414 }, 415 }, 416 "OnePersonAtEndOfSentence": { 417 Message: "this is a message for @user.", 418 Keywords: map[string][]string{"@user": {id1}}, 419 Expected: &ExplicitMentions{ 420 Mentions: map[string]MentionType{ 421 id1: KeywordMention, 422 }, 423 }, 424 }, 425 "OnePersonWithoutAtMention": { 426 Message: "this is a message for @user", 427 Keywords: map[string][]string{"this": {id1}}, 428 Expected: &ExplicitMentions{ 429 Mentions: map[string]MentionType{ 430 id1: KeywordMention, 431 }, 432 OtherPotentialMentions: []string{"user"}, 433 }, 434 }, 435 "OnePersonWithPeriodAfter": { 436 Message: "this is a message for @user.", 437 Keywords: map[string][]string{"@user": {id1}}, 438 Expected: &ExplicitMentions{ 439 Mentions: map[string]MentionType{ 440 id1: KeywordMention, 441 }, 442 }, 443 }, 444 "OnePersonWithPeriodBefore": { 445 Message: "this is a message for .@user", 446 Keywords: map[string][]string{"@user": {id1}}, 447 Expected: &ExplicitMentions{ 448 Mentions: map[string]MentionType{ 449 id1: KeywordMention, 450 }, 451 }, 452 }, 453 "OnePersonWithColonAfter": { 454 Message: "this is a message for @user:", 455 Keywords: map[string][]string{"@user": {id1}}, 456 Expected: &ExplicitMentions{ 457 Mentions: map[string]MentionType{ 458 id1: KeywordMention, 459 }, 460 }, 461 }, 462 "OnePersonWithColonBefore": { 463 Message: "this is a message for :@user", 464 Keywords: map[string][]string{"@user": {id1}}, 465 Expected: &ExplicitMentions{ 466 Mentions: map[string]MentionType{ 467 id1: KeywordMention, 468 }, 469 }, 470 }, 471 "OnePersonWithHyphenAfter": { 472 Message: "this is a message for @user.", 473 Keywords: map[string][]string{"@user": {id1}}, 474 Expected: &ExplicitMentions{ 475 Mentions: map[string]MentionType{ 476 id1: KeywordMention, 477 }, 478 }, 479 }, 480 "OnePersonWithHyphenBefore": { 481 Message: "this is a message for -@user", 482 Keywords: map[string][]string{"@user": {id1}}, 483 Expected: &ExplicitMentions{ 484 Mentions: map[string]MentionType{ 485 id1: KeywordMention, 486 }, 487 }, 488 }, 489 "MultiplePeopleWithOneWord": { 490 Message: "this is a message for @user", 491 Keywords: map[string][]string{"@user": {id1, id2}}, 492 Expected: &ExplicitMentions{ 493 Mentions: map[string]MentionType{ 494 id1: KeywordMention, 495 id2: KeywordMention, 496 }, 497 }, 498 }, 499 "OneOfMultiplePeople": { 500 Message: "this is a message for @user", 501 Keywords: map[string][]string{"@user": {id1}, "@mention": {id2}}, 502 Expected: &ExplicitMentions{ 503 Mentions: map[string]MentionType{ 504 id1: KeywordMention, 505 }, 506 }, 507 }, 508 "MultiplePeopleWithMultipleWords": { 509 Message: "this is an @mention for @user", 510 Keywords: map[string][]string{"@user": {id1}, "@mention": {id2}}, 511 Expected: &ExplicitMentions{ 512 Mentions: map[string]MentionType{ 513 id1: KeywordMention, 514 id2: KeywordMention, 515 }, 516 }, 517 }, 518 "Channel": { 519 Message: "this is an message for @channel", 520 Keywords: map[string][]string{"@channel": {id1, id2}}, 521 Expected: &ExplicitMentions{ 522 Mentions: map[string]MentionType{ 523 id1: ChannelMention, 524 id2: ChannelMention, 525 }, 526 ChannelMentioned: true, 527 }, 528 }, 529 530 "ChannelWithColonAtEnd": { 531 Message: "this is a message for @channel:", 532 Keywords: map[string][]string{"@channel": {id1, id2}}, 533 Expected: &ExplicitMentions{ 534 Mentions: map[string]MentionType{ 535 id1: ChannelMention, 536 id2: ChannelMention, 537 }, 538 ChannelMentioned: true, 539 }, 540 }, 541 "CapitalizedChannel": { 542 Message: "this is an message for @cHaNNeL", 543 Keywords: map[string][]string{"@channel": {id1, id2}}, 544 Expected: &ExplicitMentions{ 545 Mentions: map[string]MentionType{ 546 id1: ChannelMention, 547 id2: ChannelMention, 548 }, 549 ChannelMentioned: true, 550 }, 551 }, 552 "All": { 553 Message: "this is an message for @all", 554 Keywords: map[string][]string{"@all": {id1, id2}}, 555 Expected: &ExplicitMentions{ 556 Mentions: map[string]MentionType{ 557 id1: ChannelMention, 558 id2: ChannelMention, 559 }, 560 AllMentioned: true, 561 }, 562 }, 563 "AllWithColonAtEnd": { 564 Message: "this is a message for @all:", 565 Keywords: map[string][]string{"@all": {id1, id2}}, 566 Expected: &ExplicitMentions{ 567 Mentions: map[string]MentionType{ 568 id1: ChannelMention, 569 id2: ChannelMention, 570 }, 571 AllMentioned: true, 572 }, 573 }, 574 "CapitalizedAll": { 575 Message: "this is an message for @ALL", 576 Keywords: map[string][]string{"@all": {id1, id2}}, 577 Expected: &ExplicitMentions{ 578 Mentions: map[string]MentionType{ 579 id1: ChannelMention, 580 id2: ChannelMention, 581 }, 582 AllMentioned: true, 583 }, 584 }, 585 "UserWithPeriod": { 586 Message: "user.period doesn't complicate things at all by including periods in their username", 587 Keywords: map[string][]string{"user.period": {id1}, "user": {id2}}, 588 Expected: &ExplicitMentions{ 589 Mentions: map[string]MentionType{ 590 id1: KeywordMention, 591 }, 592 }, 593 }, 594 "AtUserWithColonAtEnd": { 595 Message: "this is a message for @user:", 596 Keywords: map[string][]string{"@user": {id1}}, 597 Expected: &ExplicitMentions{ 598 Mentions: map[string]MentionType{ 599 id1: KeywordMention, 600 }, 601 }, 602 }, 603 "AtUserWithPeriodAtEndOfSentence": { 604 Message: "this is a message for @user.period.", 605 Keywords: map[string][]string{"@user.period": {id1}}, 606 Expected: &ExplicitMentions{ 607 Mentions: map[string]MentionType{ 608 id1: KeywordMention, 609 }, 610 }, 611 }, 612 "UserWithPeriodAtEndOfSentence": { 613 Message: "this is a message for user.period.", 614 Keywords: map[string][]string{"user.period": {id1}}, 615 Expected: &ExplicitMentions{ 616 Mentions: map[string]MentionType{ 617 id1: KeywordMention, 618 }, 619 }, 620 }, 621 "UserWithColonAtEnd": { 622 Message: "this is a message for user:", 623 Keywords: map[string][]string{"user": {id1}}, 624 Expected: &ExplicitMentions{ 625 Mentions: map[string]MentionType{ 626 id1: KeywordMention, 627 }, 628 }, 629 }, 630 "PotentialOutOfChannelUser": { 631 Message: "this is an message for @potential and @user", 632 Keywords: map[string][]string{"@user": {id1}}, 633 Expected: &ExplicitMentions{ 634 Mentions: map[string]MentionType{ 635 id1: KeywordMention, 636 }, 637 OtherPotentialMentions: []string{"potential"}, 638 }, 639 }, 640 "PotentialOutOfChannelUserWithPeriod": { 641 Message: "this is an message for @potential.user", 642 Expected: &ExplicitMentions{ 643 OtherPotentialMentions: []string{"potential.user"}, 644 }, 645 }, 646 "InlineCode": { 647 Message: "`this shouldn't mention @channel at all`", 648 Keywords: map[string][]string{}, 649 Expected: &ExplicitMentions{}, 650 }, 651 "FencedCodeBlock": { 652 Message: "```\nthis shouldn't mention @channel at all\n```", 653 Keywords: map[string][]string{}, 654 Expected: &ExplicitMentions{}, 655 }, 656 "Emphasis": { 657 Message: "*@aaa @bbb @ccc*", 658 Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}, 659 Expected: &ExplicitMentions{ 660 Mentions: map[string]MentionType{ 661 id1: KeywordMention, 662 id2: KeywordMention, 663 id3: KeywordMention, 664 }, 665 }, 666 }, 667 "StrongEmphasis": { 668 Message: "**@aaa @bbb @ccc**", 669 Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}, 670 Expected: &ExplicitMentions{ 671 Mentions: map[string]MentionType{ 672 id1: KeywordMention, 673 id2: KeywordMention, 674 id3: KeywordMention, 675 }, 676 }, 677 }, 678 "Strikethrough": { 679 Message: "~~@aaa @bbb @ccc~~", 680 Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}, 681 Expected: &ExplicitMentions{ 682 Mentions: map[string]MentionType{ 683 id1: KeywordMention, 684 id2: KeywordMention, 685 id3: KeywordMention, 686 }, 687 }, 688 }, 689 "Heading": { 690 Message: "### @aaa", 691 Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}, 692 Expected: &ExplicitMentions{ 693 Mentions: map[string]MentionType{ 694 id1: KeywordMention, 695 }, 696 }, 697 }, 698 "BlockQuote": { 699 Message: "> @aaa", 700 Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}, 701 Expected: &ExplicitMentions{ 702 Mentions: map[string]MentionType{ 703 id1: KeywordMention, 704 }, 705 }, 706 }, 707 "Emoji": { 708 Message: ":smile:", 709 Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}, 710 Expected: &ExplicitMentions{}, 711 }, 712 "NotEmoji": { 713 Message: "smile", 714 Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}, 715 Expected: &ExplicitMentions{ 716 Mentions: map[string]MentionType{ 717 id1: KeywordMention, 718 }, 719 }, 720 }, 721 "UnclosedEmoji": { 722 Message: ":smile", 723 Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}, 724 Expected: &ExplicitMentions{ 725 Mentions: map[string]MentionType{ 726 id1: KeywordMention, 727 }, 728 }, 729 }, 730 "UnopenedEmoji": { 731 Message: "smile:", 732 Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}, 733 Expected: &ExplicitMentions{ 734 Mentions: map[string]MentionType{ 735 id1: KeywordMention, 736 }, 737 }, 738 }, 739 "IndentedCodeBlock": { 740 Message: " this shouldn't mention @channel at all", 741 Keywords: map[string][]string{}, 742 Expected: &ExplicitMentions{}, 743 }, 744 "LinkTitle": { 745 Message: `[foo](this "shouldn't mention @channel at all")`, 746 Keywords: map[string][]string{}, 747 Expected: &ExplicitMentions{}, 748 }, 749 "MalformedInlineCode": { 750 Message: "`this should mention @channel``", 751 Keywords: map[string][]string{}, 752 Expected: &ExplicitMentions{ 753 ChannelMentioned: true, 754 }, 755 }, 756 "MultibyteCharacter": { 757 Message: "My name is 萌", 758 Keywords: map[string][]string{"萌": {id1}}, 759 Expected: &ExplicitMentions{ 760 Mentions: map[string]MentionType{ 761 id1: KeywordMention, 762 }, 763 }, 764 }, 765 "MultibyteCharacterAtBeginningOfSentence": { 766 Message: "이메일을 보내다.", 767 Keywords: map[string][]string{"이메일": {id1}}, 768 Expected: &ExplicitMentions{ 769 Mentions: map[string]MentionType{ 770 id1: KeywordMention, 771 }, 772 }, 773 }, 774 "MultibyteCharacterInPartOfSentence": { 775 Message: "我爱吃番茄炒饭", 776 Keywords: map[string][]string{"番茄": {id1}}, 777 Expected: &ExplicitMentions{ 778 Mentions: map[string]MentionType{ 779 id1: KeywordMention, 780 }, 781 }, 782 }, 783 "MultibyteCharacterAtEndOfSentence": { 784 Message: "こんにちは、世界", 785 Keywords: map[string][]string{"世界": {id1}}, 786 Expected: &ExplicitMentions{ 787 Mentions: map[string]MentionType{ 788 id1: KeywordMention, 789 }, 790 }, 791 }, 792 "MultibyteCharacterTwiceInSentence": { 793 Message: "石橋さんが石橋を渡る", 794 Keywords: map[string][]string{"石橋": {id1}}, 795 Expected: &ExplicitMentions{ 796 Mentions: map[string]MentionType{ 797 id1: KeywordMention, 798 }, 799 }, 800 }, 801 802 // The following tests cover cases where the message mentions @user.name, so we shouldn't assume that 803 // the user might be intending to mention some @user that isn't in the channel. 804 "Don't include potential mention that's part of an actual mention (without trailing period)": { 805 Message: "this is an message for @user.name", 806 Keywords: map[string][]string{"@user.name": {id1}}, 807 Expected: &ExplicitMentions{ 808 Mentions: map[string]MentionType{ 809 id1: KeywordMention, 810 }, 811 }, 812 }, 813 "Don't include potential mention that's part of an actual mention (with trailing period)": { 814 Message: "this is an message for @user.name.", 815 Keywords: map[string][]string{"@user.name": {id1}}, 816 Expected: &ExplicitMentions{ 817 Mentions: map[string]MentionType{ 818 id1: KeywordMention, 819 }, 820 }, 821 }, 822 "Don't include potential mention that's part of an actual mention (with multiple trailing periods)": { 823 Message: "this is an message for @user.name...", 824 Keywords: map[string][]string{"@user.name": {id1}}, 825 Expected: &ExplicitMentions{ 826 Mentions: map[string]MentionType{ 827 id1: KeywordMention, 828 }, 829 }, 830 }, 831 "Don't include potential mention that's part of an actual mention (containing and followed by multiple periods)": { 832 Message: "this is an message for @user...name...", 833 Keywords: map[string][]string{"@user...name": {id1}}, 834 Expected: &ExplicitMentions{ 835 Mentions: map[string]MentionType{ 836 id1: KeywordMention, 837 }, 838 }, 839 }, 840 "should include the mentions from attachment text and preText": { 841 Message: "this is an message for @user1", 842 Attachments: []*model.SlackAttachment{ 843 { 844 Text: "this is a message For @user2", 845 Pretext: "this is a message for @here", 846 }, 847 }, 848 Keywords: map[string][]string{"@user1": {id1}, "@user2": {id2}}, 849 Expected: &ExplicitMentions{ 850 Mentions: map[string]MentionType{ 851 id1: KeywordMention, 852 id2: KeywordMention, 853 }, 854 HereMentioned: true, 855 }, 856 }, 857 "Name on keywords is a prefix of a mention": { 858 Message: "@other @test-two", 859 Keywords: map[string][]string{"@test": {model.NewId()}}, 860 Expected: &ExplicitMentions{ 861 OtherPotentialMentions: []string{"other", "test-two"}, 862 }, 863 }, 864 "Name on mentions is a prefix of other mention": { 865 Message: "@other-one @other @other-two", 866 Keywords: nil, 867 Expected: &ExplicitMentions{ 868 OtherPotentialMentions: []string{"other-one", "other", "other-two"}, 869 }, 870 }, 871 "No groups": { 872 Message: "@nothing", 873 Groups: map[string]*model.Group{}, 874 Expected: &ExplicitMentions{ 875 Mentions: nil, 876 OtherPotentialMentions: []string{"nothing"}, 877 }, 878 }, 879 "No matching groups": { 880 Message: "@nothing", 881 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}}, 882 Expected: &ExplicitMentions{ 883 Mentions: nil, 884 GroupMentions: nil, 885 OtherPotentialMentions: []string{"nothing"}, 886 }, 887 }, 888 "matching group with no @": { 889 Message: "engineering", 890 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}}, 891 Expected: &ExplicitMentions{ 892 Mentions: nil, 893 GroupMentions: nil, 894 OtherPotentialMentions: nil, 895 }, 896 }, 897 "matching group with preceding @": { 898 Message: "@engineering", 899 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}}, 900 Expected: &ExplicitMentions{ 901 Mentions: nil, 902 GroupMentions: map[string]*model.Group{ 903 "engineering": {Name: model.NewString("engineering")}, 904 }, 905 OtherPotentialMentions: []string{"engineering"}, 906 }, 907 }, 908 "matching upper case group with preceding @": { 909 Message: "@Engineering", 910 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}}, 911 Expected: &ExplicitMentions{ 912 Mentions: nil, 913 GroupMentions: map[string]*model.Group{ 914 "engineering": {Name: model.NewString("engineering")}, 915 }, 916 OtherPotentialMentions: []string{"Engineering"}, 917 }, 918 }, 919 } { 920 t.Run(name, func(t *testing.T) { 921 post := &model.Post{ 922 Message: tc.Message, 923 Props: model.StringInterface{ 924 "attachments": tc.Attachments, 925 }, 926 } 927 928 m := getExplicitMentions(post, tc.Keywords, tc.Groups) 929 930 assert.EqualValues(t, tc.Expected, m) 931 }) 932 } 933 } 934 935 func TestGetExplicitMentionsAtHere(t *testing.T) { 936 t.Run("Boundary cases", func(t *testing.T) { 937 // test all the boundary cases that we know can break up terms (and those that we know won't) 938 cases := map[string]bool{ 939 "": false, 940 "here": false, 941 "@here": true, 942 " @here ": true, 943 "\n@here\n": true, 944 "!@here!": true, 945 "#@here#": true, 946 "$@here$": true, 947 "%@here%": true, 948 "^@here^": true, 949 "&@here&": true, 950 "*@here*": true, 951 "(@here(": true, 952 ")@here)": true, 953 "-@here-": true, 954 "_@here_": true, 955 "=@here=": true, 956 "+@here+": true, 957 "[@here[": true, 958 "{@here{": true, 959 "]@here]": true, 960 "}@here}": true, 961 "\\@here\\": true, 962 "|@here|": true, 963 ";@here;": true, 964 "@here:": true, 965 ":@here:": false, // This case shouldn't trigger a mention since it follows the format of reactions e.g. :word: 966 "'@here'": true, 967 "\"@here\"": true, 968 ",@here,": true, 969 "<@here<": true, 970 ".@here.": true, 971 ">@here>": true, 972 "/@here/": true, 973 "?@here?": true, 974 "`@here`": false, // This case shouldn't mention since it's a code block 975 "~@here~": true, 976 "@HERE": true, 977 "@hERe": true, 978 } 979 for message, shouldMention := range cases { 980 post := &model.Post{Message: message} 981 m := getExplicitMentions(post, nil, nil) 982 require.False(t, m.HereMentioned && !shouldMention, "shouldn't have mentioned @here with \"%v\"") 983 require.False(t, !m.HereMentioned && shouldMention, "should've mentioned @here with \"%v\"") 984 } 985 }) 986 987 t.Run("Mention @here and someone", func(t *testing.T) { 988 id := model.NewId() 989 m := getExplicitMentions(&model.Post{Message: "@here @user @potential"}, map[string][]string{"@user": {id}}, nil) 990 require.True(t, m.HereMentioned, "should've mentioned @here with \"@here @user\"") 991 require.Len(t, m.Mentions, 1) 992 require.Equal(t, KeywordMention, m.Mentions[id], "should've mentioned @user with \"@here @user\"") 993 require.Equal(t, len(m.OtherPotentialMentions), 1, "should've potential mentions for @potential") 994 assert.Equal(t, "potential", m.OtherPotentialMentions[0]) 995 }) 996 997 t.Run("Username ending with period", func(t *testing.T) { 998 id := model.NewId() 999 m := getExplicitMentions(&model.Post{Message: "@potential. test"}, map[string][]string{"@user": {id}}, nil) 1000 require.Equal(t, len(m.OtherPotentialMentions), 1, "should've potential mentions for @potential") 1001 assert.Equal(t, "potential", m.OtherPotentialMentions[0]) 1002 }) 1003 } 1004 1005 func TestAllowChannelMentions(t *testing.T) { 1006 th := Setup(t).InitBasic() 1007 defer th.TearDown() 1008 1009 post := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id} 1010 1011 t.Run("should return true for a regular post with few channel members", func(t *testing.T) { 1012 allowChannelMentions := th.App.allowChannelMentions(post, 5) 1013 assert.True(t, allowChannelMentions) 1014 }) 1015 1016 t.Run("should return false for a channel header post", func(t *testing.T) { 1017 headerChangePost := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id, Type: model.POST_HEADER_CHANGE} 1018 allowChannelMentions := th.App.allowChannelMentions(headerChangePost, 5) 1019 assert.False(t, allowChannelMentions) 1020 }) 1021 1022 t.Run("should return false for a channel purpose post", func(t *testing.T) { 1023 purposeChangePost := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id, Type: model.POST_PURPOSE_CHANGE} 1024 allowChannelMentions := th.App.allowChannelMentions(purposeChangePost, 5) 1025 assert.False(t, allowChannelMentions) 1026 }) 1027 1028 t.Run("should return false for a regular post with many channel members", func(t *testing.T) { 1029 allowChannelMentions := th.App.allowChannelMentions(post, int(*th.App.Config().TeamSettings.MaxNotificationsPerChannel)+1) 1030 assert.False(t, allowChannelMentions) 1031 }) 1032 1033 t.Run("should return false for a post where the post user does not have USE_CHANNEL_MENTIONS permission", func(t *testing.T) { 1034 defer th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID) 1035 defer th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID) 1036 th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID) 1037 th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID) 1038 allowChannelMentions := th.App.allowChannelMentions(post, 5) 1039 assert.False(t, allowChannelMentions) 1040 }) 1041 } 1042 1043 func TestAllowGroupMentions(t *testing.T) { 1044 th := Setup(t).InitBasic() 1045 defer th.TearDown() 1046 1047 post := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id} 1048 1049 t.Run("should return false without ldap groups license", func(t *testing.T) { 1050 allowGroupMentions := th.App.allowGroupMentions(post) 1051 assert.False(t, allowGroupMentions) 1052 }) 1053 1054 th.App.Srv().SetLicense(model.NewTestLicense("ldap_groups")) 1055 1056 t.Run("should return true for a regular post with few channel members", func(t *testing.T) { 1057 allowGroupMentions := th.App.allowGroupMentions(post) 1058 assert.True(t, allowGroupMentions) 1059 }) 1060 1061 t.Run("should return false for a channel header post", func(t *testing.T) { 1062 headerChangePost := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id, Type: model.POST_HEADER_CHANGE} 1063 allowGroupMentions := th.App.allowGroupMentions(headerChangePost) 1064 assert.False(t, allowGroupMentions) 1065 }) 1066 1067 t.Run("should return false for a channel purpose post", func(t *testing.T) { 1068 purposeChangePost := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id, Type: model.POST_PURPOSE_CHANGE} 1069 allowGroupMentions := th.App.allowGroupMentions(purposeChangePost) 1070 assert.False(t, allowGroupMentions) 1071 }) 1072 1073 t.Run("should return false for a post where the post user does not have USE_GROUP_MENTIONS permission", func(t *testing.T) { 1074 defer func() { 1075 th.AddPermissionToRole(model.PERMISSION_USE_GROUP_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID) 1076 th.AddPermissionToRole(model.PERMISSION_USE_GROUP_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID) 1077 }() 1078 th.RemovePermissionFromRole(model.PERMISSION_USE_GROUP_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID) 1079 th.RemovePermissionFromRole(model.PERMISSION_USE_GROUP_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID) 1080 allowGroupMentions := th.App.allowGroupMentions(post) 1081 assert.False(t, allowGroupMentions) 1082 }) 1083 } 1084 1085 func TestGetMentionKeywords(t *testing.T) { 1086 th := Setup(t) 1087 defer th.TearDown() 1088 1089 // user with username or custom mentions enabled 1090 user1 := &model.User{ 1091 Id: model.NewId(), 1092 FirstName: "First", 1093 Username: "User", 1094 NotifyProps: map[string]string{ 1095 "mention_keys": "User,@User,MENTION", 1096 }, 1097 } 1098 1099 channelMemberNotifyPropsMap1Off := map[string]model.StringMap{ 1100 user1.Id: { 1101 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 1102 }, 1103 } 1104 1105 profiles := map[string]*model.User{user1.Id: user1} 1106 mentions := th.App.getMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap1Off) 1107 require.Len(t, mentions, 3, "should've returned three mention keywords") 1108 1109 ids, ok := mentions["user"] 1110 require.True(t, ok) 1111 require.Equal(t, user1.Id, ids[0], "should've returned mention key of user") 1112 ids, ok = mentions["@user"] 1113 require.True(t, ok) 1114 require.Equal(t, user1.Id, ids[0], "should've returned mention key of @user") 1115 ids, ok = mentions["mention"] 1116 require.True(t, ok) 1117 require.Equal(t, user1.Id, ids[0], "should've returned mention key of mention") 1118 1119 // user with first name mention enabled 1120 user2 := &model.User{ 1121 Id: model.NewId(), 1122 FirstName: "First", 1123 Username: "User", 1124 NotifyProps: map[string]string{ 1125 "first_name": "true", 1126 }, 1127 } 1128 1129 channelMemberNotifyPropsMap2Off := map[string]model.StringMap{ 1130 user2.Id: { 1131 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 1132 }, 1133 } 1134 1135 profiles = map[string]*model.User{user2.Id: user2} 1136 mentions = th.App.getMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap2Off) 1137 require.Len(t, mentions, 2, "should've returned two mention keyword") 1138 1139 ids, ok = mentions["First"] 1140 require.True(t, ok) 1141 require.Equal(t, user2.Id, ids[0], "should've returned mention key of First") 1142 1143 // user with @channel/@all mentions enabled 1144 user3 := &model.User{ 1145 Id: model.NewId(), 1146 FirstName: "First", 1147 Username: "User", 1148 NotifyProps: map[string]string{ 1149 "channel": "true", 1150 }, 1151 } 1152 1153 // Channel-wide mentions are not ignored on channel level 1154 channelMemberNotifyPropsMap3Off := map[string]model.StringMap{ 1155 user3.Id: { 1156 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 1157 }, 1158 } 1159 profiles = map[string]*model.User{user3.Id: user3} 1160 mentions = th.App.getMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap3Off) 1161 require.Len(t, mentions, 3, "should've returned three mention keywords") 1162 ids, ok = mentions["@channel"] 1163 require.True(t, ok) 1164 require.Equal(t, user3.Id, ids[0], "should've returned mention key of @channel") 1165 ids, ok = mentions["@all"] 1166 require.True(t, ok) 1167 require.Equal(t, user3.Id, ids[0], "should've returned mention key of @all") 1168 1169 // Channel member notify props is set to default 1170 channelMemberNotifyPropsMapDefault := map[string]model.StringMap{ 1171 user3.Id: { 1172 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_DEFAULT, 1173 }, 1174 } 1175 profiles = map[string]*model.User{user3.Id: user3} 1176 mentions = th.App.getMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMapDefault) 1177 require.Len(t, mentions, 3, "should've returned three mention keywords") 1178 ids, ok = mentions["@channel"] 1179 require.True(t, ok) 1180 require.Equal(t, user3.Id, ids[0], "should've returned mention key of @channel") 1181 ids, ok = mentions["@all"] 1182 require.True(t, ok) 1183 require.Equal(t, user3.Id, ids[0], "should've returned mention key of @all") 1184 1185 // Channel member notify props is empty 1186 channelMemberNotifyPropsMapEmpty := map[string]model.StringMap{} 1187 profiles = map[string]*model.User{user3.Id: user3} 1188 mentions = th.App.getMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMapEmpty) 1189 require.Len(t, mentions, 3, "should've returned three mention keywords") 1190 ids, ok = mentions["@channel"] 1191 require.True(t, ok) 1192 require.Equal(t, user3.Id, ids[0], "should've returned mention key of @channel") 1193 ids, ok = mentions["@all"] 1194 require.True(t, ok) 1195 require.Equal(t, user3.Id, ids[0], "should've returned mention key of @all") 1196 1197 // Channel-wide mentions are ignored channel level 1198 channelMemberNotifyPropsMap3On := map[string]model.StringMap{ 1199 user3.Id: { 1200 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_ON, 1201 }, 1202 } 1203 mentions = th.App.getMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap3On) 1204 require.NotEmpty(t, mentions, "should've not returned any keywords") 1205 1206 // user with all types of mentions enabled 1207 user4 := &model.User{ 1208 Id: model.NewId(), 1209 FirstName: "First", 1210 Username: "User", 1211 NotifyProps: map[string]string{ 1212 "mention_keys": "User,@User,MENTION", 1213 "first_name": "true", 1214 "channel": "true", 1215 }, 1216 } 1217 1218 // Channel-wide mentions are not ignored on channel level 1219 channelMemberNotifyPropsMap4Off := map[string]model.StringMap{ 1220 user4.Id: { 1221 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 1222 }, 1223 } 1224 1225 profiles = map[string]*model.User{user4.Id: user4} 1226 mentions = th.App.getMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap4Off) 1227 require.Len(t, mentions, 6, "should've returned six mention keywords") 1228 ids, ok = mentions["user"] 1229 require.True(t, ok) 1230 require.Equal(t, user4.Id, ids[0], "should've returned mention key of user") 1231 ids, ok = mentions["@user"] 1232 require.True(t, ok) 1233 require.Equal(t, user4.Id, ids[0], "should've returned mention key of @user") 1234 ids, ok = mentions["mention"] 1235 require.True(t, ok) 1236 require.Equal(t, user4.Id, ids[0], "should've returned mention key of mention") 1237 ids, ok = mentions["First"] 1238 require.True(t, ok) 1239 require.Equal(t, user4.Id, ids[0], "should've returned mention key of First") 1240 ids, ok = mentions["@channel"] 1241 require.True(t, ok) 1242 require.Equal(t, user4.Id, ids[0], "should've returned mention key of @channel") 1243 ids, ok = mentions["@all"] 1244 require.True(t, ok) 1245 require.Equal(t, user4.Id, ids[0], "should've returned mention key of @all") 1246 1247 // Channel-wide mentions are ignored on channel level 1248 channelMemberNotifyPropsMap4On := map[string]model.StringMap{ 1249 user4.Id: { 1250 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_ON, 1251 }, 1252 } 1253 mentions = th.App.getMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap4On) 1254 require.Len(t, mentions, 4, "should've returned four mention keywords") 1255 ids, ok = mentions["user"] 1256 require.True(t, ok) 1257 require.Equal(t, user4.Id, ids[0], "should've returned mention key of user") 1258 ids, ok = mentions["@user"] 1259 require.True(t, ok) 1260 require.Equal(t, user4.Id, ids[0], "should've returned mention key of @user") 1261 ids, ok = mentions["mention"] 1262 require.True(t, ok) 1263 require.Equal(t, user4.Id, ids[0], "should've returned mention key of mention") 1264 ids, ok = mentions["First"] 1265 require.True(t, ok) 1266 require.Equal(t, user4.Id, ids[0], "should've returned mention key of First") 1267 dup_count := func(list []string) map[string]int { 1268 1269 duplicate_frequency := make(map[string]int) 1270 1271 for _, item := range list { 1272 // check if the item/element exist in the duplicate_frequency map 1273 1274 _, exist := duplicate_frequency[item] 1275 1276 if exist { 1277 duplicate_frequency[item] += 1 // increase counter by 1 if already in the map 1278 } else { 1279 duplicate_frequency[item] = 1 // else start counting from 1 1280 } 1281 } 1282 return duplicate_frequency 1283 } 1284 1285 // multiple users but no more than MaxNotificationsPerChannel 1286 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxNotificationsPerChannel = 4 }) 1287 profiles = map[string]*model.User{ 1288 user1.Id: user1, 1289 user2.Id: user2, 1290 user3.Id: user3, 1291 user4.Id: user4, 1292 } 1293 // Channel-wide mentions are not ignored on channel level for all users 1294 channelMemberNotifyPropsMap5Off := map[string]model.StringMap{ 1295 user1.Id: { 1296 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 1297 }, 1298 user2.Id: { 1299 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 1300 }, 1301 user3.Id: { 1302 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 1303 }, 1304 user4.Id: { 1305 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 1306 }, 1307 } 1308 mentions = th.App.getMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap5Off) 1309 require.Len(t, mentions, 6, "should've returned six mention keywords") 1310 ids, ok = mentions["user"] 1311 require.True(t, ok) 1312 require.Len(t, ids, 2) 1313 require.False(t, ids[0] != user1.Id && ids[1] != user1.Id, "should've mentioned user1 with user") 1314 require.False(t, ids[0] != user4.Id && ids[1] != user4.Id, "should've mentioned user4 with user") 1315 idsMap := dup_count(mentions["@user"]) 1316 require.True(t, ok) 1317 require.Len(t, idsMap, 4) 1318 require.Equal(t, idsMap[user1.Id], 2, "should've mentioned user1 with @user") 1319 require.Equal(t, idsMap[user4.Id], 2, "should've mentioned user4 with @user") 1320 1321 ids, ok = mentions["mention"] 1322 require.True(t, ok) 1323 require.Len(t, ids, 2) 1324 require.False(t, ids[0] != user1.Id && ids[1] != user1.Id, "should've mentioned user1 with mention") 1325 require.False(t, ids[0] != user4.Id && ids[1] != user4.Id, "should've mentioned user4 with mention") 1326 ids, ok = mentions["First"] 1327 require.True(t, ok) 1328 require.Len(t, ids, 2) 1329 require.False(t, ids[0] != user2.Id && ids[1] != user2.Id, "should've mentioned user2 with First") 1330 require.False(t, ids[0] != user4.Id && ids[1] != user4.Id, "should've mentioned user4 with First") 1331 ids, ok = mentions["@channel"] 1332 require.True(t, ok) 1333 require.Len(t, ids, 2) 1334 require.False(t, ids[0] != user3.Id && ids[1] != user3.Id, "should've mentioned user3 with @channel") 1335 require.False(t, ids[0] != user4.Id && ids[1] != user4.Id, "should've mentioned user4 with @channel") 1336 ids, ok = mentions["@all"] 1337 require.True(t, ok) 1338 require.Len(t, ids, 2) 1339 require.False(t, ids[0] != user3.Id && ids[1] != user3.Id, "should've mentioned user3 with @all") 1340 require.False(t, ids[0] != user4.Id && ids[1] != user4.Id, "should've mentioned user4 with @all") 1341 1342 // multiple users and more than MaxNotificationsPerChannel 1343 mentions = th.App.getMentionKeywordsInChannel(profiles, false, channelMemberNotifyPropsMap4Off) 1344 require.Len(t, mentions, 4, "should've returned four mention keywords") 1345 _, ok = mentions["@channel"] 1346 require.False(t, ok, "should not have mentioned any user with @channel") 1347 _, ok = mentions["@all"] 1348 require.False(t, ok, "should not have mentioned any user with @all") 1349 _, ok = mentions["@here"] 1350 require.False(t, ok, "should not have mentioned any user with @here") 1351 // no special mentions 1352 profiles = map[string]*model.User{ 1353 user1.Id: user1, 1354 } 1355 mentions = th.App.getMentionKeywordsInChannel(profiles, false, channelMemberNotifyPropsMap4Off) 1356 require.Len(t, mentions, 3, "should've returned three mention keywords") 1357 ids, ok = mentions["user"] 1358 require.True(t, ok) 1359 require.Len(t, ids, 1) 1360 require.Equal(t, user1.Id, ids[0], "should've mentioned user1 with user") 1361 ids, ok = mentions["@user"] 1362 1363 require.True(t, ok) 1364 require.Len(t, ids, 2) 1365 require.Equal(t, user1.Id, ids[0], "should've mentioned user1 twice with @user") 1366 require.Equal(t, user1.Id, ids[1], "should've mentioned user1 twice with @user") 1367 1368 ids, ok = mentions["mention"] 1369 require.True(t, ok) 1370 require.Len(t, ids, 1) 1371 require.Equal(t, user1.Id, ids[0], "should've mentioned user1 with user") 1372 1373 _, ok = mentions["First"] 1374 require.False(t, ok, "should not have mentioned user1 with First") 1375 _, ok = mentions["@channel"] 1376 require.False(t, ok, "should not have mentioned any user with @channel") 1377 _, ok = mentions["@all"] 1378 require.False(t, ok, "should not have mentioned any user with @all") 1379 _, ok = mentions["@here"] 1380 require.False(t, ok, "should not have mentioned any user with @here") 1381 1382 // user with empty mention keys 1383 userNoMentionKeys := &model.User{ 1384 Id: model.NewId(), 1385 FirstName: "First", 1386 Username: "User", 1387 NotifyProps: map[string]string{ 1388 "mention_keys": ",", 1389 }, 1390 } 1391 1392 channelMemberNotifyPropsMapEmptyOff := map[string]model.StringMap{ 1393 userNoMentionKeys.Id: { 1394 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 1395 }, 1396 } 1397 1398 profiles = map[string]*model.User{userNoMentionKeys.Id: userNoMentionKeys} 1399 mentions = th.App.getMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMapEmptyOff) 1400 assert.Equal(t, 1, len(mentions), "should've returned one metion keyword") 1401 ids, ok = mentions["@user"] 1402 assert.True(t, ok) 1403 assert.Equal(t, userNoMentionKeys.Id, ids[0], "should've returned mention key of @user") 1404 } 1405 1406 func TestAddMentionKeywordsForUser(t *testing.T) { 1407 t.Run("should add @user", func(t *testing.T) { 1408 user := &model.User{ 1409 Id: model.NewId(), 1410 Username: "user", 1411 } 1412 channelNotifyProps := map[string]string{} 1413 1414 keywords := map[string][]string{} 1415 addMentionKeywordsForUser(keywords, user, channelNotifyProps, nil, false) 1416 1417 assert.Contains(t, keywords["@user"], user.Id) 1418 }) 1419 1420 t.Run("should add custom mention keywords", func(t *testing.T) { 1421 user := &model.User{ 1422 Id: model.NewId(), 1423 Username: "user", 1424 NotifyProps: map[string]string{ 1425 model.MENTION_KEYS_NOTIFY_PROP: "apple,BANANA,OrAnGe", 1426 }, 1427 } 1428 channelNotifyProps := map[string]string{} 1429 1430 keywords := map[string][]string{} 1431 addMentionKeywordsForUser(keywords, user, channelNotifyProps, nil, false) 1432 1433 assert.Contains(t, keywords["apple"], user.Id) 1434 assert.Contains(t, keywords["banana"], user.Id) 1435 assert.Contains(t, keywords["orange"], user.Id) 1436 }) 1437 1438 t.Run("should not add empty custom keywords", func(t *testing.T) { 1439 user := &model.User{ 1440 Id: model.NewId(), 1441 Username: "user", 1442 NotifyProps: map[string]string{ 1443 model.MENTION_KEYS_NOTIFY_PROP: ",,", 1444 }, 1445 } 1446 channelNotifyProps := map[string]string{} 1447 1448 keywords := map[string][]string{} 1449 addMentionKeywordsForUser(keywords, user, channelNotifyProps, nil, false) 1450 1451 assert.Nil(t, keywords[""]) 1452 }) 1453 1454 t.Run("should add case sensitive first name if enabled", func(t *testing.T) { 1455 user := &model.User{ 1456 Id: model.NewId(), 1457 Username: "user", 1458 FirstName: "William", 1459 LastName: "Robert", 1460 NotifyProps: map[string]string{ 1461 model.FIRST_NAME_NOTIFY_PROP: "true", 1462 }, 1463 } 1464 channelNotifyProps := map[string]string{} 1465 1466 keywords := map[string][]string{} 1467 addMentionKeywordsForUser(keywords, user, channelNotifyProps, nil, false) 1468 1469 assert.Contains(t, keywords["William"], user.Id) 1470 assert.NotContains(t, keywords["william"], user.Id) 1471 assert.NotContains(t, keywords["Robert"], user.Id) 1472 }) 1473 1474 t.Run("should not add case sensitive first name if enabled but empty First Name", func(t *testing.T) { 1475 user := &model.User{ 1476 Id: model.NewId(), 1477 Username: "user", 1478 FirstName: "", 1479 LastName: "Robert", 1480 NotifyProps: map[string]string{ 1481 model.FIRST_NAME_NOTIFY_PROP: "true", 1482 }, 1483 } 1484 channelNotifyProps := map[string]string{} 1485 1486 keywords := map[string][]string{} 1487 addMentionKeywordsForUser(keywords, user, channelNotifyProps, nil, false) 1488 1489 assert.NotContains(t, keywords[""], user.Id) 1490 }) 1491 1492 t.Run("should not add case sensitive first name if disabled", func(t *testing.T) { 1493 user := &model.User{ 1494 Id: model.NewId(), 1495 Username: "user", 1496 FirstName: "William", 1497 LastName: "Robert", 1498 NotifyProps: map[string]string{ 1499 model.FIRST_NAME_NOTIFY_PROP: "false", 1500 }, 1501 } 1502 channelNotifyProps := map[string]string{} 1503 1504 keywords := map[string][]string{} 1505 addMentionKeywordsForUser(keywords, user, channelNotifyProps, nil, false) 1506 1507 assert.NotContains(t, keywords["William"], user.Id) 1508 assert.NotContains(t, keywords["william"], user.Id) 1509 assert.NotContains(t, keywords["Robert"], user.Id) 1510 }) 1511 1512 t.Run("should add @channel/@all/@here when allowed", func(t *testing.T) { 1513 user := &model.User{ 1514 Id: model.NewId(), 1515 Username: "user", 1516 NotifyProps: map[string]string{ 1517 model.CHANNEL_MENTIONS_NOTIFY_PROP: "true", 1518 }, 1519 } 1520 channelNotifyProps := map[string]string{} 1521 status := &model.Status{ 1522 Status: model.STATUS_ONLINE, 1523 } 1524 1525 keywords := map[string][]string{} 1526 addMentionKeywordsForUser(keywords, user, channelNotifyProps, status, true) 1527 1528 assert.Contains(t, keywords["@channel"], user.Id) 1529 assert.Contains(t, keywords["@all"], user.Id) 1530 assert.Contains(t, keywords["@here"], user.Id) 1531 }) 1532 1533 t.Run("should not add @channel/@all/@here when not allowed", func(t *testing.T) { 1534 user := &model.User{ 1535 Id: model.NewId(), 1536 Username: "user", 1537 NotifyProps: map[string]string{ 1538 model.CHANNEL_MENTIONS_NOTIFY_PROP: "true", 1539 }, 1540 } 1541 channelNotifyProps := map[string]string{} 1542 status := &model.Status{ 1543 Status: model.STATUS_ONLINE, 1544 } 1545 1546 keywords := map[string][]string{} 1547 addMentionKeywordsForUser(keywords, user, channelNotifyProps, status, false) 1548 1549 assert.NotContains(t, keywords["@channel"], user.Id) 1550 assert.NotContains(t, keywords["@all"], user.Id) 1551 assert.NotContains(t, keywords["@here"], user.Id) 1552 }) 1553 1554 t.Run("should not add @channel/@all/@here when disabled for user", func(t *testing.T) { 1555 user := &model.User{ 1556 Id: model.NewId(), 1557 Username: "user", 1558 NotifyProps: map[string]string{ 1559 model.CHANNEL_MENTIONS_NOTIFY_PROP: "false", 1560 }, 1561 } 1562 channelNotifyProps := map[string]string{} 1563 status := &model.Status{ 1564 Status: model.STATUS_ONLINE, 1565 } 1566 1567 keywords := map[string][]string{} 1568 addMentionKeywordsForUser(keywords, user, channelNotifyProps, status, true) 1569 1570 assert.NotContains(t, keywords["@channel"], user.Id) 1571 assert.NotContains(t, keywords["@all"], user.Id) 1572 assert.NotContains(t, keywords["@here"], user.Id) 1573 }) 1574 1575 t.Run("should not add @channel/@all/@here when disabled for channel", func(t *testing.T) { 1576 user := &model.User{ 1577 Id: model.NewId(), 1578 Username: "user", 1579 NotifyProps: map[string]string{ 1580 model.CHANNEL_MENTIONS_NOTIFY_PROP: "true", 1581 }, 1582 } 1583 channelNotifyProps := map[string]string{ 1584 model.IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP: model.IGNORE_CHANNEL_MENTIONS_ON, 1585 } 1586 status := &model.Status{ 1587 Status: model.STATUS_ONLINE, 1588 } 1589 1590 keywords := map[string][]string{} 1591 addMentionKeywordsForUser(keywords, user, channelNotifyProps, status, true) 1592 1593 assert.NotContains(t, keywords["@channel"], user.Id) 1594 assert.NotContains(t, keywords["@all"], user.Id) 1595 assert.NotContains(t, keywords["@here"], user.Id) 1596 }) 1597 1598 t.Run("should not add @channel/@all/@here when channel is muted and channel mention setting is not updated by user", func(t *testing.T) { 1599 user := &model.User{ 1600 Id: model.NewId(), 1601 Username: "user", 1602 NotifyProps: map[string]string{ 1603 model.CHANNEL_MENTIONS_NOTIFY_PROP: "true", 1604 }, 1605 } 1606 channelNotifyProps := map[string]string{ 1607 model.MARK_UNREAD_NOTIFY_PROP: model.USER_NOTIFY_MENTION, 1608 model.IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP: model.IGNORE_CHANNEL_MENTIONS_DEFAULT, 1609 } 1610 status := &model.Status{ 1611 Status: model.STATUS_ONLINE, 1612 } 1613 1614 keywords := map[string][]string{} 1615 addMentionKeywordsForUser(keywords, user, channelNotifyProps, status, true) 1616 1617 assert.NotContains(t, keywords["@channel"], user.Id) 1618 assert.NotContains(t, keywords["@all"], user.Id) 1619 assert.NotContains(t, keywords["@here"], user.Id) 1620 }) 1621 1622 t.Run("should not add @here when when user is not online", func(t *testing.T) { 1623 user := &model.User{ 1624 Id: model.NewId(), 1625 Username: "user", 1626 NotifyProps: map[string]string{ 1627 model.CHANNEL_MENTIONS_NOTIFY_PROP: "true", 1628 }, 1629 } 1630 channelNotifyProps := map[string]string{} 1631 status := &model.Status{ 1632 Status: model.STATUS_AWAY, 1633 } 1634 1635 keywords := map[string][]string{} 1636 addMentionKeywordsForUser(keywords, user, channelNotifyProps, status, true) 1637 1638 assert.Contains(t, keywords["@channel"], user.Id) 1639 assert.Contains(t, keywords["@all"], user.Id) 1640 assert.NotContains(t, keywords["@here"], user.Id) 1641 }) 1642 1643 t.Run("should add for multiple users", func(t *testing.T) { 1644 user1 := &model.User{ 1645 Id: model.NewId(), 1646 Username: "user1", 1647 NotifyProps: map[string]string{ 1648 model.CHANNEL_MENTIONS_NOTIFY_PROP: "true", 1649 }, 1650 } 1651 user2 := &model.User{ 1652 Id: model.NewId(), 1653 Username: "user2", 1654 NotifyProps: map[string]string{ 1655 model.CHANNEL_MENTIONS_NOTIFY_PROP: "true", 1656 }, 1657 } 1658 1659 keywords := map[string][]string{} 1660 addMentionKeywordsForUser(keywords, user1, map[string]string{}, nil, true) 1661 addMentionKeywordsForUser(keywords, user2, map[string]string{}, nil, true) 1662 1663 assert.Contains(t, keywords["@user1"], user1.Id) 1664 assert.Contains(t, keywords["@user2"], user2.Id) 1665 assert.Contains(t, keywords["@all"], user1.Id) 1666 assert.Contains(t, keywords["@all"], user2.Id) 1667 }) 1668 } 1669 1670 func TestGetMentionsEnabledFields(t *testing.T) { 1671 1672 attachmentWithTextAndPreText := model.SlackAttachment{ 1673 Text: "@here with mentions", 1674 Pretext: "@Channel some comment for the channel", 1675 } 1676 1677 attachmentWithOutPreText := model.SlackAttachment{ 1678 Text: "some text", 1679 } 1680 attachments := []*model.SlackAttachment{ 1681 &attachmentWithTextAndPreText, 1682 &attachmentWithOutPreText, 1683 } 1684 1685 post := &model.Post{ 1686 Message: "This is the message", 1687 Props: model.StringInterface{ 1688 "attachments": attachments, 1689 }, 1690 } 1691 expectedFields := []string{ 1692 "This is the message", 1693 "@Channel some comment for the channel", 1694 "@here with mentions", 1695 "some text"} 1696 1697 mentionEnabledFields := getMentionsEnabledFields(post) 1698 1699 assert.EqualValues(t, 4, len(mentionEnabledFields)) 1700 assert.EqualValues(t, expectedFields, mentionEnabledFields) 1701 } 1702 1703 func TestPostNotificationGetChannelName(t *testing.T) { 1704 sender := &model.User{Id: model.NewId(), Username: "sender", FirstName: "Sender", LastName: "Sender", Nickname: "Sender"} 1705 recipient := &model.User{Id: model.NewId(), Username: "recipient", FirstName: "Recipient", LastName: "Recipient", Nickname: "Recipient"} 1706 otherUser := &model.User{Id: model.NewId(), Username: "other", FirstName: "Other", LastName: "Other", Nickname: "Other"} 1707 profileMap := map[string]*model.User{ 1708 sender.Id: sender, 1709 recipient.Id: recipient, 1710 otherUser.Id: otherUser, 1711 } 1712 1713 for name, testCase := range map[string]struct { 1714 channel *model.Channel 1715 nameFormat string 1716 recipientId string 1717 expected string 1718 }{ 1719 "regular channel": { 1720 channel: &model.Channel{Type: model.CHANNEL_OPEN, Name: "channel", DisplayName: "My Channel"}, 1721 expected: "My Channel", 1722 }, 1723 "direct channel, unspecified": { 1724 channel: &model.Channel{Type: model.CHANNEL_DIRECT}, 1725 expected: "@sender", 1726 }, 1727 "direct channel, username": { 1728 channel: &model.Channel{Type: model.CHANNEL_DIRECT}, 1729 nameFormat: model.SHOW_USERNAME, 1730 expected: "@sender", 1731 }, 1732 "direct channel, full name": { 1733 channel: &model.Channel{Type: model.CHANNEL_DIRECT}, 1734 nameFormat: model.SHOW_FULLNAME, 1735 expected: "Sender Sender", 1736 }, 1737 "direct channel, nickname": { 1738 channel: &model.Channel{Type: model.CHANNEL_DIRECT}, 1739 nameFormat: model.SHOW_NICKNAME_FULLNAME, 1740 expected: "Sender", 1741 }, 1742 "group channel, unspecified": { 1743 channel: &model.Channel{Type: model.CHANNEL_GROUP}, 1744 expected: "other, sender", 1745 }, 1746 "group channel, username": { 1747 channel: &model.Channel{Type: model.CHANNEL_GROUP}, 1748 nameFormat: model.SHOW_USERNAME, 1749 expected: "other, sender", 1750 }, 1751 "group channel, full name": { 1752 channel: &model.Channel{Type: model.CHANNEL_GROUP}, 1753 nameFormat: model.SHOW_FULLNAME, 1754 expected: "Other Other, Sender Sender", 1755 }, 1756 "group channel, nickname": { 1757 channel: &model.Channel{Type: model.CHANNEL_GROUP}, 1758 nameFormat: model.SHOW_NICKNAME_FULLNAME, 1759 expected: "Other, Sender", 1760 }, 1761 "group channel, not excluding current user": { 1762 channel: &model.Channel{Type: model.CHANNEL_GROUP}, 1763 nameFormat: model.SHOW_NICKNAME_FULLNAME, 1764 expected: "Other, Sender", 1765 recipientId: "", 1766 }, 1767 } { 1768 t.Run(name, func(t *testing.T) { 1769 notification := &PostNotification{ 1770 Channel: testCase.channel, 1771 Sender: sender, 1772 ProfileMap: profileMap, 1773 } 1774 1775 recipientId := recipient.Id 1776 if testCase.recipientId != "" { 1777 recipientId = testCase.recipientId 1778 } 1779 1780 assert.Equal(t, testCase.expected, notification.GetChannelName(testCase.nameFormat, recipientId)) 1781 }) 1782 } 1783 } 1784 1785 func TestPostNotificationGetSenderName(t *testing.T) { 1786 th := Setup(t) 1787 defer th.TearDown() 1788 1789 defaultChannel := &model.Channel{Type: model.CHANNEL_OPEN} 1790 defaultPost := &model.Post{Props: model.StringInterface{}} 1791 sender := &model.User{Id: model.NewId(), Username: "sender", FirstName: "Sender", LastName: "Sender", Nickname: "Sender"} 1792 1793 overriddenPost := &model.Post{ 1794 Props: model.StringInterface{ 1795 "override_username": "Overridden", 1796 "from_webhook": "true", 1797 }, 1798 } 1799 1800 for name, testCase := range map[string]struct { 1801 channel *model.Channel 1802 post *model.Post 1803 nameFormat string 1804 allowOverrides bool 1805 expected string 1806 }{ 1807 "name format unspecified": { 1808 expected: "@" + sender.Username, 1809 }, 1810 "name format username": { 1811 nameFormat: model.SHOW_USERNAME, 1812 expected: "@" + sender.Username, 1813 }, 1814 "name format full name": { 1815 nameFormat: model.SHOW_FULLNAME, 1816 expected: sender.FirstName + " " + sender.LastName, 1817 }, 1818 "name format nickname": { 1819 nameFormat: model.SHOW_NICKNAME_FULLNAME, 1820 expected: sender.Nickname, 1821 }, 1822 "system message": { 1823 post: &model.Post{Type: model.POST_SYSTEM_MESSAGE_PREFIX + "custom"}, 1824 expected: utils.T("system.message.name"), 1825 }, 1826 "overridden username": { 1827 post: overriddenPost, 1828 allowOverrides: true, 1829 expected: overriddenPost.GetProp("override_username").(string), 1830 }, 1831 "overridden username, direct channel": { 1832 channel: &model.Channel{Type: model.CHANNEL_DIRECT}, 1833 post: overriddenPost, 1834 allowOverrides: true, 1835 expected: "@" + sender.Username, 1836 }, 1837 "overridden username, overrides disabled": { 1838 post: overriddenPost, 1839 allowOverrides: false, 1840 expected: "@" + sender.Username, 1841 }, 1842 } { 1843 t.Run(name, func(t *testing.T) { 1844 channel := defaultChannel 1845 if testCase.channel != nil { 1846 channel = testCase.channel 1847 } 1848 1849 post := defaultPost 1850 if testCase.post != nil { 1851 post = testCase.post 1852 } 1853 1854 notification := &PostNotification{ 1855 Channel: channel, 1856 Post: post, 1857 Sender: sender, 1858 } 1859 1860 assert.Equal(t, testCase.expected, notification.GetSenderName(testCase.nameFormat, testCase.allowOverrides)) 1861 }) 1862 } 1863 } 1864 1865 func TestIsKeywordMultibyte(t *testing.T) { 1866 id1 := model.NewId() 1867 1868 for name, tc := range map[string]struct { 1869 Message string 1870 Attachments []*model.SlackAttachment 1871 Keywords map[string][]string 1872 Groups map[string]*model.Group 1873 Expected *ExplicitMentions 1874 }{ 1875 "MultibyteCharacter": { 1876 Message: "My name is 萌", 1877 Keywords: map[string][]string{"萌": {id1}}, 1878 Expected: &ExplicitMentions{ 1879 Mentions: map[string]MentionType{ 1880 id1: KeywordMention, 1881 }, 1882 }, 1883 }, 1884 "MultibyteCharacterWithNoUser": { 1885 Message: "My name is 萌", 1886 Keywords: map[string][]string{"萌": {}}, 1887 Expected: &ExplicitMentions{ 1888 Mentions: nil, 1889 }, 1890 }, 1891 "MultibyteCharacterAtBeginningOfSentence": { 1892 Message: "이메일을 보내다.", 1893 Keywords: map[string][]string{"이메일": {id1}}, 1894 Expected: &ExplicitMentions{ 1895 Mentions: map[string]MentionType{ 1896 id1: KeywordMention, 1897 }, 1898 }, 1899 }, 1900 "MultibyteCharacterAtBeginningOfSentenceWithNoUser": { 1901 Message: "이메일을 보내다.", 1902 Keywords: map[string][]string{"이메일": {}}, 1903 Expected: &ExplicitMentions{ 1904 Mentions: nil, 1905 }, 1906 }, 1907 "MultibyteCharacterInPartOfSentence": { 1908 Message: "我爱吃番茄炒饭", 1909 Keywords: map[string][]string{"番茄": {id1}}, 1910 Expected: &ExplicitMentions{ 1911 Mentions: map[string]MentionType{ 1912 id1: KeywordMention, 1913 }, 1914 }, 1915 }, 1916 "MultibyteCharacterInPartOfSentenceWithNoUser": { 1917 Message: "我爱吃番茄炒饭", 1918 Keywords: map[string][]string{"番茄": {}}, 1919 Expected: &ExplicitMentions{ 1920 Mentions: nil, 1921 }, 1922 }, 1923 "MultibyteCharacterAtEndOfSentence": { 1924 Message: "こんにちは、世界", 1925 Keywords: map[string][]string{"世界": {id1}}, 1926 Expected: &ExplicitMentions{ 1927 Mentions: map[string]MentionType{ 1928 id1: KeywordMention, 1929 }, 1930 }, 1931 }, 1932 "MultibyteCharacterAtEndOfSentenceWithNoUser": { 1933 Message: "こんにちは、世界", 1934 Keywords: map[string][]string{"世界": {}}, 1935 Expected: &ExplicitMentions{ 1936 Mentions: nil, 1937 }, 1938 }, 1939 "MultibyteCharacterTwiceInSentence": { 1940 Message: "石橋さんが石橋を渡る", 1941 Keywords: map[string][]string{"石橋": {id1}}, 1942 Expected: &ExplicitMentions{ 1943 Mentions: map[string]MentionType{ 1944 id1: KeywordMention, 1945 }, 1946 }, 1947 }, 1948 "MultibyteCharacterTwiceInSentenceWithNoUser": { 1949 Message: "石橋さんが石橋を渡る", 1950 Keywords: map[string][]string{"石橋": {}}, 1951 Expected: &ExplicitMentions{ 1952 Mentions: nil, 1953 }, 1954 }, 1955 } { 1956 t.Run(name, func(t *testing.T) { 1957 post := &model.Post{ 1958 Message: tc.Message, 1959 Props: model.StringInterface{ 1960 "attachments": tc.Attachments, 1961 }, 1962 } 1963 1964 m := getExplicitMentions(post, tc.Keywords, tc.Groups) 1965 assert.EqualValues(t, tc.Expected, m) 1966 }) 1967 } 1968 } 1969 1970 func TestAddMention(t *testing.T) { 1971 t.Run("should initialize Mentions and store new mentions", func(t *testing.T) { 1972 m := &ExplicitMentions{} 1973 1974 userID1 := model.NewId() 1975 userID2 := model.NewId() 1976 1977 m.addMention(userID1, KeywordMention) 1978 m.addMention(userID2, CommentMention) 1979 1980 assert.Equal(t, map[string]MentionType{ 1981 userID1: KeywordMention, 1982 userID2: CommentMention, 1983 }, m.Mentions) 1984 }) 1985 1986 t.Run("should replace existing mentions with higher priority ones", func(t *testing.T) { 1987 m := &ExplicitMentions{} 1988 1989 userID1 := model.NewId() 1990 userID2 := model.NewId() 1991 1992 m.addMention(userID1, ThreadMention) 1993 m.addMention(userID2, DMMention) 1994 1995 m.addMention(userID1, ChannelMention) 1996 m.addMention(userID2, KeywordMention) 1997 1998 assert.Equal(t, map[string]MentionType{ 1999 userID1: ChannelMention, 2000 userID2: KeywordMention, 2001 }, m.Mentions) 2002 }) 2003 2004 t.Run("should not replace high priority mentions with low priority ones", func(t *testing.T) { 2005 m := &ExplicitMentions{} 2006 2007 userID1 := model.NewId() 2008 userID2 := model.NewId() 2009 2010 m.addMention(userID1, KeywordMention) 2011 m.addMention(userID2, CommentMention) 2012 2013 m.addMention(userID1, DMMention) 2014 m.addMention(userID2, ThreadMention) 2015 2016 assert.Equal(t, map[string]MentionType{ 2017 userID1: KeywordMention, 2018 userID2: CommentMention, 2019 }, m.Mentions) 2020 }) 2021 } 2022 2023 func TestCheckForMentionUsers(t *testing.T) { 2024 id1 := model.NewId() 2025 id2 := model.NewId() 2026 2027 for name, tc := range map[string]struct { 2028 Word string 2029 Attachments []*model.SlackAttachment 2030 Keywords map[string][]string 2031 Expected *ExplicitMentions 2032 }{ 2033 "Nobody": { 2034 Word: "nothing", 2035 Keywords: map[string][]string{}, 2036 Expected: &ExplicitMentions{}, 2037 }, 2038 "UppercaseUser1": { 2039 Word: "@User", 2040 Keywords: map[string][]string{"@user": {id1}}, 2041 Expected: &ExplicitMentions{ 2042 Mentions: map[string]MentionType{ 2043 id1: KeywordMention, 2044 }, 2045 }, 2046 }, 2047 "LowercaseUser1": { 2048 Word: "@user", 2049 Keywords: map[string][]string{"@user": {id1}}, 2050 Expected: &ExplicitMentions{ 2051 Mentions: map[string]MentionType{ 2052 id1: KeywordMention, 2053 }, 2054 }, 2055 }, 2056 "LowercaseUser2": { 2057 Word: "@user2", 2058 Keywords: map[string][]string{"@user2": {id2}}, 2059 Expected: &ExplicitMentions{ 2060 Mentions: map[string]MentionType{ 2061 id2: KeywordMention, 2062 }, 2063 }, 2064 }, 2065 "UppercaseUser2": { 2066 Word: "@UsEr2", 2067 Keywords: map[string][]string{"@user2": {id2}}, 2068 Expected: &ExplicitMentions{ 2069 Mentions: map[string]MentionType{ 2070 id2: KeywordMention, 2071 }, 2072 }, 2073 }, 2074 "HereMention": { 2075 Word: "@here", 2076 Expected: &ExplicitMentions{ 2077 HereMentioned: true, 2078 }, 2079 }, 2080 "ChannelMention": { 2081 Word: "@channel", 2082 Expected: &ExplicitMentions{ 2083 ChannelMentioned: true, 2084 }, 2085 }, 2086 "AllMention": { 2087 Word: "@all", 2088 Expected: &ExplicitMentions{ 2089 AllMentioned: true, 2090 }, 2091 }, 2092 "UppercaseHere": { 2093 Word: "@HeRe", 2094 Expected: &ExplicitMentions{ 2095 HereMentioned: true, 2096 }, 2097 }, 2098 "UppercaseChannel": { 2099 Word: "@ChaNNel", 2100 Expected: &ExplicitMentions{ 2101 ChannelMentioned: true, 2102 }, 2103 }, 2104 "UppercaseAll": { 2105 Word: "@ALL", 2106 Expected: &ExplicitMentions{ 2107 AllMentioned: true, 2108 }, 2109 }, 2110 } { 2111 t.Run(name, func(t *testing.T) { 2112 2113 e := &ExplicitMentions{} 2114 e.checkForMention(tc.Word, tc.Keywords, nil) 2115 2116 assert.EqualValues(t, tc.Expected, e) 2117 }) 2118 } 2119 } 2120 2121 func TestAddGroupMention(t *testing.T) { 2122 for name, tc := range map[string]struct { 2123 Word string 2124 Groups map[string]*model.Group 2125 Expected bool 2126 }{ 2127 "No groups": { 2128 Word: "nothing", 2129 Groups: map[string]*model.Group{}, 2130 Expected: false, 2131 }, 2132 "No matching groups": { 2133 Word: "nothing", 2134 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2135 Expected: false, 2136 }, 2137 "matching group with no @": { 2138 Word: "engineering", 2139 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2140 Expected: false, 2141 }, 2142 "matching group with preceding @": { 2143 Word: "@engineering", 2144 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2145 Expected: true, 2146 }, 2147 "matching upper case group with preceding @": { 2148 Word: "@Engineering", 2149 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2150 Expected: true, 2151 }, 2152 } { 2153 t.Run(name, func(t *testing.T) { 2154 e := &ExplicitMentions{} 2155 groupFound := e.addGroupMention(tc.Word, tc.Groups) 2156 2157 if groupFound { 2158 require.Equal(t, len(e.GroupMentions), 1) 2159 } 2160 2161 require.Equal(t, tc.Expected, groupFound) 2162 }) 2163 } 2164 } 2165 2166 func TestProcessText(t *testing.T) { 2167 id1 := model.NewId() 2168 2169 for name, tc := range map[string]struct { 2170 Text string 2171 Keywords map[string][]string 2172 Groups map[string]*model.Group 2173 Expected *ExplicitMentions 2174 }{ 2175 "Mention user in text": { 2176 Text: "hello user @user1", 2177 Keywords: map[string][]string{"@user1": {id1}}, 2178 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2179 Expected: &ExplicitMentions{ 2180 Mentions: map[string]MentionType{ 2181 id1: KeywordMention, 2182 }, 2183 }, 2184 }, 2185 "Mention user after ending a sentence with full stop": { 2186 Text: "hello user.@user1", 2187 Keywords: map[string][]string{"@user1": {id1}}, 2188 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2189 Expected: &ExplicitMentions{ 2190 Mentions: map[string]MentionType{ 2191 id1: KeywordMention, 2192 }, 2193 }, 2194 }, 2195 "Mention user after hyphen": { 2196 Text: "hello user-@user1", 2197 Keywords: map[string][]string{"@user1": {id1}}, 2198 Expected: &ExplicitMentions{ 2199 Mentions: map[string]MentionType{ 2200 id1: KeywordMention, 2201 }, 2202 }, 2203 }, 2204 "Mention user after colon": { 2205 Text: "hello user:@user1", 2206 Keywords: map[string][]string{"@user1": {id1}}, 2207 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2208 Expected: &ExplicitMentions{ 2209 Mentions: map[string]MentionType{ 2210 id1: KeywordMention, 2211 }, 2212 }, 2213 }, 2214 "Mention here after colon": { 2215 Text: "hello all:@here", 2216 Keywords: map[string][]string{}, 2217 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2218 Expected: &ExplicitMentions{ 2219 HereMentioned: true, 2220 }, 2221 }, 2222 "Mention all after hyphen": { 2223 Text: "hello all-@all", 2224 Keywords: map[string][]string{}, 2225 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2226 Expected: &ExplicitMentions{ 2227 AllMentioned: true, 2228 }, 2229 }, 2230 "Mention channel after full stop": { 2231 Text: "hello channel.@channel", 2232 Keywords: map[string][]string{}, 2233 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2234 Expected: &ExplicitMentions{ 2235 ChannelMentioned: true, 2236 }, 2237 }, 2238 "Mention other pontential users or system calls": { 2239 Text: "hello @potentialuser and @otherpotentialuser", 2240 Keywords: map[string][]string{}, 2241 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2242 Expected: &ExplicitMentions{ 2243 OtherPotentialMentions: []string{"potentialuser", "otherpotentialuser"}, 2244 }, 2245 }, 2246 "Mention a real user and another potential user": { 2247 Text: "@user1, you can use @systembot to get help", 2248 Keywords: map[string][]string{"@user1": {id1}}, 2249 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2250 Expected: &ExplicitMentions{ 2251 Mentions: map[string]MentionType{ 2252 id1: KeywordMention, 2253 }, 2254 OtherPotentialMentions: []string{"systembot"}, 2255 }, 2256 }, 2257 "Mention a group": { 2258 Text: "@engineering", 2259 Keywords: map[string][]string{"@user1": {id1}}, 2260 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2261 Expected: &ExplicitMentions{ 2262 GroupMentions: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}}, 2263 OtherPotentialMentions: []string{"engineering"}, 2264 }, 2265 }, 2266 "Mention a real user and another potential user and a group": { 2267 Text: "@engineering @user1, you can use @systembot to get help from", 2268 Keywords: map[string][]string{"@user1": {id1}}, 2269 Groups: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}, "developers": {Name: model.NewString("developers")}}, 2270 Expected: &ExplicitMentions{ 2271 Mentions: map[string]MentionType{ 2272 id1: KeywordMention, 2273 }, 2274 GroupMentions: map[string]*model.Group{"engineering": {Name: model.NewString("engineering")}}, 2275 OtherPotentialMentions: []string{"engineering", "systembot"}, 2276 }, 2277 }, 2278 } { 2279 t.Run(name, func(t *testing.T) { 2280 e := &ExplicitMentions{} 2281 e.processText(tc.Text, tc.Keywords, tc.Groups) 2282 2283 assert.EqualValues(t, tc.Expected, e) 2284 }) 2285 } 2286 } 2287 2288 func TestGetNotificationNameFormat(t *testing.T) { 2289 th := Setup(t).InitBasic() 2290 defer th.TearDown() 2291 2292 t.Run("show full name on", func(t *testing.T) { 2293 th.App.UpdateConfig(func(cfg *model.Config) { 2294 *cfg.PrivacySettings.ShowFullName = true 2295 *cfg.TeamSettings.TeammateNameDisplay = model.SHOW_FULLNAME 2296 }) 2297 2298 assert.Equal(t, model.SHOW_FULLNAME, th.App.GetNotificationNameFormat(th.BasicUser)) 2299 }) 2300 2301 t.Run("show full name off", func(t *testing.T) { 2302 th.App.UpdateConfig(func(cfg *model.Config) { 2303 *cfg.PrivacySettings.ShowFullName = false 2304 *cfg.TeamSettings.TeammateNameDisplay = model.SHOW_FULLNAME 2305 }) 2306 2307 assert.Equal(t, model.SHOW_USERNAME, th.App.GetNotificationNameFormat(th.BasicUser)) 2308 }) 2309 } 2310 2311 func TestUserAllowsEmail(t *testing.T) { 2312 th := Setup(t) 2313 defer th.TearDown() 2314 2315 t.Run("should return true", func(t *testing.T) { 2316 user := th.CreateUser() 2317 2318 th.App.SetStatusOffline(user.Id, true) 2319 2320 channelMemberNotificationProps := model.StringMap{ 2321 model.EMAIL_NOTIFY_PROP: model.CHANNEL_NOTIFY_DEFAULT, 2322 model.MARK_UNREAD_NOTIFY_PROP: model.CHANNEL_MARK_UNREAD_ALL, 2323 } 2324 2325 assert.True(t, th.App.userAllowsEmail(user, channelMemberNotificationProps, &model.Post{Type: "some-post-type"})) 2326 }) 2327 2328 t.Run("should return false in case the status is ONLINE", func(t *testing.T) { 2329 user := th.CreateUser() 2330 2331 th.App.SetStatusOnline(user.Id, true) 2332 2333 channelMemberNotificationProps := model.StringMap{ 2334 model.EMAIL_NOTIFY_PROP: model.CHANNEL_NOTIFY_DEFAULT, 2335 model.MARK_UNREAD_NOTIFY_PROP: model.CHANNEL_MARK_UNREAD_ALL, 2336 } 2337 2338 assert.False(t, th.App.userAllowsEmail(user, channelMemberNotificationProps, &model.Post{Type: "some-post-type"})) 2339 }) 2340 2341 t.Run("should return false in case the EMAIL_NOTIFY_PROP is false", func(t *testing.T) { 2342 user := th.CreateUser() 2343 2344 th.App.SetStatusOffline(user.Id, true) 2345 2346 channelMemberNotificationProps := model.StringMap{ 2347 model.EMAIL_NOTIFY_PROP: "false", 2348 model.MARK_UNREAD_NOTIFY_PROP: model.CHANNEL_MARK_UNREAD_ALL, 2349 } 2350 2351 assert.False(t, th.App.userAllowsEmail(user, channelMemberNotificationProps, &model.Post{Type: "some-post-type"})) 2352 }) 2353 2354 t.Run("should return false in case the MARK_UNREAD_NOTIFY_PROP is CHANNEL_MARK_UNREAD_MENTION", func(t *testing.T) { 2355 user := th.CreateUser() 2356 2357 th.App.SetStatusOffline(user.Id, true) 2358 2359 channelMemberNotificationProps := model.StringMap{ 2360 model.EMAIL_NOTIFY_PROP: model.CHANNEL_NOTIFY_DEFAULT, 2361 model.MARK_UNREAD_NOTIFY_PROP: model.CHANNEL_MARK_UNREAD_MENTION, 2362 } 2363 2364 assert.False(t, th.App.userAllowsEmail(user, channelMemberNotificationProps, &model.Post{Type: "some-post-type"})) 2365 }) 2366 2367 t.Run("should return false in case the Post type is POST_AUTO_RESPONDER", func(t *testing.T) { 2368 user := th.CreateUser() 2369 2370 th.App.SetStatusOffline(user.Id, true) 2371 2372 channelMemberNotificationProps := model.StringMap{ 2373 model.EMAIL_NOTIFY_PROP: model.CHANNEL_NOTIFY_DEFAULT, 2374 model.MARK_UNREAD_NOTIFY_PROP: model.CHANNEL_MARK_UNREAD_ALL, 2375 } 2376 2377 assert.False(t, th.App.userAllowsEmail(user, channelMemberNotificationProps, &model.Post{Type: model.POST_AUTO_RESPONDER})) 2378 }) 2379 2380 t.Run("should return false in case the status is STATUS_OUT_OF_OFFICE", func(t *testing.T) { 2381 user := th.CreateUser() 2382 2383 th.App.SetStatusOutOfOffice(user.Id) 2384 2385 channelMemberNotificationProps := model.StringMap{ 2386 model.EMAIL_NOTIFY_PROP: model.CHANNEL_NOTIFY_DEFAULT, 2387 model.MARK_UNREAD_NOTIFY_PROP: model.CHANNEL_MARK_UNREAD_ALL, 2388 } 2389 2390 assert.False(t, th.App.userAllowsEmail(user, channelMemberNotificationProps, &model.Post{Type: model.POST_AUTO_RESPONDER})) 2391 }) 2392 2393 t.Run("should return false in case the status is STATUS_ONLINE", func(t *testing.T) { 2394 user := th.CreateUser() 2395 2396 th.App.SetStatusDoNotDisturb(user.Id) 2397 2398 channelMemberNotificationProps := model.StringMap{ 2399 model.EMAIL_NOTIFY_PROP: model.CHANNEL_NOTIFY_DEFAULT, 2400 model.MARK_UNREAD_NOTIFY_PROP: model.CHANNEL_MARK_UNREAD_ALL, 2401 } 2402 2403 assert.False(t, th.App.userAllowsEmail(user, channelMemberNotificationProps, &model.Post{Type: model.POST_AUTO_RESPONDER})) 2404 }) 2405 2406 } 2407 2408 func TestInsertGroupMentions(t *testing.T) { 2409 th := Setup(t).InitBasic() 2410 defer th.TearDown() 2411 2412 team := th.BasicTeam 2413 channel := th.BasicChannel 2414 group := th.CreateGroup() 2415 group.DisplayName = "engineering" 2416 group.Name = model.NewString("engineering") 2417 group, err := th.App.UpdateGroup(group) 2418 require.Nil(t, err) 2419 2420 groupChannelMember := th.CreateUser() 2421 th.LinkUserToTeam(groupChannelMember, team) 2422 th.App.AddUserToChannel(groupChannelMember, channel) 2423 _, err = th.App.UpsertGroupMember(group.Id, groupChannelMember.Id) 2424 require.Nil(t, err) 2425 2426 nonGroupChannelMember := th.CreateUser() 2427 th.LinkUserToTeam(nonGroupChannelMember, team) 2428 th.App.AddUserToChannel(nonGroupChannelMember, channel) 2429 2430 nonChannelGroupMember := th.CreateUser() 2431 th.LinkUserToTeam(nonChannelGroupMember, team) 2432 _, err = th.App.UpsertGroupMember(group.Id, nonChannelGroupMember.Id) 2433 require.Nil(t, err) 2434 2435 groupWithNoMembers := th.CreateGroup() 2436 groupWithNoMembers.DisplayName = "marketing" 2437 groupWithNoMembers.Name = model.NewString("marketing") 2438 groupWithNoMembers, err = th.App.UpdateGroup(groupWithNoMembers) 2439 require.Nil(t, err) 2440 2441 profileMap := map[string]*model.User{groupChannelMember.Id: groupChannelMember, nonGroupChannelMember.Id: nonGroupChannelMember} 2442 2443 t.Run("should add expected mentions for users part of the mentioned group", func(t *testing.T) { 2444 mentions := &ExplicitMentions{} 2445 usersMentioned, err := th.App.insertGroupMentions(group, channel, profileMap, mentions) 2446 require.Nil(t, err) 2447 require.Equal(t, usersMentioned, true) 2448 2449 // Ensure group member that is also a channel member is added to the mentions list. 2450 require.Equal(t, len(mentions.Mentions), 1) 2451 _, found := mentions.Mentions[groupChannelMember.Id] 2452 require.Equal(t, found, true) 2453 2454 // Ensure group member that is not a channel member is added to the other potential mentions list. 2455 require.Equal(t, len(mentions.OtherPotentialMentions), 1) 2456 require.Equal(t, mentions.OtherPotentialMentions[0], nonChannelGroupMember.Username) 2457 }) 2458 2459 t.Run("should add no expected or potential mentions if the group has no users ", func(t *testing.T) { 2460 mentions := &ExplicitMentions{} 2461 usersMentioned, err := th.App.insertGroupMentions(groupWithNoMembers, channel, profileMap, mentions) 2462 require.Nil(t, err) 2463 require.Equal(t, usersMentioned, false) 2464 2465 // Ensure no mentions are added for a group with no users 2466 require.Equal(t, len(mentions.Mentions), 0) 2467 require.Equal(t, len(mentions.OtherPotentialMentions), 0) 2468 }) 2469 2470 t.Run("should keep existing mentions", func(t *testing.T) { 2471 mentions := &ExplicitMentions{} 2472 th.App.insertGroupMentions(group, channel, profileMap, mentions) 2473 th.App.insertGroupMentions(groupWithNoMembers, channel, profileMap, mentions) 2474 2475 // Ensure mentions from group are kept after running with groupWithNoMembers 2476 require.Equal(t, len(mentions.Mentions), 1) 2477 require.Equal(t, len(mentions.OtherPotentialMentions), 1) 2478 }) 2479 2480 t.Run("should return true if no members mentioned while in group or direct message channel", func(t *testing.T) { 2481 mentions := &ExplicitMentions{} 2482 emptyProfileMap := make(map[string]*model.User) 2483 2484 groupChannel := &model.Channel{Type: model.CHANNEL_GROUP} 2485 usersMentioned, _ := th.App.insertGroupMentions(group, groupChannel, emptyProfileMap, mentions) 2486 // Ensure group channel with no group members mentioned always returns true 2487 require.Equal(t, usersMentioned, true) 2488 require.Equal(t, len(mentions.Mentions), 0) 2489 2490 directChannel := &model.Channel{Type: model.CHANNEL_DIRECT} 2491 usersMentioned, _ = th.App.insertGroupMentions(group, directChannel, emptyProfileMap, mentions) 2492 // Ensure direct channel with no group members mentioned always returns true 2493 require.Equal(t, usersMentioned, true) 2494 require.Equal(t, len(mentions.Mentions), 0) 2495 }) 2496 2497 t.Run("should add mentions for members while in group channel", func(t *testing.T) { 2498 groupChannel, err := th.App.CreateGroupChannel([]string{groupChannelMember.Id, nonGroupChannelMember.Id, th.BasicUser.Id}, groupChannelMember.Id) 2499 require.Nil(t, err) 2500 2501 mentions := &ExplicitMentions{} 2502 th.App.insertGroupMentions(group, groupChannel, profileMap, mentions) 2503 2504 require.Equal(t, len(mentions.Mentions), 1) 2505 _, found := mentions.Mentions[groupChannelMember.Id] 2506 require.Equal(t, found, true) 2507 }) 2508 } 2509 2510 func TestGetGroupsAllowedForReferenceInChannel(t *testing.T) { 2511 th := Setup(t).InitBasic() 2512 defer th.TearDown() 2513 2514 var err *model.AppError 2515 2516 team := th.BasicTeam 2517 channel := th.BasicChannel 2518 group1 := th.CreateGroup() 2519 2520 t.Run("should return empty map when no groups with allow reference", func(t *testing.T) { 2521 groupsMap, nErr := th.App.getGroupsAllowedForReferenceInChannel(channel, team) 2522 require.NoError(t, nErr) 2523 require.Len(t, groupsMap, 0) 2524 }) 2525 2526 group1.AllowReference = true 2527 group1, err = th.App.UpdateGroup(group1) 2528 require.Nil(t, err) 2529 2530 group2 := th.CreateGroup() 2531 t.Run("should only return groups with allow reference", func(t *testing.T) { 2532 groupsMap, nErr := th.App.getGroupsAllowedForReferenceInChannel(channel, team) 2533 require.NoError(t, nErr) 2534 require.Len(t, groupsMap, 1) 2535 require.Nil(t, groupsMap[*group2.Name]) 2536 require.Equal(t, groupsMap[*group1.Name], group1) 2537 }) 2538 2539 group2.AllowReference = true 2540 group2, err = th.App.UpdateGroup(group2) 2541 require.Nil(t, err) 2542 2543 // Sync first group to constrained channel 2544 constrainedChannel := th.CreateChannel(th.BasicTeam) 2545 constrainedChannel.GroupConstrained = model.NewBool(true) 2546 constrainedChannel, err = th.App.UpdateChannel(constrainedChannel) 2547 require.Nil(t, err) 2548 _, err = th.App.UpsertGroupSyncable(&model.GroupSyncable{ 2549 GroupId: group1.Id, 2550 Type: model.GroupSyncableTypeChannel, 2551 SyncableId: constrainedChannel.Id, 2552 }) 2553 require.Nil(t, err) 2554 2555 t.Run("should return only groups synced to channel if channel is group constrained", func(t *testing.T) { 2556 groupsMap, nErr := th.App.getGroupsAllowedForReferenceInChannel(constrainedChannel, team) 2557 require.NoError(t, nErr) 2558 require.Len(t, groupsMap, 1) 2559 require.Nil(t, groupsMap[*group2.Name]) 2560 require.Equal(t, groupsMap[*group1.Name], group1) 2561 }) 2562 2563 // Create a third group not synced with a team or channel 2564 group3 := th.CreateGroup() 2565 group3.AllowReference = true 2566 group3, err = th.App.UpdateGroup(group3) 2567 require.Nil(t, err) 2568 2569 // Sync group2 to the team 2570 team.GroupConstrained = model.NewBool(true) 2571 team, err = th.App.UpdateTeam(team) 2572 require.Nil(t, err) 2573 _, err = th.App.UpsertGroupSyncable(&model.GroupSyncable{ 2574 GroupId: group2.Id, 2575 Type: model.GroupSyncableTypeTeam, 2576 SyncableId: team.Id, 2577 }) 2578 require.Nil(t, err) 2579 2580 t.Run("should return union of groups synced to team and any channels if team is group constrained", func(t *testing.T) { 2581 groupsMap, nErr := th.App.getGroupsAllowedForReferenceInChannel(channel, team) 2582 require.NoError(t, nErr) 2583 require.Len(t, groupsMap, 2) 2584 require.Nil(t, groupsMap[*group3.Name]) 2585 require.Equal(t, groupsMap[*group2.Name], group2) 2586 require.Equal(t, groupsMap[*group1.Name], group1) 2587 }) 2588 2589 t.Run("should return only subset of groups synced to channel for group constrained channel when team is also group constrained", func(t *testing.T) { 2590 groupsMap, nErr := th.App.getGroupsAllowedForReferenceInChannel(constrainedChannel, team) 2591 require.NoError(t, nErr) 2592 require.Len(t, groupsMap, 1) 2593 require.Nil(t, groupsMap[*group3.Name]) 2594 require.Nil(t, groupsMap[*group2.Name]) 2595 require.Equal(t, groupsMap[*group1.Name], group1) 2596 }) 2597 2598 team.GroupConstrained = model.NewBool(false) 2599 team, err = th.App.UpdateTeam(team) 2600 require.Nil(t, err) 2601 2602 t.Run("should return all groups when team and channel are not group constrained", func(t *testing.T) { 2603 groupsMap, nErr := th.App.getGroupsAllowedForReferenceInChannel(channel, team) 2604 require.NoError(t, nErr) 2605 require.Len(t, groupsMap, 3) 2606 require.Equal(t, groupsMap[*group1.Name], group1) 2607 require.Equal(t, groupsMap[*group2.Name], group2) 2608 require.Equal(t, groupsMap[*group3.Name], group3) 2609 }) 2610 }