status-go/protocol/messenger_backup_test.go
Mohamed Javid 17c30ac532
fix_: Flaky TestBackupKeypairs test (#5811)
This commit fixes the flaky TestBackupKeypairs test.

The flakiness is due to the order in which the backup is processed. The test expects the profile keypair addresses first but sometimes, the non-profile keypair is processed before the profile keypair. This commit updates the test to ensure the received addresses exist in the expected.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-09-09 20:30:05 +05:30

1018 lines
32 KiB
Go

package protocol
import (
"context"
"encoding/json"
"fmt"
"reflect"
"testing"
"time"
"go.uber.org/zap"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
v1protocol "github.com/status-im/status-go/protocol/v1"
"github.com/status-im/status-go/protocol/wakusync"
"github.com/status-im/status-go/services/accounts/accountsevent"
"github.com/stretchr/testify/suite"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/images"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/multiaccounts/settings"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
)
func TestMessengerBackupSuite(t *testing.T) {
suite.Run(t, new(MessengerBackupSuite))
}
type MessengerBackupSuite struct {
MessengerBaseTestSuite
}
func (s *MessengerBackupSuite) TestBackupContacts() {
bob1 := s.m
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
// Create 2 contacts
contact1Key, err := crypto.GenerateKey()
s.Require().NoError(err)
contactID1 := types.EncodeHex(crypto.FromECDSAPub(&contact1Key.PublicKey))
_, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID1})
s.Require().NoError(err)
contact2Key, err := crypto.GenerateKey()
s.Require().NoError(err)
contactID2 := types.EncodeHex(crypto.FromECDSAPub(&contact2Key.PublicKey))
_, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID2})
s.Require().NoError(err)
s.Require().Len(bob1.Contacts(), 2)
actualContacts := bob1.Contacts()
if actualContacts[0].ID == contactID1 {
s.Require().Equal(actualContacts[0].ID, contactID1)
s.Require().Equal(actualContacts[1].ID, contactID2)
} else {
s.Require().Equal(actualContacts[0].ID, contactID2)
s.Require().Equal(actualContacts[1].ID, contactID1)
}
s.Require().Equal(ContactRequestStateSent, actualContacts[0].ContactRequestLocalState)
s.Require().Equal(ContactRequestStateSent, actualContacts[1].ContactRequestLocalState)
s.Require().True(actualContacts[0].added())
s.Require().True(actualContacts[1].added())
// Backup
clock, err := bob1.BackupData(context.Background())
s.Require().NoError(err)
// Safety check
s.Require().Len(bob2.Contacts(), 0)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
return r.BackupHandled
},
"no messages",
)
s.Require().NoError(err)
s.Require().Len(bob2.AddedContacts(), 2)
actualContacts = bob2.AddedContacts()
if actualContacts[0].ID == contactID1 {
s.Require().Equal(actualContacts[0].ID, contactID1)
s.Require().Equal(actualContacts[1].ID, contactID2)
} else {
s.Require().Equal(actualContacts[0].ID, contactID2)
s.Require().Equal(actualContacts[1].ID, contactID1)
}
s.Require().Equal(ContactRequestStateSent, actualContacts[0].ContactRequestLocalState)
s.Require().Equal(ContactRequestStateSent, actualContacts[1].ContactRequestLocalState)
lastBackup, err := bob1.lastBackup()
s.Require().NoError(err)
s.Require().NotEmpty(lastBackup)
s.Require().Equal(clock, lastBackup)
}
func (s *MessengerBackupSuite) TestBackupProfile() {
const bob1DisplayName = "bobby"
// Create bob1
bob1 := s.m
bobProfileKp := accounts.GetProfileKeypairForTest(true, false, false)
bobProfileKp.KeyUID = bob1.account.KeyUID
bobProfileKp.Accounts[0].KeyUID = bob1.account.KeyUID
err := bob1.settings.SaveOrUpdateKeypair(bobProfileKp)
s.Require().NoError(err)
err = bob1.SetDisplayName(bob1DisplayName)
s.Require().NoError(err)
bob1KeyUID := bob1.account.KeyUID
imagesExpected := fmt.Sprintf(`[{"keyUid":"%s","type":"large","uri":"data:image/png;base64,iVBORw0KGgoAAAANSUg=","width":240,"height":300,"fileSize":1024,"resizeTarget":240,"clock":0},{"keyUid":"%s","type":"thumbnail","uri":"data:image/jpeg;base64,/9j/2wCEAFA3PEY8MlA=","width":80,"height":80,"fileSize":256,"resizeTarget":80,"clock":0}]`,
bob1KeyUID, bob1KeyUID)
iis := images.SampleIdentityImages()
s.Require().NoError(bob1.multiAccounts.StoreIdentityImages(bob1KeyUID, iis, false))
bob1EnsUsernameDetail, err := bob1.saveEnsUsernameDetailProto(&protobuf.SyncEnsUsernameDetail{
Clock: 1,
Username: "bob1.eth",
ChainId: 1,
Removed: false,
})
s.Require().NoError(err)
profileShowcasePreferences := DummyProfileShowcasePreferences(false)
err = bob1.SetProfileShowcasePreferences(profileShowcasePreferences, false)
s.Require().NoError(err)
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
// Check bob1
storedBob1DisplayName, err := bob1.settings.DisplayName()
s.Require().NoError(err)
s.Require().Equal(bob1DisplayName, storedBob1DisplayName)
bob1Images, err := bob1.multiAccounts.GetIdentityImages(bob1KeyUID)
s.Require().NoError(err)
jBob1Images, err := json.Marshal(bob1Images)
s.Require().NoError(err)
s.Require().Equal(imagesExpected, string(jBob1Images))
bob1EnsUsernameDetails, err := bob1.getEnsUsernameDetails()
s.Require().NoError(err)
s.Require().Equal(1, len(bob1EnsUsernameDetails))
bob1ProfileShowcasePreferences, err := bob1.GetProfileShowcasePreferences()
s.Require().NoError(err)
s.Require().NotNil(bob1ProfileShowcasePreferences)
s.Require().Greater(bob1ProfileShowcasePreferences.Clock, uint64(0))
profileShowcasePreferences.Clock = bob1ProfileShowcasePreferences.Clock // override clock for simpler comparison
s.Require().True(reflect.DeepEqual(profileShowcasePreferences, bob1ProfileShowcasePreferences))
// Check bob2
storedBob2DisplayName, err := bob2.settings.DisplayName()
s.Require().NoError(err)
s.Require().Equal(DefaultProfileDisplayName, storedBob2DisplayName)
var expectedEmpty []*images.IdentityImage
bob2Images, err := bob2.multiAccounts.GetIdentityImages(bob1KeyUID)
s.Require().NoError(err)
s.Require().Equal(expectedEmpty, bob2Images)
bob2EnsUsernameDetails, err := bob2.getEnsUsernameDetails()
s.Require().NoError(err)
s.Require().Equal(0, len(bob2EnsUsernameDetails))
bob2ProfileShowcasePreferences, err := bob2.GetProfileShowcasePreferences()
s.Require().NoError(err)
s.Require().NotNil(bob2ProfileShowcasePreferences)
s.Require().Equal(uint64(0), bob2ProfileShowcasePreferences.Clock)
s.Require().Len(bob2ProfileShowcasePreferences.Communities, 0)
s.Require().Len(bob2ProfileShowcasePreferences.Accounts, 0)
s.Require().Len(bob2ProfileShowcasePreferences.Collectibles, 0)
s.Require().Len(bob2ProfileShowcasePreferences.VerifiedTokens, 0)
s.Require().Len(bob2ProfileShowcasePreferences.UnverifiedTokens, 0)
s.Require().Len(bob2ProfileShowcasePreferences.SocialLinks, 0)
// Backup
clock, err := bob1.BackupData(context.Background())
s.Require().NoError(err)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
return r.BackupHandled
},
"no messages",
)
s.Require().NoError(err)
// Check bob2
storedBob2DisplayName, err = bob2.settings.DisplayName()
s.Require().NoError(err)
s.Require().Equal(bob1DisplayName, storedBob2DisplayName)
bob2Images, err = bob2.multiAccounts.GetIdentityImages(bob1KeyUID)
s.Require().NoError(err)
s.Require().Equal(2, len(bob2Images))
s.Require().Equal(bob2Images[0].Payload, bob1Images[0].Payload)
s.Require().Equal(bob2Images[1].Payload, bob1Images[1].Payload)
bob2EnsUsernameDetails, err = bob2.getEnsUsernameDetails()
s.Require().NoError(err)
s.Require().Equal(1, len(bob2EnsUsernameDetails))
s.Require().Equal(bob1EnsUsernameDetail, bob2EnsUsernameDetails[0])
bob2ProfileShowcasePreferences, err = bob1.GetProfileShowcasePreferences()
s.Require().NoError(err)
s.Require().True(reflect.DeepEqual(bob1ProfileShowcasePreferences, bob2ProfileShowcasePreferences))
lastBackup, err := bob1.lastBackup()
s.Require().NoError(err)
s.Require().NotEmpty(lastBackup)
s.Require().Equal(clock, lastBackup)
}
func (s *MessengerBackupSuite) TestBackupProfileWithInvalidDisplayName() {
// Create bob1
bob1 := s.m
state := ReceivedMessageState{
Response: &MessengerResponse{},
}
err := bob1.HandleBackup(
&state,
&protobuf.Backup{
Profile: &protobuf.BackedUpProfile{
DisplayName: "bad-display-name_eth",
},
Clock: 1,
},
&v1protocol.StatusMessage{},
)
// The backup will still work, but the display name will be skipped
s.Require().NoError(err)
storedBob1DisplayName, err := bob1.settings.DisplayName()
s.Require().NoError(err)
s.Require().Equal("", storedBob1DisplayName)
}
func (s *MessengerBackupSuite) TestBackupSettings() {
s.T().Skip("flaky test")
const (
bob1DisplayName = "bobby"
bob1Currency = "eur"
bob1MessagesFromContactsOnly = true
bob1ProfilePicturesShowTo = settings.ProfilePicturesShowToEveryone
bob1ProfilePicturesVisibility = settings.ProfilePicturesVisibilityEveryone
bob1Bio = "bio"
bob1Mnemonic = ""
bob1MnemonicRemoved = true
bob1PreferredName = "talent"
bob1UrlUnfUnfurlingMode = settings.URLUnfurlingEnableAll
)
// Create bob1 and set fields which are supposed to be backed up to/fetched from waku
bob1 := s.m
err := bob1.settings.SaveSettingField(settings.DisplayName, bob1DisplayName)
s.Require().NoError(err)
err = bob1.settings.SaveSettingField(settings.Currency, bob1Currency)
s.Require().NoError(err)
err = bob1.settings.SaveSettingField(settings.MessagesFromContactsOnly, bob1MessagesFromContactsOnly)
s.Require().NoError(err)
err = bob1.settings.SaveSettingField(settings.ProfilePicturesShowTo, bob1ProfilePicturesShowTo)
s.Require().NoError(err)
err = bob1.settings.SaveSettingField(settings.ProfilePicturesVisibility, bob1ProfilePicturesVisibility)
s.Require().NoError(err)
err = bob1.settings.SaveSettingField(settings.Bio, bob1Bio)
s.Require().NoError(err)
err = bob1.settings.SaveSettingField(settings.Mnemonic, bob1Mnemonic)
s.Require().NoError(err)
err = bob1.settings.SaveSettingField(settings.PreferredName, bob1PreferredName)
s.Require().NoError(err)
err = bob1.settings.SaveSettingField(settings.URLUnfurlingMode, bob1UrlUnfUnfurlingMode)
s.Require().NoError(err)
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
// Check bob1
storedBob1DisplayName, err := bob1.settings.DisplayName()
s.Require().NoError(err)
s.Require().Equal(bob1DisplayName, storedBob1DisplayName)
storedBob1Currency, err := bob1.settings.GetCurrency()
s.Require().NoError(err)
s.Require().Equal(bob1Currency, storedBob1Currency)
storedBob1MessagesFromContactsOnly, err := bob1.settings.GetMessagesFromContactsOnly()
s.Require().NoError(err)
s.Require().Equal(bob1MessagesFromContactsOnly, storedBob1MessagesFromContactsOnly)
storedBob1ProfilePicturesShowTo, err := bob1.settings.GetProfilePicturesShowTo()
s.Require().NoError(err)
s.Require().Equal(int64(bob1ProfilePicturesShowTo), storedBob1ProfilePicturesShowTo)
storedBob1ProfilePicturesVisibility, err := bob1.settings.GetProfilePicturesVisibility()
s.Require().NoError(err)
s.Require().Equal(int(bob1ProfilePicturesVisibility), storedBob1ProfilePicturesVisibility)
storedBob1Bio, err := bob1.settings.Bio()
s.Require().NoError(err)
s.Require().Equal(bob1Bio, storedBob1Bio)
storedMnemonic, err := bob1.settings.Mnemonic()
s.Require().NoError(err)
s.Require().Equal(bob1Mnemonic, storedMnemonic)
storedMnemonicRemoved, err := bob1.settings.MnemonicRemoved()
s.Require().NoError(err)
s.Require().Equal(bob1MnemonicRemoved, storedMnemonicRemoved)
storedPreferredName, err := bob1.settings.GetPreferredUsername()
s.NoError(err)
s.Require().Equal(bob1PreferredName, storedPreferredName)
storedBob1UrlUnfurlingMode, err := bob1.settings.URLUnfurlingMode()
s.NoError(err)
s.Require().Equal(int64(bob1UrlUnfUnfurlingMode), storedBob1UrlUnfurlingMode)
// Check bob2
storedBob2DisplayName, err := bob2.settings.DisplayName()
s.Require().NoError(err)
s.Require().NotEqual(storedBob1DisplayName, storedBob2DisplayName)
storedBob2Currency, err := bob2.settings.GetCurrency()
s.Require().NoError(err)
s.Require().NotEqual(storedBob1Currency, storedBob2Currency)
storedBob2MessagesFromContactsOnly, err := bob2.settings.GetMessagesFromContactsOnly()
s.Require().NoError(err)
s.Require().NotEqual(storedBob1MessagesFromContactsOnly, storedBob2MessagesFromContactsOnly)
storedBob2ProfilePicturesShowTo, err := bob2.settings.GetProfilePicturesShowTo()
s.Require().NoError(err)
s.Require().NotEqual(storedBob1ProfilePicturesShowTo, storedBob2ProfilePicturesShowTo)
storedBob2ProfilePicturesVisibility, err := bob2.settings.GetProfilePicturesVisibility()
s.Require().NoError(err)
s.Require().NotEqual(storedBob1ProfilePicturesVisibility, storedBob2ProfilePicturesVisibility)
storedBob2Bio, err := bob2.settings.Bio()
s.Require().NoError(err)
s.Require().NotEqual(storedBob1Bio, storedBob2Bio)
storedBob2MnemonicRemoved, err := bob2.settings.MnemonicRemoved()
s.Require().NoError(err)
s.Require().Equal(false, storedBob2MnemonicRemoved)
storedBob2PreferredName, err := bob2.settings.GetPreferredUsername()
s.NoError(err)
s.Require().Equal("", storedBob2PreferredName)
storedBob2UrlUnfurlingMode, err := bob2.settings.URLUnfurlingMode()
s.NoError(err)
s.Require().Equal(int64(settings.URLUnfurlingAlwaysAsk), storedBob2UrlUnfurlingMode)
// Backup
clock, err := bob1.BackupData(context.Background())
s.Require().NoError(err)
// Wait for the message to reach its destination
_, err = WaitOnSignaledSendWakuFetchingBackupProgress(bob2, func(r *wakusync.WakuBackedUpDataResponse) bool {
detailsMap := r.FetchingBackedUpDataDetails()
if settingDetails, ok := detailsMap[SyncWakuSectionKeySettings]; ok {
return settingDetails.DataNumber == settingDetails.TotalNumber
}
return false
}, "no messages")
s.Require().NoError(err)
// Check bob2
storedBob2DisplayName, err = bob2.settings.DisplayName()
s.Require().NoError(err)
s.Require().Equal(storedBob1DisplayName, storedBob2DisplayName)
storedBob2Currency, err = bob2.settings.GetCurrency()
s.Require().NoError(err)
s.Require().Equal(storedBob1Currency, storedBob2Currency)
storedBob2MessagesFromContactsOnly, err = bob2.settings.GetMessagesFromContactsOnly()
s.Require().NoError(err)
s.Require().Equal(storedBob1MessagesFromContactsOnly, storedBob2MessagesFromContactsOnly)
storedBob2ProfilePicturesShowTo, err = bob2.settings.GetProfilePicturesShowTo()
s.Require().NoError(err)
s.Require().Equal(storedBob1ProfilePicturesShowTo, storedBob2ProfilePicturesShowTo)
storedBob2ProfilePicturesVisibility, err = bob2.settings.GetProfilePicturesVisibility()
s.Require().NoError(err)
s.Require().Equal(storedBob1ProfilePicturesVisibility, storedBob2ProfilePicturesVisibility)
storedBob2Bio, err = bob2.settings.Bio()
s.Require().NoError(err)
s.Require().Equal(storedBob1Bio, storedBob2Bio)
storedBob2MnemonicRemoved, err = bob2.settings.MnemonicRemoved()
s.Require().NoError(err)
s.Require().Equal(bob1MnemonicRemoved, storedBob2MnemonicRemoved)
storedBob2PreferredName, err = bob2.settings.GetPreferredUsername()
s.NoError(err)
s.Require().Equal(bob1PreferredName, storedBob2PreferredName)
s.Require().Equal(bob1PreferredName, bob2.account.Name)
storedBob2UrlUnfurlingMode, err = bob2.settings.URLUnfurlingMode()
s.NoError(err)
s.Require().Equal(storedBob1UrlUnfurlingMode, storedBob2UrlUnfurlingMode)
lastBackup, err := bob1.lastBackup()
s.Require().NoError(err)
s.Require().NotEmpty(lastBackup)
s.Require().Equal(clock, lastBackup)
}
func (s *MessengerBackupSuite) TestBackupContactsGreaterThanBatch() {
bob1 := s.m
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
// Create contacts
for i := 0; i < BackupContactsPerBatch*2; i++ {
contactKey, err := crypto.GenerateKey()
s.Require().NoError(err)
contactID := types.EncodeHex(crypto.FromECDSAPub(&contactKey.PublicKey))
_, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID})
s.Require().NoError(err)
}
// Backup
_, err = bob1.BackupData(context.Background())
s.Require().NoError(err)
// Safety check
s.Require().Len(bob2.Contacts(), 0)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
return r.BackupHandled
},
"no messages",
)
s.Require().NoError(err)
s.Require().Equal(BackupContactsPerBatch*2, len(bob2.Contacts()))
s.Require().Len(bob2.AddedContacts(), BackupContactsPerBatch*2)
}
func (s *MessengerBackupSuite) TestBackupRemovedContact() {
bob1 := s.m
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
// Create 2 contacts on bob 1
contact1Key, err := crypto.GenerateKey()
s.Require().NoError(err)
contactID1 := types.EncodeHex(crypto.FromECDSAPub(&contact1Key.PublicKey))
_, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID1})
s.Require().NoError(err)
contact2Key, err := crypto.GenerateKey()
s.Require().NoError(err)
contactID2 := types.EncodeHex(crypto.FromECDSAPub(&contact2Key.PublicKey))
_, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID2})
s.Require().NoError(err)
s.Require().Len(bob1.Contacts(), 2)
actualContacts := bob1.Contacts()
if actualContacts[0].ID == contactID1 {
s.Require().Equal(actualContacts[0].ID, contactID1)
s.Require().Equal(actualContacts[1].ID, contactID2)
} else {
s.Require().Equal(actualContacts[0].ID, contactID2)
s.Require().Equal(actualContacts[1].ID, contactID1)
}
// Bob 2 add one of the same contacts
_, err = bob2.AddContact(context.Background(), &requests.AddContact{ID: contactID2})
s.Require().NoError(err)
// Bob 1 now removes one of the contact that was also on bob 2
_, err = bob1.RemoveContact(context.Background(), contactID2)
s.Require().NoError(err)
// Backup
clock, err := bob1.BackupData(context.Background())
s.Require().NoError(err)
// Safety check
s.Require().Len(bob2.Contacts(), 1)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
return r.BackupHandled
},
"no messages",
)
// Bob 2 should remove the contact
s.Require().NoError(err)
s.Require().Len(bob2.AddedContacts(), 1)
s.Require().Equal(contactID1, bob2.AddedContacts()[0].ID)
lastBackup, err := bob1.lastBackup()
s.Require().NoError(err)
s.Require().NotEmpty(lastBackup)
s.Require().Equal(clock, lastBackup)
}
func (s *MessengerBackupSuite) TestBackupLocalNickname() {
bob1 := s.m
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
nickname := "don van vliet"
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
// Set contact nickname
contact1Key, err := crypto.GenerateKey()
s.Require().NoError(err)
contactID1 := types.EncodeHex(crypto.FromECDSAPub(&contact1Key.PublicKey))
_, err = bob1.SetContactLocalNickname(&requests.SetContactLocalNickname{ID: types.Hex2Bytes(contactID1), Nickname: nickname})
s.Require().NoError(err)
// Backup
clock, err := bob1.BackupData(context.Background())
s.Require().NoError(err)
// Safety check
s.Require().Len(bob2.Contacts(), 0)
var actualContact *Contact
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
return r.BackupHandled
},
"no messages",
)
s.Require().NoError(err)
for _, c := range bob2.Contacts() {
if c.ID == contactID1 {
actualContact = c
break
}
}
s.Require().Equal(actualContact.LocalNickname, nickname)
lastBackup, err := bob1.lastBackup()
s.Require().NoError(err)
s.Require().NotEmpty(lastBackup)
s.Require().Equal(clock, lastBackup)
}
func (s *MessengerBackupSuite) TestBackupBlockedContacts() {
bob1 := s.m
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
// Create contact
contact1Key, err := crypto.GenerateKey()
s.Require().NoError(err)
contactID1 := types.EncodeHex(crypto.FromECDSAPub(&contact1Key.PublicKey))
_, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID1})
s.Require().NoError(err)
// Backup
_, err = bob1.BackupData(context.Background())
s.Require().NoError(err)
// Safety check
s.Require().Len(bob2.Contacts(), 0)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
return r.BackupHandled
},
"no messages",
)
s.Require().NoError(err)
s.Require().Len(bob2.AddedContacts(), 1)
actualContacts := bob2.AddedContacts()
s.Require().Equal(actualContacts[0].ID, contactID1)
_, err = bob1.BlockContact(context.Background(), contactID1, false)
s.Require().NoError(err)
// Backup
_, err = bob1.BackupData(context.Background())
s.Require().NoError(err)
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
return r.BackupHandled
},
"no messages",
)
s.Require().NoError(err)
s.Require().Len(bob2.BlockedContacts(), 1)
}
func (s *MessengerBackupSuite) TestBackupCommunities() {
bob1 := s.m
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
// Create a communitie
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
Name: "status",
Color: "#ffffff",
Description: "status community description",
}
// Create a community chat
response, err := bob1.CreateCommunity(description, true)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
// Backup
clock, err := bob1.BackupData(context.Background())
s.Require().NoError(err)
// Safety check
communities, err := bob2.Communities()
s.Require().NoError(err)
s.Require().Len(communities, 0)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
return r.BackupHandled
},
"no messages",
)
s.Require().NoError(err)
communities, err = bob2.JoinedCommunities()
s.Require().NoError(err)
s.Require().Len(communities, 1)
lastBackup, err := bob1.lastBackup()
s.Require().NoError(err)
s.Require().NotEmpty(lastBackup)
s.Require().Equal(clock, lastBackup)
}
func (s *MessengerBackupSuite) TestBackupKeypairs() {
// Create bob1
bob1 := s.m
profileKp := accounts.GetProfileKeypairForTest(true, true, true)
seedKp := accounts.GetSeedImportedKeypair1ForTest()
// Create a main account on bob1
err := bob1.settings.SaveOrUpdateKeypair(profileKp)
s.Require().NoError(err, "profile keypair bob1")
err = bob1.settings.SaveOrUpdateKeypair(seedKp)
s.Require().NoError(err, "seed keypair bob1")
// Check account is present in the db for bob1
dbProfileKp1, err := bob1.settings.GetKeypairByKeyUID(profileKp.KeyUID)
s.Require().NoError(err)
s.Require().True(accounts.SameKeypairs(profileKp, dbProfileKp1))
dbSeedKp1, err := bob1.settings.GetKeypairByKeyUID(seedKp.KeyUID)
s.Require().NoError(err)
s.Require().True(accounts.SameKeypairs(seedKp, dbSeedKp1))
// Create bob2
accountsFeed := &event.Feed{}
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, []Option{WithAccountsFeed(accountsFeed)})
s.Require().NoError(err)
s.Require().NotNil(bob2.config.accountsFeed)
ch := make(chan accountsevent.Event, 20)
sub := bob2.config.accountsFeed.Subscribe(ch)
defer TearDownMessenger(&s.Suite, bob2)
// Backup
_, err = bob1.BackupData(context.Background())
s.Require().NoError(err)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
return r.BackupHandled
},
"no messages",
)
s.Require().NoError(err)
// Check account is present in the db for bob2
dbProfileKp2, err := bob2.settings.GetKeypairByKeyUID(profileKp.KeyUID)
s.Require().NoError(err)
s.Require().Equal(profileKp.Name, dbProfileKp2.Name)
s.Require().Equal(accounts.SyncedFromBackup, dbProfileKp2.SyncedFrom)
for _, acc := range profileKp.Accounts {
s.Require().True(accounts.Contains(dbProfileKp2.Accounts, acc, accounts.SameAccounts))
}
dbSeedKp2, err := bob2.settings.GetKeypairByKeyUID(seedKp.KeyUID)
s.Require().NoError(err)
s.Require().True(accounts.SameKeypairsWithDifferentSyncedFrom(seedKp, dbSeedKp2, false, accounts.SyncedFromBackup, accounts.AccountNonOperable))
keypairs, err := bob2.settings.GetAllKeypairs()
s.Require().NoError(err)
// Check whether accounts added event is sent
expectedAddresses := make(map[common.Address]struct{}, 0)
for _, acc := range dbProfileKp2.Accounts {
if acc.Chat {
continue
}
expectedAddresses[common.Address(acc.Address)] = struct{}{}
}
for _, acc := range dbSeedKp2.Accounts {
expectedAddresses[common.Address(acc.Address)] = struct{}{}
}
for i := 0; i < len(keypairs); i++ {
select {
case <-time.After(1 * time.Second):
s.Fail("Timed out waiting for accountsevent")
case event := <-ch:
switch event.Type {
case accountsevent.EventTypeAdded:
for _, address := range event.Accounts {
if _, exists := expectedAddresses[address]; !exists {
s.logger.Debug("missing address in the accounts event", zap.Any("address", address))
s.Fail("address not received in the event")
}
}
}
}
}
sub.Unsubscribe()
}
func (s *MessengerBackupSuite) TestBackupKeycards() {
// Create bob1
bob1 := s.m
kp1 := accounts.GetProfileKeypairForTest(true, true, true)
keycard1 := accounts.GetProfileKeycardForTest()
kp2 := accounts.GetSeedImportedKeypair1ForTest()
keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
keycard2Copy := accounts.GetKeycardForSeedImportedKeypair1ForTest()
keycard2Copy.KeycardUID = keycard2Copy.KeycardUID + "C"
keycard2Copy.KeycardName = keycard2Copy.KeycardName + "Copy"
kp3 := accounts.GetSeedImportedKeypair2ForTest()
keycard3 := accounts.GetKeycardForSeedImportedKeypair2ForTest()
// Pre-condition
err := bob1.settings.SaveOrUpdateKeypair(kp1)
s.Require().NoError(err)
err = bob1.settings.SaveOrUpdateKeypair(kp2)
s.Require().NoError(err)
err = bob1.settings.SaveOrUpdateKeypair(kp3)
s.Require().NoError(err)
dbKeypairs, err := bob1.settings.GetActiveKeypairs()
s.Require().NoError(err)
s.Require().Equal(3, len(dbKeypairs))
err = bob1.settings.SaveOrUpdateKeycard(*keycard1, 0, false)
s.Require().NoError(err)
err = bob1.settings.SaveOrUpdateKeycard(*keycard2, 0, false)
s.Require().NoError(err)
err = bob1.settings.SaveOrUpdateKeycard(*keycard2Copy, 0, false)
s.Require().NoError(err)
err = bob1.settings.SaveOrUpdateKeycard(*keycard3, 0, false)
s.Require().NoError(err)
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
// Backup
_, err = bob1.BackupData(context.Background())
s.Require().NoError(err)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
syncedKeycards, err := bob2.settings.GetAllKnownKeycards()
s.Require().NoError(err)
return r.BackupHandled && len(syncedKeycards) == 4
},
"no messages",
)
s.Require().NoError(err)
syncedKeycards, err := bob2.settings.GetAllKnownKeycards()
s.Require().NoError(err)
s.Require().Equal(4, len(syncedKeycards))
s.Require().True(accounts.Contains(syncedKeycards, keycard1, accounts.SameKeycards))
s.Require().True(accounts.Contains(syncedKeycards, keycard2, accounts.SameKeycards))
s.Require().True(accounts.Contains(syncedKeycards, keycard2Copy, accounts.SameKeycards))
s.Require().True(accounts.Contains(syncedKeycards, keycard3, accounts.SameKeycards))
}
func (s *MessengerBackupSuite) TestBackupWatchOnlyAccounts() {
// Create bob1
bob1 := s.m
woAccounts := accounts.GetWatchOnlyAccountsForTest()
err := bob1.settings.SaveOrUpdateAccounts(woAccounts, false)
s.Require().NoError(err)
dbWoAccounts1, err := bob1.settings.GetActiveWatchOnlyAccounts()
s.Require().NoError(err)
s.Require().Equal(len(woAccounts), len(dbWoAccounts1))
s.Require().True(haveSameElements(woAccounts, dbWoAccounts1, accounts.SameAccounts))
// Create bob2
accountsFeed := &event.Feed{}
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, []Option{WithAccountsFeed(accountsFeed)})
s.Require().NoError(err)
s.Require().NotNil(bob2.config.accountsFeed)
ch := make(chan accountsevent.Event, 20)
sub := bob2.config.accountsFeed.Subscribe(ch)
defer TearDownMessenger(&s.Suite, bob2)
// Backup
_, err = bob1.BackupData(context.Background())
s.Require().NoError(err)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
c, err := bob2.settings.GetActiveWatchOnlyAccounts()
if err != nil {
return false
}
return r.BackupHandled && len(woAccounts) == len(c)
},
"no messages",
)
s.Require().NoError(err)
dbWoAccounts2, err := bob2.settings.GetActiveWatchOnlyAccounts()
s.Require().NoError(err)
s.Require().Equal(len(woAccounts), len(dbWoAccounts2))
s.Require().True(haveSameElements(woAccounts, dbWoAccounts2, accounts.SameAccounts))
// Check whether accounts added event is sent
select {
case <-time.After(1 * time.Second):
s.Fail("Timed out waiting for accountsevent")
case event := <-ch:
switch event.Type {
case accountsevent.EventTypeAdded:
s.Require().Len(event.Accounts, 1)
s.Require().Equal(common.Address(dbWoAccounts2[0].Address), event.Accounts[0])
}
}
sub.Unsubscribe()
}
func (s *MessengerBackupSuite) TestBackupChats() {
// Create bob1
bob1 := s.m
response, err := bob1.CreateGroupChatWithMembers(context.Background(), "group", []string{})
s.NoError(err)
s.Require().Len(response.Chats(), 1)
ourGroupChat := response.Chats()[0]
err = bob1.SaveChat(ourGroupChat)
s.NoError(err)
alice := s.newMessenger()
defer TearDownMessenger(&s.Suite, alice)
ourOneOneChat := CreateOneToOneChat("Our 1TO1", &alice.identity.PublicKey, alice.transport)
err = bob1.SaveChat(ourOneOneChat)
s.Require().NoError(err)
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
// Backup
_, err = bob1.BackupData(context.Background())
s.Require().NoError(err)
// Wait for the message to reach its destination
response, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
return r.BackupHandled
},
"no messages",
)
s.Require().NoError(err)
// -- Group chat
// Check response
chat, ok := response.chats[ourGroupChat.ID]
s.Require().True(ok)
s.Require().Equal(ourGroupChat.Name, chat.Name)
// Check stored chats
chat, ok = bob2.allChats.Load(ourGroupChat.ID)
s.Require().True(ok)
s.Require().Equal(ourGroupChat.Name, chat.Name)
// -- One to One chat
// Check response
chat, ok = response.chats[ourOneOneChat.ID]
s.Require().True(ok)
s.Require().Equal("", chat.Name) // We set 1-1 chat names to "" because the name is not good
// Check stored chats
chat, ok = bob2.allChats.Load(ourOneOneChat.ID)
s.Require().True(ok)
s.Require().Equal("", chat.Name)
}
func (s *MessengerBackupSuite) TestLeftCommunitiesAreBackedUp() {
bob1 := s.m
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob2)
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT,
Name: "other-status",
Color: "#fffff4",
Description: "other status community description",
}
// Create another community chat
response, err := bob1.CreateCommunity(description, true)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
newCommunity := response.Communities()[0]
response, err = bob1.LeaveCommunity(newCommunity.ID())
s.Require().NoError(err)
s.Require().NotNil(response)
// trigger artificial Backup
_, err = bob1.BackupData(context.Background())
s.Require().NoError(err)
communities, err := bob1.Communities()
s.Require().NoError(err)
s.Require().Len(communities, 1)
// Safety check
communities, err = bob2.Communities()
s.Require().NoError(err)
s.Require().Len(communities, 0)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
return r.BackupHandled
},
"no messages",
)
s.Require().NoError(err)
communities, err = bob2.JoinedCommunities()
s.Require().NoError(err)
s.Require().Len(communities, 0)
}