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