status-im/status-go

View on GitHub
protocol/messenger_profile_showcase_test.go

Summary

Maintainability
A
0 mins
Test Coverage
package protocol

import (
    "context"
    "errors"
    "math/big"
    "testing"
    "time"

    gethcommon "github.com/ethereum/go-ethereum/common"

    "github.com/status-im/status-go/eth-node/crypto"
    "github.com/status-im/status-go/eth-node/types"
    "github.com/status-im/status-go/multiaccounts/accounts"
    "github.com/status-im/status-go/protocol/common"
    "github.com/status-im/status-go/protocol/communities"
    "github.com/status-im/status-go/protocol/identity"
    "github.com/status-im/status-go/protocol/protobuf"
    "github.com/status-im/status-go/protocol/requests"
    "github.com/status-im/status-go/protocol/tt"
    "github.com/status-im/status-go/services/wallet/bigint"
    "github.com/status-im/status-go/services/wallet/thirdparty"

    "github.com/stretchr/testify/suite"
)

func TestMessengerProfileShowcaseSuite(t *testing.T) { // nolint: deadcode,unused
    suite.Run(t, new(TestMessengerProfileShowcase))
}

type TestMessengerProfileShowcase struct {
    CommunitiesMessengerTestSuiteBase
    m *Messenger // main instance of Messenger
}

func (s *TestMessengerProfileShowcase) SetupTest() {
    s.CommunitiesMessengerTestSuiteBase.SetupTest()
    s.m = s.newMessenger("", []string{})
    _, err := s.m.Start()
    s.Require().NoError(err)
}

func (s *TestMessengerProfileShowcase) TearDownTest() {
    TearDownMessenger(&s.Suite, s.m)
    s.CommunitiesMessengerTestSuiteBase.TearDownTest()
}

func (s *TestMessengerProfileShowcase) mutualContact(theirMessenger *Messenger) {
    messageText := "hello!"

    contactID := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
    request := &requests.SendContactRequest{
        ID:      contactID,
        Message: messageText,
    }

    // Send contact request
    _, err := s.m.SendContactRequest(context.Background(), request)
    s.Require().NoError(err)

    // Wait for the message to reach its destination
    _, err = WaitOnMessengerResponse(
        theirMessenger,
        func(r *MessengerResponse) bool {
            return len(r.Contacts) > 0 && len(r.Messages()) > 0
        },
        "no messages",
    )
    s.Require().NoError(err)

    // Make sure it's the pending contact requests
    contactRequests, _, err := theirMessenger.PendingContactRequests("", 10)
    s.Require().NoError(err)
    s.Require().Len(contactRequests, 1)
    s.Require().Equal(contactRequests[0].ContactRequestState, common.ContactRequestStatePending)

    // Accept contact request, receiver side
    _, err = theirMessenger.AcceptContactRequest(context.Background(), &requests.AcceptContactRequest{ID: types.Hex2Bytes(contactRequests[0].ID)})
    s.Require().NoError(err)

    // Wait for the message to reach its destination
    resp, err := WaitOnMessengerResponse(
        s.m,
        func(r *MessengerResponse) bool {
            return len(r.Contacts) == 1 && len(r.Messages()) == 2 && len(r.ActivityCenterNotifications()) == 1
        },
        "no messages",
    )
    s.Require().NoError(err)

    // Check the contact state is correctly set
    s.Require().Len(resp.Contacts, 1)
    s.Require().True(resp.Contacts[0].mutual())
}

func (s *TestMessengerProfileShowcase) verifiedContact(theirMessenger *Messenger) {
    theirPk := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
    challenge := "Want to see what I'm hiding in my profile showcase?"

    _, err := s.m.SendContactVerificationRequest(context.Background(), theirPk, challenge)
    s.Require().NoError(err)

    // Wait for the message to reach its destination
    resp, err := WaitOnMessengerResponse(
        theirMessenger,
        func(r *MessengerResponse) bool {
            return len(r.VerificationRequests()) == 1 && len(r.ActivityCenterNotifications()) == 1
        },
        "no messages",
    )
    s.Require().NoError(err)
    s.Require().Len(resp.VerificationRequests(), 1)
    verificationRequestID := resp.VerificationRequests()[0].ID

    _, err = theirMessenger.AcceptContactVerificationRequest(context.Background(), verificationRequestID, "For sure!")
    s.Require().NoError(err)

    s.Require().NoError(err)

    // Wait for the message to reach its destination
    _, err = WaitOnMessengerResponse(
        s.m,
        func(r *MessengerResponse) bool {
            return len(r.VerificationRequests()) == 1
        },
        "no messages",
    )
    s.Require().NoError(err)

    resp, err = s.m.VerifiedTrusted(context.Background(), &requests.VerifiedTrusted{ID: types.FromHex(verificationRequestID)})
    s.Require().NoError(err)

    s.Require().Len(resp.Messages(), 1)
    s.Require().Equal(common.ContactVerificationStateTrusted, resp.Messages()[0].ContactVerificationState)
}

