mirror of
https://github.com/status-im/status-go.git
synced 2026-08-31 00:51:12 +00:00
Part of the Go project layout migration, item 27. Pure move plus import-path rewrite across 502 files. No API or behaviour change. `internal/` keeps the messaging application logic unimportable from outside the module, which is what the issue asks for -- status-go is consumed through the C-bindings in mobile/, not as a Go library. Things that had to follow the move, beyond the Go imports: - tools/generate-handlers/template.txt. messenger_handlers.go is generated, and the template hard-codes the imports it emits, so the generated file kept importing protocol/common and failed typecheck. - .gitignore. The ignore rule for that generated file was pinned to the old path; without moving it, a 1486-line generated file starts being tracked. - Makefile: the logosstorage and torrent test targets (both the archive packages and ./protocol itself), the archive README, migration-protocol. - scripts/run_unit_tests.sh, which names the protocol package explicitly to shard its tests. - scripts/cleanup_generated_files.sh and .golangci.yml. scripts/migration_check.sh also needed a fix that is not specific to this move: it validated every file the branch touched under a migration dir against the timestamp naming rule, and a directory rename makes every migration in it look newly added. It now excludes renames, so moving a migration is not mistaken for adding one. refs #7067
1366 lines
41 KiB
Go
1366 lines
41 KiB
Go
package protocol
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
"github.com/status-im/status-go/internal/crypto"
|
|
"github.com/status-im/status-go/internal/crypto/types"
|
|
"github.com/status-im/status-go/internal/protocol/common"
|
|
"github.com/status-im/status-go/internal/protocol/communities"
|
|
"github.com/status-im/status-go/internal/protocol/contacts"
|
|
"github.com/status-im/status-go/internal/protocol/protobuf"
|
|
"github.com/status-im/status-go/internal/protocol/pushnotificationclient"
|
|
"github.com/status-im/status-go/internal/protocol/pushnotificationserver"
|
|
"github.com/status-im/status-go/internal/protocol/requests"
|
|
"github.com/status-im/status-go/internal/testutils"
|
|
)
|
|
|
|
const (
|
|
bob1DeviceToken = "token-1"
|
|
bob2DeviceToken = "token-2"
|
|
testAPNTopic = "topic"
|
|
)
|
|
|
|
func TestMessengerPushNotificationSuite(t *testing.T) {
|
|
suite.Run(t, new(MessengerPushNotificationSuite))
|
|
}
|
|
|
|
type MessengerPushNotificationSuite struct {
|
|
MessengerBaseTestSuite
|
|
|
|
gorushMock *http.Server
|
|
gorushMockURL string
|
|
}
|
|
|
|
func (s *MessengerPushNotificationSuite) SetupSuite() {
|
|
// Create a new HTTP server as a mock for gorush
|
|
s.gorushMock = &http.Server{
|
|
Addr: "127.0.0.1:0",
|
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}),
|
|
ReadHeaderTimeout: 5 * time.Second,
|
|
}
|
|
listener, err := net.Listen("tcp", s.gorushMock.Addr)
|
|
s.Require().NoError(err)
|
|
|
|
// Get the actual server URL after binding to a port
|
|
s.gorushMockURL = fmt.Sprintf("http://%s", listener.Addr().String())
|
|
|
|
// Start the server in a goroutine
|
|
go func() {
|
|
err := s.gorushMock.Serve(listener)
|
|
s.Require().ErrorIs(err, http.ErrServerClosed)
|
|
}()
|
|
}
|
|
|
|
func (s *MessengerPushNotificationSuite) TearDownSuite() {
|
|
// Create a context with a timeout for graceful shutdown
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
// Shutdown the server
|
|
if err := s.gorushMock.Shutdown(ctx); err != nil {
|
|
s.T().Errorf("Server shutdown error: %v", err)
|
|
}
|
|
fmt.Println("HTTP server stopped")
|
|
}
|
|
|
|
func (s *MessengerPushNotificationSuite) SetupTest() {
|
|
s.MessengerBaseTestSuite.setupMessaging()
|
|
s.m = s.newMessenger()
|
|
s.privateKey = s.m.identity
|
|
}
|
|
|
|
func (s *MessengerPushNotificationSuite) newMessenger() *Messenger {
|
|
messenger, err := newRunningTestMessenger(s.T(), s.messagingEnv, testMessengerConfig{extraOptions: []Option{WithPushNotifications()}})
|
|
s.Require().NoError(err)
|
|
return messenger
|
|
}
|
|
|
|
func (s *MessengerPushNotificationSuite) newPushNotificationServer() (*Messenger, *pushnotificationserver.Server) {
|
|
privateKey, err := crypto.GenerateKey()
|
|
s.Require().NoError(err)
|
|
|
|
serverConfig := &pushnotificationserver.Config{
|
|
Enabled: true,
|
|
Logger: testutils.MustCreateTestLogger(),
|
|
Identity: privateKey,
|
|
GorushURL: s.gorushMockURL,
|
|
}
|
|
|
|
server := pushnotificationserver.New(serverConfig)
|
|
|
|
messenger, err := newRunningTestMessenger(s.T(), s.messagingEnv, testMessengerConfig{privateKey: privateKey, extraOptions: []Option{WithPushNotificationServer(server)}})
|
|
s.Require().NoError(err)
|
|
|
|
serverPersistence := pushnotificationserver.NewSQLitePersistence(messenger.database)
|
|
err = server.Start(serverPersistence, messenger.Messaging())
|
|
s.Require().NoError(err)
|
|
|
|
s.T().Cleanup(func() {
|
|
server.Stop()
|
|
})
|
|
|
|
return messenger, server
|
|
}
|
|
|
|
func (s *MessengerPushNotificationSuite) TestReceivePushNotification() {
|
|
bob1 := s.m
|
|
bob2, err := newRunningTestMessenger(s.T(), s.messagingEnv, testMessengerConfig{privateKey: s.m.identity, extraOptions: []Option{WithPushNotifications()}})
|
|
s.Require().NoError(err)
|
|
|
|
messenger, _ := s.newPushNotificationServer()
|
|
|
|
alice := s.newMessenger()
|
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
|
bobInstallationIDs := []string{bob1.installationID, bob2.installationID}
|
|
|
|
// Register bob1
|
|
err = bob1.AddPushNotificationsServer(context.Background(), &messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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(), &messenger.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
|
|
s.Require().NoError(err)
|
|
|
|
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
|
|
s.Require().NoError(err)
|
|
|
|
err = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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.getTimesource())
|
|
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)
|
|
|
|
// Each paired device advertises only its own installation's push info on the
|
|
// shared contact-code topic, and processing one such advertisement stamps a
|
|
// query timestamp that suppresses the authoritative server query for
|
|
// staleQueryTimeInSeconds. alice only subscribes to that topic when she starts
|
|
// the chat above, so a device that advertised earlier is missed. Re-advertise
|
|
// from both devices now that she is listening so she receives both.
|
|
s.Require().NoError(bob1.PublishIdentityImage())
|
|
s.Require().NoError(bob2.PublishIdentityImage())
|
|
|
|
infoMap := make(map[string]*pushnotificationclient.PushNotificationInfo)
|
|
err = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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
|
|
messenger, _ := s.newPushNotificationServer()
|
|
alice := s.newMessenger()
|
|
|
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
|
bobInstallationIDs := []string{bob.installationID}
|
|
|
|
// Register bob
|
|
err := bob.AddPushNotificationsServer(context.Background(), &messenger.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
|
|
s.Require().NoError(err)
|
|
|
|
// Add alice has a contact
|
|
aliceContact := &contacts.Contact{
|
|
ID: types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)),
|
|
EnsName: "Some Contact",
|
|
ContactRequestLocalState: contacts.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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.getTimesource())
|
|
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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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
|
|
messenger, _ := s.newPushNotificationServer()
|
|
alice := s.newMessenger()
|
|
// another contact to invalidate the token
|
|
frank := s.newMessenger()
|
|
|
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
|
bobInstallationIDs := []string{bob.installationID}
|
|
|
|
// Register bob
|
|
err := bob.AddPushNotificationsServer(context.Background(), &messenger.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
|
|
s.Require().NoError(err)
|
|
|
|
// Add alice has a contact
|
|
aliceContact := &contacts.Contact{
|
|
ID: types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)),
|
|
EnsName: "Some Contact",
|
|
ContactRequestLocalState: contacts.ContactRequestStateSent,
|
|
}
|
|
|
|
_, err = bob.AddContact(context.Background(), &requests.AddContact{ID: aliceContact.ID})
|
|
s.Require().NoError(err)
|
|
|
|
// Add frank has a contact
|
|
frankContact := &contacts.Contact{
|
|
ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)),
|
|
EnsName: "Some Contact",
|
|
ContactRequestLocalState: contacts.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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.getTimesource())
|
|
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 messenger
|
|
var info []*pushnotificationclient.PushNotificationInfo
|
|
err = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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 = &contacts.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 messenger and bob to check we are correctly registered
|
|
// Pull servers and check we registered
|
|
err = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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
|
|
messenger, _ := s.newPushNotificationServer()
|
|
alice := s.newMessenger()
|
|
|
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
|
|
|
// Register bob1
|
|
err := bob1.AddPushNotificationsServer(context.Background(), &messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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))
|
|
|
|
}
|
|
|
|
// TestContactCodeAdvertisementStoresPushInfoViaHandler is a regression test for the bug
|
|
// where Messenger.HandleContactCodeAdvertisement stopped forwarding the advertisement to
|
|
// the push notification client (the forwarding was dropped in the "generate handlers"
|
|
// refactor). The push info carried in a contact code is the channel light clients rely on
|
|
// to learn a contact's push registration — the live query response is ephemeral and is
|
|
// routinely missed by light clients — so the handler MUST forward it. Unlike TestContactCode
|
|
// (which calls the push client method directly and so never exercised this wiring), this
|
|
// test drives the messenger handler and asserts the recipient's push info is stored.
|
|
func (s *MessengerPushNotificationSuite) TestContactCodeAdvertisementStoresPushInfoViaHandler() {
|
|
bob1 := s.m
|
|
messenger, _ := s.newPushNotificationServer()
|
|
alice := s.newMessenger()
|
|
|
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
|
|
|
// Register bob1 with the push notification server so its contact code carries push info.
|
|
err := bob1.AddPushNotificationsServer(context.Background(), &messenger.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
|
|
s.Require().NoError(err)
|
|
|
|
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
|
|
s.Require().NoError(err)
|
|
|
|
err = testutils.RetryWithBackOff(func() error {
|
|
if _, err := messenger.RetrieveAll(); err != nil {
|
|
return err
|
|
}
|
|
if _, err := bob1.RetrieveAll(); err != nil {
|
|
return err
|
|
}
|
|
registered, err := bob1.RegisteredForPushNotifications()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !registered {
|
|
return errors.New("not registered")
|
|
}
|
|
return nil
|
|
})
|
|
s.Require().NoError(err)
|
|
|
|
contactCodeAdvertisement, err := bob1.buildContactCodeAdvertisement()
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(contactCodeAdvertisement)
|
|
s.Require().NotEmpty(contactCodeAdvertisement.PushNotificationInfo)
|
|
// The common case for periodic push-info re-advertisements has no chat identity attached;
|
|
// this is precisely the shape the buggy handler dropped (it bailed when ChatIdentity == nil).
|
|
s.Require().Nil(contactCodeAdvertisement.ChatIdentity)
|
|
|
|
// alice has never interacted with bob1, so it must not yet have bob1's push info.
|
|
infoBefore, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, nil)
|
|
s.Require().NoError(err)
|
|
s.Require().Empty(infoBefore)
|
|
|
|
// Drive the messenger handler (the wiring under test) rather than the push client directly.
|
|
state := &ReceivedMessageState{
|
|
CurrentMessageState: &CurrentMessageState{
|
|
PublicKey: &bob1.identity.PublicKey,
|
|
},
|
|
}
|
|
s.Require().NoError(alice.HandleContactCodeAdvertisement(context.Background(), state, contactCodeAdvertisement, nil))
|
|
|
|
// The handler must have forwarded the advertisement to the push client, which stores
|
|
// bob1's push registration so a subsequent send can dispatch a notification.
|
|
infoAfter, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, nil)
|
|
s.Require().NoError(err)
|
|
s.Require().NotEmpty(infoAfter, "messenger handler must forward contact-code push info to the push notification client")
|
|
}
|
|
|
|
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationMention() {
|
|
bob := s.m
|
|
messenger, _ := s.newPushNotificationServer()
|
|
alice := s.newMessenger()
|
|
|
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
|
bobInstallationIDs := []string{bob.installationID}
|
|
|
|
// Create public chat and join for both alice and bob
|
|
chat := CreatePublicChat("status", s.m.getTimesource())
|
|
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(), &messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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
|
|
messenger, server := s.newPushNotificationServer()
|
|
alice := s.newMessenger()
|
|
|
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
|
|
|
// Register bob
|
|
err := bob.AddPushNotificationsServer(context.Background(), &messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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(crypto.PubkeyToHex(&alice.identity.PublicKey), &alice.identity.PublicKey, alice.getTimesource())
|
|
|
|
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 = testutils.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 := createRequestToJoinCommunity(&s.Suite, community.ID(), alice, alicePassword, []string{aliceAddress1})
|
|
alice.communitiesManager.PermissionChecker = &testPermissionChecker{}
|
|
// 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, crypto.PubkeyToHex(&alice.identity.PublicKey))
|
|
s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State)
|
|
|
|
err = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.RetrieveAll()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = alice.RetrieveAll()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if server.SentRequests != 1 {
|
|
return errors.New("request not sent")
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
s.Require().NoError(err)
|
|
|
|
}
|
|
|
|
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationPairedDevices() {
|
|
|
|
bob1 := s.m
|
|
bob2, err := newRunningTestMessenger(s.T(), s.messagingEnv, testMessengerConfig{privateKey: s.m.identity, extraOptions: []Option{WithPushNotifications()}})
|
|
s.Require().NoError(err)
|
|
|
|
messenger, _ := s.newPushNotificationServer()
|
|
alice := s.newMessenger()
|
|
|
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
|
bobInstallationIDs := []string{bob1.installationID, bob2.installationID}
|
|
|
|
// Register bob1
|
|
err = bob1.AddPushNotificationsServer(context.Background(), &messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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(), &messenger.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
|
|
s.Require().NoError(err)
|
|
|
|
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
|
|
s.Require().NoError(err)
|
|
|
|
err = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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.getTimesource())
|
|
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)
|
|
|
|
// Each paired device advertises only its own installation's push info on the
|
|
// shared contact-code topic, and processing one such advertisement stamps a
|
|
// query timestamp that suppresses the authoritative server query for
|
|
// staleQueryTimeInSeconds. alice only subscribes to that topic when she starts
|
|
// the chat above, so a device that advertised earlier is missed. Re-advertise
|
|
// from both devices now that she is listening so she receives both.
|
|
s.Require().NoError(bob1.PublishIdentityImage())
|
|
s.Require().NoError(bob2.PublishIdentityImage())
|
|
|
|
infoMap := make(map[string]*pushnotificationclient.PushNotificationInfo)
|
|
err = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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
|
|
messenger, _ := s.newPushNotificationServer()
|
|
alice := s.newMessenger()
|
|
|
|
s.Require().NoError(alice.EnableSendingPushNotifications())
|
|
bobInstallationIDs := []string{bob.installationID}
|
|
|
|
// Create public chat and join for both alice and bob
|
|
chat := CreatePublicChat("status", s.m.getTimesource())
|
|
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(), &messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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 = testutils.RetryWithBackOff(func() error {
|
|
_, err = messenger.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)
|
|
}
|