github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/notification_push_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  	"net/http"
     9  	"net/http/httptest"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/mattermost/mattermost-server/v5/model"
    15  	"github.com/mattermost/mattermost-server/v5/store/storetest/mocks"
    16  	"github.com/mattermost/mattermost-server/v5/utils"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/mock"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  func TestDoesNotifyPropsAllowPushNotification(t *testing.T) {
    23  	tt := []struct {
    24  		name                 string
    25  		userNotifySetting    string
    26  		channelNotifySetting string
    27  		withSystemPost       bool
    28  		wasMentioned         bool
    29  		isMuted              bool
    30  		expected             bool
    31  	}{
    32  		{
    33  			name:                 "When post is a System Message and has no mentions",
    34  			userNotifySetting:    model.USER_NOTIFY_ALL,
    35  			channelNotifySetting: "",
    36  			withSystemPost:       true,
    37  			wasMentioned:         false,
    38  			isMuted:              false,
    39  			expected:             false,
    40  		},
    41  		{
    42  			name:                 "When post is a System Message and has mentions",
    43  			userNotifySetting:    model.USER_NOTIFY_ALL,
    44  			channelNotifySetting: "",
    45  			withSystemPost:       true,
    46  			wasMentioned:         true,
    47  			isMuted:              false,
    48  			expected:             false,
    49  		},
    50  		{
    51  			name:                 "When default is ALL, no channel props is set and has no mentions",
    52  			userNotifySetting:    model.USER_NOTIFY_ALL,
    53  			channelNotifySetting: "",
    54  			withSystemPost:       false,
    55  			wasMentioned:         false,
    56  			isMuted:              false,
    57  			expected:             true,
    58  		},
    59  		{
    60  			name:                 "When default is ALL, no channel props is set and has mentions",
    61  			userNotifySetting:    model.USER_NOTIFY_ALL,
    62  			channelNotifySetting: "",
    63  			withSystemPost:       false,
    64  			wasMentioned:         true,
    65  			isMuted:              false,
    66  			expected:             true,
    67  		},
    68  		{
    69  			name:                 "When default is MENTION, no channel props is set and has no mentions",
    70  			userNotifySetting:    model.USER_NOTIFY_MENTION,
    71  			channelNotifySetting: "",
    72  			withSystemPost:       false,
    73  			wasMentioned:         false,
    74  			isMuted:              false,
    75  			expected:             false,
    76  		},
    77  		{
    78  			name:                 "When default is MENTION, no channel props is set and has mentions",
    79  			userNotifySetting:    model.USER_NOTIFY_MENTION,
    80  			channelNotifySetting: "",
    81  			withSystemPost:       false,
    82  			wasMentioned:         true,
    83  			isMuted:              false,
    84  			expected:             true,
    85  		},
    86  		{
    87  			name:                 "When default is NONE, no channel props is set and has no mentions",
    88  			userNotifySetting:    model.USER_NOTIFY_NONE,
    89  			channelNotifySetting: "",
    90  			withSystemPost:       false,
    91  			wasMentioned:         false,
    92  			isMuted:              false,
    93  			expected:             false,
    94  		},
    95  		{
    96  			name:                 "When default is NONE, no channel props is set and has mentions",
    97  			userNotifySetting:    model.USER_NOTIFY_NONE,
    98  			channelNotifySetting: "",
    99  			withSystemPost:       false,
   100  			wasMentioned:         true,
   101  			isMuted:              false,
   102  			expected:             false,
   103  		},
   104  		{
   105  			name:                 "When default is ALL, channel is DEFAULT and has no mentions",
   106  			userNotifySetting:    model.USER_NOTIFY_ALL,
   107  			channelNotifySetting: model.CHANNEL_NOTIFY_DEFAULT,
   108  			withSystemPost:       false,
   109  			wasMentioned:         false,
   110  			isMuted:              false,
   111  			expected:             true,
   112  		},
   113  		{
   114  			name:                 "When default is ALL, channel is DEFAULT and has mentions",
   115  			userNotifySetting:    model.USER_NOTIFY_ALL,
   116  			channelNotifySetting: model.CHANNEL_NOTIFY_DEFAULT,
   117  			withSystemPost:       false,
   118  			wasMentioned:         true,
   119  			isMuted:              false,
   120  			expected:             true,
   121  		},
   122  		{
   123  			name:                 "When default is MENTION, channel is DEFAULT and has no mentions",
   124  			userNotifySetting:    model.USER_NOTIFY_MENTION,
   125  			channelNotifySetting: model.CHANNEL_NOTIFY_DEFAULT,
   126  			withSystemPost:       false,
   127  			wasMentioned:         false,
   128  			isMuted:              false,
   129  			expected:             false,
   130  		},
   131  		{
   132  			name:                 "When default is MENTION, channel is DEFAULT and has mentions",
   133  			userNotifySetting:    model.USER_NOTIFY_MENTION,
   134  			channelNotifySetting: model.CHANNEL_NOTIFY_DEFAULT,
   135  			withSystemPost:       false,
   136  			wasMentioned:         true,
   137  			isMuted:              false,
   138  			expected:             true,
   139  		},
   140  		{
   141  			name:                 "When default is NONE, channel is DEFAULT and has no mentions",
   142  			userNotifySetting:    model.USER_NOTIFY_NONE,
   143  			channelNotifySetting: model.CHANNEL_NOTIFY_DEFAULT,
   144  			withSystemPost:       false,
   145  			wasMentioned:         false,
   146  			isMuted:              false,
   147  			expected:             false,
   148  		},
   149  		{
   150  			name:                 "When default is NONE, channel is DEFAULT and has mentions",
   151  			userNotifySetting:    model.USER_NOTIFY_NONE,
   152  			channelNotifySetting: model.CHANNEL_NOTIFY_DEFAULT,
   153  			withSystemPost:       false,
   154  			wasMentioned:         true,
   155  			isMuted:              false,
   156  			expected:             false,
   157  		},
   158  		{
   159  			name:                 "When default is ALL, channel is ALL and has no mentions",
   160  			userNotifySetting:    model.USER_NOTIFY_ALL,
   161  			channelNotifySetting: model.CHANNEL_NOTIFY_ALL,
   162  			withSystemPost:       false,
   163  			wasMentioned:         false,
   164  			isMuted:              false,
   165  			expected:             true,
   166  		},
   167  		{
   168  			name:                 "When default is ALL, channel is ALL and has mentions",
   169  			userNotifySetting:    model.USER_NOTIFY_ALL,
   170  			channelNotifySetting: model.CHANNEL_NOTIFY_ALL,
   171  			withSystemPost:       false,
   172  			wasMentioned:         true,
   173  			isMuted:              false,
   174  			expected:             true,
   175  		},
   176  		{
   177  			name:                 "When default is MENTION, channel is ALL and has no mentions",
   178  			userNotifySetting:    model.USER_NOTIFY_MENTION,
   179  			channelNotifySetting: model.CHANNEL_NOTIFY_ALL,
   180  			withSystemPost:       false,
   181  			wasMentioned:         false,
   182  			isMuted:              false,
   183  			expected:             true,
   184  		},
   185  		{
   186  			name:                 "When default is MENTION, channel is ALL and has mentions",
   187  			userNotifySetting:    model.USER_NOTIFY_MENTION,
   188  			channelNotifySetting: model.CHANNEL_NOTIFY_ALL,
   189  			withSystemPost:       false,
   190  			wasMentioned:         true,
   191  			isMuted:              false,
   192  			expected:             true,
   193  		},
   194  		{
   195  			name:                 "When default is NONE, channel is ALL and has no mentions",
   196  			userNotifySetting:    model.USER_NOTIFY_NONE,
   197  			channelNotifySetting: model.CHANNEL_NOTIFY_ALL,
   198  			withSystemPost:       false,
   199  			wasMentioned:         false,
   200  			isMuted:              false,
   201  			expected:             true,
   202  		},
   203  		{
   204  			name:                 "When default is NONE, channel is ALL and has mentions",
   205  			userNotifySetting:    model.USER_NOTIFY_NONE,
   206  			channelNotifySetting: model.CHANNEL_NOTIFY_ALL,
   207  			withSystemPost:       false,
   208  			wasMentioned:         true,
   209  			isMuted:              false,
   210  			expected:             true,
   211  		},
   212  		{
   213  			name:                 "When default is ALL, channel is MENTION and has no mentions",
   214  			userNotifySetting:    model.USER_NOTIFY_ALL,
   215  			channelNotifySetting: model.CHANNEL_NOTIFY_MENTION,
   216  			withSystemPost:       false,
   217  			wasMentioned:         false,
   218  			isMuted:              false,
   219  			expected:             false,
   220  		},
   221  		{
   222  			name:                 "When default is ALL, channel is MENTION and has mentions",
   223  			userNotifySetting:    model.USER_NOTIFY_ALL,
   224  			channelNotifySetting: model.CHANNEL_NOTIFY_MENTION,
   225  			withSystemPost:       false,
   226  			wasMentioned:         true,
   227  			isMuted:              false,
   228  			expected:             true,
   229  		},
   230  		{
   231  			name:                 "When default is MENTION, channel is MENTION and has no mentions",
   232  			userNotifySetting:    model.USER_NOTIFY_MENTION,
   233  			channelNotifySetting: model.CHANNEL_NOTIFY_MENTION,
   234  			withSystemPost:       false,
   235  			wasMentioned:         false,
   236  			isMuted:              false,
   237  			expected:             false,
   238  		},
   239  		{
   240  			name:                 "When default is MENTION, channel is MENTION and has mentions",
   241  			userNotifySetting:    model.USER_NOTIFY_MENTION,
   242  			channelNotifySetting: model.CHANNEL_NOTIFY_MENTION,
   243  			withSystemPost:       false,
   244  			wasMentioned:         true,
   245  			isMuted:              false,
   246  			expected:             true,
   247  		},
   248  		{
   249  			name:                 "When default is NONE, channel is MENTION and has no mentions",
   250  			userNotifySetting:    model.USER_NOTIFY_NONE,
   251  			channelNotifySetting: model.CHANNEL_NOTIFY_MENTION,
   252  			withSystemPost:       false,
   253  			wasMentioned:         false,
   254  			isMuted:              false,
   255  			expected:             false,
   256  		},
   257  		{
   258  			name:                 "When default is NONE, channel is MENTION and has mentions",
   259  			userNotifySetting:    model.USER_NOTIFY_NONE,
   260  			channelNotifySetting: model.CHANNEL_NOTIFY_MENTION,
   261  			withSystemPost:       false,
   262  			wasMentioned:         true,
   263  			isMuted:              false,
   264  			expected:             true,
   265  		},
   266  		{
   267  			name:                 "When default is ALL, channel is NONE and has no mentions",
   268  			userNotifySetting:    model.USER_NOTIFY_ALL,
   269  			channelNotifySetting: model.CHANNEL_NOTIFY_NONE,
   270  			withSystemPost:       false,
   271  			wasMentioned:         false,
   272  			isMuted:              false,
   273  			expected:             false,
   274  		},
   275  		{
   276  			name:                 "When default is ALL, channel is NONE and has mentions",
   277  			userNotifySetting:    model.USER_NOTIFY_ALL,
   278  			channelNotifySetting: model.CHANNEL_NOTIFY_NONE,
   279  			withSystemPost:       false,
   280  			wasMentioned:         true,
   281  			isMuted:              false,
   282  			expected:             false,
   283  		},
   284  		{
   285  			name:                 "When default is MENTION, channel is NONE and has no mentions",
   286  			userNotifySetting:    model.USER_NOTIFY_MENTION,
   287  			channelNotifySetting: model.CHANNEL_NOTIFY_NONE,
   288  			withSystemPost:       false,
   289  			wasMentioned:         false,
   290  			isMuted:              false,
   291  			expected:             false,
   292  		},
   293  		{
   294  			name:                 "When default is MENTION, channel is NONE and has mentions",
   295  			userNotifySetting:    model.USER_NOTIFY_MENTION,
   296  			channelNotifySetting: model.CHANNEL_NOTIFY_NONE,
   297  			withSystemPost:       false,
   298  			wasMentioned:         true,
   299  			isMuted:              false,
   300  			expected:             false,
   301  		},
   302  		{
   303  			name:                 "When default is NONE, channel is NONE and has no mentions",
   304  			userNotifySetting:    model.USER_NOTIFY_NONE,
   305  			channelNotifySetting: model.CHANNEL_NOTIFY_NONE,
   306  			withSystemPost:       false,
   307  			wasMentioned:         false,
   308  			isMuted:              false,
   309  			expected:             false,
   310  		},
   311  		{
   312  			name:                 "When default is NONE, channel is NONE and has mentions",
   313  			userNotifySetting:    model.USER_NOTIFY_NONE,
   314  			channelNotifySetting: model.CHANNEL_NOTIFY_NONE,
   315  			withSystemPost:       false,
   316  			wasMentioned:         true,
   317  			isMuted:              false,
   318  			expected:             false,
   319  		},
   320  		{
   321  			name:                 "When default is ALL, and channel is MUTED",
   322  			userNotifySetting:    model.USER_NOTIFY_ALL,
   323  			channelNotifySetting: "",
   324  			withSystemPost:       false,
   325  			wasMentioned:         false,
   326  			isMuted:              true,
   327  			expected:             false,
   328  		},
   329  	}
   330  
   331  	for _, tc := range tt {
   332  		t.Run(tc.name, func(t *testing.T) {
   333  			user := &model.User{Id: model.NewId(), Email: "unit@test.com", NotifyProps: make(map[string]string)}
   334  			user.NotifyProps[model.PUSH_NOTIFY_PROP] = tc.userNotifySetting
   335  			post := &model.Post{UserId: user.Id, ChannelId: model.NewId()}
   336  			if tc.withSystemPost {
   337  				post.Type = model.POST_JOIN_CHANNEL
   338  			}
   339  
   340  			channelNotifyProps := make(map[string]string)
   341  			if tc.channelNotifySetting != "" {
   342  				channelNotifyProps[model.PUSH_NOTIFY_PROP] = tc.channelNotifySetting
   343  			}
   344  			if tc.isMuted {
   345  				channelNotifyProps[model.MARK_UNREAD_NOTIFY_PROP] = model.CHANNEL_MARK_UNREAD_MENTION
   346  			}
   347  			assert.Equal(t, tc.expected, DoesNotifyPropsAllowPushNotification(user, channelNotifyProps, post, tc.wasMentioned))
   348  		})
   349  	}
   350  }
   351  
   352  func TestDoesStatusAllowPushNotification(t *testing.T) {
   353  	userId := model.NewId()
   354  	channelId := model.NewId()
   355  
   356  	offline := &model.Status{UserId: userId, Status: model.STATUS_OFFLINE, Manual: false, LastActivityAt: 0, ActiveChannel: ""}
   357  	away := &model.Status{UserId: userId, Status: model.STATUS_AWAY, Manual: false, LastActivityAt: 0, ActiveChannel: ""}
   358  	online := &model.Status{UserId: userId, Status: model.STATUS_ONLINE, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
   359  	dnd := &model.Status{UserId: userId, Status: model.STATUS_DND, Manual: true, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
   360  
   361  	tt := []struct {
   362  		name              string
   363  		userNotifySetting string
   364  		status            *model.Status
   365  		channelId         string
   366  		expected          bool
   367  	}{
   368  		{
   369  			name:              "WHEN props is ONLINE and user is offline with channel",
   370  			userNotifySetting: model.STATUS_ONLINE,
   371  			status:            offline,
   372  			channelId:         channelId,
   373  			expected:          true,
   374  		},
   375  		{
   376  			name:              "WHEN props is ONLINE and user is offline without channel",
   377  			userNotifySetting: model.STATUS_ONLINE,
   378  			status:            offline,
   379  			channelId:         "",
   380  			expected:          true,
   381  		},
   382  		{
   383  			name:              "WHEN props is ONLINE and user is away with channel",
   384  			userNotifySetting: model.STATUS_ONLINE,
   385  			status:            away,
   386  			channelId:         channelId,
   387  			expected:          true,
   388  		},
   389  		{
   390  			name:              "WHEN props is ONLINE and user is away without channel",
   391  			userNotifySetting: model.STATUS_ONLINE,
   392  			status:            away,
   393  			channelId:         "",
   394  			expected:          true,
   395  		},
   396  		{
   397  			name:              "WHEN props is ONLINE and user is online with channel",
   398  			userNotifySetting: model.STATUS_ONLINE,
   399  			status:            online,
   400  			channelId:         channelId,
   401  			expected:          true,
   402  		},
   403  		{
   404  			name:              "WHEN props is ONLINE and user is online without channel",
   405  			userNotifySetting: model.STATUS_ONLINE,
   406  			status:            online,
   407  			channelId:         "",
   408  			expected:          false,
   409  		},
   410  		{
   411  			name:              "WHEN props is ONLINE and user is dnd with channel",
   412  			userNotifySetting: model.STATUS_ONLINE,
   413  			status:            dnd,
   414  			channelId:         channelId,
   415  			expected:          false,
   416  		},
   417  		{
   418  			name:              "WHEN props is ONLINE and user is dnd without channel",
   419  			userNotifySetting: model.STATUS_ONLINE,
   420  			status:            dnd,
   421  			channelId:         "",
   422  			expected:          false,
   423  		},
   424  		{
   425  			name:              "WHEN props is AWAY and user is offline with channel",
   426  			userNotifySetting: model.STATUS_AWAY,
   427  			status:            offline,
   428  			channelId:         channelId,
   429  			expected:          true,
   430  		},
   431  		{
   432  			name:              "WHEN props is AWAY and user is offline without channel",
   433  			userNotifySetting: model.STATUS_AWAY,
   434  			status:            offline,
   435  			channelId:         "",
   436  			expected:          true,
   437  		},
   438  		{
   439  			name:              "WHEN props is AWAY and user is away with channel",
   440  			userNotifySetting: model.STATUS_AWAY,
   441  			status:            away,
   442  			channelId:         channelId,
   443  			expected:          true,
   444  		},
   445  		{
   446  			name:              "WHEN props is AWAY and user is away without channel",
   447  			userNotifySetting: model.STATUS_AWAY,
   448  			status:            away,
   449  			channelId:         "",
   450  			expected:          true,
   451  		},
   452  		{
   453  			name:              "WHEN props is AWAY and user is online with channel",
   454  			userNotifySetting: model.STATUS_AWAY,
   455  			status:            online,
   456  			channelId:         channelId,
   457  			expected:          false,
   458  		},
   459  		{
   460  			name:              "WHEN props is AWAY and user is online without channel",
   461  			userNotifySetting: model.STATUS_AWAY,
   462  			status:            online,
   463  			channelId:         "",
   464  			expected:          false,
   465  		},
   466  		{
   467  			name:              "WHEN props is AWAY and user is dnd with channel",
   468  			userNotifySetting: model.STATUS_AWAY,
   469  			status:            dnd,
   470  			channelId:         channelId,
   471  			expected:          false,
   472  		},
   473  		{
   474  			name:              "WHEN props is AWAY and user is dnd without channel",
   475  			userNotifySetting: model.STATUS_AWAY,
   476  			status:            dnd,
   477  			channelId:         "",
   478  			expected:          false,
   479  		},
   480  		{
   481  			name:              "WHEN props is OFFLINE and user is offline with channel",
   482  			userNotifySetting: model.STATUS_OFFLINE,
   483  			status:            offline,
   484  			channelId:         channelId,
   485  			expected:          true,
   486  		},
   487  		{
   488  			name:              "WHEN props is OFFLINE and user is offline without channel",
   489  			userNotifySetting: model.STATUS_OFFLINE,
   490  			status:            offline,
   491  			channelId:         "",
   492  			expected:          true,
   493  		},
   494  		{
   495  			name:              "WHEN props is OFFLINE and user is away with channel",
   496  			userNotifySetting: model.STATUS_OFFLINE,
   497  			status:            away,
   498  			channelId:         channelId,
   499  			expected:          false,
   500  		},
   501  		{
   502  			name:              "WHEN props is OFFLINE and user is away without channel",
   503  			userNotifySetting: model.STATUS_OFFLINE,
   504  			status:            away,
   505  			channelId:         "",
   506  			expected:          false,
   507  		},
   508  		{
   509  			name:              "WHEN props is OFFLINE and user is online with channel",
   510  			userNotifySetting: model.STATUS_OFFLINE,
   511  			status:            online,
   512  			channelId:         channelId,
   513  			expected:          false,
   514  		},
   515  		{
   516  			name:              "WHEN props is OFFLINE and user is online without channel",
   517  			userNotifySetting: model.STATUS_OFFLINE,
   518  			status:            online,
   519  			channelId:         "",
   520  			expected:          false,
   521  		},
   522  		{
   523  			name:              "WHEN props is OFFLINE and user is dnd with channel",
   524  			userNotifySetting: model.STATUS_OFFLINE,
   525  			status:            dnd,
   526  			channelId:         channelId,
   527  			expected:          false,
   528  		},
   529  		{
   530  			name:              "WHEN props is OFFLINE and user is dnd without channel",
   531  			userNotifySetting: model.STATUS_OFFLINE,
   532  			status:            dnd,
   533  			channelId:         "",
   534  			expected:          false,
   535  		},
   536  	}
   537  
   538  	for _, tc := range tt {
   539  		t.Run(tc.name, func(t *testing.T) {
   540  			userNotifyProps := make(map[string]string)
   541  			userNotifyProps["push_status"] = tc.userNotifySetting
   542  			assert.Equal(t, tc.expected, DoesStatusAllowPushNotification(userNotifyProps, tc.status, tc.channelId))
   543  		})
   544  	}
   545  }
   546  
   547  func TestGetPushNotificationMessage(t *testing.T) {
   548  	th := SetupWithStoreMock(t)
   549  	defer th.TearDown()
   550  
   551  	mockStore := th.App.Srv().Store.(*mocks.Store)
   552  	mockUserStore := mocks.UserStore{}
   553  	mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
   554  	mockPostStore := mocks.PostStore{}
   555  	mockPostStore.On("GetMaxPostSize").Return(65535, nil)
   556  	mockSystemStore := mocks.SystemStore{}
   557  	mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)
   558  	mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil)
   559  
   560  	mockStore.On("User").Return(&mockUserStore)
   561  	mockStore.On("Post").Return(&mockPostStore)
   562  	mockStore.On("System").Return(&mockSystemStore)
   563  
   564  	for name, tc := range map[string]struct {
   565  		Message                  string
   566  		explicitMention          bool
   567  		channelWideMention       bool
   568  		HasFiles                 bool
   569  		replyToThreadType        string
   570  		Locale                   string
   571  		PushNotificationContents string
   572  		ChannelType              string
   573  
   574  		ExpectedMessage string
   575  	}{
   576  		"full message, public channel, no mention": {
   577  			Message:         "this is a message",
   578  			ChannelType:     model.CHANNEL_OPEN,
   579  			ExpectedMessage: "user: this is a message",
   580  		},
   581  		"full message, public channel, mention": {
   582  			Message:         "this is a message",
   583  			explicitMention: true,
   584  			ChannelType:     model.CHANNEL_OPEN,
   585  			ExpectedMessage: "user: this is a message",
   586  		},
   587  		"full message, public channel, channel wide mention": {
   588  			Message:            "this is a message",
   589  			channelWideMention: true,
   590  			ChannelType:        model.CHANNEL_OPEN,
   591  			ExpectedMessage:    "user: this is a message",
   592  		},
   593  		"full message, public channel, commented on post": {
   594  			Message:           "this is a message",
   595  			replyToThreadType: model.COMMENTS_NOTIFY_ROOT,
   596  			ChannelType:       model.CHANNEL_OPEN,
   597  			ExpectedMessage:   "user: this is a message",
   598  		},
   599  		"full message, public channel, commented on thread": {
   600  			Message:           "this is a message",
   601  			replyToThreadType: model.COMMENTS_NOTIFY_ANY,
   602  			ChannelType:       model.CHANNEL_OPEN,
   603  			ExpectedMessage:   "user: this is a message",
   604  		},
   605  		"full message, private channel, no mention": {
   606  			Message:         "this is a message",
   607  			ChannelType:     model.CHANNEL_PRIVATE,
   608  			ExpectedMessage: "user: this is a message",
   609  		},
   610  		"full message, private channel, mention": {
   611  			Message:         "this is a message",
   612  			explicitMention: true,
   613  			ChannelType:     model.CHANNEL_PRIVATE,
   614  			ExpectedMessage: "user: this is a message",
   615  		},
   616  		"full message, private channel, commented on post": {
   617  			Message:           "this is a message",
   618  			replyToThreadType: model.COMMENTS_NOTIFY_ROOT,
   619  			ChannelType:       model.CHANNEL_PRIVATE,
   620  			ExpectedMessage:   "user: this is a message",
   621  		},
   622  		"full message, private channel, commented on thread": {
   623  			Message:           "this is a message",
   624  			replyToThreadType: model.COMMENTS_NOTIFY_ANY,
   625  			ChannelType:       model.CHANNEL_PRIVATE,
   626  			ExpectedMessage:   "user: this is a message",
   627  		},
   628  		"full message, group message channel, no mention": {
   629  			Message:         "this is a message",
   630  			ChannelType:     model.CHANNEL_GROUP,
   631  			ExpectedMessage: "user: this is a message",
   632  		},
   633  		"full message, group message channel, mention": {
   634  			Message:         "this is a message",
   635  			explicitMention: true,
   636  			ChannelType:     model.CHANNEL_GROUP,
   637  			ExpectedMessage: "user: this is a message",
   638  		},
   639  		"full message, group message channel, commented on post": {
   640  			Message:           "this is a message",
   641  			replyToThreadType: model.COMMENTS_NOTIFY_ROOT,
   642  			ChannelType:       model.CHANNEL_GROUP,
   643  			ExpectedMessage:   "user: this is a message",
   644  		},
   645  		"full message, group message channel, commented on thread": {
   646  			Message:           "this is a message",
   647  			replyToThreadType: model.COMMENTS_NOTIFY_ANY,
   648  			ChannelType:       model.CHANNEL_GROUP,
   649  			ExpectedMessage:   "user: this is a message",
   650  		},
   651  		"full message, direct message channel, no mention": {
   652  			Message:         "this is a message",
   653  			ChannelType:     model.CHANNEL_DIRECT,
   654  			ExpectedMessage: "this is a message",
   655  		},
   656  		"full message, direct message channel, mention": {
   657  			Message:         "this is a message",
   658  			explicitMention: true,
   659  			ChannelType:     model.CHANNEL_DIRECT,
   660  			ExpectedMessage: "this is a message",
   661  		},
   662  		"full message, direct message channel, commented on post": {
   663  			Message:           "this is a message",
   664  			replyToThreadType: model.COMMENTS_NOTIFY_ROOT,
   665  			ChannelType:       model.CHANNEL_DIRECT,
   666  			ExpectedMessage:   "this is a message",
   667  		},
   668  		"full message, direct message channel, commented on thread": {
   669  			Message:           "this is a message",
   670  			replyToThreadType: model.COMMENTS_NOTIFY_ANY,
   671  			ChannelType:       model.CHANNEL_DIRECT,
   672  			ExpectedMessage:   "this is a message",
   673  		},
   674  		"generic message with channel, public channel, no mention": {
   675  			Message:                  "this is a message",
   676  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   677  			ChannelType:              model.CHANNEL_OPEN,
   678  			ExpectedMessage:          "user posted a message.",
   679  		},
   680  		"generic message with channel, public channel, mention": {
   681  			Message:                  "this is a message",
   682  			explicitMention:          true,
   683  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   684  			ChannelType:              model.CHANNEL_OPEN,
   685  			ExpectedMessage:          "user mentioned you.",
   686  		},
   687  		"generic message with channel, public channel, channel wide mention": {
   688  			Message:                  "this is a message",
   689  			channelWideMention:       true,
   690  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   691  			ChannelType:              model.CHANNEL_OPEN,
   692  			ExpectedMessage:          "user notified the channel.",
   693  		},
   694  		"generic message, public channel, commented on post": {
   695  			Message:                  "this is a message",
   696  			replyToThreadType:        model.COMMENTS_NOTIFY_ROOT,
   697  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   698  			ChannelType:              model.CHANNEL_OPEN,
   699  			ExpectedMessage:          "user commented on your post.",
   700  		},
   701  		"generic message, public channel, commented on thread": {
   702  			Message:                  "this is a message",
   703  			replyToThreadType:        model.COMMENTS_NOTIFY_ANY,
   704  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   705  			ChannelType:              model.CHANNEL_OPEN,
   706  			ExpectedMessage:          "user commented on a thread you participated in.",
   707  		},
   708  		"generic message with channel, private channel, no mention": {
   709  			Message:                  "this is a message",
   710  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   711  			ChannelType:              model.CHANNEL_PRIVATE,
   712  			ExpectedMessage:          "user posted a message.",
   713  		},
   714  		"generic message with channel, private channel, mention": {
   715  			Message:                  "this is a message",
   716  			explicitMention:          true,
   717  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   718  			ChannelType:              model.CHANNEL_PRIVATE,
   719  			ExpectedMessage:          "user mentioned you.",
   720  		},
   721  		"generic message with channel, private channel, channel wide mention": {
   722  			Message:                  "this is a message",
   723  			channelWideMention:       true,
   724  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   725  			ChannelType:              model.CHANNEL_PRIVATE,
   726  			ExpectedMessage:          "user notified the channel.",
   727  		},
   728  		"generic message, public private, commented on post": {
   729  			Message:                  "this is a message",
   730  			replyToThreadType:        model.COMMENTS_NOTIFY_ROOT,
   731  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   732  			ChannelType:              model.CHANNEL_PRIVATE,
   733  			ExpectedMessage:          "user commented on your post.",
   734  		},
   735  		"generic message, public private, commented on thread": {
   736  			Message:                  "this is a message",
   737  			replyToThreadType:        model.COMMENTS_NOTIFY_ANY,
   738  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   739  			ChannelType:              model.CHANNEL_PRIVATE,
   740  			ExpectedMessage:          "user commented on a thread you participated in.",
   741  		},
   742  		"generic message with channel, group message channel, no mention": {
   743  			Message:                  "this is a message",
   744  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   745  			ChannelType:              model.CHANNEL_GROUP,
   746  			ExpectedMessage:          "user posted a message.",
   747  		},
   748  		"generic message with channel, group message channel, mention": {
   749  			Message:                  "this is a message",
   750  			explicitMention:          true,
   751  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   752  			ChannelType:              model.CHANNEL_GROUP,
   753  			ExpectedMessage:          "user mentioned you.",
   754  		},
   755  		"generic message with channel, group message channel, channel wide mention": {
   756  			Message:                  "this is a message",
   757  			channelWideMention:       true,
   758  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   759  			ChannelType:              model.CHANNEL_GROUP,
   760  			ExpectedMessage:          "user notified the channel.",
   761  		},
   762  		"generic message, group message channel, commented on post": {
   763  			Message:                  "this is a message",
   764  			replyToThreadType:        model.COMMENTS_NOTIFY_ROOT,
   765  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   766  			ChannelType:              model.CHANNEL_GROUP,
   767  			ExpectedMessage:          "user commented on your post.",
   768  		},
   769  		"generic message, group message channel, commented on thread": {
   770  			Message:                  "this is a message",
   771  			replyToThreadType:        model.COMMENTS_NOTIFY_ANY,
   772  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   773  			ChannelType:              model.CHANNEL_GROUP,
   774  			ExpectedMessage:          "user commented on a thread you participated in.",
   775  		},
   776  		"generic message with channel, direct message channel, no mention": {
   777  			Message:                  "this is a message",
   778  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   779  			ChannelType:              model.CHANNEL_DIRECT,
   780  			ExpectedMessage:          "sent you a message.",
   781  		},
   782  		"generic message with channel, direct message channel, mention": {
   783  			Message:                  "this is a message",
   784  			explicitMention:          true,
   785  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   786  			ChannelType:              model.CHANNEL_DIRECT,
   787  			ExpectedMessage:          "sent you a message.",
   788  		},
   789  		"generic message with channel, direct message channel, channel wide mention": {
   790  			Message:                  "this is a message",
   791  			channelWideMention:       true,
   792  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   793  			ChannelType:              model.CHANNEL_DIRECT,
   794  			ExpectedMessage:          "sent you a message.",
   795  		},
   796  		"generic message, direct message channel, commented on post": {
   797  			Message:                  "this is a message",
   798  			replyToThreadType:        model.COMMENTS_NOTIFY_ROOT,
   799  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   800  			ChannelType:              model.CHANNEL_DIRECT,
   801  			ExpectedMessage:          "sent you a message.",
   802  		},
   803  		"generic message, direct message channel, commented on thread": {
   804  			Message:                  "this is a message",
   805  			replyToThreadType:        model.COMMENTS_NOTIFY_ANY,
   806  			PushNotificationContents: model.GENERIC_NOTIFICATION,
   807  			ChannelType:              model.CHANNEL_DIRECT,
   808  			ExpectedMessage:          "sent you a message.",
   809  		},
   810  		"generic message without channel, public channel, no mention": {
   811  			Message:                  "this is a message",
   812  			PushNotificationContents: model.GENERIC_NO_CHANNEL_NOTIFICATION,
   813  			ChannelType:              model.CHANNEL_OPEN,
   814  			ExpectedMessage:          "user posted a message.",
   815  		},
   816  		"generic message without channel, public channel, mention": {
   817  			Message:                  "this is a message",
   818  			explicitMention:          true,
   819  			PushNotificationContents: model.GENERIC_NO_CHANNEL_NOTIFICATION,
   820  			ChannelType:              model.CHANNEL_OPEN,
   821  			ExpectedMessage:          "user mentioned you.",
   822  		},
   823  		"generic message without channel, private channel, no mention": {
   824  			Message:                  "this is a message",
   825  			PushNotificationContents: model.GENERIC_NO_CHANNEL_NOTIFICATION,
   826  			ChannelType:              model.CHANNEL_PRIVATE,
   827  			ExpectedMessage:          "user posted a message.",
   828  		},
   829  		"generic message without channel, private channel, mention": {
   830  			Message:                  "this is a message",
   831  			explicitMention:          true,
   832  			PushNotificationContents: model.GENERIC_NO_CHANNEL_NOTIFICATION,
   833  			ChannelType:              model.CHANNEL_PRIVATE,
   834  			ExpectedMessage:          "user mentioned you.",
   835  		},
   836  		"generic message without channel, group message channel, no mention": {
   837  			Message:                  "this is a message",
   838  			PushNotificationContents: model.GENERIC_NO_CHANNEL_NOTIFICATION,
   839  			ChannelType:              model.CHANNEL_GROUP,
   840  			ExpectedMessage:          "user posted a message.",
   841  		},
   842  		"generic message without channel, group message channel, mention": {
   843  			Message:                  "this is a message",
   844  			explicitMention:          true,
   845  			PushNotificationContents: model.GENERIC_NO_CHANNEL_NOTIFICATION,
   846  			ChannelType:              model.CHANNEL_GROUP,
   847  			ExpectedMessage:          "user mentioned you.",
   848  		},
   849  		"generic message without channel, direct message channel, no mention": {
   850  			Message:                  "this is a message",
   851  			PushNotificationContents: model.GENERIC_NO_CHANNEL_NOTIFICATION,
   852  			ChannelType:              model.CHANNEL_DIRECT,
   853  			ExpectedMessage:          "sent you a message.",
   854  		},
   855  		"generic message without channel, direct message channel, mention": {
   856  			Message:                  "this is a message",
   857  			explicitMention:          true,
   858  			PushNotificationContents: model.GENERIC_NO_CHANNEL_NOTIFICATION,
   859  			ChannelType:              model.CHANNEL_DIRECT,
   860  			ExpectedMessage:          "sent you a message.",
   861  		},
   862  		"only files, public channel": {
   863  			HasFiles:        true,
   864  			ChannelType:     model.CHANNEL_OPEN,
   865  			ExpectedMessage: "user attached a file.",
   866  		},
   867  		"only files, private channel": {
   868  			HasFiles:        true,
   869  			ChannelType:     model.CHANNEL_PRIVATE,
   870  			ExpectedMessage: "user attached a file.",
   871  		},
   872  		"only files, group message channel": {
   873  			HasFiles:        true,
   874  			ChannelType:     model.CHANNEL_GROUP,
   875  			ExpectedMessage: "user attached a file.",
   876  		},
   877  		"only files, direct message channel": {
   878  			HasFiles:        true,
   879  			ChannelType:     model.CHANNEL_DIRECT,
   880  			ExpectedMessage: "attached a file.",
   881  		},
   882  		"only files without channel, public channel": {
   883  			HasFiles:                 true,
   884  			PushNotificationContents: model.GENERIC_NO_CHANNEL_NOTIFICATION,
   885  			ChannelType:              model.CHANNEL_OPEN,
   886  			ExpectedMessage:          "user attached a file.",
   887  		},
   888  	} {
   889  		t.Run(name, func(t *testing.T) {
   890  			locale := tc.Locale
   891  			if locale == "" {
   892  				locale = "en"
   893  			}
   894  
   895  			pushNotificationContents := tc.PushNotificationContents
   896  			if pushNotificationContents == "" {
   897  				pushNotificationContents = model.FULL_NOTIFICATION
   898  			}
   899  
   900  			th.App.UpdateConfig(func(cfg *model.Config) {
   901  				*cfg.EmailSettings.PushNotificationContents = pushNotificationContents
   902  			})
   903  
   904  			actualMessage := th.App.getPushNotificationMessage(
   905  				pushNotificationContents,
   906  				tc.Message,
   907  				tc.explicitMention,
   908  				tc.channelWideMention,
   909  				tc.HasFiles,
   910  				"user",
   911  				"channel",
   912  				tc.ChannelType,
   913  				tc.replyToThreadType,
   914  				utils.GetUserTranslations(locale),
   915  			)
   916  
   917  			assert.Equal(t, tc.ExpectedMessage, actualMessage)
   918  		})
   919  	}
   920  }
   921  
   922  func TestBuildPushNotificationMessageMentions(t *testing.T) {
   923  	th := Setup(t).InitBasic()
   924  	defer th.TearDown()
   925  
   926  	team := th.CreateTeam()
   927  	sender := th.CreateUser()
   928  	receiver := th.CreateUser()
   929  	th.LinkUserToTeam(sender, team)
   930  	th.LinkUserToTeam(receiver, team)
   931  	channel1 := th.CreateChannel(team)
   932  	th.AddUserToChannel(sender, channel1)
   933  	th.AddUserToChannel(receiver, channel1)
   934  
   935  	channel2 := th.CreateChannel(team)
   936  	th.AddUserToChannel(sender, channel2)
   937  	th.AddUserToChannel(receiver, channel2)
   938  
   939  	// Create three mention posts and two non-mention posts
   940  	th.CreateMessagePost(channel1, "@channel Hello")
   941  	th.CreateMessagePost(channel1, "@all Hello")
   942  	th.CreateMessagePost(channel1, fmt.Sprintf("@%s Hello in channel 1", receiver.Username))
   943  	th.CreateMessagePost(channel2, fmt.Sprintf("@%s Hello in channel 2", receiver.Username))
   944  	th.CreatePost(channel1)
   945  	post := th.CreatePost(channel1)
   946  
   947  	for name, tc := range map[string]struct {
   948  		explicitMention    bool
   949  		channelWideMention bool
   950  		replyToThreadType  string
   951  		pushNotifyProps    string
   952  		expectedBadge      int
   953  	}{
   954  		"only mentions included for notify_props=mention": {
   955  			explicitMention:    false,
   956  			channelWideMention: true,
   957  			replyToThreadType:  "",
   958  			pushNotifyProps:    "mention",
   959  			expectedBadge:      4,
   960  		},
   961  		"only mentions included for notify_props=all": {
   962  			explicitMention:    false,
   963  			channelWideMention: true,
   964  			replyToThreadType:  "",
   965  			pushNotifyProps:    "all",
   966  			expectedBadge:      4,
   967  		},
   968  	} {
   969  		t.Run(name, func(t *testing.T) {
   970  			receiver.NotifyProps["push"] = tc.pushNotifyProps
   971  			msg, err := th.App.BuildPushNotificationMessage(model.FULL_NOTIFICATION, post, receiver, channel1, channel1.Name, sender.Username, tc.explicitMention, tc.channelWideMention, tc.replyToThreadType)
   972  			require.Nil(t, err)
   973  			assert.Equal(t, tc.expectedBadge, msg.Badge)
   974  		})
   975  	}
   976  }
   977  
   978  func TestSendPushNotifications(t *testing.T) {
   979  	th := Setup(t).InitBasic()
   980  	defer th.TearDown()
   981  	_, err := th.App.CreateSession(&model.Session{
   982  		UserId:    th.BasicUser.Id,
   983  		DeviceId:  "test",
   984  		ExpiresAt: model.GetMillis() + 100000,
   985  	})
   986  	require.Nil(t, err)
   987  
   988  	t.Run("should return error if data is not valid or nil", func(t *testing.T) {
   989  		err := th.App.sendPushNotificationToAllSessions(nil, th.BasicUser.Id, "")
   990  		require.NotNil(t, err)
   991  		assert.Equal(t, "api.push_notifications.message.parse.app_error", err.Id)
   992  		// Errors derived of using an empty object are handled internally through the notifications log
   993  		err = th.App.sendPushNotificationToAllSessions(&model.PushNotification{}, th.BasicUser.Id, "")
   994  		require.Nil(t, err)
   995  	})
   996  }
   997  
   998  // testPushNotificationHandler is an HTTP handler to record push notifications
   999  // being sent from the client.
  1000  // It records the number of requests sent to it, and stores all the requests
  1001  // to be verified later.
  1002  type testPushNotificationHandler struct {
  1003  	t                 testing.TB
  1004  	serialUserMap     sync.Map
  1005  	mut               sync.RWMutex
  1006  	behavior          string
  1007  	_numReqs          int
  1008  	_notifications    []*model.PushNotification
  1009  	_notificationAcks []*model.PushNotificationAck
  1010  }
  1011  
  1012  // handleReq parses a push notification from the body, and stores it.
  1013  // It also sends an appropriate response depending on the behavior set.
  1014  // If the behavior is simple, it always sends an OK response. Otherwise,
  1015  // it alternates between an OK and a REMOVE response.
  1016  func (h *testPushNotificationHandler) handleReq(w http.ResponseWriter, r *http.Request) {
  1017  	switch r.URL.Path {
  1018  	case "/api/v1/send_push", "/api/v1/ack":
  1019  		h.t.Helper()
  1020  
  1021  		// Don't do any checking if it's a benchmark
  1022  		if _, ok := h.t.(*testing.B); ok {
  1023  			resp := model.NewOkPushResponse()
  1024  			fmt.Fprintln(w, (&resp).ToJson())
  1025  			return
  1026  		}
  1027  
  1028  		var notification *model.PushNotification
  1029  		var notificationAck *model.PushNotificationAck
  1030  		var err error
  1031  		if r.URL.Path == "/api/v1/send_push" {
  1032  			notification, err = model.PushNotificationFromJson(r.Body)
  1033  			if err != nil {
  1034  				resp := model.NewErrorPushResponse("fail")
  1035  				fmt.Fprintln(w, (&resp).ToJson())
  1036  				return
  1037  			}
  1038  			// We verify that messages are being sent in order per-device.
  1039  			if notification.DeviceId != "" {
  1040  				if _, ok := h.serialUserMap.Load(notification.DeviceId); ok {
  1041  					h.t.Fatalf("device id: %s being sent concurrently", notification.DeviceId)
  1042  				}
  1043  				h.serialUserMap.LoadOrStore(notification.DeviceId, true)
  1044  				defer h.serialUserMap.Delete(notification.DeviceId)
  1045  			}
  1046  		} else {
  1047  			notificationAck, err = model.PushNotificationAckFromJson(r.Body)
  1048  			if err != nil {
  1049  				resp := model.NewErrorPushResponse("fail")
  1050  				fmt.Fprintln(w, (&resp).ToJson())
  1051  				return
  1052  			}
  1053  		}
  1054  		// Updating internal state.
  1055  		h.mut.Lock()
  1056  		defer h.mut.Unlock()
  1057  		h._numReqs++
  1058  		// Little bit of duplicate condition check so that we can check the in-order property
  1059  		// first.
  1060  		if r.URL.Path == "/api/v1/send_push" {
  1061  			h._notifications = append(h._notifications, notification)
  1062  		} else {
  1063  			h._notificationAcks = append(h._notificationAcks, notificationAck)
  1064  		}
  1065  
  1066  		var resp model.PushResponse
  1067  		if h.behavior == "simple" {
  1068  			resp = model.NewOkPushResponse()
  1069  		} else {
  1070  			// alternating between ok and remove response to test both code paths.
  1071  			if h._numReqs%2 == 0 {
  1072  				resp = model.NewOkPushResponse()
  1073  			} else {
  1074  				resp = model.NewRemovePushResponse()
  1075  			}
  1076  		}
  1077  		fmt.Fprintln(w, (&resp).ToJson())
  1078  	}
  1079  }
  1080  
  1081  func (h *testPushNotificationHandler) numReqs() int {
  1082  	h.mut.RLock()
  1083  	defer h.mut.RUnlock()
  1084  	return h._numReqs
  1085  }
  1086  
  1087  func (h *testPushNotificationHandler) notifications() []*model.PushNotification {
  1088  	h.mut.RLock()
  1089  	defer h.mut.RUnlock()
  1090  	return h._notifications
  1091  }
  1092  
  1093  func (h *testPushNotificationHandler) notificationAcks() []*model.PushNotificationAck {
  1094  	h.mut.RLock()
  1095  	defer h.mut.RUnlock()
  1096  	return h._notificationAcks
  1097  }
  1098  
  1099  func TestClearPushNotificationSync(t *testing.T) {
  1100  	th := SetupWithStoreMock(t)
  1101  	defer th.TearDown()
  1102  
  1103  	handler := &testPushNotificationHandler{t: t}
  1104  	pushServer := httptest.NewServer(
  1105  		http.HandlerFunc(handler.handleReq),
  1106  	)
  1107  	defer pushServer.Close()
  1108  
  1109  	sess1 := &model.Session{
  1110  		Id:        "id1",
  1111  		UserId:    "user1",
  1112  		DeviceId:  "test1",
  1113  		ExpiresAt: model.GetMillis() + 100000,
  1114  	}
  1115  	sess2 := &model.Session{
  1116  		Id:        "id2",
  1117  		UserId:    "user1",
  1118  		DeviceId:  "test2",
  1119  		ExpiresAt: model.GetMillis() + 100000,
  1120  	}
  1121  
  1122  	mockStore := th.App.Srv().Store.(*mocks.Store)
  1123  	mockUserStore := mocks.UserStore{}
  1124  	mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
  1125  	mockUserStore.On("GetUnreadCount", mock.AnythingOfType("string")).Return(int64(1), nil)
  1126  	mockPostStore := mocks.PostStore{}
  1127  	mockPostStore.On("GetMaxPostSize").Return(65535, nil)
  1128  	mockSystemStore := mocks.SystemStore{}
  1129  	mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)
  1130  	mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil)
  1131  
  1132  	mockSessionStore := mocks.SessionStore{}
  1133  	mockSessionStore.On("GetSessionsWithActiveDeviceIds", mock.AnythingOfType("string")).Return([]*model.Session{sess1, sess2}, nil)
  1134  	mockSessionStore.On("UpdateDeviceId", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("int64")).Return("testdeviceID", nil)
  1135  	mockStore.On("User").Return(&mockUserStore)
  1136  	mockStore.On("Post").Return(&mockPostStore)
  1137  	mockStore.On("System").Return(&mockSystemStore)
  1138  	mockStore.On("Session").Return(&mockSessionStore)
  1139  
  1140  	th.App.UpdateConfig(func(cfg *model.Config) {
  1141  		*cfg.EmailSettings.PushNotificationServer = pushServer.URL
  1142  	})
  1143  
  1144  	err := th.App.clearPushNotificationSync(sess1.Id, "user1", "channel1")
  1145  	require.Nil(t, err)
  1146  	// Server side verification.
  1147  	// We verify that 1 request has been sent, and also check the message contents.
  1148  	require.Equal(t, 1, handler.numReqs())
  1149  	assert.Equal(t, "channel1", handler.notifications()[0].ChannelId)
  1150  	assert.Equal(t, model.PUSH_TYPE_CLEAR, handler.notifications()[0].Type)
  1151  }
  1152  
  1153  func TestUpdateMobileAppBadgeSync(t *testing.T) {
  1154  	th := SetupWithStoreMock(t)
  1155  	defer th.TearDown()
  1156  
  1157  	handler := &testPushNotificationHandler{t: t}
  1158  	pushServer := httptest.NewServer(
  1159  		http.HandlerFunc(handler.handleReq),
  1160  	)
  1161  	defer pushServer.Close()
  1162  
  1163  	sess1 := &model.Session{
  1164  		Id:        "id1",
  1165  		UserId:    "user1",
  1166  		DeviceId:  "test1",
  1167  		ExpiresAt: model.GetMillis() + 100000,
  1168  	}
  1169  	sess2 := &model.Session{
  1170  		Id:        "id2",
  1171  		UserId:    "user1",
  1172  		DeviceId:  "test2",
  1173  		ExpiresAt: model.GetMillis() + 100000,
  1174  	}
  1175  
  1176  	mockStore := th.App.Srv().Store.(*mocks.Store)
  1177  	mockUserStore := mocks.UserStore{}
  1178  	mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
  1179  	mockUserStore.On("GetUnreadCount", mock.AnythingOfType("string")).Return(int64(1), nil)
  1180  	mockPostStore := mocks.PostStore{}
  1181  	mockPostStore.On("GetMaxPostSize").Return(65535, nil)
  1182  	mockSystemStore := mocks.SystemStore{}
  1183  	mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)
  1184  	mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil)
  1185  
  1186  	mockSessionStore := mocks.SessionStore{}
  1187  	mockSessionStore.On("GetSessionsWithActiveDeviceIds", mock.AnythingOfType("string")).Return([]*model.Session{sess1, sess2}, nil)
  1188  	mockSessionStore.On("UpdateDeviceId", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("int64")).Return("testdeviceID", nil)
  1189  	mockStore.On("User").Return(&mockUserStore)
  1190  	mockStore.On("Post").Return(&mockPostStore)
  1191  	mockStore.On("System").Return(&mockSystemStore)
  1192  	mockStore.On("Session").Return(&mockSessionStore)
  1193  
  1194  	th.App.UpdateConfig(func(cfg *model.Config) {
  1195  		*cfg.EmailSettings.PushNotificationServer = pushServer.URL
  1196  	})
  1197  
  1198  	err := th.App.updateMobileAppBadgeSync("user1")
  1199  	require.Nil(t, err)
  1200  	// Server side verification.
  1201  	// We verify that 2 requests have been sent, and also check the message contents.
  1202  	require.Equal(t, 2, handler.numReqs())
  1203  	assert.Equal(t, 1, handler.notifications()[0].ContentAvailable)
  1204  	assert.Equal(t, model.PUSH_TYPE_UPDATE_BADGE, handler.notifications()[0].Type)
  1205  	assert.Equal(t, 1, handler.notifications()[1].ContentAvailable)
  1206  	assert.Equal(t, model.PUSH_TYPE_UPDATE_BADGE, handler.notifications()[1].Type)
  1207  }
  1208  
  1209  func TestSendAckToPushProxy(t *testing.T) {
  1210  	th := SetupWithStoreMock(t)
  1211  	defer th.TearDown()
  1212  
  1213  	handler := &testPushNotificationHandler{t: t}
  1214  	pushServer := httptest.NewServer(
  1215  		http.HandlerFunc(handler.handleReq),
  1216  	)
  1217  	defer pushServer.Close()
  1218  
  1219  	mockStore := th.App.Srv().Store.(*mocks.Store)
  1220  	mockUserStore := mocks.UserStore{}
  1221  	mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
  1222  	mockPostStore := mocks.PostStore{}
  1223  	mockPostStore.On("GetMaxPostSize").Return(65535, nil)
  1224  	mockSystemStore := mocks.SystemStore{}
  1225  	mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)
  1226  	mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil)
  1227  
  1228  	mockStore.On("User").Return(&mockUserStore)
  1229  	mockStore.On("Post").Return(&mockPostStore)
  1230  	mockStore.On("System").Return(&mockSystemStore)
  1231  
  1232  	th.App.UpdateConfig(func(cfg *model.Config) {
  1233  		*cfg.EmailSettings.PushNotificationServer = pushServer.URL
  1234  	})
  1235  
  1236  	ack := &model.PushNotificationAck{
  1237  		Id:               "testid",
  1238  		NotificationType: model.PUSH_TYPE_MESSAGE,
  1239  	}
  1240  	err := th.App.SendAckToPushProxy(ack)
  1241  	require.Nil(t, err)
  1242  	// Server side verification.
  1243  	// We verify that 1 request has been sent, and also check the message contents.
  1244  	require.Equal(t, 1, handler.numReqs())
  1245  	assert.Equal(t, ack.Id, handler.notificationAcks()[0].Id)
  1246  	assert.Equal(t, ack.NotificationType, handler.notificationAcks()[0].NotificationType)
  1247  }
  1248  
  1249  // TestAllPushNotifications is a master test which sends all verious types
  1250  // of notifications and verifies they have been properly sent.
  1251  func TestAllPushNotifications(t *testing.T) {
  1252  	if testing.Short() {
  1253  		t.Skip("skipping all push notifications test in short mode")
  1254  	}
  1255  
  1256  	th := Setup(t).InitBasic()
  1257  	defer th.TearDown()
  1258  
  1259  	// Create 10 users, each having 2 sessions.
  1260  	type userSession struct {
  1261  		user    *model.User
  1262  		session *model.Session
  1263  	}
  1264  	var testData []userSession
  1265  	for i := 0; i < 10; i++ {
  1266  		u := th.CreateUser()
  1267  		sess, err := th.App.CreateSession(&model.Session{
  1268  			UserId:    u.Id,
  1269  			DeviceId:  "deviceID" + u.Id,
  1270  			ExpiresAt: model.GetMillis() + 100000,
  1271  		})
  1272  		require.Nil(t, err)
  1273  		// We don't need to track the 2nd session.
  1274  		_, err = th.App.CreateSession(&model.Session{
  1275  			UserId:    u.Id,
  1276  			DeviceId:  "deviceID" + u.Id,
  1277  			ExpiresAt: model.GetMillis() + 100000,
  1278  		})
  1279  		require.Nil(t, err)
  1280  		_, err = th.App.AddTeamMember(th.BasicTeam.Id, u.Id)
  1281  		require.Nil(t, err)
  1282  		th.AddUserToChannel(u, th.BasicChannel)
  1283  		testData = append(testData, userSession{
  1284  			user:    u,
  1285  			session: sess,
  1286  		})
  1287  	}
  1288  
  1289  	handler := &testPushNotificationHandler{
  1290  		t:        t,
  1291  		behavior: "simple",
  1292  	}
  1293  	pushServer := httptest.NewServer(
  1294  		http.HandlerFunc(handler.handleReq),
  1295  	)
  1296  	defer pushServer.Close()
  1297  
  1298  	th.App.UpdateConfig(func(cfg *model.Config) {
  1299  		*cfg.EmailSettings.PushNotificationContents = model.GENERIC_NOTIFICATION
  1300  		*cfg.EmailSettings.PushNotificationServer = pushServer.URL
  1301  	})
  1302  
  1303  	var wg sync.WaitGroup
  1304  	for i, data := range testData {
  1305  		wg.Add(1)
  1306  		// Ranging between 3 types of notifications.
  1307  		switch i % 3 {
  1308  		case 0:
  1309  			go func(user model.User) {
  1310  				defer wg.Done()
  1311  				notification := &PostNotification{
  1312  					Post:    th.CreatePost(th.BasicChannel),
  1313  					Channel: th.BasicChannel,
  1314  					ProfileMap: map[string]*model.User{
  1315  						user.Id: &user,
  1316  					},
  1317  					Sender: &user,
  1318  				}
  1319  				// testing all 3 notification types.
  1320  				th.App.sendPushNotification(notification, &user, true, false, model.COMMENTS_NOTIFY_ANY)
  1321  			}(*data.user)
  1322  		case 1:
  1323  			go func(id string) {
  1324  				defer wg.Done()
  1325  				th.App.UpdateMobileAppBadge(id)
  1326  			}(data.user.Id)
  1327  		case 2:
  1328  			go func(sessID, userID string) {
  1329  				defer wg.Done()
  1330  				th.App.clearPushNotification(sessID, userID, th.BasicChannel.Id)
  1331  			}(data.session.Id, data.user.Id)
  1332  		}
  1333  	}
  1334  	wg.Wait()
  1335  
  1336  	// Hack to let the worker goroutines complete.
  1337  	time.Sleep(1 * time.Second)
  1338  	// Server side verification.
  1339  	assert.Equal(t, 17, handler.numReqs())
  1340  	var numClears, numMessages, numUpdateBadges int
  1341  	for _, n := range handler.notifications() {
  1342  		switch n.Type {
  1343  		case model.PUSH_TYPE_CLEAR:
  1344  			numClears++
  1345  			assert.Equal(t, th.BasicChannel.Id, n.ChannelId)
  1346  		case model.PUSH_TYPE_MESSAGE:
  1347  			numMessages++
  1348  			assert.Equal(t, th.BasicChannel.Id, n.ChannelId)
  1349  			assert.Contains(t, n.Message, "mentioned you")
  1350  		case model.PUSH_TYPE_UPDATE_BADGE:
  1351  			numUpdateBadges++
  1352  			assert.Equal(t, "none", n.Sound)
  1353  			assert.Equal(t, 1, n.ContentAvailable)
  1354  		}
  1355  	}
  1356  	assert.Equal(t, 8, numMessages)
  1357  	assert.Equal(t, 3, numClears)
  1358  	assert.Equal(t, 6, numUpdateBadges)
  1359  }
  1360  
  1361  // Run it with | grep -v '{"level"' to prevent spamming the console.
  1362  func BenchmarkPushNotificationThroughput(b *testing.B) {
  1363  	th := SetupWithStoreMock(b)
  1364  	defer th.TearDown()
  1365  
  1366  	handler := &testPushNotificationHandler{
  1367  		t:        b,
  1368  		behavior: "simple",
  1369  	}
  1370  	pushServer := httptest.NewServer(
  1371  		http.HandlerFunc(handler.handleReq),
  1372  	)
  1373  	defer pushServer.Close()
  1374  
  1375  	mockStore := th.App.Srv().Store.(*mocks.Store)
  1376  	mockUserStore := mocks.UserStore{}
  1377  	mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
  1378  	mockUserStore.On("GetUnreadCount", mock.AnythingOfType("string")).Return(int64(1), nil)
  1379  	mockPostStore := mocks.PostStore{}
  1380  	mockPostStore.On("GetMaxPostSize").Return(65535, nil)
  1381  	mockSystemStore := mocks.SystemStore{}
  1382  	mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)
  1383  	mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil)
  1384  
  1385  	mockSessionStore := mocks.SessionStore{}
  1386  	mockPreferenceStore := mocks.PreferenceStore{}
  1387  	mockPreferenceStore.On("Get", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(&model.Preference{Value: "test"}, nil)
  1388  	mockStore.On("User").Return(&mockUserStore)
  1389  	mockStore.On("Post").Return(&mockPostStore)
  1390  	mockStore.On("System").Return(&mockSystemStore)
  1391  	mockStore.On("Session").Return(&mockSessionStore)
  1392  	mockStore.On("Preference").Return(&mockPreferenceStore)
  1393  
  1394  	// create 50 users, each having 2 sessions.
  1395  	type userSession struct {
  1396  		user    *model.User
  1397  		session *model.Session
  1398  	}
  1399  	var testData []userSession
  1400  	for i := 0; i < 50; i++ {
  1401  		id := model.NewId()
  1402  		u := &model.User{
  1403  			Id:            id,
  1404  			Email:         "success+" + id + "@simulator.amazonses.com",
  1405  			Username:      "un_" + id,
  1406  			Nickname:      "nn_" + id,
  1407  			Password:      "Password1",
  1408  			EmailVerified: true,
  1409  		}
  1410  		sess1 := &model.Session{
  1411  			Id:        "id1",
  1412  			UserId:    u.Id,
  1413  			DeviceId:  "deviceID" + u.Id,
  1414  			ExpiresAt: model.GetMillis() + 100000,
  1415  		}
  1416  		sess2 := &model.Session{
  1417  			Id:        "id2",
  1418  			UserId:    u.Id,
  1419  			DeviceId:  "deviceID" + u.Id,
  1420  			ExpiresAt: model.GetMillis() + 100000,
  1421  		}
  1422  		mockSessionStore.On("GetSessionsWithActiveDeviceIds", u.Id).Return([]*model.Session{sess1, sess2}, nil)
  1423  		mockSessionStore.On("UpdateDeviceId", sess1.Id, "deviceID"+u.Id, mock.AnythingOfType("int64")).Return("deviceID"+u.Id, nil)
  1424  
  1425  		testData = append(testData, userSession{
  1426  			user:    u,
  1427  			session: sess1,
  1428  		})
  1429  	}
  1430  
  1431  	th.App.UpdateConfig(func(cfg *model.Config) {
  1432  		*cfg.EmailSettings.PushNotificationServer = pushServer.URL
  1433  		*cfg.LogSettings.EnableConsole = false
  1434  		*cfg.NotificationLogSettings.EnableConsole = false
  1435  	})
  1436  
  1437  	ch := &model.Channel{
  1438  		Id:       model.NewId(),
  1439  		CreateAt: model.GetMillis(),
  1440  		Type:     model.CHANNEL_OPEN,
  1441  		Name:     "testch",
  1442  	}
  1443  
  1444  	b.ResetTimer()
  1445  	// We have an inner loop which ranges the testdata slice
  1446  	// and we just repeat that.
  1447  	then := time.Now()
  1448  	cnt := 0
  1449  	for i := 0; i < b.N; i++ {
  1450  		cnt++
  1451  		var wg sync.WaitGroup
  1452  		for j, data := range testData {
  1453  			wg.Add(1)
  1454  			// Ranging between 3 types of notifications.
  1455  			switch j % 3 {
  1456  			case 0:
  1457  				go func(user model.User) {
  1458  					defer wg.Done()
  1459  					post := &model.Post{
  1460  						UserId:    user.Id,
  1461  						ChannelId: ch.Id,
  1462  						Message:   "test message",
  1463  						CreateAt:  model.GetMillis(),
  1464  					}
  1465  					notification := &PostNotification{
  1466  						Post:    post,
  1467  						Channel: ch,
  1468  						ProfileMap: map[string]*model.User{
  1469  							user.Id: &user,
  1470  						},
  1471  						Sender: &user,
  1472  					}
  1473  					th.App.sendPushNotification(notification, &user, true, false, model.COMMENTS_NOTIFY_ANY)
  1474  				}(*data.user)
  1475  			case 1:
  1476  				go func(id string) {
  1477  					defer wg.Done()
  1478  					th.App.UpdateMobileAppBadge(id)
  1479  				}(data.user.Id)
  1480  			case 2:
  1481  				go func(sessID, userID string) {
  1482  					defer wg.Done()
  1483  					th.App.clearPushNotification(sessID, userID, ch.Id)
  1484  				}(data.session.Id, data.user.Id)
  1485  			}
  1486  		}
  1487  		wg.Wait()
  1488  	}
  1489  	b.Logf("throughput: %f reqs/s", float64(len(testData)*cnt)/time.Since(then).Seconds())
  1490  	b.StopTimer()
  1491  	time.Sleep(2 * time.Second)
  1492  }