func (s *TestMessengerProfileShowcase) TestSaveAndGetProfileShowcasePreferences() {
    request := DummyProfileShowcasePreferences(true)

    // Provide collectible balances test response
    collectible := request.Collectibles[0]
    collectibleID, err := toCollectibleUniqueID(collectible.ContractAddress, collectible.TokenID, collectible.ChainID)
    s.Require().NoError(err)
    balances := []thirdparty.AccountBalance{
        thirdparty.AccountBalance{
            Address:     gethcommon.HexToAddress(request.Accounts[0].Address),
            Balance:     &bigint.BigInt{Int: big.NewInt(5)},
            TxTimestamp: 0,
        },
    }
    s.collectiblesManagerMock.SetCollectibleOwnershipResponse(collectibleID, balances)

    err = s.m.SetProfileShowcasePreferences(request, false)
    s.Require().NoError(err)

    // Restored preferences shoulf be same as stored
    response, err := s.m.GetProfileShowcasePreferences()
    s.Require().NoError(err)

    s.Require().Equal(len(response.Communities), len(request.Communities))
    for i := 0; i < len(response.Communities); i++ {
        s.Require().Equal(*response.Communities[i], *request.Communities[i])
    }

    s.Require().Equal(len(response.Accounts), len(request.Accounts))
    for i := 0; i < len(response.Accounts); i++ {
        s.Require().Equal(*response.Accounts[i], *request.Accounts[i])
    }

    s.Require().Equal(len(response.Collectibles), len(request.Collectibles))
    for i := 0; i < len(response.Collectibles); i++ {
        s.Require().Equal(*response.Collectibles[i], *request.Collectibles[i])
    }

    s.Require().Equal(len(response.VerifiedTokens), len(request.VerifiedTokens))
    for i := 0; i < len(response.VerifiedTokens); i++ {
        s.Require().Equal(*response.VerifiedTokens[i], *request.VerifiedTokens[i])
    }

    s.Require().Equal(len(response.UnverifiedTokens), len(request.UnverifiedTokens))
    for i := 0; i < len(response.UnverifiedTokens); i++ {
        s.Require().Equal(*response.UnverifiedTokens[i], *request.UnverifiedTokens[i])
    }

    s.Require().Equal(len(response.SocialLinks), len(request.SocialLinks))
    for i := 0; i < len(response.SocialLinks); i++ {
        s.Require().Equal(*response.SocialLinks[i], *request.SocialLinks[i])
    }
}

func (s *TestMessengerProfileShowcase) TestFailToSaveProfileShowcasePreferencesWithWrongVisibility() {
    accountEntry := &identity.ProfileShowcaseAccountPreference{
        Address:            "0x0000000000000000000000000032433445133424",
        ShowcaseVisibility: identity.ProfileShowcaseVisibilityIDVerifiedContacts,
        Order:              17,
    }

    collectibleEntry := &identity.ProfileShowcaseCollectiblePreference{
        ContractAddress:    "0x12378534257568678487683576",
        ChainID:            11155111,
        TokenID:            "12321389592999903",
        ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts,
        Order:              17,
    }

    request := &identity.ProfileShowcasePreferences{
        Accounts:     []*identity.ProfileShowcaseAccountPreference{accountEntry},
        Collectibles: []*identity.ProfileShowcaseCollectiblePreference{collectibleEntry},
    }

    // Provide collectible balances test response
    collectible := request.Collectibles[0]
    collectibleID, err := toCollectibleUniqueID(collectible.ContractAddress, collectible.TokenID, collectible.ChainID)
    s.Require().NoError(err)
    balances := []thirdparty.AccountBalance{
        thirdparty.AccountBalance{
            Address:     gethcommon.HexToAddress(request.Accounts[0].Address),
            Balance:     &bigint.BigInt{Int: big.NewInt(5)},
            TxTimestamp: 0,
        },
    }
    s.collectiblesManagerMock.SetCollectibleOwnershipResponse(collectibleID, balances)

    err = s.m.SetProfileShowcasePreferences(request, false)
    s.Require().Equal(errorAccountVisibilityLowerThanCollectible, err)
}

