status-im/status-go

View on GitHub
protocol/pushnotificationclient/persistence_test.go

Summary

Maintainability
A
0 mins
Test Coverage
package pushnotificationclient

import (
    "crypto/ecdsa"
    "testing"
    "time"

    "github.com/golang/protobuf/proto"
    "github.com/stretchr/testify/suite"

    "github.com/status-im/status-go/appdatabase"
    "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"
    "github.com/status-im/status-go/t/helpers"
)

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
    persistence *Persistence
}

func (s *SQLitePersistenceSuite) SetupTest() {
    db, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
    s.Require().NoError(err)
    err = sqlite.Migrate(db)
    s.Require().NoError(err)

    s.persistence = NewPersistence(db)
}

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)
}