github.com/status-im/status-go@v1.1.0/protocol/pushnotificationclient/client_test.go (about)

     1  package pushnotificationclient
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/ecdsa"
     6  	"math/rand"
     7  	"testing"
     8  
     9  	"github.com/google/uuid"
    10  	"github.com/stretchr/testify/suite"
    11  
    12  	"github.com/status-im/status-go/appdatabase"
    13  	"github.com/status-im/status-go/eth-node/crypto"
    14  	"github.com/status-im/status-go/eth-node/crypto/ecies"
    15  	"github.com/status-im/status-go/eth-node/types"
    16  	"github.com/status-im/status-go/protocol/common"
    17  	"github.com/status-im/status-go/protocol/protobuf"
    18  	"github.com/status-im/status-go/protocol/sqlite"
    19  	"github.com/status-im/status-go/protocol/tt"
    20  	"github.com/status-im/status-go/t/helpers"
    21  )
    22  
    23  const testDeviceToken = "test-token"
    24  
    25  type ClientSuite struct {
    26  	suite.Suite
    27  	persistence    *Persistence
    28  	identity       *ecdsa.PrivateKey
    29  	installationID string
    30  	client         *Client
    31  }
    32  
    33  func TestClientSuite(t *testing.T) {
    34  	s := new(ClientSuite)
    35  	s.installationID = "c6ae4fde-bb65-11ea-b3de-0242ac130004"
    36  
    37  	suite.Run(t, s)
    38  }
    39  
    40  func (s *ClientSuite) SetupTest() {
    41  	db, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
    42  	s.Require().NoError(err)
    43  	err = sqlite.Migrate(db)
    44  	s.Require().NoError(err)
    45  
    46  	s.persistence = NewPersistence(db)
    47  
    48  	identity, err := crypto.GenerateKey()
    49  	s.Require().NoError(err)
    50  	s.identity = identity
    51  
    52  	config := &Config{
    53  		Identity:                   identity,
    54  		Logger:                     tt.MustCreateTestLogger(),
    55  		RemoteNotificationsEnabled: true,
    56  		InstallationID:             s.installationID,
    57  	}
    58  
    59  	s.client = New(s.persistence, config, nil, nil)
    60  }
    61  
    62  func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() {
    63  	mutedChatList := []string{"a", "b"}
    64  	blockedChatList := []string{"c", "d"}
    65  
    66  	// build chat lish hashes
    67  	var mutedChatListHashes [][]byte
    68  	for _, chatID := range mutedChatList {
    69  		mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID)))
    70  	}
    71  	// Build Blocked chat list hashes
    72  	var blockedChatListHashes [][]byte
    73  	for _, chatID := range blockedChatList {
    74  		blockedChatListHashes = append(blockedChatListHashes, common.Shake256([]byte(chatID)))
    75  	}
    76  
    77  	contactKey, err := crypto.GenerateKey()
    78  	s.Require().NoError(err)
    79  	contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey}
    80  
    81  	options := &RegistrationOptions{
    82  		ContactIDs:     contactIDs,
    83  		MutedChatIDs:   mutedChatList,
    84  		BlockedChatIDs: blockedChatList,
    85  	}
    86  
    87  	// Set random generator for uuid
    88  	var seed int64 = 1
    89  	uuid.SetRand(rand.New(rand.NewSource(seed))) // nolint: gosec
    90  
    91  	// Get token
    92  	expectedUUID := uuid.New().String()
    93  
    94  	// Reset random generator
    95  	uuid.SetRand(rand.New(rand.NewSource(seed))) // nolint: gosec
    96  
    97  	s.client.deviceToken = testDeviceToken
    98  	// Set reader
    99  	s.client.reader = bytes.NewReader([]byte(expectedUUID))
   100  
   101  	registration := &protobuf.PushNotificationRegistration{
   102  		Version:         1,
   103  		AccessToken:     expectedUUID,
   104  		DeviceToken:     testDeviceToken,
   105  		InstallationId:  s.installationID,
   106  		Enabled:         true,
   107  		MutedChatList:   mutedChatListHashes,
   108  		BlockedChatList: blockedChatListHashes,
   109  	}
   110  
   111  	actualMessage, err := s.client.buildPushNotificationRegistrationMessage(options)
   112  	s.Require().NoError(err)
   113  
   114  	s.Require().Equal(registration, actualMessage)
   115  }
   116  
   117  func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsOnly() {
   118  	mutedChatList := []string{"a", "b"}
   119  	publicChatList := []string{"c", "d"}
   120  	blockedChatList := []string{"e", "f"}
   121  
   122  	// build muted chat lish hashes
   123  	var mutedChatListHashes [][]byte
   124  	for _, chatID := range mutedChatList {
   125  		mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID)))
   126  	}
   127  
   128  	// build blocked chat lish hashes
   129  	var blockedChatListHashes [][]byte
   130  	for _, chatID := range blockedChatList {
   131  		blockedChatListHashes = append(blockedChatListHashes, common.Shake256([]byte(chatID)))
   132  	}
   133  
   134  	// build public chat lish hashes
   135  	var publicChatListHashes [][]byte
   136  	for _, chatID := range publicChatList {
   137  		publicChatListHashes = append(publicChatListHashes, common.Shake256([]byte(chatID)))
   138  	}
   139  
   140  	contactKey, err := crypto.GenerateKey()
   141  	s.Require().NoError(err)
   142  	contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey}
   143  	options := &RegistrationOptions{
   144  		ContactIDs:     contactIDs,
   145  		MutedChatIDs:   mutedChatList,
   146  		BlockedChatIDs: blockedChatList,
   147  		PublicChatIDs:  publicChatList,
   148  	}
   149  
   150  	// Set random generator for uuid
   151  	var seed int64 = 1
   152  	uuid.SetRand(rand.New(rand.NewSource(seed))) // nolint: gosec
   153  
   154  	// Get token
   155  	expectedUUID := uuid.New().String()
   156  
   157  	// set up reader
   158  	reader := bytes.NewReader([]byte(expectedUUID))
   159  
   160  	sharedKey, err := ecies.ImportECDSA(s.identity).GenerateShared(
   161  		ecies.ImportECDSAPublic(&contactKey.PublicKey),
   162  		accessTokenKeyLength,
   163  		accessTokenKeyLength,
   164  	)
   165  	s.Require().NoError(err)
   166  	// build encrypted token
   167  	encryptedToken, err := encryptAccessToken([]byte(expectedUUID), sharedKey, reader)
   168  	s.Require().NoError(err)
   169  
   170  	// Reset random generator
   171  	uuid.SetRand(rand.New(rand.NewSource(seed))) // nolint: gosec
   172  
   173  	s.client.config.AllowFromContactsOnly = true
   174  	s.client.deviceToken = testDeviceToken
   175  	// Set reader
   176  	s.client.reader = bytes.NewReader([]byte(expectedUUID))
   177  
   178  	registration := &protobuf.PushNotificationRegistration{
   179  		Version:                 1,
   180  		AccessToken:             expectedUUID,
   181  		DeviceToken:             testDeviceToken,
   182  		InstallationId:          s.installationID,
   183  		AllowFromContactsOnly:   true,
   184  		Enabled:                 true,
   185  		BlockedChatList:         blockedChatListHashes,
   186  		MutedChatList:           mutedChatListHashes,
   187  		AllowedKeyList:          [][]byte{encryptedToken},
   188  		AllowedMentionsChatList: publicChatListHashes,
   189  	}
   190  
   191  	actualMessage, err := s.client.buildPushNotificationRegistrationMessage(options)
   192  	s.Require().NoError(err)
   193  
   194  	s.Require().Equal(registration, actualMessage)
   195  }
   196  
   197  func (s *ClientSuite) TestHandleMessageScheduled() {
   198  	messageID := []byte("message-id")
   199  	chatID := "chat-id"
   200  	installationID1 := "1"
   201  	installationID2 := "2"
   202  	rawMessage := &common.RawMessage{
   203  		ID:                   types.EncodeHex(messageID),
   204  		SendPushNotification: true,
   205  		LocalChatID:          chatID,
   206  	}
   207  
   208  	event := &common.MessageEvent{
   209  		RawMessage: rawMessage,
   210  	}
   211  
   212  	s.Require().NoError(s.client.handleMessageScheduled(event))
   213  
   214  	key1, err := crypto.GenerateKey()
   215  	s.Require().NoError(err)
   216  
   217  	// First time, should notify
   218  	response, err := s.client.shouldNotifyOn(&key1.PublicKey, installationID1, messageID)
   219  	s.Require().NoError(err)
   220  	s.Require().True(response)
   221  
   222  	// Save notification
   223  	s.Require().NoError(s.client.notifiedOn(&key1.PublicKey, installationID1, messageID, chatID, protobuf.PushNotification_MESSAGE))
   224  
   225  	// Second time, should not notify
   226  	response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID1, messageID)
   227  	s.Require().NoError(err)
   228  	s.Require().False(response)
   229  
   230  	// Different installationID
   231  	response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID2, messageID)
   232  	s.Require().NoError(err)
   233  	s.Require().True(response)
   234  
   235  	key2, err := crypto.GenerateKey()
   236  	s.Require().NoError(err)
   237  	// different key, should notify
   238  	response, err = s.client.shouldNotifyOn(&key2.PublicKey, installationID1, messageID)
   239  	s.Require().NoError(err)
   240  	s.Require().True(response)
   241  
   242  	// non tracked message id
   243  	response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID1, []byte("not-existing"))
   244  	s.Require().NoError(err)
   245  	s.Require().False(response)
   246  }
   247  
   248  func (s *ClientSuite) TestShouldRefreshToken() {
   249  	key1, err := crypto.GenerateKey()
   250  	s.Require().NoError(err)
   251  	key2, err := crypto.GenerateKey()
   252  	s.Require().NoError(err)
   253  	key3, err := crypto.GenerateKey()
   254  	s.Require().NoError(err)
   255  	key4, err := crypto.GenerateKey()
   256  	s.Require().NoError(err)
   257  
   258  	// Contacts are added
   259  	s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey, &key3.PublicKey, &key4.PublicKey}, true, true))
   260  
   261  	// everything the same
   262  	s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, true, true))
   263  
   264  	// A contact is removed
   265  	s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey}, true, true))
   266  
   267  	// allow from contacts only is disabled
   268  	s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, true, false))
   269  
   270  	// allow from contacts only is enabled
   271  	s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, false, true))
   272  }
   273  
   274  func (s *ClientSuite) TestHandleMessageScheduledFromPairedDevice() {
   275  	messageID := []byte("message-id")
   276  	installationID1 := "1"
   277  
   278  	// Should return nil
   279  	response, err := s.client.shouldNotifyOn(&s.identity.PublicKey, installationID1, messageID)
   280  	s.Require().NoError(err)
   281  	s.Require().False(response)
   282  }