func (s *TestMessengerProfileShowcase) TestEncryptAndDecryptProfileShowcaseEntries() {
    // Add mutual contact
    theirMessenger := s.newMessenger(accountPassword, []string{commonAccountAddress})
    _, err := theirMessenger.Start()
    s.Require().NoError(err)

    defer TearDownMessenger(&s.Suite, theirMessenger)

    s.mutualContact(theirMessenger)

    entries := &protobuf.ProfileShowcaseEntries{
        Communities: []*protobuf.ProfileShowcaseCommunity{
            &protobuf.ProfileShowcaseCommunity{
                CommunityId: "0x01312357798976535235432345",
                Order:       12,
            },
            &protobuf.ProfileShowcaseCommunity{
                CommunityId: "0x12378534257568678487683576",
                Order:       11,
            },
        },
        Accounts: []*protobuf.ProfileShowcaseAccount{
            &protobuf.ProfileShowcaseAccount{
                Address: "0x00000323245",
                Name:    "Default",
                ColorId: "red",
                Emoji:   "(=^ ◡ ^=)",
                Order:   1,
            },
        },
        Collectibles: []*protobuf.ProfileShowcaseCollectible{
            &protobuf.ProfileShowcaseCollectible{
                ContractAddress: "0x12378534257568678487683576",
                ChainId:         1,
                TokenId:         "12321389592999903",
                AccountAddress:  "0x32433445133424",
                CommunityId:     "0x12378534257568678487683576",
                Order:           0,
            },
        },
        VerifiedTokens: []*protobuf.ProfileShowcaseVerifiedToken{
            &protobuf.ProfileShowcaseVerifiedToken{
                Symbol: "ETH",
                Order:  1,
            },
            &protobuf.ProfileShowcaseVerifiedToken{
                Symbol: "DAI",
                Order:  2,
            },
            &protobuf.ProfileShowcaseVerifiedToken{
                Symbol: "SNT",
                Order:  3,
            },
        },
        UnverifiedTokens: []*protobuf.ProfileShowcaseUnverifiedToken{
            &protobuf.ProfileShowcaseUnverifiedToken{
                ContractAddress: "0x454525452023452",
                ChainId:         11155111,
                Order:           0,
            },
            &protobuf.ProfileShowcaseUnverifiedToken{
                ContractAddress: "0x12312323323233",
                ChainId:         1,
                Order:           1,
            },
        },
        SocialLinks: []*protobuf.ProfileShowcaseSocialLink{
            &protobuf.ProfileShowcaseSocialLink{
                Text:  identity.TwitterID,
                Url:   "https://twitter.com/ethstatus",
                Order: 1,
            },
            &protobuf.ProfileShowcaseSocialLink{
                Text:  identity.TwitterID,
                Url:   "https://twitter.com/StatusIMBlog",
                Order: 2,
            },
            &protobuf.ProfileShowcaseSocialLink{
                Text:  identity.GithubID,
                Url:   "https://github.com/status-im",
                Order: 3,
            },
        },
    }
    data, err := s.m.EncryptProfileShowcaseEntriesWithContactPubKeys(entries, s.m.Contacts())
    s.Require().NoError(err)

    entriesBack, err := theirMessenger.DecryptProfileShowcaseEntriesWithPubKey(&s.m.identity.PublicKey, data)
    s.Require().NoError(err)

    s.Require().Equal(len(entries.Communities), len(entriesBack.Communities))
    for i := 0; i < len(entriesBack.Communities); i++ {
        s.Require().Equal(entries.Communities[i].CommunityId, entriesBack.Communities[i].CommunityId)
        s.Require().Equal(entries.Communities[i].Order, entriesBack.Communities[i].Order)
    }

    s.Require().Equal(len(entries.Accounts), len(entriesBack.Accounts))
    for i := 0; i < len(entriesBack.Accounts); i++ {
        s.Require().Equal(entries.Accounts[i].Address, entriesBack.Accounts[i].Address)
        s.Require().Equal(entries.Accounts[i].Name, entriesBack.Accounts[i].Name)
        s.Require().Equal(entries.Accounts[i].ColorId, entriesBack.Accounts[i].ColorId)
        s.Require().Equal(entries.Accounts[i].Emoji, entriesBack.Accounts[i].Emoji)
        s.Require().Equal(entries.Accounts[i].Order, entriesBack.Accounts[i].Order)
    }

    s.Require().Equal(len(entries.Collectibles), len(entriesBack.Collectibles))
    for i := 0; i < len(entriesBack.Collectibles); i++ {
        s.Require().Equal(entries.Collectibles[i].TokenId, entriesBack.Collectibles[i].TokenId)
        s.Require().Equal(entries.Collectibles[i].ChainId, entriesBack.Collectibles[i].ChainId)
        s.Require().Equal(entries.Collectibles[i].ContractAddress, entriesBack.Collectibles[i].ContractAddress)
        s.Require().Equal(entries.Collectibles[i].AccountAddress, entriesBack.Collectibles[i].AccountAddress)
        s.Require().Equal(entries.Collectibles[i].CommunityId, entriesBack.Collectibles[i].CommunityId)
        s.Require().Equal(entries.Collectibles[i].Order, entriesBack.Collectibles[i].Order)
    }

    s.Require().Equal(len(entries.VerifiedTokens), len(entriesBack.VerifiedTokens))
    for i := 0; i < len(entriesBack.VerifiedTokens); i++ {
        s.Require().Equal(entries.VerifiedTokens[i].Symbol, entriesBack.VerifiedTokens[i].Symbol)
        s.Require().Equal(entries.VerifiedTokens[i].Order, entriesBack.VerifiedTokens[i].Order)
    }

    s.Require().Equal(len(entries.UnverifiedTokens), len(entriesBack.UnverifiedTokens))
    for i := 0; i < len(entriesBack.UnverifiedTokens); i++ {
        s.Require().Equal(entries.UnverifiedTokens[i].ContractAddress, entriesBack.UnverifiedTokens[i].ContractAddress)
        s.Require().Equal(entries.UnverifiedTokens[i].ChainId, entriesBack.UnverifiedTokens[i].ChainId)
        s.Require().Equal(entries.UnverifiedTokens[i].Order, entriesBack.UnverifiedTokens[i].Order)
    }

    s.Require().Equal(len(entries.SocialLinks), len(entriesBack.SocialLinks))
    for i := 0; i < len(entriesBack.SocialLinks); i++ {
        s.Require().Equal(entries.SocialLinks[i].Text, entriesBack.SocialLinks[i].Text)
        s.Require().Equal(entries.SocialLinks[i].Url, entriesBack.SocialLinks[i].Url)
        s.Require().Equal(entries.SocialLinks[i].Order, entriesBack.SocialLinks[i].Order)
    }
}

