github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/app/notification_test.go (about)

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