mirror of
https://github.com/status-im/status-go.git
synced 2025-01-20 11:40:29 +00:00
c49a0fc314
The test was flaky because of re-receiving the message, which resulted in multiple identical requestsToJoin being added to messengerResponse. We should in general avoid using arrays in messengerResponse and prefer maps for exactly this reason.
1168 lines
33 KiB
Go
1168 lines
33 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
|
|
_, err := s.m.Start()
|
|
s.Require().NoError(err)
|
|
}
|
|
|
|
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)
|
|
// start alice and enable sending push notifications
|
|
_, err = alice.Start()
|
|
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) 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)
|
|
// start alice and enable push notifications
|
|
_, err = alice.Start()
|
|
s.Require().NoError(err)
|
|
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)
|
|
_, err = frank.Start()
|
|
s.Require().NoError(err)
|
|
defer TearDownMessenger(&s.Suite, frank)
|
|
|
|
// start alice and enable push notifications
|
|
_, err = alice.Start()
|
|
s.Require().NoError(err)
|
|
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)
|
|
// start alice and enable sending push notifications
|
|
_, err = alice.Start()
|
|
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)
|
|
// start alice and enable sending push notifications
|
|
_, err = alice.Start()
|
|
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)
|
|
// start alice and enable sending push notifications
|
|
_, err = alice.Start()
|
|
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)
|
|
// start alice and enable sending push notifications
|
|
_, err = alice.Start()
|
|
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)
|
|
}
|