func (s *TestMessengerProfileShowcase) TestShareShowcasePreferences() {
    // Set Display name to pass shouldPublishChatIdentity check
    profileKp := accounts.GetProfileKeypairForTest(true, false, false)
    profileKp.KeyUID = s.m.account.KeyUID
    profileKp.Accounts[0].KeyUID = s.m.account.KeyUID

    err := s.m.settings.SaveOrUpdateKeypair(profileKp)
    s.Require().NoError(err)

    err = s.m.SetDisplayName("bobby")
    s.Require().NoError(err)

    // Save preferences to dispatch changes
    request := DummyProfileShowcasePreferences(true)

    // Save wallet accounts to pass the validation
    acc1 := &accounts.Account{
        Address: types.HexToAddress(request.Accounts[0].Address),
        Type:    accounts.AccountTypeGenerated,
        Name:    "Test Account 1",
        ColorID: "",
        Emoji:   "emoji",
    }
    acc2 := &accounts.Account{
        Address: types.HexToAddress(request.Accounts[1].Address),
        Type:    accounts.AccountTypeSeed,
        Name:    "Test Account 2",
        ColorID: "",
        Emoji:   "emoji",
    }
    err = s.m.settings.SaveOrUpdateAccounts([]*accounts.Account{acc1, acc2}, true)
    s.Require().NoError(err)

    // Provide collectible balances test response
    collectible := request.Collectibles[0]
    collectibleID, err := toCollectibleUniqueID(collectible.ContractAddress, collectible.TokenID, collectible.ChainID)
    s.Require().NoError(err)
    balances := []thirdparty.AccountBalance{
        thirdparty.AccountBalance{
            Address:     gethcommon.HexToAddress(request.Accounts[0].Address),
            Balance:     &bigint.BigInt{Int: big.NewInt(1)},
            TxTimestamp: 32443424,
        },
    }
    s.collectiblesManagerMock.SetCollectibleOwnershipResponse(collectibleID, balances)

    err = s.m.SetProfileShowcasePreferences(request, false)
    s.Require().NoError(err)

    profileShowcasePreferences, err := s.m.GetProfileShowcasePreferences()
    s.Require().NoError(err)

    // Make sure the count for entries is correct
    s.Require().Len(profileShowcasePreferences.Communities, 2)
    s.Require().Len(profileShowcasePreferences.Accounts, 2)
    s.Require().Len(profileShowcasePreferences.Collectibles, 1)
    s.Require().Len(profileShowcasePreferences.VerifiedTokens, 3)
    s.Require().Len(profileShowcasePreferences.UnverifiedTokens, 2)

    // Add mutual contact
    mutualContact := s.newMessenger(alicePassword, []string{aliceAccountAddress})
    _, err = mutualContact.Start()
    s.Require().NoError(err)
    defer TearDownMessenger(&s.Suite, mutualContact)

    s.mutualContact(mutualContact)

    // Add identity verified contact
    verifiedContact := s.newMessenger(bobPassword, []string{bobAccountAddress})
    _, err = verifiedContact.Start()
    s.Require().NoError(err)
    defer TearDownMessenger(&s.Suite, verifiedContact)

    s.mutualContact(verifiedContact)
    s.verifiedContact(verifiedContact)

    contactID := types.EncodeHex(crypto.FromECDSAPub(&s.m.identity.PublicKey))
    // Get summarised profile data for mutual contact
    _, err = WaitOnMessengerResponse(
        mutualContact,
        func(r *MessengerResponse) bool {
            return r.updatedProfileShowcaseContactIDs[contactID] == true
        },
        "no messages",
    )
    s.Require().NoError(err)

    profileShowcase, err := mutualContact.GetProfileShowcaseForContact(contactID, false)
    s.Require().NoError(err)

    s.Require().Len(profileShowcase.Communities, 2)
    s.Require().Equal(profileShowcase.Communities[0].CommunityID, request.Communities[0].CommunityID)
    s.Require().Equal(profileShowcase.Communities[0].Order, request.Communities[0].Order)
    s.Require().Equal(profileShowcase.Communities[1].CommunityID, request.Communities[1].CommunityID)
    s.Require().Equal(profileShowcase.Communities[1].Order, request.Communities[1].Order)

    s.Require().Len(profileShowcase.Accounts, 2)
    s.Require().Equal(profileShowcase.Accounts[0].Address, request.Accounts[0].Address)
    s.Require().Equal(profileShowcase.Accounts[0].Order, request.Accounts[0].Order)
    s.Require().Equal(profileShowcase.Accounts[1].Address, request.Accounts[1].Address)
    s.Require().Equal(profileShowcase.Accounts[1].Order, request.Accounts[1].Order)

    s.Require().Len(profileShowcase.Collectibles, 1)
    s.Require().Equal(profileShowcase.Collectibles[0].TokenID, request.Collectibles[0].TokenID)
    s.Require().Equal(profileShowcase.Collectibles[0].ChainID, request.Collectibles[0].ChainID)
    s.Require().Equal(profileShowcase.Collectibles[0].ContractAddress, request.Collectibles[0].ContractAddress)
    s.Require().Equal(profileShowcase.Collectibles[0].Order, request.Collectibles[0].Order)

    s.Require().Len(profileShowcase.VerifiedTokens, 1)
    s.Require().Equal(profileShowcase.VerifiedTokens[0].Symbol, request.VerifiedTokens[0].Symbol)
    s.Require().Equal(profileShowcase.VerifiedTokens[0].Order, request.VerifiedTokens[0].Order)

    s.Require().Len(profileShowcase.UnverifiedTokens, 2)
    s.Require().Equal(profileShowcase.UnverifiedTokens[0].ContractAddress, request.UnverifiedTokens[0].ContractAddress)
    s.Require().Equal(profileShowcase.UnverifiedTokens[0].ChainID, request.UnverifiedTokens[0].ChainID)
    s.Require().Equal(profileShowcase.UnverifiedTokens[0].Order, request.UnverifiedTokens[0].Order)
    s.Require().Equal(profileShowcase.UnverifiedTokens[1].ContractAddress, request.UnverifiedTokens[1].ContractAddress)
    s.Require().Equal(profileShowcase.UnverifiedTokens[1].ChainID, request.UnverifiedTokens[1].ChainID)
    s.Require().Equal(profileShowcase.UnverifiedTokens[1].Order, request.UnverifiedTokens[1].Order)

    s.Require().Len(profileShowcase.SocialLinks, 2)
    s.Require().Equal(profileShowcase.SocialLinks[0].Text, request.SocialLinks[0].Text)
    s.Require().Equal(profileShowcase.SocialLinks[0].URL, request.SocialLinks[0].URL)
    s.Require().Equal(profileShowcase.SocialLinks[0].Order, request.SocialLinks[0].Order)
    s.Require().Equal(profileShowcase.SocialLinks[1].Text, request.SocialLinks[2].Text)
    s.Require().Equal(profileShowcase.SocialLinks[1].URL, request.SocialLinks[2].URL)
    s.Require().Equal(profileShowcase.SocialLinks[1].Order, request.SocialLinks[2].Order)

    // Get summarised profile data for verified contact
    _, err = WaitOnMessengerResponse(
        verifiedContact,
        func(r *MessengerResponse) bool {
            return r.updatedProfileShowcaseContactIDs[contactID] == true
        },
        "no messages",
    )
    s.Require().NoError(err)

    profileShowcase, err = verifiedContact.GetProfileShowcaseForContact(contactID, false)
    s.Require().NoError(err)

    s.Require().Len(profileShowcase.Accounts, 2)
    s.Require().Equal(profileShowcase.Accounts[0].Address, request.Accounts[0].Address)
    s.Require().Equal(profileShowcase.Accounts[0].Order, request.Accounts[0].Order)
    s.Require().Equal(profileShowcase.Accounts[1].Address, request.Accounts[1].Address)
    s.Require().Equal(profileShowcase.Accounts[1].Order, request.Accounts[1].Order)

    s.Require().Len(profileShowcase.Collectibles, 1)
    s.Require().Equal(profileShowcase.Collectibles[0].ContractAddress, request.Collectibles[0].ContractAddress)
    s.Require().Equal(profileShowcase.Collectibles[0].ChainID, request.Collectibles[0].ChainID)
    s.Require().Equal(profileShowcase.Collectibles[0].TokenID, request.Collectibles[0].TokenID)
    s.Require().Equal(profileShowcase.Collectibles[0].Order, request.Collectibles[0].Order)

    s.Require().Len(profileShowcase.VerifiedTokens, 2)
    s.Require().Equal(profileShowcase.VerifiedTokens[0].Symbol, request.VerifiedTokens[0].Symbol)
    s.Require().Equal(profileShowcase.VerifiedTokens[0].Order, request.VerifiedTokens[0].Order)
    s.Require().Equal(profileShowcase.VerifiedTokens[1].Symbol, request.VerifiedTokens[1].Symbol)
    s.Require().Equal(profileShowcase.VerifiedTokens[1].Order, request.VerifiedTokens[1].Order)

    s.Require().Len(profileShowcase.UnverifiedTokens, 2)
    s.Require().Equal(profileShowcase.UnverifiedTokens[0].ContractAddress, request.UnverifiedTokens[0].ContractAddress)
    s.Require().Equal(profileShowcase.UnverifiedTokens[0].ChainID, request.UnverifiedTokens[0].ChainID)
    s.Require().Equal(profileShowcase.UnverifiedTokens[0].Order, request.UnverifiedTokens[0].Order)
    s.Require().Equal(profileShowcase.UnverifiedTokens[1].ContractAddress, request.UnverifiedTokens[1].ContractAddress)
    s.Require().Equal(profileShowcase.UnverifiedTokens[1].ChainID, request.UnverifiedTokens[1].ChainID)
    s.Require().Equal(profileShowcase.UnverifiedTokens[1].Order, request.UnverifiedTokens[1].Order)

    s.Require().Len(profileShowcase.SocialLinks, 3)
    s.Require().Equal(profileShowcase.SocialLinks[0].Text, request.SocialLinks[0].Text)
    s.Require().Equal(profileShowcase.SocialLinks[0].URL, request.SocialLinks[0].URL)
    s.Require().Equal(profileShowcase.SocialLinks[0].Order, request.SocialLinks[0].Order)
    s.Require().Equal(profileShowcase.SocialLinks[1].Text, request.SocialLinks[1].Text)
    s.Require().Equal(profileShowcase.SocialLinks[1].URL, request.SocialLinks[1].URL)
    s.Require().Equal(profileShowcase.SocialLinks[1].Order, request.SocialLinks[1].Order)
    s.Require().Equal(profileShowcase.SocialLinks[2].Text, request.SocialLinks[2].Text)
    s.Require().Equal(profileShowcase.SocialLinks[2].URL, request.SocialLinks[2].URL)
    s.Require().Equal(profileShowcase.SocialLinks[2].Order, request.SocialLinks[2].Order)
}

