status-go/protocol/pushnotificationclient/persistence_test.go

335 lines
10 KiB
Go

package pushnotificationclient
import (
"crypto/ecdsa"
"io/ioutil"
"os"
"testing"
"time"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/suite"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/sqlite"
)
const (
testAccessToken = "token"
installationID1 = "installation-id-1"
installationID2 = "installation-id-2"
installationID3 = "installation-id-3"
)
func TestSQLitePersistenceSuite(t *testing.T) {
suite.Run(t, new(SQLitePersistenceSuite))
}
type SQLitePersistenceSuite struct {
suite.Suite
tmpFile *os.File
persistence *Persistence
}
func (s *SQLitePersistenceSuite) SetupTest() {
tmpFile, err := ioutil.TempFile("", "")
s.Require().NoError(err)
s.tmpFile = tmpFile
database, err := sqlite.Open(s.tmpFile.Name(), "", sqlite.ReducedKDFIterationsNumber)
s.Require().NoError(err)
s.persistence = NewPersistence(database)
}
func (s *SQLitePersistenceSuite) TearDownTest() {
_ = os.Remove(s.tmpFile.Name())
}
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveServer() {
key, err := crypto.GenerateKey()
s.Require().NoError(err)
server := &PushNotificationServer{
PublicKey: &key.PublicKey,
Registered: true,
RegisteredAt: 1,
AccessToken: testAccessToken,
}
s.Require().NoError(s.persistence.UpsertServer(server))
retrievedServers, err := s.persistence.GetServers()
s.Require().NoError(err)
s.Require().Len(retrievedServers, 1)
s.Require().True(retrievedServers[0].Registered)
s.Require().Equal(int64(1), retrievedServers[0].RegisteredAt)
s.Require().True(common.IsPubKeyEqual(retrievedServers[0].PublicKey, &key.PublicKey))
s.Require().Equal(testAccessToken, retrievedServers[0].AccessToken)
server.Registered = false
server.RegisteredAt = 2
s.Require().NoError(s.persistence.UpsertServer(server))
retrievedServers, err = s.persistence.GetServers()
s.Require().NoError(err)
s.Require().Len(retrievedServers, 1)
s.Require().False(retrievedServers[0].Registered)
s.Require().Equal(int64(2), retrievedServers[0].RegisteredAt)
s.Require().True(common.IsPubKeyEqual(retrievedServers[0].PublicKey, &key.PublicKey))
}
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() {
key1, err := crypto.GenerateKey()
s.Require().NoError(err)
key2, err := crypto.GenerateKey()
s.Require().NoError(err)
serverKey, err := crypto.GenerateKey()
s.Require().NoError(err)
infos := []*PushNotificationInfo{
{
PublicKey: &key1.PublicKey,
ServerPublicKey: &serverKey.PublicKey,
RetrievedAt: 1,
Version: 1,
AccessToken: testAccessToken,
InstallationID: installationID1,
},
{
PublicKey: &key1.PublicKey,
ServerPublicKey: &serverKey.PublicKey,
RetrievedAt: 1,
Version: 1,
AccessToken: testAccessToken,
InstallationID: installationID2,
},
{
PublicKey: &key1.PublicKey,
ServerPublicKey: &serverKey.PublicKey,
RetrievedAt: 1,
Version: 1,
AccessToken: testAccessToken,
InstallationID: installationID3,
},
{
PublicKey: &key2.PublicKey,
ServerPublicKey: &serverKey.PublicKey,
RetrievedAt: 1,
Version: 1,
AccessToken: testAccessToken,
InstallationID: installationID1,
},
{
PublicKey: &key2.PublicKey,
ServerPublicKey: &serverKey.PublicKey,
RetrievedAt: 1,
Version: 1,
AccessToken: testAccessToken,
InstallationID: installationID2,
},
{
PublicKey: &key2.PublicKey,
ServerPublicKey: &serverKey.PublicKey,
RetrievedAt: 1,
Version: 1,
AccessToken: testAccessToken,
InstallationID: installationID3,
},
}
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
retrievedInfos, err := s.persistence.GetPushNotificationInfo(&key1.PublicKey, []string{installationID1, installationID2})
s.Require().NoError(err)
s.Require().Len(retrievedInfos, 2)
}
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfoWithVersion() {
installationID := "installation-id-1"
key, err := crypto.GenerateKey()
s.Require().NoError(err)
serverKey1, err := crypto.GenerateKey()
s.Require().NoError(err)
serverKey2, err := crypto.GenerateKey()
s.Require().NoError(err)
infos := []*PushNotificationInfo{
{
PublicKey: &key.PublicKey,
ServerPublicKey: &serverKey1.PublicKey,
RetrievedAt: 1,
Version: 1,
AccessToken: testAccessToken,
InstallationID: installationID,
},
{
PublicKey: &key.PublicKey,
ServerPublicKey: &serverKey2.PublicKey,
RetrievedAt: 1,
Version: 1,
AccessToken: testAccessToken,
InstallationID: installationID,
},
}
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
retrievedInfos, err := s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID})
s.Require().NoError(err)
// We should retrieve both
s.Require().Len(retrievedInfos, 2)
s.Require().Equal(uint64(1), retrievedInfos[0].Version)
// Bump version
infos[0].Version = 2
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
retrievedInfos, err = s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID})
s.Require().NoError(err)
// Only one should be retrieved now
s.Require().Len(retrievedInfos, 1)
s.Require().Equal(uint64(2), retrievedInfos[0].Version)
// Lower version
infos[0].Version = 1
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
retrievedInfos, err = s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID})
s.Require().NoError(err)
s.Require().Len(retrievedInfos, 1)
s.Require().Equal(uint64(2), retrievedInfos[0].Version)
}
func (s *SQLitePersistenceSuite) TestNotifiedOnAndUpdateNotificationResponse() {
key, err := crypto.GenerateKey()
s.Require().NoError(err)
installationID := "installation-id"
messageID := []byte("message-id")
sentNotification := &SentNotification{
PublicKey: &key.PublicKey,
InstallationID: installationID,
MessageID: messageID,
LastTriedAt: time.Now().Unix(),
}
s.Require().NoError(s.persistence.UpsertSentNotification(sentNotification))
retrievedNotification, err := s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
s.Require().NoError(err)
s.Require().Equal(sentNotification, retrievedNotification)
retriableNotifications, err := s.persistence.GetRetriablePushNotifications()
s.Require().NoError(err)
s.Require().Len(retriableNotifications, 0)
response := &protobuf.PushNotificationReport{
Success: false,
Error: protobuf.PushNotificationReport_WRONG_TOKEN,
PublicKey: sentNotification.HashedPublicKey(),
InstallationId: installationID,
}
s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response))
// This notification should be retriable
retriableNotifications, err = s.persistence.GetRetriablePushNotifications()
s.Require().NoError(err)
s.Require().Len(retriableNotifications, 1)
sentNotification.Error = protobuf.PushNotificationReport_WRONG_TOKEN
retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
s.Require().NoError(err)
s.Require().Equal(sentNotification, retrievedNotification)
// Update with a successful notification
response = &protobuf.PushNotificationReport{
Success: true,
PublicKey: sentNotification.HashedPublicKey(),
InstallationId: installationID,
}
s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response))
sentNotification.Success = true
sentNotification.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE
retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
s.Require().NoError(err)
s.Require().Equal(sentNotification, retrievedNotification)
// This notification should not be retriable
retriableNotifications, err = s.persistence.GetRetriablePushNotifications()
s.Require().NoError(err)
s.Require().Len(retriableNotifications, 0)
// Update with a unsuccessful notification, it should be ignored
response = &protobuf.PushNotificationReport{
Success: false,
Error: protobuf.PushNotificationReport_WRONG_TOKEN,
PublicKey: sentNotification.HashedPublicKey(),
InstallationId: installationID,
}
s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response))
sentNotification.Success = true
sentNotification.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE
retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
s.Require().NoError(err)
s.Require().Equal(sentNotification, retrievedNotification)
}
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveRegistration() {
// Try with nil first
retrievedRegistration, retrievedContactIDs, err := s.persistence.GetLastPushNotificationRegistration()
s.Require().NoError(err)
s.Require().Nil(retrievedRegistration)
s.Require().Nil(retrievedContactIDs)
// Save & retrieve registration
registration := &protobuf.PushNotificationRegistration{
AccessToken: "test",
Version: 3,
}
key1, err := crypto.GenerateKey()
s.Require().NoError(err)
key2, err := crypto.GenerateKey()
s.Require().NoError(err)
key3, err := crypto.GenerateKey()
s.Require().NoError(err)
publicKeys := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}
s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration, publicKeys))
retrievedRegistration, retrievedContactIDs, err = s.persistence.GetLastPushNotificationRegistration()
s.Require().NoError(err)
s.Require().True(proto.Equal(registration, retrievedRegistration))
s.Require().Equal(publicKeys, retrievedContactIDs)
// Override and retrieve
registration.Version = 5
publicKeys = append(publicKeys, &key3.PublicKey)
s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration, publicKeys))
retrievedRegistration, retrievedContactIDs, err = s.persistence.GetLastPushNotificationRegistration()
s.Require().NoError(err)
s.Require().True(proto.Equal(registration, retrievedRegistration))
s.Require().Equal(publicKeys, retrievedContactIDs)
}