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 }