func (s *TestMessengerProfileShowcase) TestProfileShowcaseProofOfMembershipUnencryptedCommunities() {
    alice := s.m

    // Set Display name to pass shouldPublishChatIdentity check
    profileKp := accounts.GetProfileKeypairForTest(true, false, false)
    profileKp.KeyUID = alice.account.KeyUID
    profileKp.Accounts[0].KeyUID = alice.account.KeyUID

    err := alice.settings.SaveOrUpdateKeypair(profileKp)
    s.Require().NoError(err)

    err = alice.SetDisplayName("Alice")
    s.Require().NoError(err)

    // Add bob as a mutual contact
    bob := s.newMessenger(bobPassword, []string{bobAccountAddress})
    _, err = bob.Start()
    s.Require().NoError(err)
    defer TearDownMessenger(&s.Suite, bob)

    s.mutualContact(bob)

    // Alice creates a community
    aliceCommunity, _ := createCommunityConfigurable(&s.Suite, alice, protobuf.CommunityPermissions_MANUAL_ACCEPT)
    advertiseCommunityTo(&s.Suite, aliceCommunity, alice, bob)

    // Bobs creates an another community
    bobCommunity, _ := createCommunityConfigurable(&s.Suite, bob, protobuf.CommunityPermissions_AUTO_ACCEPT)

    // Add community to the Alice's profile showcase & get it on the Bob's side
    err = alice.SetProfileShowcasePreferences(&identity.ProfileShowcasePreferences{
        Communities: []*identity.ProfileShowcaseCommunityPreference{
            &identity.ProfileShowcaseCommunityPreference{
                CommunityID:        aliceCommunity.IDString(),
                ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts,
                Order:              0,
            },
            &identity.ProfileShowcaseCommunityPreference{
                CommunityID:        bobCommunity.IDString(),
                ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone,
                Order:              2,
            },
        },
    }, false)
    s.Require().NoError(err)

    contactID := types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey))
    _, err = WaitOnMessengerResponse(
        bob,
        func(r *MessengerResponse) bool {
            return r.updatedProfileShowcaseContactIDs[contactID] == true
        },
        "no messages",
    )
    s.Require().NoError(err)

    profileShowcase, err := bob.GetProfileShowcaseForContact(contactID, true)
    s.Require().NoError(err)

    // Verify community's data
    s.Require().Len(profileShowcase.Communities, 2)
    s.Require().Equal(profileShowcase.Communities[0].CommunityID, aliceCommunity.IDString())
    s.Require().Equal(profileShowcase.Communities[0].MembershipStatus, identity.ProfileShowcaseMembershipStatusProvenMember)
    s.Require().Equal(profileShowcase.Communities[1].CommunityID, bobCommunity.IDString())
    s.Require().Equal(profileShowcase.Communities[1].MembershipStatus, identity.ProfileShowcaseMembershipStatusNotAMember)
}

