github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/notification_test.go (about)

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