github.com/adacta-ru/mattermost-server@v5.11.1+incompatible/app/notification_test.go (about) 1 // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package app 5 6 import ( 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 11 "github.com/mattermost/mattermost-server/model" 12 "github.com/mattermost/mattermost-server/utils" 13 ) 14 15 func TestSendNotifications(t *testing.T) { 16 th := Setup(t).InitBasic() 17 defer th.TearDown() 18 19 th.App.AddUserToChannel(th.BasicUser2, th.BasicChannel) 20 21 post1, err := th.App.CreatePostMissingChannel(&model.Post{ 22 UserId: th.BasicUser.Id, 23 ChannelId: th.BasicChannel.Id, 24 Message: "@" + th.BasicUser2.Username, 25 Type: model.POST_ADD_TO_CHANNEL, 26 Props: map[string]interface{}{model.POST_PROPS_ADDED_USER_ID: "junk"}, 27 }, true) 28 29 if err != nil { 30 t.Fatal(err) 31 } 32 33 mentions, err := th.App.SendNotifications(post1, th.BasicTeam, th.BasicChannel, th.BasicUser, nil) 34 if err != nil { 35 t.Fatal(err) 36 } else if mentions == nil { 37 t.Log(mentions) 38 t.Fatal("user should have been mentioned") 39 } else if !utils.StringInSlice(th.BasicUser2.Id, mentions) { 40 t.Log(mentions) 41 t.Fatal("user should have been mentioned") 42 } 43 44 dm, err := th.App.GetOrCreateDirectChannel(th.BasicUser.Id, th.BasicUser2.Id) 45 if err != nil { 46 t.Fatal(err) 47 } 48 49 post2, err := th.App.CreatePostMissingChannel(&model.Post{ 50 UserId: th.BasicUser.Id, 51 ChannelId: dm.Id, 52 Message: "dm message", 53 }, true) 54 55 if err != nil { 56 t.Fatal(err) 57 } 58 59 _, err = th.App.SendNotifications(post2, th.BasicTeam, dm, th.BasicUser, nil) 60 if err != nil { 61 t.Fatal(err) 62 } 63 64 th.App.UpdateActive(th.BasicUser2, false) 65 th.App.InvalidateAllCaches() 66 67 post3, err := th.App.CreatePostMissingChannel(&model.Post{ 68 UserId: th.BasicUser.Id, 69 ChannelId: dm.Id, 70 Message: "dm message", 71 }, true) 72 73 if err != nil { 74 t.Fatal(err) 75 } 76 77 _, err = th.App.SendNotifications(post3, th.BasicTeam, dm, th.BasicUser, nil) 78 if err != nil { 79 t.Fatal(err) 80 } 81 82 th.BasicChannel.DeleteAt = 1 83 mentions, err = th.App.SendNotifications(post1, th.BasicTeam, th.BasicChannel, th.BasicUser, nil) 84 assert.Nil(t, err) 85 assert.Len(t, mentions, 0) 86 } 87 88 func TestGetExplicitMentions(t *testing.T) { 89 id1 := model.NewId() 90 id2 := model.NewId() 91 id3 := model.NewId() 92 93 for name, tc := range map[string]struct { 94 Message string 95 Attachments []*model.SlackAttachment 96 Keywords map[string][]string 97 Expected *ExplicitMentions 98 }{ 99 "Nobody": { 100 Message: "this is a message", 101 Keywords: map[string][]string{}, 102 Expected: &ExplicitMentions{}, 103 }, 104 "NonexistentUser": { 105 Message: "this is a message for @user", 106 Expected: &ExplicitMentions{ 107 OtherPotentialMentions: []string{"user"}, 108 }, 109 }, 110 "OnePerson": { 111 Message: "this is a message for @user", 112 Keywords: map[string][]string{"@user": {id1}}, 113 Expected: &ExplicitMentions{ 114 MentionedUserIds: map[string]bool{ 115 id1: true, 116 }, 117 }, 118 }, 119 "OnePersonWithPeriodAtEndOfUsername": { 120 Message: "this is a message for @user.name.", 121 Keywords: map[string][]string{"@user.name.": {id1}}, 122 Expected: &ExplicitMentions{ 123 MentionedUserIds: map[string]bool{ 124 id1: true, 125 }, 126 }, 127 }, 128 "OnePersonWithPeriodAtEndOfUsernameButNotSimilarName": { 129 Message: "this is a message for @user.name.", 130 Keywords: map[string][]string{"@user.name.": {id1}, "@user.name": {id2}}, 131 Expected: &ExplicitMentions{ 132 MentionedUserIds: map[string]bool{ 133 id1: true, 134 }, 135 }, 136 }, 137 "OnePersonAtEndOfSentence": { 138 Message: "this is a message for @user.", 139 Keywords: map[string][]string{"@user": {id1}}, 140 Expected: &ExplicitMentions{ 141 MentionedUserIds: map[string]bool{ 142 id1: true, 143 }, 144 }, 145 }, 146 "OnePersonWithoutAtMention": { 147 Message: "this is a message for @user", 148 Keywords: map[string][]string{"this": {id1}}, 149 Expected: &ExplicitMentions{ 150 MentionedUserIds: map[string]bool{ 151 id1: true, 152 }, 153 OtherPotentialMentions: []string{"user"}, 154 }, 155 }, 156 "OnePersonWithPeriodAfter": { 157 Message: "this is a message for @user.", 158 Keywords: map[string][]string{"@user": {id1}}, 159 Expected: &ExplicitMentions{ 160 MentionedUserIds: map[string]bool{ 161 id1: true, 162 }, 163 }, 164 }, 165 "OnePersonWithPeriodBefore": { 166 Message: "this is a message for .@user", 167 Keywords: map[string][]string{"@user": {id1}}, 168 Expected: &ExplicitMentions{ 169 MentionedUserIds: map[string]bool{ 170 id1: true, 171 }, 172 }, 173 }, 174 "OnePersonWithColonAfter": { 175 Message: "this is a message for @user:", 176 Keywords: map[string][]string{"@user": {id1}}, 177 Expected: &ExplicitMentions{ 178 MentionedUserIds: map[string]bool{ 179 id1: true, 180 }, 181 }, 182 }, 183 "OnePersonWithColonBefore": { 184 Message: "this is a message for :@user", 185 Keywords: map[string][]string{"@user": {id1}}, 186 Expected: &ExplicitMentions{ 187 MentionedUserIds: map[string]bool{ 188 id1: true, 189 }, 190 }, 191 }, 192 "OnePersonWithHyphenAfter": { 193 Message: "this is a message for @user.", 194 Keywords: map[string][]string{"@user": {id1}}, 195 Expected: &ExplicitMentions{ 196 MentionedUserIds: map[string]bool{ 197 id1: true, 198 }, 199 }, 200 }, 201 "OnePersonWithHyphenBefore": { 202 Message: "this is a message for -@user", 203 Keywords: map[string][]string{"@user": {id1}}, 204 Expected: &ExplicitMentions{ 205 MentionedUserIds: map[string]bool{ 206 id1: true, 207 }, 208 }, 209 }, 210 "MultiplePeopleWithOneWord": { 211 Message: "this is a message for @user", 212 Keywords: map[string][]string{"@user": {id1, id2}}, 213 Expected: &ExplicitMentions{ 214 MentionedUserIds: map[string]bool{ 215 id1: true, 216 id2: true, 217 }, 218 }, 219 }, 220 "OneOfMultiplePeople": { 221 Message: "this is a message for @user", 222 Keywords: map[string][]string{"@user": {id1}, "@mention": {id2}}, 223 Expected: &ExplicitMentions{ 224 MentionedUserIds: map[string]bool{ 225 id1: true, 226 }, 227 }, 228 }, 229 "MultiplePeopleWithMultipleWords": { 230 Message: "this is an @mention for @user", 231 Keywords: map[string][]string{"@user": {id1}, "@mention": {id2}}, 232 Expected: &ExplicitMentions{ 233 MentionedUserIds: map[string]bool{ 234 id1: true, 235 id2: true, 236 }, 237 }, 238 }, 239 "Channel": { 240 Message: "this is an message for @channel", 241 Keywords: map[string][]string{"@channel": {id1, id2}}, 242 Expected: &ExplicitMentions{ 243 MentionedUserIds: map[string]bool{ 244 id1: true, 245 id2: true, 246 }, 247 ChannelMentioned: true, 248 }, 249 }, 250 251 "ChannelWithColonAtEnd": { 252 Message: "this is a message for @channel:", 253 Keywords: map[string][]string{"@channel": {id1, id2}}, 254 Expected: &ExplicitMentions{ 255 MentionedUserIds: map[string]bool{ 256 id1: true, 257 id2: true, 258 }, 259 ChannelMentioned: true, 260 }, 261 }, 262 "CapitalizedChannel": { 263 Message: "this is an message for @cHaNNeL", 264 Keywords: map[string][]string{"@channel": {id1, id2}}, 265 Expected: &ExplicitMentions{ 266 MentionedUserIds: map[string]bool{ 267 id1: true, 268 id2: true, 269 }, 270 ChannelMentioned: true, 271 }, 272 }, 273 "All": { 274 Message: "this is an message for @all", 275 Keywords: map[string][]string{"@all": {id1, id2}}, 276 Expected: &ExplicitMentions{ 277 MentionedUserIds: map[string]bool{ 278 id1: true, 279 id2: true, 280 }, 281 AllMentioned: true, 282 }, 283 }, 284 "AllWithColonAtEnd": { 285 Message: "this is a message for @all:", 286 Keywords: map[string][]string{"@all": {id1, id2}}, 287 Expected: &ExplicitMentions{ 288 MentionedUserIds: map[string]bool{ 289 id1: true, 290 id2: true, 291 }, 292 AllMentioned: true, 293 }, 294 }, 295 "CapitalizedAll": { 296 Message: "this is an message for @ALL", 297 Keywords: map[string][]string{"@all": {id1, id2}}, 298 Expected: &ExplicitMentions{ 299 MentionedUserIds: map[string]bool{ 300 id1: true, 301 id2: true, 302 }, 303 AllMentioned: true, 304 }, 305 }, 306 "UserWithPeriod": { 307 Message: "user.period doesn't complicate things at all by including periods in their username", 308 Keywords: map[string][]string{"user.period": {id1}, "user": {id2}}, 309 Expected: &ExplicitMentions{ 310 MentionedUserIds: map[string]bool{ 311 id1: true, 312 }, 313 }, 314 }, 315 "AtUserWithColonAtEnd": { 316 Message: "this is a message for @user:", 317 Keywords: map[string][]string{"@user": {id1}}, 318 Expected: &ExplicitMentions{ 319 MentionedUserIds: map[string]bool{ 320 id1: true, 321 }, 322 }, 323 }, 324 "AtUserWithPeriodAtEndOfSentence": { 325 Message: "this is a message for @user.period.", 326 Keywords: map[string][]string{"@user.period": {id1}}, 327 Expected: &ExplicitMentions{ 328 MentionedUserIds: map[string]bool{ 329 id1: true, 330 }, 331 }, 332 }, 333 "UserWithPeriodAtEndOfSentence": { 334 Message: "this is a message for user.period.", 335 Keywords: map[string][]string{"user.period": {id1}}, 336 Expected: &ExplicitMentions{ 337 MentionedUserIds: map[string]bool{ 338 id1: true, 339 }, 340 }, 341 }, 342 "UserWithColonAtEnd": { 343 Message: "this is a message for user:", 344 Keywords: map[string][]string{"user": {id1}}, 345 Expected: &ExplicitMentions{ 346 MentionedUserIds: map[string]bool{ 347 id1: true, 348 }, 349 }, 350 }, 351 "PotentialOutOfChannelUser": { 352 Message: "this is an message for @potential and @user", 353 Keywords: map[string][]string{"@user": {id1}}, 354 Expected: &ExplicitMentions{ 355 MentionedUserIds: map[string]bool{ 356 id1: true, 357 }, 358 OtherPotentialMentions: []string{"potential"}, 359 }, 360 }, 361 "PotentialOutOfChannelUserWithPeriod": { 362 Message: "this is an message for @potential.user", 363 Expected: &ExplicitMentions{ 364 OtherPotentialMentions: []string{"potential.user"}, 365 }, 366 }, 367 "InlineCode": { 368 Message: "`this shouldn't mention @channel at all`", 369 Keywords: map[string][]string{}, 370 Expected: &ExplicitMentions{}, 371 }, 372 "FencedCodeBlock": { 373 Message: "```\nthis shouldn't mention @channel at all\n```", 374 Keywords: map[string][]string{}, 375 Expected: &ExplicitMentions{}, 376 }, 377 "Emphasis": { 378 Message: "*@aaa @bbb @ccc*", 379 Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}, 380 Expected: &ExplicitMentions{ 381 MentionedUserIds: map[string]bool{ 382 id1: true, 383 id2: true, 384 id3: true, 385 }, 386 }, 387 }, 388 "StrongEmphasis": { 389 Message: "**@aaa @bbb @ccc**", 390 Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}, 391 Expected: &ExplicitMentions{ 392 MentionedUserIds: map[string]bool{ 393 id1: true, 394 id2: true, 395 id3: true, 396 }, 397 }, 398 }, 399 "Strikethrough": { 400 Message: "~~@aaa @bbb @ccc~~", 401 Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}, 402 Expected: &ExplicitMentions{ 403 MentionedUserIds: map[string]bool{ 404 id1: true, 405 id2: true, 406 id3: true, 407 }, 408 }, 409 }, 410 "Heading": { 411 Message: "### @aaa", 412 Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}, 413 Expected: &ExplicitMentions{ 414 MentionedUserIds: map[string]bool{ 415 id1: true, 416 }, 417 }, 418 }, 419 "BlockQuote": { 420 Message: "> @aaa", 421 Keywords: map[string][]string{"@aaa": {id1}, "@bbb": {id2}, "@ccc": {id3}}, 422 Expected: &ExplicitMentions{ 423 MentionedUserIds: map[string]bool{ 424 id1: true, 425 }, 426 }, 427 }, 428 "Emoji": { 429 Message: ":smile:", 430 Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}, 431 Expected: &ExplicitMentions{}, 432 }, 433 "NotEmoji": { 434 Message: "smile", 435 Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}, 436 Expected: &ExplicitMentions{ 437 MentionedUserIds: map[string]bool{ 438 id1: true, 439 }, 440 }, 441 }, 442 "UnclosedEmoji": { 443 Message: ":smile", 444 Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}, 445 Expected: &ExplicitMentions{ 446 MentionedUserIds: map[string]bool{ 447 id1: true, 448 }, 449 }, 450 }, 451 "UnopenedEmoji": { 452 Message: "smile:", 453 Keywords: map[string][]string{"smile": {id1}, "smiley": {id2}, "smiley_cat": {id3}}, 454 Expected: &ExplicitMentions{ 455 MentionedUserIds: map[string]bool{ 456 id1: true, 457 }, 458 }, 459 }, 460 "IndentedCodeBlock": { 461 Message: " this shouldn't mention @channel at all", 462 Keywords: map[string][]string{}, 463 Expected: &ExplicitMentions{}, 464 }, 465 "LinkTitle": { 466 Message: `[foo](this "shouldn't mention @channel at all")`, 467 Keywords: map[string][]string{}, 468 Expected: &ExplicitMentions{}, 469 }, 470 "MalformedInlineCode": { 471 Message: "`this should mention @channel``", 472 Keywords: map[string][]string{}, 473 Expected: &ExplicitMentions{ 474 ChannelMentioned: true, 475 }, 476 }, 477 "MultibyteCharacter": { 478 Message: "My name is 萌", 479 Keywords: map[string][]string{"萌": {id1}}, 480 Expected: &ExplicitMentions{ 481 MentionedUserIds: map[string]bool{ 482 id1: true, 483 }, 484 }, 485 }, 486 "MultibyteCharacterAtBeginningOfSentence": { 487 Message: "이메일을 보내다.", 488 Keywords: map[string][]string{"이메일": {id1}}, 489 Expected: &ExplicitMentions{ 490 MentionedUserIds: map[string]bool{ 491 id1: true, 492 }, 493 }, 494 }, 495 "MultibyteCharacterInPartOfSentence": { 496 Message: "我爱吃番茄炒饭", 497 Keywords: map[string][]string{"番茄": {id1}}, 498 Expected: &ExplicitMentions{ 499 MentionedUserIds: map[string]bool{ 500 id1: true, 501 }, 502 }, 503 }, 504 "MultibyteCharacterAtEndOfSentence": { 505 Message: "こんにちは、世界", 506 Keywords: map[string][]string{"世界": {id1}}, 507 Expected: &ExplicitMentions{ 508 MentionedUserIds: map[string]bool{ 509 id1: true, 510 }, 511 }, 512 }, 513 "MultibyteCharacterTwiceInSentence": { 514 Message: "石橋さんが石橋を渡る", 515 Keywords: map[string][]string{"石橋": {id1}}, 516 Expected: &ExplicitMentions{ 517 MentionedUserIds: map[string]bool{ 518 id1: true, 519 }, 520 }, 521 }, 522 523 // The following tests cover cases where the message mentions @user.name, so we shouldn't assume that 524 // the user might be intending to mention some @user that isn't in the channel. 525 "Don't include potential mention that's part of an actual mention (without trailing period)": { 526 Message: "this is an message for @user.name", 527 Keywords: map[string][]string{"@user.name": {id1}}, 528 Expected: &ExplicitMentions{ 529 MentionedUserIds: map[string]bool{ 530 id1: true, 531 }, 532 }, 533 }, 534 "Don't include potential mention that's part of an actual mention (with trailing period)": { 535 Message: "this is an message for @user.name.", 536 Keywords: map[string][]string{"@user.name": {id1}}, 537 Expected: &ExplicitMentions{ 538 MentionedUserIds: map[string]bool{ 539 id1: true, 540 }, 541 }, 542 }, 543 "Don't include potential mention that's part of an actual mention (with multiple trailing periods)": { 544 Message: "this is an message for @user.name...", 545 Keywords: map[string][]string{"@user.name": {id1}}, 546 Expected: &ExplicitMentions{ 547 MentionedUserIds: map[string]bool{ 548 id1: true, 549 }, 550 }, 551 }, 552 "Don't include potential mention that's part of an actual mention (containing and followed by multiple periods)": { 553 Message: "this is an message for @user...name...", 554 Keywords: map[string][]string{"@user...name": {id1}}, 555 Expected: &ExplicitMentions{ 556 MentionedUserIds: map[string]bool{ 557 id1: true, 558 }, 559 }, 560 }, 561 "should include the mentions from attachment text and preText": { 562 Message: "this is an message for @user1", 563 Attachments: []*model.SlackAttachment{ 564 { 565 Text: "this is a message For @user2", 566 Pretext: "this is a message for @here", 567 }, 568 }, 569 Keywords: map[string][]string{"@user1": {id1}, "@user2": {id2}}, 570 Expected: &ExplicitMentions{ 571 MentionedUserIds: map[string]bool{ 572 id1: true, 573 id2: true, 574 }, 575 HereMentioned: true, 576 }, 577 }, 578 "Name on keywords is a prefix of a mention": { 579 Message: "@other @test-two", 580 Keywords: map[string][]string{"@test": {model.NewId()}}, 581 Expected: &ExplicitMentions{ 582 OtherPotentialMentions: []string{"other", "test-two"}, 583 }, 584 }, 585 "Name on mentions is a prefix of other mention": { 586 Message: "@other-one @other @other-two", 587 Keywords: nil, 588 Expected: &ExplicitMentions{ 589 OtherPotentialMentions: []string{"other-one", "other", "other-two"}, 590 }, 591 }, 592 } { 593 t.Run(name, func(t *testing.T) { 594 595 post := &model.Post{Message: tc.Message, Props: model.StringInterface{ 596 "attachments": tc.Attachments, 597 }, 598 } 599 600 m := GetExplicitMentions(post, tc.Keywords) 601 if tc.Expected.MentionedUserIds == nil { 602 tc.Expected.MentionedUserIds = make(map[string]bool) 603 } 604 assert.EqualValues(t, tc.Expected, m) 605 }) 606 } 607 } 608 609 func TestGetExplicitMentionsAtHere(t *testing.T) { 610 // test all the boundary cases that we know can break up terms (and those that we know won't) 611 cases := map[string]bool{ 612 "": false, 613 "here": false, 614 "@here": true, 615 " @here ": true, 616 "\n@here\n": true, 617 "!@here!": true, 618 "#@here#": true, 619 "$@here$": true, 620 "%@here%": true, 621 "^@here^": true, 622 "&@here&": true, 623 "*@here*": true, 624 "(@here(": true, 625 ")@here)": true, 626 "-@here-": true, 627 "_@here_": true, 628 "=@here=": true, 629 "+@here+": true, 630 "[@here[": true, 631 "{@here{": true, 632 "]@here]": true, 633 "}@here}": true, 634 "\\@here\\": true, 635 "|@here|": true, 636 ";@here;": true, 637 "@here:": true, 638 ":@here:": false, // This case shouldn't trigger a mention since it follows the format of reactions e.g. :word: 639 "'@here'": true, 640 "\"@here\"": true, 641 ",@here,": true, 642 "<@here<": true, 643 ".@here.": true, 644 ">@here>": true, 645 "/@here/": true, 646 "?@here?": true, 647 "`@here`": false, // This case shouldn't mention since it's a code block 648 "~@here~": true, 649 "@HERE": true, 650 "@hERe": true, 651 } 652 653 for message, shouldMention := range cases { 654 post := &model.Post{Message: message} 655 if m := GetExplicitMentions(post, nil); m.HereMentioned && !shouldMention { 656 t.Fatalf("shouldn't have mentioned @here with \"%v\"", message) 657 } else if !m.HereMentioned && shouldMention { 658 t.Fatalf("should've mentioned @here with \"%v\"", message) 659 } 660 } 661 662 // mentioning @here and someone 663 id := model.NewId() 664 if m := GetExplicitMentions(&model.Post{Message: "@here @user @potential"}, map[string][]string{"@user": {id}}); !m.HereMentioned { 665 t.Fatal("should've mentioned @here with \"@here @user\"") 666 } else if len(m.MentionedUserIds) != 1 || !m.MentionedUserIds[id] { 667 t.Fatal("should've mentioned @user with \"@here @user\"") 668 } else if len(m.OtherPotentialMentions) > 1 { 669 t.Fatal("should've potential mentions for @potential") 670 } 671 } 672 673 func TestGetMentionKeywords(t *testing.T) { 674 th := Setup(t) 675 defer th.TearDown() 676 677 // user with username or custom mentions enabled 678 user1 := &model.User{ 679 Id: model.NewId(), 680 FirstName: "First", 681 Username: "User", 682 NotifyProps: map[string]string{ 683 "mention_keys": "User,@User,MENTION", 684 }, 685 } 686 687 channelMemberNotifyPropsMap1Off := map[string]model.StringMap{ 688 user1.Id: { 689 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 690 }, 691 } 692 693 profiles := map[string]*model.User{user1.Id: user1} 694 mentions := th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap1Off) 695 if len(mentions) != 3 { 696 t.Fatal("should've returned three mention keywords") 697 } else if ids, ok := mentions["user"]; !ok || ids[0] != user1.Id { 698 t.Fatal("should've returned mention key of user") 699 } else if ids, ok := mentions["@user"]; !ok || ids[0] != user1.Id { 700 t.Fatal("should've returned mention key of @user") 701 } else if ids, ok := mentions["mention"]; !ok || ids[0] != user1.Id { 702 t.Fatal("should've returned mention key of mention") 703 } 704 705 // user with first name mention enabled 706 user2 := &model.User{ 707 Id: model.NewId(), 708 FirstName: "First", 709 Username: "User", 710 NotifyProps: map[string]string{ 711 "first_name": "true", 712 }, 713 } 714 715 channelMemberNotifyPropsMap2Off := map[string]model.StringMap{ 716 user2.Id: { 717 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 718 }, 719 } 720 721 profiles = map[string]*model.User{user2.Id: user2} 722 mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap2Off) 723 if len(mentions) != 2 { 724 t.Fatal("should've returned two mention keyword") 725 } else if ids, ok := mentions["First"]; !ok || ids[0] != user2.Id { 726 t.Fatal("should've returned mention key of First") 727 } 728 729 // user with @channel/@all mentions enabled 730 user3 := &model.User{ 731 Id: model.NewId(), 732 FirstName: "First", 733 Username: "User", 734 NotifyProps: map[string]string{ 735 "channel": "true", 736 }, 737 } 738 739 // Channel-wide mentions are not ignored on channel level 740 channelMemberNotifyPropsMap3Off := map[string]model.StringMap{ 741 user3.Id: { 742 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 743 }, 744 } 745 profiles = map[string]*model.User{user3.Id: user3} 746 mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap3Off) 747 if len(mentions) != 3 { 748 t.Fatal("should've returned three mention keywords") 749 } else if ids, ok := mentions["@channel"]; !ok || ids[0] != user3.Id { 750 t.Fatal("should've returned mention key of @channel") 751 } else if ids, ok := mentions["@all"]; !ok || ids[0] != user3.Id { 752 t.Fatal("should've returned mention key of @all") 753 } 754 755 // Channel member notify props is set to default 756 channelMemberNotifyPropsMapDefault := map[string]model.StringMap{ 757 user3.Id: { 758 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_DEFAULT, 759 }, 760 } 761 profiles = map[string]*model.User{user3.Id: user3} 762 mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMapDefault) 763 if len(mentions) != 3 { 764 t.Fatal("should've returned three mention keywords") 765 } else if ids, ok := mentions["@channel"]; !ok || ids[0] != user3.Id { 766 t.Fatal("should've returned mention key of @channel") 767 } else if ids, ok := mentions["@all"]; !ok || ids[0] != user3.Id { 768 t.Fatal("should've returned mention key of @all") 769 } 770 771 // Channel member notify props is empty 772 channelMemberNotifyPropsMapEmpty := map[string]model.StringMap{} 773 profiles = map[string]*model.User{user3.Id: user3} 774 mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMapEmpty) 775 if len(mentions) != 3 { 776 t.Fatal("should've returned three mention keywords") 777 } else if ids, ok := mentions["@channel"]; !ok || ids[0] != user3.Id { 778 t.Fatal("should've returned mention key of @channel") 779 } else if ids, ok := mentions["@all"]; !ok || ids[0] != user3.Id { 780 t.Fatal("should've returned mention key of @all") 781 } 782 783 // Channel-wide mentions are ignored channel level 784 channelMemberNotifyPropsMap3On := map[string]model.StringMap{ 785 user3.Id: { 786 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_ON, 787 }, 788 } 789 mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap3On) 790 if len(mentions) == 0 { 791 t.Fatal("should've not returned any keywords") 792 } 793 794 // user with all types of mentions enabled 795 user4 := &model.User{ 796 Id: model.NewId(), 797 FirstName: "First", 798 Username: "User", 799 NotifyProps: map[string]string{ 800 "mention_keys": "User,@User,MENTION", 801 "first_name": "true", 802 "channel": "true", 803 }, 804 } 805 806 // Channel-wide mentions are not ignored on channel level 807 channelMemberNotifyPropsMap4Off := map[string]model.StringMap{ 808 user4.Id: { 809 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 810 }, 811 } 812 813 profiles = map[string]*model.User{user4.Id: user4} 814 mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap4Off) 815 if len(mentions) != 6 { 816 t.Fatal("should've returned six mention keywords") 817 } else if ids, ok := mentions["user"]; !ok || ids[0] != user4.Id { 818 t.Fatal("should've returned mention key of user") 819 } else if ids, ok := mentions["@user"]; !ok || ids[0] != user4.Id { 820 t.Fatal("should've returned mention key of @user") 821 } else if ids, ok := mentions["mention"]; !ok || ids[0] != user4.Id { 822 t.Fatal("should've returned mention key of mention") 823 } else if ids, ok := mentions["First"]; !ok || ids[0] != user4.Id { 824 t.Fatal("should've returned mention key of First") 825 } else if ids, ok := mentions["@channel"]; !ok || ids[0] != user4.Id { 826 t.Fatal("should've returned mention key of @channel") 827 } else if ids, ok := mentions["@all"]; !ok || ids[0] != user4.Id { 828 t.Fatal("should've returned mention key of @all") 829 } 830 831 // Channel-wide mentions are ignored on channel level 832 channelMemberNotifyPropsMap4On := map[string]model.StringMap{ 833 user4.Id: { 834 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_ON, 835 }, 836 } 837 mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap4On) 838 if len(mentions) != 4 { 839 t.Fatal("should've returned four mention keywords") 840 } else if ids, ok := mentions["user"]; !ok || ids[0] != user4.Id { 841 t.Fatal("should've returned mention key of user") 842 } else if ids, ok := mentions["@user"]; !ok || ids[0] != user4.Id { 843 t.Fatal("should've returned mention key of @user") 844 } else if ids, ok := mentions["mention"]; !ok || ids[0] != user4.Id { 845 t.Fatal("should've returned mention key of mention") 846 } else if ids, ok := mentions["First"]; !ok || ids[0] != user4.Id { 847 t.Fatal("should've returned mention key of First") 848 } 849 850 dup_count := func(list []string) map[string]int { 851 852 duplicate_frequency := make(map[string]int) 853 854 for _, item := range list { 855 // check if the item/element exist in the duplicate_frequency map 856 857 _, exist := duplicate_frequency[item] 858 859 if exist { 860 duplicate_frequency[item] += 1 // increase counter by 1 if already in the map 861 } else { 862 duplicate_frequency[item] = 1 // else start counting from 1 863 } 864 } 865 return duplicate_frequency 866 } 867 868 // multiple users but no more than MaxNotificationsPerChannel 869 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxNotificationsPerChannel = 4 }) 870 profiles = map[string]*model.User{ 871 user1.Id: user1, 872 user2.Id: user2, 873 user3.Id: user3, 874 user4.Id: user4, 875 } 876 // Channel-wide mentions are not ignored on channel level for all users 877 channelMemberNotifyPropsMap5Off := map[string]model.StringMap{ 878 user1.Id: { 879 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 880 }, 881 user2.Id: { 882 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 883 }, 884 user3.Id: { 885 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 886 }, 887 user4.Id: { 888 "ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF, 889 }, 890 } 891 mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap5Off) 892 if len(mentions) != 6 { 893 t.Fatal("should've returned six mention keywords") 894 } else if ids, ok := mentions["user"]; !ok || len(ids) != 2 || (ids[0] != user1.Id && ids[1] != user1.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) { 895 t.Fatal("should've mentioned user1 and user4 with user") 896 } else if ids := dup_count(mentions["@user"]); len(ids) != 4 || (ids[user1.Id] != 2) || (ids[user4.Id] != 2) { 897 t.Fatal("should've mentioned user1 and user4 with @user") 898 } else if ids, ok := mentions["mention"]; !ok || len(ids) != 2 || (ids[0] != user1.Id && ids[1] != user1.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) { 899 t.Fatal("should've mentioned user1 and user4 with mention") 900 } else if ids, ok := mentions["First"]; !ok || len(ids) != 2 || (ids[0] != user2.Id && ids[1] != user2.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) { 901 t.Fatal("should've mentioned user2 and user4 with First") 902 } else if ids, ok := mentions["@channel"]; !ok || len(ids) != 2 || (ids[0] != user3.Id && ids[1] != user3.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) { 903 t.Fatal("should've mentioned user3 and user4 with @channel") 904 } else if ids, ok := mentions["@all"]; !ok || len(ids) != 2 || (ids[0] != user3.Id && ids[1] != user3.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) { 905 t.Fatal("should've mentioned user3 and user4 with @all") 906 } 907 908 // multiple users and more than MaxNotificationsPerChannel 909 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxNotificationsPerChannel = 3 }) 910 mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap4Off) 911 if len(mentions) != 4 { 912 t.Fatal("should've returned four mention keywords") 913 } else if _, ok := mentions["@channel"]; ok { 914 t.Fatal("should not have mentioned any user with @channel") 915 } else if _, ok := mentions["@all"]; ok { 916 t.Fatal("should not have mentioned any user with @all") 917 } else if _, ok := mentions["@here"]; ok { 918 t.Fatal("should not have mentioned any user with @here") 919 } 920 921 // no special mentions 922 profiles = map[string]*model.User{ 923 user1.Id: user1, 924 } 925 mentions = th.App.GetMentionKeywordsInChannel(profiles, false, channelMemberNotifyPropsMap4Off) 926 if len(mentions) != 3 { 927 t.Fatal("should've returned three mention keywords") 928 } else if ids, ok := mentions["user"]; !ok || len(ids) != 1 || ids[0] != user1.Id { 929 t.Fatal("should've mentioned user1 with user") 930 } else if ids, ok := mentions["@user"]; !ok || len(ids) != 2 || ids[0] != user1.Id || ids[1] != user1.Id { 931 t.Fatal("should've mentioned user1 twice with @user") 932 } else if ids, ok := mentions["mention"]; !ok || len(ids) != 1 || ids[0] != user1.Id { 933 t.Fatal("should've mentioned user1 with mention") 934 } else if _, ok := mentions["First"]; ok { 935 t.Fatal("should not have mentioned user1 with First") 936 } else if _, ok := mentions["@channel"]; ok { 937 t.Fatal("should not have mentioned any user with @channel") 938 } else if _, ok := mentions["@all"]; ok { 939 t.Fatal("should not have mentioned any user with @all") 940 } else if _, ok := mentions["@here"]; ok { 941 t.Fatal("should not have mentioned any user with @here") 942 } 943 } 944 945 func TestGetMentionsEnabledFields(t *testing.T) { 946 947 attachmentWithTextAndPreText := model.SlackAttachment{ 948 Text: "@here with mentions", 949 Pretext: "@Channel some comment for the channel", 950 } 951 952 attachmentWithOutPreText := model.SlackAttachment{ 953 Text: "some text", 954 } 955 attachments := []*model.SlackAttachment{ 956 &attachmentWithTextAndPreText, 957 &attachmentWithOutPreText, 958 } 959 960 post := &model.Post{ 961 Message: "This is the message", 962 Props: model.StringInterface{ 963 "attachments": attachments, 964 }, 965 } 966 expectedFields := []string{ 967 "This is the message", 968 "@Channel some comment for the channel", 969 "@here with mentions", 970 "some text"} 971 972 mentionEnabledFields := GetMentionsEnabledFields(post) 973 974 assert.EqualValues(t, 4, len(mentionEnabledFields)) 975 assert.EqualValues(t, expectedFields, mentionEnabledFields) 976 } 977 978 func TestPostNotificationGetChannelName(t *testing.T) { 979 sender := &model.User{Id: model.NewId(), Username: "sender", FirstName: "Sender", LastName: "Sender", Nickname: "Sender"} 980 recipient := &model.User{Id: model.NewId(), Username: "recipient", FirstName: "Recipient", LastName: "Recipient", Nickname: "Recipient"} 981 otherUser := &model.User{Id: model.NewId(), Username: "other", FirstName: "Other", LastName: "Other", Nickname: "Other"} 982 profileMap := map[string]*model.User{ 983 sender.Id: sender, 984 recipient.Id: recipient, 985 otherUser.Id: otherUser, 986 } 987 988 for name, testCase := range map[string]struct { 989 channel *model.Channel 990 nameFormat string 991 recipientId string 992 expected string 993 }{ 994 "regular channel": { 995 channel: &model.Channel{Type: model.CHANNEL_OPEN, Name: "channel", DisplayName: "My Channel"}, 996 expected: "My Channel", 997 }, 998 "direct channel, unspecified": { 999 channel: &model.Channel{Type: model.CHANNEL_DIRECT}, 1000 expected: "@sender", 1001 }, 1002 "direct channel, username": { 1003 channel: &model.Channel{Type: model.CHANNEL_DIRECT}, 1004 nameFormat: model.SHOW_USERNAME, 1005 expected: "@sender", 1006 }, 1007 "direct channel, full name": { 1008 channel: &model.Channel{Type: model.CHANNEL_DIRECT}, 1009 nameFormat: model.SHOW_FULLNAME, 1010 expected: "@Sender Sender", 1011 }, 1012 "direct channel, nickname": { 1013 channel: &model.Channel{Type: model.CHANNEL_DIRECT}, 1014 nameFormat: model.SHOW_NICKNAME_FULLNAME, 1015 expected: "@Sender", 1016 }, 1017 "group channel, unspecified": { 1018 channel: &model.Channel{Type: model.CHANNEL_GROUP}, 1019 expected: "other, sender", 1020 }, 1021 "group channel, username": { 1022 channel: &model.Channel{Type: model.CHANNEL_GROUP}, 1023 nameFormat: model.SHOW_USERNAME, 1024 expected: "other, sender", 1025 }, 1026 "group channel, full name": { 1027 channel: &model.Channel{Type: model.CHANNEL_GROUP}, 1028 nameFormat: model.SHOW_FULLNAME, 1029 expected: "Other Other, Sender Sender", 1030 }, 1031 "group channel, nickname": { 1032 channel: &model.Channel{Type: model.CHANNEL_GROUP}, 1033 nameFormat: model.SHOW_NICKNAME_FULLNAME, 1034 expected: "Other, Sender", 1035 }, 1036 "group channel, not excluding current user": { 1037 channel: &model.Channel{Type: model.CHANNEL_GROUP}, 1038 nameFormat: model.SHOW_NICKNAME_FULLNAME, 1039 expected: "Other, Sender", 1040 recipientId: "", 1041 }, 1042 } { 1043 t.Run(name, func(t *testing.T) { 1044 notification := &postNotification{ 1045 channel: testCase.channel, 1046 sender: sender, 1047 profileMap: profileMap, 1048 } 1049 1050 recipientId := recipient.Id 1051 if testCase.recipientId != "" { 1052 recipientId = testCase.recipientId 1053 } 1054 1055 assert.Equal(t, testCase.expected, notification.GetChannelName(testCase.nameFormat, recipientId)) 1056 }) 1057 } 1058 } 1059 1060 func TestPostNotificationGetSenderName(t *testing.T) { 1061 th := Setup(t) 1062 defer th.TearDown() 1063 1064 defaultChannel := &model.Channel{Type: model.CHANNEL_OPEN} 1065 defaultPost := &model.Post{Props: model.StringInterface{}} 1066 sender := &model.User{Id: model.NewId(), Username: "sender", FirstName: "Sender", LastName: "Sender", Nickname: "Sender"} 1067 1068 overriddenPost := &model.Post{ 1069 Props: model.StringInterface{ 1070 "override_username": "Overridden", 1071 "from_webhook": "true", 1072 }, 1073 } 1074 1075 for name, testCase := range map[string]struct { 1076 channel *model.Channel 1077 post *model.Post 1078 nameFormat string 1079 allowOverrides bool 1080 expected string 1081 }{ 1082 "name format unspecified": { 1083 expected: sender.Username, 1084 }, 1085 "name format username": { 1086 nameFormat: model.SHOW_USERNAME, 1087 expected: sender.Username, 1088 }, 1089 "name format full name": { 1090 nameFormat: model.SHOW_FULLNAME, 1091 expected: sender.FirstName + " " + sender.LastName, 1092 }, 1093 "name format nickname": { 1094 nameFormat: model.SHOW_NICKNAME_FULLNAME, 1095 expected: sender.Nickname, 1096 }, 1097 "system message": { 1098 post: &model.Post{Type: model.POST_SYSTEM_MESSAGE_PREFIX + "custom"}, 1099 expected: utils.T("system.message.name"), 1100 }, 1101 "overridden username": { 1102 post: overriddenPost, 1103 allowOverrides: true, 1104 expected: overriddenPost.Props["override_username"].(string), 1105 }, 1106 "overridden username, direct channel": { 1107 channel: &model.Channel{Type: model.CHANNEL_DIRECT}, 1108 post: overriddenPost, 1109 allowOverrides: true, 1110 expected: sender.Username, 1111 }, 1112 "overridden username, overrides disabled": { 1113 post: overriddenPost, 1114 allowOverrides: false, 1115 expected: sender.Username, 1116 }, 1117 } { 1118 t.Run(name, func(t *testing.T) { 1119 channel := defaultChannel 1120 if testCase.channel != nil { 1121 channel = testCase.channel 1122 } 1123 1124 post := defaultPost 1125 if testCase.post != nil { 1126 post = testCase.post 1127 } 1128 1129 notification := &postNotification{ 1130 channel: channel, 1131 post: post, 1132 sender: sender, 1133 } 1134 1135 assert.Equal(t, testCase.expected, notification.GetSenderName(testCase.nameFormat, testCase.allowOverrides)) 1136 }) 1137 } 1138 }