func (s *TestMessengerProfileShowcase) TestProfileShowcaseProofOfMembershipEncryptedCommunity() {
    alice := s.m

    // Set Display name to pass shouldPublishChatIdentity check
    profileKp := accounts.GetProfileKeypairForTest(true, false, false)
    profileKp.KeyUID = alice.account.KeyUID
    profileKp.Accounts[0].KeyUID = alice.account.KeyUID

    err := alice.settings.SaveOrUpdateKeypair(profileKp)
    s.Require().NoError(err)

    err = alice.SetDisplayName("Alice")
    s.Require().NoError(err)

    // Add bob as a mutual contact
    bob := s.newMessenger(bobPassword, []string{bobAccountAddress})
    _, err = bob.Start()
    s.Require().NoError(err)
    defer TearDownMessenger(&s.Suite, bob)

    s.mutualContact(bob)

    // Alice creates an ecrypted community
    aliceCommunity, _ := createEncryptedCommunity(&s.Suite, alice)
    s.Require().True(aliceCommunity.Encrypted())
    advertiseCommunityTo(&s.Suite, aliceCommunity, alice, bob)

    // Bob creates an another encryped community
    bobCommunity, _ := createEncryptedCommunity(&s.Suite, bob)
    s.Require().True(bobCommunity.Encrypted())
    advertiseCommunityTo(&s.Suite, bobCommunity, bob, alice)

    // Add community to the Alice's profile showcase & get it on the Bob's side
    err = alice.SetProfileShowcasePreferences(&identity.ProfileShowcasePreferences{
        Communities: []*identity.ProfileShowcaseCommunityPreference{
            &identity.ProfileShowcaseCommunityPreference{
                CommunityID:        aliceCommunity.IDString(),
                ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts,
                Order:              0,
            },
            &identity.ProfileShowcaseCommunityPreference{
                CommunityID:        bobCommunity.IDString(),
                ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone,
                Order:              1,
            },
        },
    }, false)
    s.Require().NoError(err)

    contactID := types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey))
    _, err = WaitOnMessengerResponse(
        bob,
        func(r *MessengerResponse) bool {
            return r.updatedProfileShowcaseContactIDs[contactID] == true
        },
        "no messages",
    )
    s.Require().NoError(err)

    profileShowcase, err := bob.GetProfileShowcaseForContact(contactID, true)
    s.Require().NoError(err)

    // Verify community's data
    s.Require().Len(profileShowcase.Communities, 2)
    s.Require().Equal(profileShowcase.Communities[0].CommunityID, aliceCommunity.IDString())
    s.Require().Equal(profileShowcase.Communities[0].MembershipStatus, identity.ProfileShowcaseMembershipStatusProvenMember)
    s.Require().Equal(profileShowcase.Communities[1].CommunityID, bobCommunity.IDString())
    s.Require().Equal(profileShowcase.Communities[1].MembershipStatus, identity.ProfileShowcaseMembershipStatusUnproven)
}

