2
0
mirror of https://github.com/status-im/status-go.git synced 2025-02-25 05:05:46 +00:00
status-go/protocol/activity_center_persistence_test.go
Sean Hagstrom 717814e83f
fix!: ensure deleting message does not accidentally delete unrelated notifications ()
This change attempts to resolve an issue with notifications being deleted for users after another user deletes the last message inside a community.

The cause of the issue seemed to be related to how notifications could reference a message with the `LastMessage` field. And if the last message was deleted from a community, then the notifications that reference the last message via `LastMessage` field would also removed/deleted.

Moving forward, we will no longer use the `LastMessage` field as a way to detect notifications that need to be deleted, and we now only use the `Message` field of a notification, and we have adapted the creation of notifications to avoid using the `LastMessage` field.
2024-09-03 10:12:44 +01:00

1294 lines
39 KiB
Go

package protocol
import (
"strconv"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf"
)
func TestActivityCenterPersistence(t *testing.T) {
suite.Run(t, new(ActivityCenterPersistenceTestSuite))
}
type ActivityCenterPersistenceTestSuite struct {
suite.Suite
idCounter int
}
func (s *ActivityCenterPersistenceTestSuite) SetupTest() {
s.idCounter = 0
}
func currentMilliseconds() uint64 {
c := time.Now().UnixMilli()
return uint64(c)
}
func (s *ActivityCenterPersistenceTestSuite) createNotifications(p *sqlitePersistence, notifications []*ActivityCenterNotification) []*ActivityCenterNotification {
now := currentMilliseconds()
for index, notif := range notifications {
if notif.Timestamp == 0 {
notif.Timestamp = now
}
if len(notif.ID) == 0 {
s.idCounter++
notif.ID = types.HexBytes(strconv.Itoa(s.idCounter + index))
}
if notif.UpdatedAt == 0 {
notif.UpdatedAt = now
}
_, err := p.SaveActivityCenterNotification(notif, true)
s.Require().NoError(err, notif.ID)
}
// Fetches notifications to get an up-to-date slice.
var createdNotifications []*ActivityCenterNotification
for _, notif := range notifications {
n, err := p.GetActivityCenterNotificationByID(notif.ID)
s.Require().NoError(err, notif.ID)
createdNotifications = append(createdNotifications, n)
}
return createdNotifications
}
func (s *ActivityCenterPersistenceTestSuite) Test_DeleteActivityCenterNotificationsWhenEmpty() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
chat := CreatePublicChat("test-chat", &testTimeSource{})
message := common.NewMessage()
message.Text = "sample text"
chat.LastMessage = message
err = p.SaveChat(*chat)
s.Require().NoError(err)
s.createNotifications(p, []*ActivityCenterNotification{
{
Type: ActivityCenterNotificationTypeMention,
},
})
var count uint64
count, _ = p.ActivityCenterNotificationsCount([]ActivityCenterType{}, ActivityCenterQueryParamsReadUnread, false)
s.Require().Equal(uint64(1), count)
_, err = p.MarkActivityCenterNotificationsDeleted([]types.HexBytes{}, currentMilliseconds())
s.Require().NoError(err)
count, _ = p.ActivityCenterNotificationsCount([]ActivityCenterType{}, ActivityCenterQueryParamsReadUnread, false)
s.Require().Equal(uint64(1), count)
}
func (s *ActivityCenterPersistenceTestSuite) Test_DeleteActivityCenterNotificationsWithMultipleIds() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
chat := CreatePublicChat("test-chat", &testTimeSource{})
message := common.NewMessage()
message.Text = "sample text"
chat.LastMessage = message
err = p.SaveChat(*chat)
s.Require().NoError(err)
notifications := s.createNotifications(p, []*ActivityCenterNotification{
{Type: ActivityCenterNotificationTypeMention},
{Type: ActivityCenterNotificationTypeNewOneToOne},
{Type: ActivityCenterNotificationTypeNewOneToOne},
})
var count uint64
count, _ = p.ActivityCenterNotificationsCount([]ActivityCenterType{}, ActivityCenterQueryParamsReadUnread, false)
s.Require().Equal(uint64(3), count)
_, err = p.MarkActivityCenterNotificationsDeleted([]types.HexBytes{notifications[1].ID, notifications[2].ID}, currentMilliseconds())
s.Require().NoError(err)
count, _ = p.ActivityCenterNotificationsCount([]ActivityCenterType{}, ActivityCenterQueryParamsReadUnread, false)
s.Require().Equal(uint64(1), count)
}
func (s *ActivityCenterPersistenceTestSuite) Test_DeleteActivityCenterNotificationsForMessage() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
chat := CreatePublicChat("test-chat", &testTimeSource{})
err = p.SaveChat(*chat)
s.Require().NoError(err)
chat2 := CreatePublicChat("test-chat", &testTimeSource{})
err = p.SaveChat(*chat2)
s.Require().NoError(err)
messages := []*common.Message{
{
ID: "0x1",
ChatMessage: &protobuf.ChatMessage{},
LocalChatID: chat.ID,
},
{
ID: "0x2",
ChatMessage: &protobuf.ChatMessage{},
LocalChatID: chat.ID,
},
{
ChatMessage: &protobuf.ChatMessage{},
ID: "0x3",
},
}
err = p.SaveMessages(messages)
s.Require().NoError(err)
chat.LastMessage = messages[1]
err = p.SaveChat(*chat)
s.Require().NoError(err)
chatMessages, _, err := p.MessageByChatID(chat.ID, "", 2)
s.Require().NoError(err)
s.Require().Len(chatMessages, 2)
nID1 := types.HexBytes("1")
nID2 := types.HexBytes("2")
nID3 := types.HexBytes("3")
nID4 := types.HexBytes("4")
s.createNotifications(p, []*ActivityCenterNotification{
{
ID: nID1,
ChatID: chat.ID,
Type: ActivityCenterNotificationTypeMention,
Message: messages[0],
},
{
ID: nID2,
ChatID: chat.ID,
Type: ActivityCenterNotificationTypeMention,
Message: messages[1],
},
{
ID: nID3,
ChatID: chat.ID,
Type: ActivityCenterNotificationTypeMention,
},
{
ID: nID4,
Type: ActivityCenterNotificationTypeMention,
},
})
// Test: soft delete only the notifications that have Message.ID == messages[0].ID.
_, err = p.DeleteActivityCenterNotificationForMessage(chat.ID, messages[0].ID, currentMilliseconds())
s.Require().NoError(err)
notif, err := p.GetActivityCenterNotificationByID(nID1)
s.Require().NoError(err)
s.Require().NotNil(notif)
s.Require().True(notif.Deleted)
s.Require().True(notif.Dismissed)
s.Require().True(notif.Read)
// Other notifications are not affected.
for _, id := range []types.HexBytes{nID2, nID3, nID4} {
notif, err = p.GetActivityCenterNotificationByID(id)
s.Require().NoError(err)
s.Require().NotNil(notif)
s.Require().False(notif.Deleted, notif.ID)
s.Require().False(notif.Dismissed, notif.ID)
s.Require().False(notif.Read, notif.ID)
}
// Test: soft delete the notifications that have Message.ID == messages[1].ID
_, err = p.DeleteActivityCenterNotificationForMessage(chat.ID, messages[1].ID, currentMilliseconds())
s.Require().NoError(err)
notif, err = p.GetActivityCenterNotificationByID(nID2)
s.Require().NoError(err)
s.Require().NotNil(notif)
s.Require().True(notif.Deleted, notif.ID)
s.Require().True(notif.Dismissed, notif.ID)
s.Require().True(notif.Read, notif.ID)
// Check that notifications with LastMessage.ID == messages[1].ID will remain.
notif, err = p.GetActivityCenterNotificationByID(nID3)
s.Require().NoError(err)
s.Require().NotNil(notif)
s.Require().False(notif.Deleted, notif.ID)
s.Require().False(notif.Dismissed, notif.ID)
s.Require().False(notif.Read, notif.ID)
notif, err = p.GetActivityCenterNotificationByID(nID4)
s.Require().NoError(err)
s.Require().NotNil(notif)
s.Require().False(notif.Deleted)
s.Require().False(notif.Dismissed)
s.Require().False(notif.Read)
// Test: don't do anything if passed a chat and message without notifications.
_, err = p.DeleteActivityCenterNotificationForMessage(chat2.ID, messages[2].ID, currentMilliseconds())
s.Require().NoError(err)
}
func (s *ActivityCenterPersistenceTestSuite) Test_DeleteActivityCenterNotificationsForMessage_LastMessage() {
// Create the temporary test-database that will be used to store chats, messages, and notifications
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
// Create and save the public chat that will be used to group our test messages
chat := CreatePublicChat("test-chat", &testTimeSource{})
err = p.SaveChat(*chat)
s.Require().NoError(err)
// Define multiple test messages for our chat so we can emulate a chat with a latest message.
messages := []*common.Message{
{
ID: "0x1",
ChatMessage: &protobuf.ChatMessage{},
LocalChatID: chat.ID,
},
{
ID: "0x2",
ChatMessage: &protobuf.ChatMessage{},
LocalChatID: chat.ID,
},
}
err = p.SaveMessages(messages)
s.Require().NoError(err)
chat.LastMessage = messages[1]
err = p.SaveChat(*chat)
s.Require().NoError(err)
chatMessages, _, err := p.MessageByChatID(chat.ID, "", 2)
s.Require().NoError(err)
s.Require().Len(chatMessages, 2)
// Define multiple notifications of different types to emulate the removal of notifications when deleting a message
nID1 := types.HexBytes("1")
nID2 := types.HexBytes("2")
nID3 := types.HexBytes("3")
nID4 := types.HexBytes("4")
s.createNotifications(p, []*ActivityCenterNotification{
{
ID: nID1,
ChatID: chat.ID,
Type: ActivityCenterNotificationTypeMention,
Message: messages[0],
LastMessage: messages[1],
},
{
ID: nID2,
ChatID: chat.ID,
Type: ActivityCenterNotificationTypeMention,
Message: messages[1],
},
{
ID: nID3,
ChatID: chat.ID,
Type: ActivityCenterNotificationTypeNewOneToOne,
LastMessage: messages[1],
},
{
ID: nID4,
ChatID: chat.ID,
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
LastMessage: messages[1],
},
})
// Action: soft delete notifications related to a chat and message
_, err = p.DeleteActivityCenterNotificationForMessage(chat.ID, messages[1].ID, currentMilliseconds())
s.Require().NoError(err)
// Test: check that notifications unrelated to the message are not affected.
notif, err := p.GetActivityCenterNotificationByID(nID1)
s.Require().NoError(err)
s.Require().NotNil(notif)
s.Require().False(notif.Deleted, notif.ID)
s.Require().False(notif.Dismissed, notif.ID)
s.Require().False(notif.Read, notif.ID)
// Test: check notifications directly related to the message are soft deleted
notif, err = p.GetActivityCenterNotificationByID(nID2)
s.Require().NoError(err)
s.Require().NotNil(notif)
s.Require().True(notif.Deleted)
s.Require().True(notif.Dismissed)
s.Require().True(notif.Read)
// Test: check NewOneToOne or NewPrivateGroupChat notifications that are indirectly related to the message are soft deleted
for _, id := range []types.HexBytes{nID3, nID4} {
notif, err = p.GetActivityCenterNotificationByID(id)
s.Require().NoError(err)
s.Require().NotNil(notif)
s.Require().False(notif.Deleted)
s.Require().False(notif.Dismissed)
s.Require().False(notif.Read)
}
}
func (s *ActivityCenterPersistenceTestSuite) Test_AcceptActivityCenterNotificationsForInvitesFromUser() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
nID1 := types.HexBytes("1")
nID2 := types.HexBytes("2")
nID3 := types.HexBytes("3")
nID4 := types.HexBytes("4")
userPublicKey := "zQ3sh"
notifications := []*ActivityCenterNotification{
{
ID: nID1,
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Timestamp: 1,
},
{
ID: nID2,
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Timestamp: 1,
Author: userPublicKey,
},
{
ID: nID3,
Type: ActivityCenterNotificationTypeMention,
Timestamp: 1,
Author: userPublicKey,
},
{
ID: nID4,
Timestamp: 1,
Type: ActivityCenterNotificationTypeMention,
},
}
var notif *ActivityCenterNotification
for _, notif = range notifications {
_, err = p.SaveActivityCenterNotification(notif, true)
s.Require().NoError(err, notif.ID)
}
// Only notifications of type new private group chat and with Author equal to
// userPublicKey should be marked as accepted & read.
_, err = p.GetActivityCenterNotificationByID(nID2)
s.Require().NoError(err)
s.Require().False(notifications[0].Accepted)
s.Require().False(notifications[0].Read)
notifications, err = p.AcceptActivityCenterNotificationsForInvitesFromUser(userPublicKey, 1)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
s.Require().Equal(nID2, notifications[0].ID)
notif, err = p.GetActivityCenterNotificationByID(nID2)
s.Require().NoError(err)
s.Require().True(notif.Accepted)
s.Require().True(notif.Read)
// Deleted notifications are ignored.
notif = &ActivityCenterNotification{
ID: types.HexBytes("99"),
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Timestamp: 1,
Author: userPublicKey,
Deleted: true,
}
_, err = p.SaveActivityCenterNotification(notif, true)
s.Require().NoError(err)
_, err = p.AcceptActivityCenterNotificationsForInvitesFromUser(userPublicKey, currentMilliseconds())
s.Require().NoError(err)
notif, err = p.GetActivityCenterNotificationByID(notif.ID)
s.Require().NoError(err)
s.Require().False(notif.Accepted)
s.Require().False(notif.Read)
s.Require().True(notif.Deleted)
// Dismissed notifications are ignored.
notif = &ActivityCenterNotification{
ID: types.HexBytes("100"),
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Timestamp: 1,
Author: userPublicKey,
Dismissed: true,
}
_, err = p.SaveActivityCenterNotification(notif, true)
s.Require().NoError(err)
_, err = p.AcceptActivityCenterNotificationsForInvitesFromUser(userPublicKey, currentMilliseconds())
s.Require().NoError(err)
notif, err = p.GetActivityCenterNotificationByID(notif.ID)
s.Require().NoError(err)
s.Require().False(notif.Accepted)
s.Require().False(notif.Read)
s.Require().True(notif.Dismissed)
}
func (s *ActivityCenterPersistenceTestSuite) Test_GetToProcessActivityCenterNotificationIds() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
notifications := s.createNotifications(p, []*ActivityCenterNotification{
{
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Deleted: true,
},
{
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Dismissed: true,
},
{
Type: ActivityCenterNotificationTypeMention,
Accepted: true,
},
{
Type: ActivityCenterNotificationTypeMention,
},
})
ids, err := p.GetToProcessActivityCenterNotificationIds()
s.Require().NoError(err)
s.Require().Len(ids, 1)
s.Require().Equal(notifications[3].ID, types.HexBytes(ids[0]))
}
func (s *ActivityCenterPersistenceTestSuite) Test_HasPendingNotificationsForChat() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
chat := CreatePublicChat("test-chat", &testTimeSource{})
err = p.SaveChat(*chat)
s.Require().NoError(err)
// Test: there are no notifications.
result, err := p.HasPendingNotificationsForChat(chat.ID)
s.Require().NoError(err)
s.Require().False(result)
// Test: there are only deleted, dismissed or accepted notifications,
// therefore, no pending notifications.
s.createNotifications(p, []*ActivityCenterNotification{
{
ChatID: chat.ID,
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Deleted: true,
},
{
ChatID: chat.ID,
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Dismissed: true,
},
{
ChatID: chat.ID,
Type: ActivityCenterNotificationTypeMention,
Accepted: true,
},
})
result, err = p.HasPendingNotificationsForChat(chat.ID)
s.Require().NoError(err)
s.Require().False(result)
// Test: there's one pending notification.
notif := &ActivityCenterNotification{
ID: types.HexBytes("99"),
ChatID: chat.ID,
Type: ActivityCenterNotificationTypeCommunityRequest,
Timestamp: 1,
}
_, err = p.SaveActivityCenterNotification(notif, true)
s.Require().NoError(err)
result, err = p.HasPendingNotificationsForChat(chat.ID)
s.Require().NoError(err)
s.Require().True(result)
}
func (s *ActivityCenterPersistenceTestSuite) Test_DismissAllActivityCenterNotificationsFromUser() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
publicKey := "0x04"
notifications := s.createNotifications(p, []*ActivityCenterNotification{
{
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Author: publicKey,
Deleted: true,
},
{
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Author: publicKey,
Dismissed: true,
},
{
Type: ActivityCenterNotificationTypeMention,
Author: publicKey,
Accepted: true,
},
{
Type: ActivityCenterNotificationTypeMention,
Author: "0x09",
},
{
Type: ActivityCenterNotificationTypeMention,
Author: publicKey,
},
})
_, err = p.DismissAllActivityCenterNotificationsFromUser(publicKey, 1)
s.Require().NoError(err)
// Ignores already soft deleted.
notif, err := p.GetActivityCenterNotificationByID(notifications[0].ID)
s.Require().NoError(err)
s.Require().False(notif.Accepted)
s.Require().False(notif.Read)
s.Require().False(notif.Dismissed)
s.Require().True(notif.Deleted)
// Ignores already dismissed.
notif, err = p.GetActivityCenterNotificationByID(notifications[1].ID)
s.Require().NoError(err)
s.Require().False(notif.Accepted)
s.Require().False(notif.Read)
s.Require().True(notif.Dismissed)
s.Require().False(notif.Deleted)
// Ignores already accepted.
notif, err = p.GetActivityCenterNotificationByID(notifications[2].ID)
s.Require().NoError(err)
s.Require().True(notif.Accepted)
s.Require().False(notif.Read)
s.Require().False(notif.Dismissed)
s.Require().False(notif.Deleted)
// Ignores notification from different author.
notif, err = p.GetActivityCenterNotificationByID(notifications[3].ID)
s.Require().NoError(err)
s.Require().False(notif.Accepted)
s.Require().False(notif.Read)
s.Require().False(notif.Dismissed)
s.Require().False(notif.Deleted)
// Finally, dismiss and mark as read this one notification.
notif, err = p.GetActivityCenterNotificationByID(notifications[4].ID)
s.Require().NoError(err)
s.Require().False(notif.Accepted)
s.Require().True(notif.Read)
s.Require().True(notif.Dismissed)
s.Require().False(notif.Deleted)
}
func (s *ActivityCenterPersistenceTestSuite) Test_DismissAllActivityCenterNotificationsFromChatID() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
chatID := "0x99"
notifications := s.createNotifications(p, []*ActivityCenterNotification{
{
ChatID: chatID,
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Deleted: true,
},
{
ChatID: chatID,
Type: ActivityCenterNotificationTypeNewPrivateGroupChat,
Dismissed: true,
},
{
ChatID: chatID,
Type: ActivityCenterNotificationTypeMention,
Accepted: true,
},
{
Type: ActivityCenterNotificationTypeMention,
},
{
ChatID: chatID,
Type: ActivityCenterNotificationTypeContactRequest,
},
{
ChatID: chatID,
Type: ActivityCenterNotificationTypeMention,
},
})
_, err = p.DismissAllActivityCenterNotificationsFromChatID(chatID, currentMilliseconds())
s.Require().NoError(err)
// Ignores already soft deleted.
notif, err := p.GetActivityCenterNotificationByID(notifications[0].ID)
s.Require().NoError(err)
s.Require().False(notif.Accepted)
s.Require().False(notif.Read)
s.Require().False(notif.Dismissed)
s.Require().True(notif.Deleted)
// Do not ignore already dismissed, because notifications can become
// read/unread AND dismissed, and the method should still update the Read
// column.
notif, err = p.GetActivityCenterNotificationByID(notifications[1].ID)
s.Require().NoError(err)
s.Require().False(notif.Accepted)
s.Require().True(notif.Read)
s.Require().True(notif.Dismissed)
s.Require().False(notif.Deleted)
// Ignores already accepted.
notif, err = p.GetActivityCenterNotificationByID(notifications[2].ID)
s.Require().NoError(err)
s.Require().True(notif.Accepted)
s.Require().False(notif.Read)
s.Require().False(notif.Dismissed)
s.Require().False(notif.Deleted)
// Ignores notification from different chat.
notif, err = p.GetActivityCenterNotificationByID(notifications[3].ID)
s.Require().NoError(err)
s.Require().False(notif.Accepted)
s.Require().False(notif.Read)
s.Require().False(notif.Dismissed)
s.Require().False(notif.Deleted)
// Ignores contact request notifications.
notif, err = p.GetActivityCenterNotificationByID(notifications[4].ID)
s.Require().NoError(err)
s.Require().False(notif.Accepted)
s.Require().False(notif.Read)
s.Require().False(notif.Dismissed)
s.Require().False(notif.Deleted)
// Finally, dismiss and mark as read this one notification.
notif, err = p.GetActivityCenterNotificationByID(notifications[5].ID)
s.Require().NoError(err)
s.Require().False(notif.Accepted)
s.Require().True(notif.Read)
s.Require().True(notif.Dismissed)
s.Require().False(notif.Deleted)
}
func (s *ActivityCenterPersistenceTestSuite) Test_ActiveContactRequestNotification() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
chat := CreatePublicChat("test-chat", &testTimeSource{})
err = p.SaveChat(*chat)
s.Require().NoError(err)
contactID := "0x99"
// Test: ignores deleted/dismissed/accepted notifications, as well as
// notifications not associated to any chat.
s.createNotifications(p, []*ActivityCenterNotification{
{
ChatID: chat.ID,
Author: contactID,
Type: ActivityCenterNotificationTypeContactRequest,
Deleted: true,
},
{
ChatID: chat.ID,
Author: contactID,
Type: ActivityCenterNotificationTypeContactRequest,
Dismissed: true,
},
{
ChatID: chat.ID,
Author: contactID,
Type: ActivityCenterNotificationTypeContactRequest,
Accepted: true,
},
})
notif, err := p.ActiveContactRequestNotification(contactID)
s.Require().NoError(err)
s.Require().Nil(notif)
// Test: Ignores notifications that are not contact requests.
s.createNotifications(p, []*ActivityCenterNotification{
{ChatID: chat.ID, Author: contactID, Type: ActivityCenterNotificationTypeCommunityInvitation},
{ChatID: chat.ID, Author: contactID, Type: ActivityCenterNotificationTypeCommunityKicked},
{ChatID: chat.ID, Author: contactID, Type: ActivityCenterNotificationTypeCommunityMembershipRequest},
{ChatID: chat.ID, Author: contactID, Type: ActivityCenterNotificationTypeCommunityRequest},
{ChatID: chat.ID, Author: contactID, Type: ActivityCenterNotificationTypeContactVerification},
{ChatID: chat.ID, Author: contactID, Type: ActivityCenterNotificationTypeMention},
{ChatID: chat.ID, Author: contactID, Type: ActivityCenterNotificationTypeNewOneToOne},
{ChatID: chat.ID, Author: contactID, Type: ActivityCenterNotificationTypeNewPrivateGroupChat},
{ChatID: chat.ID, Author: contactID, Type: ActivityCenterNotificationTypeReply},
})
notif, err = p.ActiveContactRequestNotification(contactID)
s.Require().NoError(err)
s.Require().Nil(notif)
// Test: Returns one, and only one contact request notification for the
// contact under test.
s.createNotifications(p, []*ActivityCenterNotification{
{ChatID: chat.ID, Author: contactID, Type: ActivityCenterNotificationTypeContactRequest},
})
notif, err = p.ActiveContactRequestNotification(contactID)
s.Require().NoError(err)
s.Require().NotNil(notif)
s.Require().Equal(ActivityCenterNotificationTypeContactRequest, notif.Type)
// Test: In case there's more than one notification, return the most recent
// one according to the notification's timestamp.
expectedID := types.HexBytes("667")
t1 := currentMilliseconds()
t2 := currentMilliseconds()
s.createNotifications(p, []*ActivityCenterNotification{
{
ID: expectedID,
Timestamp: t2 + 1,
ChatID: chat.ID,
Author: contactID,
Type: ActivityCenterNotificationTypeContactRequest,
},
{
ID: types.HexBytes("666"),
Timestamp: t1,
ChatID: chat.ID,
Author: contactID,
Type: ActivityCenterNotificationTypeContactRequest,
},
})
notif, err = p.ActiveContactRequestNotification(contactID)
s.Require().NoError(err)
s.Require().Equal(expectedID, notif.ID)
}
func (s *ActivityCenterPersistenceTestSuite) Test_UnreadActivityCenterNotificationsCount() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
s.createNotifications(p, []*ActivityCenterNotification{
{Type: ActivityCenterNotificationTypeMention, Read: true},
{Type: ActivityCenterNotificationTypeNewOneToOne, Deleted: true},
{Type: ActivityCenterNotificationTypeMention, Dismissed: true},
{Type: ActivityCenterNotificationTypeCommunityRequest, Accepted: true},
{Type: ActivityCenterNotificationTypeContactRequest},
})
// Test: Ignore soft deleted and accepted.
count, err := p.ActivityCenterNotificationsCount([]ActivityCenterType{}, ActivityCenterQueryParamsReadUnread, true)
s.Require().NoError(err)
s.Require().Equal(uint64(3), count)
}
func (s *ActivityCenterPersistenceTestSuite) Test_UnreadAndAcceptedActivityCenterNotificationsCount() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
s.createNotifications(p, []*ActivityCenterNotification{
{Type: ActivityCenterNotificationTypeMention, Read: true},
{Type: ActivityCenterNotificationTypeNewOneToOne, Deleted: true},
{Type: ActivityCenterNotificationTypeMention, Dismissed: true},
{Type: ActivityCenterNotificationTypeCommunityRequest, Accepted: true},
{Type: ActivityCenterNotificationTypeContactRequest},
})
// Test: counts everything, except soft deleted notifications.
count, err := p.ActivityCenterNotificationsCount([]ActivityCenterType{}, ActivityCenterQueryParamsReadUnread, true)
s.Require().NoError(err)
s.Require().Equal(uint64(3), count)
// Test: counts everything, except soft deleted ones and limit by type.
count, err = p.ActivityCenterNotificationsCount([]ActivityCenterType{
ActivityCenterNotificationTypeContactRequest,
}, ActivityCenterQueryParamsReadUnread, true)
s.Require().NoError(err)
s.Require().Equal(uint64(1), count)
}
func (s *ActivityCenterPersistenceTestSuite) Test_ActivityCenterPersistence() {
nID1 := types.HexBytes([]byte("1"))
nID2 := types.HexBytes([]byte("2"))
nID3 := types.HexBytes([]byte("3"))
nID4 := types.HexBytes([]byte("4"))
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
chat := CreatePublicChat("test-chat", &testTimeSource{})
message := common.NewMessage()
message.Text = "sample text"
chat.LastMessage = message
err = p.SaveChat(*chat)
s.Require().NoError(err)
notification := &ActivityCenterNotification{
ID: nID1,
Type: ActivityCenterNotificationTypeNewOneToOne,
ChatID: chat.ID,
Timestamp: 1,
}
_, err = p.SaveActivityCenterNotification(notification, true)
s.Require().NoError(err)
cursor, notifications, err := p.ActivityCenterNotifications("", 2, []ActivityCenterType{}, ActivityCenterQueryParamsReadAll, false)
s.Require().NoError(err)
s.Require().Empty(cursor)
s.Require().Len(notifications, 1)
s.Require().Equal(chat.ID, notifications[0].ChatID)
s.Require().Equal(message, notifications[0].LastMessage)
// Add another notification
notification = &ActivityCenterNotification{
ID: nID2,
Type: ActivityCenterNotificationTypeNewOneToOne,
Timestamp: 2,
}
_, err = p.SaveActivityCenterNotification(notification, true)
s.Require().NoError(err)
cursor, notifications, err = p.ActivityCenterNotifications("", 1, []ActivityCenterType{}, ActivityCenterQueryParamsReadAll, false)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
s.Require().NotEmpty(cursor)
s.Require().Equal(nID2, notifications[0].ID)
// fetch next pagination
cursor, notifications, err = p.ActivityCenterNotifications(cursor, 1, []ActivityCenterType{}, ActivityCenterQueryParamsReadAll, false)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
s.Require().Empty(cursor)
s.Require().False(notifications[0].Read)
s.Require().Equal(nID1, notifications[0].ID)
// Check count
count, err := p.ActivityCenterNotificationsCount([]ActivityCenterType{}, ActivityCenterQueryParamsReadUnread, false)
s.Require().NoError(err)
s.Require().Equal(uint64(2), count)
var updatedAt uint64 = 1
// Mark first one as read
s.Require().NoError(p.MarkActivityCenterNotificationsRead([]types.HexBytes{nID1}, updatedAt))
count, err = p.ActivityCenterNotificationsCount([]ActivityCenterType{}, ActivityCenterQueryParamsReadUnread, false)
s.Require().NoError(err)
s.Require().Equal(uint64(1), count)
// Mark first one as unread
updatedAt++
_, err = p.MarkActivityCenterNotificationsUnread([]types.HexBytes{nID1}, updatedAt)
s.Require().NoError(err)
count, err = p.ActivityCenterNotificationsCount([]ActivityCenterType{}, ActivityCenterQueryParamsReadUnread, false)
s.Require().NoError(err)
s.Require().Equal(uint64(2), count)
// Mark all read
updatedAt++
s.Require().NoError(p.MarkAllActivityCenterNotificationsRead(updatedAt))
_, notifications, err = p.ActivityCenterNotifications(cursor, 2, []ActivityCenterType{}, ActivityCenterQueryParamsReadAll, false)
s.Require().NoError(err)
s.Require().Len(notifications, 2)
s.Require().Empty(cursor)
s.Require().True(notifications[0].Read)
s.Require().True(notifications[1].Read)
// Check count
count, err = p.ActivityCenterNotificationsCount([]ActivityCenterType{}, ActivityCenterQueryParamsReadUnread, false)
s.Require().NoError(err)
s.Require().Equal(uint64(0), count)
// Mark first one as accepted
updatedAt++
notifications, err = p.AcceptActivityCenterNotifications([]types.HexBytes{nID1}, updatedAt)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
_, notifications, err = p.ActivityCenterNotifications("", 2, []ActivityCenterType{}, ActivityCenterQueryParamsReadAll, false)
s.Require().NoError(err)
// It should not be returned anymore
s.Require().Len(notifications, 1)
// Mark last one as dismissed
updatedAt++
s.Require().NoError(p.DismissActivityCenterNotifications([]types.HexBytes{nID2}, updatedAt))
_, notifications, err = p.ActivityCenterNotifications("", 2, []ActivityCenterType{}, ActivityCenterQueryParamsReadAll, false)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
s.Require().True(notifications[0].Dismissed)
// Insert new notification
notification = &ActivityCenterNotification{
ID: nID3,
Type: ActivityCenterNotificationTypeNewOneToOne,
Timestamp: 3,
}
_, err = p.SaveActivityCenterNotification(notification, true)
s.Require().NoError(err)
// Mark all as accepted
updatedAt++
notifications, err = p.AcceptAllActivityCenterNotifications(updatedAt)
s.Require().NoError(err)
s.Require().Len(notifications, 2)
_, notifications, err = p.ActivityCenterNotifications("", 2, []ActivityCenterType{}, ActivityCenterQueryParamsReadAll, false)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
// Insert new notification
notification = &ActivityCenterNotification{
ID: nID4,
Type: ActivityCenterNotificationTypeNewOneToOne,
Timestamp: 4,
}
_, err = p.SaveActivityCenterNotification(notification, true)
s.Require().NoError(err)
// Mark all as dismissed
updatedAt++
s.Require().NoError(p.DismissAllActivityCenterNotifications(updatedAt))
_, notifications, err = p.ActivityCenterNotifications("", 2, []ActivityCenterType{}, ActivityCenterQueryParamsReadAll, false)
s.Require().NoError(err)
s.Require().Len(notifications, 2)
s.Require().True(notifications[0].Dismissed)
s.Require().True(notifications[1].Dismissed)
}
func (s *ActivityCenterPersistenceTestSuite) Test_ActivityCenterReadUnreadPagination() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
initialOrFinalCursor := ""
chat := CreatePublicChat("test-chat", &testTimeSource{})
message := common.NewMessage()
message.Text = "sample text"
chat.LastMessage = message
err = p.SaveChat(*chat)
s.Require().NoError(err)
nID1 := types.HexBytes("1")
nID2 := types.HexBytes("2")
nID3 := types.HexBytes("3")
nID4 := types.HexBytes("4")
nID5 := types.HexBytes("5")
allNotifications := []*ActivityCenterNotification{
{
ID: nID1,
Type: ActivityCenterNotificationTypeNewOneToOne,
ChatID: chat.ID,
Timestamp: 1,
},
{
ID: nID2,
Type: ActivityCenterNotificationTypeNewOneToOne,
ChatID: chat.ID,
Timestamp: 1,
},
{
ID: nID3,
Type: ActivityCenterNotificationTypeNewOneToOne,
ChatID: chat.ID,
Timestamp: 1,
},
{
ID: nID4,
Type: ActivityCenterNotificationTypeNewOneToOne,
ChatID: chat.ID,
Timestamp: 1,
},
{
ID: nID5,
Type: ActivityCenterNotificationTypeNewOneToOne,
ChatID: chat.ID,
Timestamp: 1,
},
}
for _, notification := range allNotifications {
_, err = p.SaveActivityCenterNotification(notification, true)
s.Require().NoError(err)
}
// Mark the notification as read
err = p.MarkActivityCenterNotificationsRead([]types.HexBytes{nID2}, currentMilliseconds())
s.Require().NoError(err)
err = p.MarkActivityCenterNotificationsRead([]types.HexBytes{nID4}, currentMilliseconds())
s.Require().NoError(err)
// Fetch UNREAD notifications, first page.
cursor, notifications, err := p.ActivityCenterNotifications(
initialOrFinalCursor,
1,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
ActivityCenterQueryParamsReadUnread,
false,
)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
s.Require().Equal(nID5, notifications[0].ID)
s.Require().NotEmpty(cursor)
// Fetch next pages.
cursor, notifications, err = p.ActivityCenterNotifications(
cursor,
1,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
ActivityCenterQueryParamsReadUnread,
false,
)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
s.Require().Equal(nID3, notifications[0].ID)
s.Require().NotEmpty(cursor)
cursor, notifications, err = p.ActivityCenterNotifications(
cursor,
1,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
ActivityCenterQueryParamsReadUnread,
false,
)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
s.Require().Equal(nID1, notifications[0].ID)
s.Require().Empty(cursor)
// Fetch READ notifications, first page.
cursor, notifications, err = p.ActivityCenterNotifications(
initialOrFinalCursor,
1,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
ActivityCenterQueryParamsReadRead,
false,
)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
s.Require().Equal(nID4, notifications[0].ID)
s.Require().NotEmpty(cursor)
// Fetch next page.
cursor, notifications, err = p.ActivityCenterNotifications(
cursor,
1,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
ActivityCenterQueryParamsReadRead,
false,
)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
s.Require().Equal(nID2, notifications[0].ID)
s.Require().Empty(cursor)
}
func (s *ActivityCenterPersistenceTestSuite) Test_ActivityCenterReadUnreadFilterByTypes() {
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
chat := CreatePublicChat("test-chat", &testTimeSource{})
message := common.NewMessage()
message.Text = "sample text"
chat.LastMessage = message
err = p.SaveChat(*chat)
s.Require().NoError(err)
initialCursor := ""
limit := uint64(3)
nID1 := types.HexBytes("1")
nID2 := types.HexBytes("2")
nID3 := types.HexBytes("3")
allNotifications := []*ActivityCenterNotification{
{
ID: nID1,
Type: ActivityCenterNotificationTypeMention,
ChatID: chat.ID,
Timestamp: 1,
},
{
ID: nID2,
Type: ActivityCenterNotificationTypeNewOneToOne,
ChatID: chat.ID,
Timestamp: 1,
},
{
ID: nID3,
Type: ActivityCenterNotificationTypeMention,
ChatID: chat.ID,
Timestamp: 1,
},
}
for _, notification := range allNotifications {
_, err = p.SaveActivityCenterNotification(notification, true)
s.Require().NoError(err)
}
// Don't filter by type if the array of types is empty.
_, notifications, err := p.ActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{},
ActivityCenterQueryParamsReadUnread,
false,
)
s.Require().NoError(err)
s.Require().Len(notifications, 3)
s.Require().Equal(nID3, notifications[0].ID)
s.Require().Equal(nID2, notifications[1].ID)
s.Require().Equal(nID1, notifications[2].ID)
_, notifications, err = p.ActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
ActivityCenterQueryParamsReadUnread,
false,
)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
s.Require().Equal(nID2, notifications[0].ID)
_, notifications, err = p.ActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{ActivityCenterNotificationTypeMention},
ActivityCenterQueryParamsReadUnread,
false,
)
s.Require().NoError(err)
s.Require().Len(notifications, 2)
s.Require().Equal(nID3, notifications[0].ID)
s.Require().Equal(nID1, notifications[1].ID)
_, notifications, err = p.ActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{ActivityCenterNotificationTypeMention, ActivityCenterNotificationTypeNewOneToOne},
ActivityCenterQueryParamsReadUnread,
false,
)
s.Require().NoError(err)
s.Require().Len(notifications, 3)
s.Require().Equal(nID3, notifications[0].ID)
s.Require().Equal(nID2, notifications[1].ID)
s.Require().Equal(nID1, notifications[2].ID)
// Mark all notifications as read.
for _, notification := range allNotifications {
err = p.MarkActivityCenterNotificationsRead([]types.HexBytes{notification.ID}, currentMilliseconds())
s.Require().NoError(err)
}
_, notifications, err = p.ActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
ActivityCenterQueryParamsReadRead,
false,
)
s.Require().NoError(err)
s.Require().Len(notifications, 1)
s.Require().Equal(nID2, notifications[0].ID)
_, notifications, err = p.ActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{ActivityCenterNotificationTypeMention},
ActivityCenterQueryParamsReadRead,
false,
)
s.Require().NoError(err)
s.Require().Len(notifications, 2)
s.Require().Equal(nID3, notifications[0].ID)
s.Require().Equal(nID1, notifications[1].ID)
}
func (s *ActivityCenterPersistenceTestSuite) Test_ActivityCenterReadUnread() {
nID1 := types.HexBytes("1")
nID2 := types.HexBytes("2")
db, err := openTestDB()
s.Require().NoError(err)
p := newSQLitePersistence(db)
chat := CreatePublicChat("test-chat", &testTimeSource{})
message := common.NewMessage()
message.Text = "sample text"
chat.LastMessage = message
err = p.SaveChat(*chat)
s.Require().NoError(err)
notification := &ActivityCenterNotification{
ID: nID1,
Type: ActivityCenterNotificationTypeNewOneToOne,
ChatID: chat.ID,
Timestamp: 1,
}
_, err = p.SaveActivityCenterNotification(notification, true)
s.Require().NoError(err)
notification = &ActivityCenterNotification{
ID: nID2,
Type: ActivityCenterNotificationTypeNewOneToOne,
ChatID: chat.ID,
Timestamp: 1,
}
_, err = p.SaveActivityCenterNotification(notification, true)
s.Require().NoError(err)
// Mark the notification as read
err = p.MarkActivityCenterNotificationsRead([]types.HexBytes{nID2}, currentMilliseconds())
s.Require().NoError(err)
cursor, notifications, err := p.ActivityCenterNotifications(
"",
2,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
ActivityCenterQueryParamsReadUnread,
false,
)
s.Require().NoError(err)
s.Require().Empty(cursor)
s.Require().Len(notifications, 1)
s.Require().Equal(nID1, notifications[0].ID)
cursor, notifications, err = p.ActivityCenterNotifications(
"",
2,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
ActivityCenterQueryParamsReadRead,
false,
)
s.Require().NoError(err)
s.Require().Empty(cursor)
s.Require().Len(notifications, 1)
s.Require().Equal(nID2, notifications[0].ID)
}