status-go/protocol/push_notification_test.go

1306 lines
37 KiB
Go

package protocol
import (
"context"
"crypto/ecdsa"
"encoding/hex"
"errors"
"testing"
"github.com/stretchr/testify/suite"
"go.uber.org/zap"
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/communities"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/pushnotificationclient"
"github.com/status-im/status-go/protocol/pushnotificationserver"
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/waku"
)
const (
bob1DeviceToken = "token-1"
bob2DeviceToken = "token-2"
testAPNTopic = "topic"
)
func TestMessengerPushNotificationSuite(t *testing.T) {
suite.Run(t, new(MessengerPushNotificationSuite))
}
type MessengerPushNotificationSuite struct {
suite.Suite
m *Messenger // main instance of Messenger
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
// If one wants to send messages between different instances of Messenger,
// a single Waku service should be shared.
shh types.Waku
logger *zap.Logger
}
func (s *MessengerPushNotificationSuite) SetupTest() {
s.logger = tt.MustCreateTestLogger()
config := waku.DefaultConfig
config.MinimumAcceptedPoW = 0
shh := waku.New(&config, s.logger)
s.shh = gethbridge.NewGethWakuWrapper(shh)
s.Require().NoError(shh.Start())
s.m = s.newMessenger(s.shh)
s.privateKey = s.m.identity
}
func (s *MessengerPushNotificationSuite) TearDownTest() {
TearDownMessenger(&s.Suite, s.m)
_ = s.logger.Sync()
}
func (s *MessengerPushNotificationSuite) newMessenger(shh types.Waku) *Messenger {
privateKey, err := crypto.GenerateKey()
s.Require().NoError(err)
messenger, err := newMessengerWithKey(s.shh, privateKey, s.logger, []Option{WithPushNotifications()})
s.Require().NoError(err)
return messenger
}
func (s *MessengerPushNotificationSuite) newPushNotificationServer(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
serverConfig := &pushnotificationserver.Config{
Enabled: true,
Logger: s.logger,
Identity: privateKey,
}
options := []Option{
WithPushNotificationServerConfig(serverConfig),
}
messenger, err := newMessengerWithKey(shh, privateKey, s.logger, options)
s.Require().NoError(err)
return messenger
}
func (s *MessengerPushNotificationSuite) TestReceivePushNotification() {
bob1 := s.m
bob2, err := newMessengerWithKey(s.shh, s.m.identity, s.logger, []Option{WithPushNotifications()})
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
serverKey, err := crypto.GenerateKey()
s.Require().NoError(err)
server := s.newPushNotificationServer(s.shh, serverKey)
defer TearDownMessenger(&s.Suite, server)
alice := s.newMessenger(s.shh)
defer TearDownMessenger(&s.Suite, alice)
s.Require().NoError(alice.EnableSendingPushNotifications())
bobInstallationIDs := []string{bob1.installationID, bob2.installationID}
// Register bob1
err = bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
s.Require().NoError(err)
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob1.RetrieveAll()
if err != nil {
return err
}
registered, err := bob1.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
bobServers, err := bob1.GetPushNotificationsServers()
if err != nil {
return err
}
if len(bobServers) == 0 {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
bob1Servers, err := bob1.GetPushNotificationsServers()
s.Require().NoError(err)
// Register bob2
err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
s.Require().NoError(err)
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
s.Require().NoError(err)
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob2.RetrieveAll()
if err != nil {
return err
}
registered, err := bob2.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
bobServers, err := bob2.GetPushNotificationsServers()
if err != nil {
return err
}
if len(bobServers) == 0 {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
bob2Servers, err := bob2.GetPushNotificationsServers()
s.Require().NoError(err)
// Create one to one chat & send message
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
s.Require().NoError(alice.SaveChat(chat))
inputMessage := buildTestMessage(*chat)
response, err := alice.SendChatMessage(context.Background(), inputMessage)
s.Require().NoError(err)
messageIDString := response.Messages()[0].ID
messageID, err := hex.DecodeString(messageIDString[2:])
s.Require().NoError(err)
infoMap := make(map[string]*pushnotificationclient.PushNotificationInfo)
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
info, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
if err != nil {
return err
}
for _, i := range info {
infoMap[i.AccessToken] = i
}
// Check we have replies for both bob1 and bob2
if len(infoMap) != 2 {
return errors.New("info not fetched")
}
return nil
})
s.Require().Len(infoMap, 2)
// Check we have replies for both bob1 and bob2
var bob1Info, bob2Info *pushnotificationclient.PushNotificationInfo
bob1Info = infoMap[bob1Servers[0].AccessToken]
bob2Info = infoMap[bob2Servers[0].AccessToken]
s.Require().NotNil(bob1Info)
s.Require().Equal(bob1.installationID, bob1Info.InstallationID)
s.Require().Equal(bob1Servers[0].AccessToken, bob1Info.AccessToken)
s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey)
s.Require().NotNil(bob2Info)
s.Require().Equal(bob2.installationID, bob2Info.InstallationID)
s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken)
s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey)
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
s.Require().NoError(err)
s.Require().NotNil(retrievedNotificationInfo)
s.Require().Len(retrievedNotificationInfo, 2)
var sentNotification *pushnotificationclient.SentNotification
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob1.identity.PublicKey), bob1.installationID, messageID)
if err != nil {
return err
}
if sentNotification == nil {
return errors.New("sent notification not found")
}
if !sentNotification.Success {
return errors.New("sent notification not successul")
}
return nil
})
s.Require().NoError(err)
}
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactOnly() {
bob := s.m
serverKey, err := crypto.GenerateKey()
s.Require().NoError(err)
server := s.newPushNotificationServer(s.shh, serverKey)
defer TearDownMessenger(&s.Suite, server)
alice := s.newMessenger(s.shh)
defer TearDownMessenger(&s.Suite, alice)
s.Require().NoError(alice.EnableSendingPushNotifications())
bobInstallationIDs := []string{bob.installationID}
// Register bob
err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
s.Require().NoError(err)
// Add alice has a contact
aliceContact := &Contact{
ID: types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)),
EnsName: "Some Contact",
ContactRequestLocalState: ContactRequestStateSent,
}
_, err = bob.AddContact(context.Background(), &requests.AddContact{ID: aliceContact.ID})
s.Require().NoError(err)
// Enable from contacts only
err = bob.EnablePushNotificationsFromContactsOnly()
s.Require().NoError(err)
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
s.Require().NoError(err)
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob.RetrieveAll()
if err != nil {
return err
}
registered, err := bob.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
bobServers, err := bob.GetPushNotificationsServers()
if err != nil {
return err
}
if len(bobServers) == 0 {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
bobServers, err := bob.GetPushNotificationsServers()
s.Require().NoError(err)
// Create one to one chat & send message
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
s.Require().NoError(alice.SaveChat(chat))
inputMessage := buildTestMessage(*chat)
response, err := alice.SendChatMessage(context.Background(), inputMessage)
s.Require().NoError(err)
messageIDString := response.Messages()[0].ID
messageID, err := hex.DecodeString(messageIDString[2:])
s.Require().NoError(err)
var info []*pushnotificationclient.PushNotificationInfo
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
if err != nil {
return err
}
// Check we have replies for bob
if len(info) != 1 {
return errors.New("info not fetched")
}
return nil
})
s.Require().NoError(err)
s.Require().NotNil(info)
s.Require().Equal(bob.installationID, info[0].InstallationID)
s.Require().Equal(bobServers[0].AccessToken, info[0].AccessToken)
s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey)
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
s.Require().NoError(err)
s.Require().NotNil(retrievedNotificationInfo)
s.Require().Len(retrievedNotificationInfo, 1)
var sentNotification *pushnotificationclient.SentNotification
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID)
if err != nil {
return err
}
if sentNotification == nil {
return errors.New("sent notification not found")
}
if !sentNotification.Success {
return errors.New("sent notification not successul")
}
return nil
})
s.Require().NoError(err)
}
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
bob := s.m
serverKey, err := crypto.GenerateKey()
s.Require().NoError(err)
server := s.newPushNotificationServer(s.shh, serverKey)
defer TearDownMessenger(&s.Suite, server)
alice := s.newMessenger(s.shh)
// another contact to invalidate the token
frank := s.newMessenger(s.shh)
defer TearDownMessenger(&s.Suite, frank)
defer TearDownMessenger(&s.Suite, alice)
s.Require().NoError(alice.EnableSendingPushNotifications())
bobInstallationIDs := []string{bob.installationID}
// Register bob
err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
s.Require().NoError(err)
// Add alice has a contact
aliceContact := &Contact{
ID: types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)),
EnsName: "Some Contact",
ContactRequestLocalState: ContactRequestStateSent,
}
_, err = bob.AddContact(context.Background(), &requests.AddContact{ID: aliceContact.ID})
s.Require().NoError(err)
// Add frank has a contact
frankContact := &Contact{
ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)),
EnsName: "Some Contact",
ContactRequestLocalState: ContactRequestStateSent,
}
_, err = bob.AddContact(context.Background(), &requests.AddContact{ID: frankContact.ID})
s.Require().NoError(err)
// Enable from contacts only
err = bob.EnablePushNotificationsFromContactsOnly()
s.Require().NoError(err)
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
s.Require().NoError(err)
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob.RetrieveAll()
if err != nil {
return err
}
registered, err := bob.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
bobServers, err := bob.GetPushNotificationsServers()
if err != nil {
return err
}
if len(bobServers) == 0 {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
bobServers, err := bob.GetPushNotificationsServers()
s.Require().NoError(err)
// Create one to one chat & send message
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
s.Require().NoError(alice.SaveChat(chat))
inputMessage := buildTestMessage(*chat)
_, err = alice.SendChatMessage(context.Background(), inputMessage)
s.Require().NoError(err)
// We check that alice retrieves the info from the server
var info []*pushnotificationclient.PushNotificationInfo
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
if err != nil {
return err
}
// Check we have replies for bob
if len(info) != 1 {
return errors.New("info not fetched")
}
return nil
})
s.Require().NoError(err)
s.Require().NotNil(info)
s.Require().Equal(bob.installationID, info[0].InstallationID)
s.Require().Equal(bobServers[0].AccessToken, info[0].AccessToken)
s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey)
// The message has been sent, but not received, now we remove a contact so that the token is invalidated
frankContact = &Contact{
ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)),
EnsName: "Some Contact",
}
_, err = bob.RemoveContact(context.Background(), frankContact.ID)
s.Require().NoError(err)
// Re-registration should be triggered, pull from server and bob to check we are correctly registered
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob.RetrieveAll()
if err != nil {
return err
}
registered, err := bob.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
return nil
})
newBobServers, err := bob.GetPushNotificationsServers()
s.Require().NoError(err)
// Make sure access token is not the same
s.Require().NotEqual(newBobServers[0].AccessToken, bobServers[0].AccessToken)
// Send another message, here the token will not be valid
inputMessage = buildTestMessage(*chat)
response, err := alice.SendChatMessage(context.Background(), inputMessage)
s.Require().NoError(err)
messageIDString := response.Messages()[0].ID
messageID, err := hex.DecodeString(messageIDString[2:])
s.Require().NoError(err)
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
if err != nil {
return err
}
// Check we have replies for bob
if len(info) != 1 {
return errors.New("info not fetched")
}
if newBobServers[0].AccessToken != info[0].AccessToken {
return errors.New("still using the old access token")
}
return nil
})
s.Require().NoError(err)
s.Require().NotNil(info)
s.Require().Equal(bob.installationID, info[0].InstallationID)
s.Require().Equal(newBobServers[0].AccessToken, info[0].AccessToken)
s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey)
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
s.Require().NoError(err)
s.Require().NotNil(retrievedNotificationInfo)
s.Require().Len(retrievedNotificationInfo, 1)
var sentNotification *pushnotificationclient.SentNotification
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID)
if err != nil {
return err
}
if sentNotification == nil {
return errors.New("sent notification not found")
}
if !sentNotification.Success {
return errors.New("sent notification not successul")
}
return nil
})
s.Require().NoError(err)
}
func (s *MessengerPushNotificationSuite) TestContactCode() {
bob1 := s.m
serverKey, err := crypto.GenerateKey()
s.Require().NoError(err)
server := s.newPushNotificationServer(s.shh, serverKey)
defer TearDownMessenger(&s.Suite, server)
alice := s.newMessenger(s.shh)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, alice)
s.Require().NoError(alice.EnableSendingPushNotifications())
// Register bob1
err = bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
s.Require().NoError(err)
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob1.RetrieveAll()
if err != nil {
return err
}
registered, err := bob1.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
bobServers, err := bob1.GetPushNotificationsServers()
if err != nil {
return err
}
if len(bobServers) == 0 {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
contactCodeAdvertisement, err := bob1.buildContactCodeAdvertisement()
s.Require().NoError(err)
s.Require().NotNil(contactCodeAdvertisement)
s.Require().NoError(alice.pushNotificationClient.HandleContactCodeAdvertisement(&bob1.identity.PublicKey, contactCodeAdvertisement))
}
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationMention() {
bob := s.m
serverKey, err := crypto.GenerateKey()
s.Require().NoError(err)
server := s.newPushNotificationServer(s.shh, serverKey)
defer TearDownMessenger(&s.Suite, server)
alice := s.newMessenger(s.shh)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, alice)
s.Require().NoError(alice.EnableSendingPushNotifications())
bobInstallationIDs := []string{bob.installationID}
// Create public chat and join for both alice and bob
chat := CreatePublicChat("status", s.m.transport)
err = bob.SaveChat(chat)
s.Require().NoError(err)
_, err = bob.Join(chat)
s.Require().NoError(err)
err = alice.SaveChat(chat)
s.Require().NoError(err)
_, err = alice.Join(chat)
s.Require().NoError(err)
// Register bob
err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
s.Require().NoError(err)
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob.RetrieveAll()
if err != nil {
return err
}
registered, err := bob.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
bobServers, err := bob.GetPushNotificationsServers()
if err != nil {
return err
}
if len(bobServers) == 0 {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
bobServers, err := bob.GetPushNotificationsServers()
s.Require().NoError(err)
inputMessage := buildTestMessage(*chat)
// message contains a mention
inputMessage.Text = "Hey @" + types.EncodeHex(crypto.FromECDSAPub(&bob.identity.PublicKey))
response, err := alice.SendChatMessage(context.Background(), inputMessage)
s.Require().NoError(err)
messageIDString := response.Messages()[0].ID
messageID, err := hex.DecodeString(messageIDString[2:])
s.Require().NoError(err)
var bobInfo []*pushnotificationclient.PushNotificationInfo
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
bobInfo, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
if err != nil {
return err
}
// Check we have replies for bob
if len(bobInfo) != 1 {
return errors.New("info not fetched")
}
return nil
})
s.Require().NoError(err)
s.Require().NotEmpty(bobInfo)
s.Require().Equal(bob.installationID, bobInfo[0].InstallationID)
s.Require().Equal(bobServers[0].AccessToken, bobInfo[0].AccessToken)
s.Require().Equal(&bob.identity.PublicKey, bobInfo[0].PublicKey)
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
s.Require().NoError(err)
s.Require().NotNil(retrievedNotificationInfo)
s.Require().Len(retrievedNotificationInfo, 1)
var sentNotification *pushnotificationclient.SentNotification
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID)
if err != nil {
return err
}
if sentNotification == nil {
return errors.New("sent notification not found")
}
if !sentNotification.Success {
return errors.New("sent notification not successul")
}
return nil
})
s.Require().NoError(err)
}
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationCommunityRequest() {
bob := s.m
serverKey, err := crypto.GenerateKey()
s.Require().NoError(err)
server := s.newPushNotificationServer(s.shh, serverKey)
defer TearDownMessenger(&s.Suite, server)
alice := s.newMessenger(s.shh)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, alice)
s.Require().NoError(alice.EnableSendingPushNotifications())
// Register bob
err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
s.Require().NoError(err)
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob.RetrieveAll()
if err != nil {
return err
}
registered, err := bob.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
bobServers, err := bob.GetPushNotificationsServers()
if err != nil {
return err
}
if len(bobServers) == 0 {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
_, err = bob.GetPushNotificationsServers()
s.Require().NoError(err)
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT,
Name: "status",
Color: "#ffffff",
Description: "status community description",
}
response, err := bob.CreateCommunity(description, true)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
community := response.Communities()[0]
// Send a community message
chat := CreateOneToOneChat(common.PubkeyToHex(&alice.identity.PublicKey), &alice.identity.PublicKey, alice.transport)
inputMessage := common.NewMessage()
inputMessage.ChatId = chat.ID
inputMessage.Text = "some text"
inputMessage.CommunityID = community.IDString()
err = bob.SaveChat(chat)
s.NoError(err)
_, err = bob.SendChatMessage(context.Background(), inputMessage)
s.NoError(err)
// Pull message and make sure org is received
err = tt.RetryWithBackOff(func() error {
response, err = alice.RetrieveAll()
if err != nil {
return err
}
if len(response.Communities()) == 0 {
return errors.New("community not received")
}
return nil
})
request := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
// We try to join the org
response, err = alice.RequestToJoinCommunity(request)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.RequestsToJoinCommunity(), 1)
requestToJoin1 := response.RequestsToJoinCommunity()[0]
s.Require().NotNil(requestToJoin1)
s.Require().Equal(community.ID(), requestToJoin1.CommunityID)
s.Require().True(requestToJoin1.Our)
s.Require().NotEmpty(requestToJoin1.ID)
s.Require().NotEmpty(requestToJoin1.Clock)
s.Require().Equal(requestToJoin1.PublicKey, common.PubkeyToHex(&alice.identity.PublicKey))
s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State)
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
if server.pushNotificationServer.SentRequests != 1 {
return errors.New("request not sent")
}
return nil
})
s.Require().NoError(err)
}
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationPairedDevices() {
bob1 := s.m
bob2, err := newMessengerWithKey(s.shh, s.m.identity, s.logger, []Option{WithPushNotifications()})
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
serverKey, err := crypto.GenerateKey()
s.Require().NoError(err)
server := s.newPushNotificationServer(s.shh, serverKey)
defer TearDownMessenger(&s.Suite, server)
alice := s.newMessenger(s.shh)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, alice)
s.Require().NoError(alice.EnableSendingPushNotifications())
bobInstallationIDs := []string{bob1.installationID, bob2.installationID}
// Register bob1
err = bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
s.Require().NoError(err)
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob1.RetrieveAll()
if err != nil {
return err
}
registered, err := bob1.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
bobServers, err := bob1.GetPushNotificationsServers()
if err != nil {
return err
}
if len(bobServers) == 0 {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
bob1Servers, err := bob1.GetPushNotificationsServers()
s.Require().NoError(err)
// Register bob2
err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
s.Require().NoError(err)
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
s.Require().NoError(err)
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob2.RetrieveAll()
if err != nil {
return err
}
registered, err := bob2.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
bobServers, err := bob2.GetPushNotificationsServers()
if err != nil {
return err
}
if len(bobServers) == 0 {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
bob2Servers, err := bob2.GetPushNotificationsServers()
s.Require().NoError(err)
// Create one to one chat & send message
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
s.Require().NoError(alice.SaveChat(chat))
inputMessage := buildTestMessage(*chat)
response, err := alice.SendChatMessage(context.Background(), inputMessage)
s.Require().NoError(err)
messageIDString := response.Messages()[0].ID
messageID, err := hex.DecodeString(messageIDString[2:])
s.Require().NoError(err)
infoMap := make(map[string]*pushnotificationclient.PushNotificationInfo)
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
info, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
if err != nil {
return err
}
for _, i := range info {
infoMap[i.AccessToken] = i
}
// Check we have replies for both bob1 and bob2
if len(infoMap) != 2 {
return errors.New("info not fetched")
}
return nil
})
s.Require().Len(infoMap, 2)
// Check we have replies for both bob1 and bob2
var bob1Info, bob2Info *pushnotificationclient.PushNotificationInfo
bob1Info = infoMap[bob1Servers[0].AccessToken]
bob2Info = infoMap[bob2Servers[0].AccessToken]
s.Require().NotNil(bob1Info)
s.Require().Equal(bob1.installationID, bob1Info.InstallationID)
s.Require().Equal(bob1Servers[0].AccessToken, bob1Info.AccessToken)
s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey)
s.Require().NotNil(bob2Info)
s.Require().Equal(bob2.installationID, bob2Info.InstallationID)
s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken)
s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey)
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
s.Require().NoError(err)
s.Require().NotNil(retrievedNotificationInfo)
s.Require().Len(retrievedNotificationInfo, 2)
var sentNotification *pushnotificationclient.SentNotification
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob1.identity.PublicKey), bob1.installationID, messageID)
if err != nil {
return err
}
if sentNotification == nil {
return errors.New("sent notification not found")
}
if !sentNotification.Success {
return errors.New("sent notification not successul")
}
return nil
})
s.Require().NoError(err)
}
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationReply() {
bob := s.m
serverKey, err := crypto.GenerateKey()
s.Require().NoError(err)
server := s.newPushNotificationServer(s.shh, serverKey)
defer TearDownMessenger(&s.Suite, server)
alice := s.newMessenger(s.shh)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, alice)
s.Require().NoError(alice.EnableSendingPushNotifications())
bobInstallationIDs := []string{bob.installationID}
// Create public chat and join for both alice and bob
chat := CreatePublicChat("status", s.m.transport)
err = bob.SaveChat(chat)
s.Require().NoError(err)
_, err = bob.Join(chat)
s.Require().NoError(err)
err = alice.SaveChat(chat)
s.Require().NoError(err)
_, err = alice.Join(chat)
s.Require().NoError(err)
// Register bob
err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
s.Require().NoError(err)
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob.RetrieveAll()
if err != nil {
return err
}
registered, err := bob.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
bobServers, err := bob.GetPushNotificationsServers()
if err != nil {
return err
}
if len(bobServers) == 0 {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
bobServers, err := bob.GetPushNotificationsServers()
s.Require().NoError(err)
firstMessage := buildTestMessage(*chat)
firstMessage.Text = "Hello!"
response, err := bob.SendChatMessage(context.Background(), firstMessage)
s.Require().NoError(err)
messageIDString := response.Messages()[0].ID
_, err = WaitOnMessengerResponse(
alice,
func(r *MessengerResponse) bool {
for _, message := range r.Messages() {
if message.ID == messageIDString {
return true
}
}
return false
},
"no messages",
)
replyMessage := buildTestMessage(*chat)
replyMessage.Text = "Hello reply"
replyMessage.ResponseTo = messageIDString
response, err = alice.SendChatMessage(context.Background(), replyMessage)
s.Require().NoError(err)
messageIDString = response.Messages()[0].ID
messageID, err := hex.DecodeString(messageIDString[2:])
s.Require().NoError(err)
var bobInfo []*pushnotificationclient.PushNotificationInfo
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
bobInfo, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
if err != nil {
return err
}
// Check we have replies for bob
if len(bobInfo) != 1 {
return errors.New("info not fetched")
}
return nil
})
s.Require().NoError(err)
s.Require().NotEmpty(bobInfo)
s.Require().Equal(bob.installationID, bobInfo[0].InstallationID)
s.Require().Equal(bobServers[0].AccessToken, bobInfo[0].AccessToken)
s.Require().Equal(&bob.identity.PublicKey, bobInfo[0].PublicKey)
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
s.Require().NoError(err)
s.Require().NotNil(retrievedNotificationInfo)
s.Require().Len(retrievedNotificationInfo, 1)
var sentNotification *pushnotificationclient.SentNotification
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID)
if err != nil {
return err
}
if sentNotification == nil {
return errors.New("sent notification not found")
}
if !sentNotification.Success {
return errors.New("sent notification not successul")
}
return nil
})
s.Require().NoError(err)
}