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 }