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