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  }