// The scenario tested is as follow:
// 1) Alice creates an encrypted community
// 2) Bob add Alice become a mutual contacts
// 4) Alice presents the community in her profile showcase
// 5) Bob gets the community from Alice's profile showcase and tries to validate community's membership with expired grant
func (s *TestMessengerProfileShowcase) TestProfileShowcaseCommuniesGrantExpires() {
    grantInvokesProfileDispatchIntervalBackup := grantInvokesProfileDispatchInterval
    grantInvokesProfileDispatchInterval = 1 * time.Millisecond
    communitiesGrantExpirationTimeBackup := communities.GrantExpirationTime
    communities.GrantExpirationTime = 1 * time.Millisecond
    alice := s.m

    // Set Display name to pass shouldPublishChatIdentity check
    profileKp := accounts.GetProfileKeypairForTest(true, false, false)
    profileKp.KeyUID = alice.account.KeyUID
    profileKp.Accounts[0].KeyUID = alice.account.KeyUID

    err := alice.settings.SaveOrUpdateKeypair(profileKp)
    s.Require().NoError(err)

    err = alice.SetDisplayName("Alice")
    s.Require().NoError(err)

    // 1) Alice creates an encrypted community
    alice.communitiesManager.PermissionChecker = &testPermissionChecker{}

    community, _ := createEncryptedCommunity(&s.Suite, alice)
    s.Require().True(community.Encrypted())

    // 2) Bob add Alice become a mutual contacts
    bob := s.newMessenger(bobPassword, []string{bobAccountAddress})
    _, err = bob.Start()
    s.Require().NoError(err)
    defer TearDownMessenger(&s.Suite, bob)

    s.mutualContact(bob)
    advertiseCommunityTo(&s.Suite, community, alice, bob)

    // 3) Alice presents the community in her profile showcase
    err = alice.SetProfileShowcasePreferences(&identity.ProfileShowcasePreferences{
        Communities: []*identity.ProfileShowcaseCommunityPreference{
            &identity.ProfileShowcaseCommunityPreference{
                CommunityID:        community.IDString(),
                ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone,
                Order:              0,
            },
        },
    }, false)
    s.Require().NoError(err)

    // 5) Bob gets the community from Alice's profile showcase and tries to validate community's membership with expired grant
    contactID := types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey))
    _, err = WaitOnMessengerResponse(
        bob,
        func(r *MessengerResponse) bool {
            return r.updatedProfileShowcaseContactIDs[contactID] == true
        },
        "no messages",
    )
    s.Require().NoError(err)

    profileShowcase, err := bob.GetProfileShowcaseForContact(contactID, true)
    s.Require().NoError(err)
    s.Require().Len(profileShowcase.Communities, 1)
    s.Require().Equal(community.IDString(), profileShowcase.Communities[0].CommunityID)
    s.Require().Equal(identity.ProfileShowcaseMembershipStatusUnproven, profileShowcase.Communities[0].MembershipStatus)

    // Return values back because they can affect other tests
    grantInvokesProfileDispatchInterval = grantInvokesProfileDispatchIntervalBackup
    communities.GrantExpirationTime = communitiesGrantExpirationTimeBackup
}

// The scenario tested is as follow:
// 1) Owner creates an encrypted community
// 2) Bob add Alice become a mutual contacts
// 3) Alice joins the community
// 4) Alice presents the community in her profile showcase
// 5) Bob gets the community from Alice's profile showcase and validates community's membership with grant
// 6) Owner updates the grant
// 7) Bob should be able to validate the membership again
func (s *TestMessengerProfileShowcase) TestProfileShowcaseCommuniesDispatchOnGrantUpdate() {
    grantInvokesProfileDispatchIntervalBackup := grantInvokesProfileDispatchInterval
    grantInvokesProfileDispatchInterval = 1 * time.Millisecond
    alice := s.m

    // Set Display name to pass shouldPublishChatIdentity check
    profileKp := accounts.GetProfileKeypairForTest(true, false, false)
    profileKp.KeyUID = alice.account.KeyUID
    profileKp.Accounts[0].KeyUID = alice.account.KeyUID

    err := alice.settings.SaveOrUpdateKeypair(profileKp)
    s.Require().NoError(err)

    err = alice.SetDisplayName("Alice")
    s.Require().NoError(err)

    // 1) Owner creates an encrypted community
    owner := s.newMessenger("", []string{})
    _, err = owner.Start()
    s.Require().NoError(err)
    defer TearDownMessenger(&s.Suite, owner)

    owner.communitiesManager.PermissionChecker = &testPermissionChecker{}

    community, _ := createEncryptedCommunity(&s.Suite, owner)
    s.Require().True(community.Encrypted())

    // 2) Bob add Alice become a mutual contacts
    bob := s.newMessenger(bobPassword, []string{bobAccountAddress})
    _, err = bob.Start()
    s.Require().NoError(err)
    defer TearDownMessenger(&s.Suite, bob)

    s.mutualContact(bob)

    // 3) Alice joins the community
    advertiseCommunityTo(&s.Suite, community, owner, alice)
    advertiseCommunityTo(&s.Suite, community, owner, bob)

    alice.communitiesManager.PermissionChecker = &testPermissionChecker{}
    joinCommunity(&s.Suite, community.ID(), owner, alice, aliceAccountAddress, []string{aliceAddress1})

    joinedCommunities, err := alice.communitiesManager.Joined()
    s.Require().NoError(err)
    s.Require().Len(joinedCommunities, 1)
    s.Require().Equal(joinedCommunities[0].IDString(), community.IDString())
    s.Require().True(joinedCommunities[0].Encrypted())

    grant, clock, err := alice.communitiesManager.GetCommunityGrant(community.IDString())
    s.Require().NoError(err)
    s.Require().NotEqual(grant, []byte{})
    s.Require().True(clock > 0)

    // 4) Alice presents the community in her profile showcase
    err = alice.SetProfileShowcasePreferences(&identity.ProfileShowcasePreferences{
        Communities: []*identity.ProfileShowcaseCommunityPreference{
            &identity.ProfileShowcaseCommunityPreference{
                CommunityID:        community.IDString(),
                ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone,
                Order:              0,
            },
        },
    }, false)
    s.Require().NoError(err)

    // 5) Bob gets the community from Alice's profile showcase and validates community's membership with grant
    contactID := types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey))
    _, err = WaitOnMessengerResponse(
        bob,
        func(r *MessengerResponse) bool {
            return r.updatedProfileShowcaseContactIDs[contactID] == true
        },
        "no messages",
    )
    s.Require().NoError(err)

    profileShowcase, err := bob.GetProfileShowcaseForContact(contactID, true)
    s.Require().NoError(err)
    s.Require().Len(profileShowcase.Communities, 1)
    s.Require().Equal(community.IDString(), profileShowcase.Communities[0].CommunityID)
    s.Require().Equal(identity.ProfileShowcaseMembershipStatusProvenMember, profileShowcase.Communities[0].MembershipStatus)

    // 6) Owner updates the grant
    owner.updateGrantsForControlledCommunities()

    // Retrieve for grant clock update
    err = tt.RetryWithBackOff(func() error {
        _, err = alice.RetrieveAll()
        if err != nil {
            return err
        }
        _, updatedClock, err := alice.communitiesManager.GetCommunityGrant(community.IDString())
        if err != nil {
            return err
        }

        if clock == updatedClock {
            return errors.New("can't recive an updated grant")
        }
        return nil
    })
    s.Require().NoError(err)

    // 7) Bob should be able to validate the membership again
    _, err = WaitOnMessengerResponse(
        bob,
        func(r *MessengerResponse) bool {
            return r.updatedProfileShowcaseContactIDs[contactID] == true
        },
        "no messages",
    )
    s.Require().NoError(err)

    profileShowcase, err = bob.GetProfileShowcaseForContact(contactID, true)
    s.Require().NoError(err)
    s.Require().Len(profileShowcase.Communities, 1)
    s.Require().Equal(profileShowcase.Communities[0].CommunityID, community.IDString())
    s.Require().Equal(profileShowcase.Communities[0].MembershipStatus, identity.ProfileShowcaseMembershipStatusProvenMember)

    // Return values back because they can affect other tests
    grantInvokesProfileDispatchInterval = grantInvokesProfileDispatchIntervalBackup
}