github.com/status-im/status-go@v1.1.0/protocol/communities_messenger_test.go (about)

     1  package protocol
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/ecdsa"
     7  	"crypto/tls"
     8  	"encoding/base64"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"net/http"
    14  	"net/url"
    15  	"os"
    16  	"reflect"
    17  	"strings"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/golang/protobuf/proto"
    22  	"github.com/stretchr/testify/suite"
    23  	"go.uber.org/zap"
    24  
    25  	gethcommon "github.com/ethereum/go-ethereum/common"
    26  
    27  	"github.com/status-im/status-go/eth-node/crypto"
    28  	"github.com/status-im/status-go/eth-node/types"
    29  	"github.com/status-im/status-go/images"
    30  	"github.com/status-im/status-go/multiaccounts/accounts"
    31  	multiaccountscommon "github.com/status-im/status-go/multiaccounts/common"
    32  	"github.com/status-im/status-go/protocol/common"
    33  	"github.com/status-im/status-go/protocol/communities"
    34  	"github.com/status-im/status-go/protocol/discord"
    35  	"github.com/status-im/status-go/protocol/encryption/multidevice"
    36  	"github.com/status-im/status-go/protocol/protobuf"
    37  	"github.com/status-im/status-go/protocol/requests"
    38  	"github.com/status-im/status-go/protocol/transport"
    39  	"github.com/status-im/status-go/protocol/tt"
    40  	v1protocol "github.com/status-im/status-go/protocol/v1"
    41  	"github.com/status-im/status-go/server"
    42  	localnotifications "github.com/status-im/status-go/services/local-notifications"
    43  )
    44  
    45  func TestMessengerCommunitiesSuite(t *testing.T) {
    46  	suite.Run(t, new(MessengerCommunitiesSuite))
    47  }
    48  
    49  type MessengerCommunitiesSuite struct {
    50  	CommunitiesMessengerTestSuiteBase
    51  	owner *Messenger
    52  	bob   *Messenger
    53  	alice *Messenger
    54  }
    55  
    56  func (s *MessengerCommunitiesSuite) SetupTest() {
    57  	s.CommunitiesMessengerTestSuiteBase.SetupTest()
    58  
    59  	s.owner = s.newMessenger("", []string{})
    60  	s.owner.account.CustomizationColor = multiaccountscommon.CustomizationColorOrange
    61  	s.bob = s.newMessenger(bobPassword, []string{bobAccountAddress})
    62  	s.bob.account.CustomizationColor = multiaccountscommon.CustomizationColorBlue
    63  	s.alice = s.newMessenger(alicePassword, []string{aliceAccountAddress})
    64  	s.alice.account.CustomizationColor = multiaccountscommon.CustomizationColorArmy
    65  
    66  	s.owner.communitiesManager.RekeyInterval = 50 * time.Millisecond
    67  
    68  	_, err := s.owner.Start()
    69  	s.Require().NoError(err)
    70  	_, err = s.bob.Start()
    71  	s.Require().NoError(err)
    72  	_, err = s.alice.Start()
    73  	s.Require().NoError(err)
    74  
    75  	s.setMessengerDisplayName(s.owner, "Charlie")
    76  	s.setMessengerDisplayName(s.bob, "Bobby")
    77  	s.setMessengerDisplayName(s.alice, "Alice")
    78  }
    79  
    80  func (s *MessengerCommunitiesSuite) TearDownTest() {
    81  	TearDownMessenger(&s.Suite, s.owner)
    82  	TearDownMessenger(&s.Suite, s.bob)
    83  	TearDownMessenger(&s.Suite, s.alice)
    84  	s.CommunitiesMessengerTestSuiteBase.TearDownTest()
    85  }
    86  
    87  func (s *MessengerCommunitiesSuite) setMessengerDisplayName(m *Messenger, name string) {
    88  	profileKp := accounts.GetProfileKeypairForTest(true, false, false)
    89  	profileKp.KeyUID = m.account.KeyUID
    90  	profileKp.Name = DefaultProfileDisplayName
    91  	profileKp.Accounts[0].KeyUID = m.account.KeyUID
    92  
    93  	err := m.settings.SaveOrUpdateKeypair(profileKp)
    94  	s.Require().NoError(err)
    95  
    96  	err = m.SetDisplayName(name)
    97  	s.Require().NoError(err)
    98  }
    99  
   100  func (s *MessengerCommunitiesSuite) TestCreateCommunity() {
   101  	description := &requests.CreateCommunity{
   102  		Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
   103  		Name:        "status",
   104  		Color:       "#ffffff",
   105  		Description: "status community description",
   106  	}
   107  	response, err := s.bob.CreateCommunity(description, true)
   108  
   109  	s.Require().NoError(err)
   110  	s.Require().NotNil(response)
   111  	s.Require().Len(response.Communities(), 1)
   112  	s.Require().Len(response.Chats(), 1)
   113  }
   114  
   115  func (s *MessengerCommunitiesSuite) TestCreateCommunity_WithoutDefaultChannel() {
   116  
   117  	description := &requests.CreateCommunity{
   118  		Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
   119  		Name:        "status",
   120  		Color:       "#ffffff",
   121  		Description: "status community description",
   122  	}
   123  	response, err := s.bob.CreateCommunity(description, false)
   124  
   125  	s.Require().NoError(err)
   126  	s.Require().NotNil(response)
   127  	s.Require().Len(response.Communities(), 1)
   128  	s.Require().Len(response.Chats(), 0)
   129  }
   130  
   131  func (s *MessengerCommunitiesSuite) TestRetrieveCommunity() {
   132  	description := &requests.CreateCommunity{
   133  		Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
   134  		Name:        "status",
   135  		Color:       "#ffffff",
   136  		Description: "status community description",
   137  	}
   138  
   139  	response, err := s.bob.CreateCommunity(description, true)
   140  	s.Require().NoError(err)
   141  	s.Require().NotNil(response)
   142  	s.Require().Len(response.Communities(), 1)
   143  	s.Require().Len(response.CommunitiesSettings(), 1)
   144  	s.Require().Len(response.Chats(), 1)
   145  
   146  	community := response.Communities()[0]
   147  	communitySettings := response.CommunitiesSettings()[0]
   148  
   149  	s.Require().Equal(communitySettings.CommunityID, community.IDString())
   150  	s.Require().Equal(communitySettings.HistoryArchiveSupportEnabled, false)
   151  
   152  	// Send a community message
   153  	chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport)
   154  
   155  	inputMessage := common.NewMessage()
   156  	inputMessage.ChatId = chat.ID
   157  	inputMessage.Text = "some text"
   158  	inputMessage.CommunityID = community.IDString()
   159  
   160  	err = s.bob.SaveChat(chat)
   161  	s.Require().NoError(err)
   162  	_, err = s.bob.SendChatMessage(context.Background(), inputMessage)
   163  	s.Require().NoError(err)
   164  
   165  	// Pull message and make sure org is received
   166  	err = tt.RetryWithBackOff(func() error {
   167  		response, err = s.alice.RetrieveAll()
   168  		if err != nil {
   169  			return err
   170  		}
   171  		if len(response.Communities()) == 0 {
   172  			return errors.New("community not received")
   173  		}
   174  		return nil
   175  	})
   176  
   177  	s.Require().NoError(err)
   178  	communities, err := s.alice.Communities()
   179  	s.Require().NoError(err)
   180  	s.Require().Len(communities, 1)
   181  	s.Require().Len(response.Communities(), 1)
   182  	s.Require().Len(response.Messages(), 1)
   183  	s.Require().Equal(community.IDString(), response.Messages()[0].CommunityID)
   184  }
   185  
   186  func (s *MessengerCommunitiesSuite) TestJoiningOpenCommunityReturnsChatsResponse() {
   187  	ctx := context.Background()
   188  
   189  	openCommunityDescription := &requests.CreateCommunity{
   190  		Name:                         "open community",
   191  		Description:                  "open community to join with no requests",
   192  		Color:                        "#26a69a",
   193  		HistoryArchiveSupportEnabled: true,
   194  		Membership:                   protobuf.CommunityPermissions_AUTO_ACCEPT,
   195  		PinMessageAllMembersEnabled:  false,
   196  	}
   197  
   198  	response, err := s.bob.CreateCommunity(openCommunityDescription, true)
   199  	generalChannelChatID := response.Chats()[0].ID
   200  	s.Require().NoError(err)
   201  	s.Require().NotNil(response)
   202  	s.Require().Len(response.Communities(), 1)
   203  	s.Require().Len(response.CommunitiesSettings(), 1)
   204  	s.Require().Len(response.Chats(), 1)
   205  
   206  	community := response.Communities()[0]
   207  
   208  	chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport)
   209  
   210  	s.Require().NoError(s.bob.SaveChat(chat))
   211  
   212  	message := buildTestMessage(*chat)
   213  	message.CommunityID = community.IDString()
   214  
   215  	// Bob sends the community link to Alice
   216  	response, err = s.bob.SendChatMessage(ctx, message)
   217  	s.Require().NoError(err)
   218  	s.Require().NotNil(response)
   219  
   220  	// Retrieve community link & community for Alice
   221  	response, err = WaitOnMessengerResponse(
   222  		s.alice,
   223  		func(r *MessengerResponse) bool {
   224  			return len(r.Communities()) > 0
   225  		},
   226  		"message not received",
   227  	)
   228  	s.Require().NoError(err)
   229  	s.Require().NotNil(response)
   230  	s.Require().Len(response.Chats(), 1)
   231  
   232  	// Alice request to join community
   233  	request := s.createRequestToJoinCommunity(community.ID(), s.alice)
   234  	response, err = s.alice.RequestToJoinCommunity(request)
   235  	s.Require().NoError(err)
   236  	s.Require().NotNil(response)
   237  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
   238  
   239  	requestToJoin := response.RequestsToJoinCommunity()[0]
   240  	s.Require().NotNil(requestToJoin)
   241  	s.Require().Equal(community.ID(), requestToJoin.CommunityID)
   242  	s.Require().NotEmpty(requestToJoin.ID)
   243  	s.Require().NotEmpty(requestToJoin.Clock)
   244  	s.Require().Equal(requestToJoin.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
   245  	s.Require().Len(response.Communities(), 1)
   246  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin.State)
   247  
   248  	// Bobs receives the request to join and it's automatically accepted
   249  	response, err = WaitOnMessengerResponse(
   250  		s.bob,
   251  		func(r *MessengerResponse) bool {
   252  			return len(r.Communities()) > 0 && len(r.RequestsToJoinCommunity()) > 0
   253  		},
   254  		"message not received",
   255  	)
   256  	s.Require().NoError(err)
   257  	s.Require().NotNil(response)
   258  
   259  	// Alice receives the updated community description with channel information
   260  	response, err = WaitOnMessengerResponse(
   261  		s.alice,
   262  		func(r *MessengerResponse) bool {
   263  			return len(r.Communities()) > 0 && len(r.chats) > 0
   264  		},
   265  		"message not received",
   266  	)
   267  	s.Require().NoError(err)
   268  	s.Require().NotNil(response)
   269  
   270  	// Check whether community's general chat is available for Alice
   271  	_, exists := response.chats[generalChannelChatID]
   272  	s.Require().True(exists)
   273  }
   274  
   275  func (s *MessengerCommunitiesSuite) TestJoinCommunity() {
   276  	ctx := context.Background()
   277  
   278  	description := &requests.CreateCommunity{
   279  		Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
   280  		Name:        "status",
   281  		Color:       "#ffffff",
   282  		Description: "status community description",
   283  	}
   284  
   285  	// Create an community chat
   286  	response, err := s.bob.CreateCommunity(description, true)
   287  	s.Require().NoError(err)
   288  	s.Require().NotNil(response)
   289  	s.Require().Len(response.Communities(), 1)
   290  	s.Require().Len(response.CommunitiesSettings(), 1)
   291  
   292  	communitySettings := response.CommunitiesSettings()[0]
   293  	community := response.Communities()[0]
   294  
   295  	s.Require().Equal(communitySettings.CommunityID, community.IDString())
   296  	s.Require().Equal(communitySettings.HistoryArchiveSupportEnabled, false)
   297  
   298  	orgChat := &protobuf.CommunityChat{
   299  		Permissions: &protobuf.CommunityPermissions{
   300  			Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
   301  		},
   302  		Identity: &protobuf.ChatIdentity{
   303  			DisplayName: "status-core",
   304  			Emoji:       "😎",
   305  			Description: "status-core community chat",
   306  		},
   307  		HideIfPermissionsNotMet: true,
   308  	}
   309  	response, err = s.bob.CreateCommunityChat(community.ID(), orgChat)
   310  	s.Require().NoError(err)
   311  	s.Require().NotNil(response)
   312  	s.Require().Len(response.Communities(), 1)
   313  	s.Require().Len(response.Chats(), 1)
   314  
   315  	createdChat := response.Chats()[0]
   316  	s.Require().Equal(community.IDString(), createdChat.CommunityID)
   317  	s.Require().Equal(orgChat.Identity.DisplayName, createdChat.Name)
   318  	s.Require().Equal(orgChat.Identity.Emoji, createdChat.Emoji)
   319  	s.Require().NotEmpty(createdChat.ID)
   320  	s.Require().Equal(ChatTypeCommunityChat, createdChat.ChatType)
   321  	s.Require().True(createdChat.Active)
   322  	s.Require().NotEmpty(createdChat.Timestamp)
   323  	s.Require().True(strings.HasPrefix(createdChat.ID, community.IDString()))
   324  	s.Require().True(createdChat.HideIfPermissionsNotMet)
   325  
   326  	// Make sure the changes are reflect in the community
   327  	community = response.Communities()[0]
   328  
   329  	var chatIds []string
   330  	for k := range community.Chats() {
   331  		chatIds = append(chatIds, k)
   332  	}
   333  	category := &requests.CreateCommunityCategory{
   334  		CommunityID:  community.ID(),
   335  		CategoryName: "category-name",
   336  		ChatIDs:      chatIds,
   337  	}
   338  
   339  	response, err = s.bob.CreateCommunityCategory(category)
   340  	s.Require().NoError(err)
   341  	s.Require().NotNil(response)
   342  	s.Require().Len(response.Communities(), 1)
   343  	s.Require().Len(response.Communities()[0].Categories(), 1)
   344  
   345  	// Make sure the changes are reflect in the community
   346  	community = response.Communities()[0]
   347  	chats := community.Chats()
   348  	s.Require().Len(chats, 2)
   349  
   350  	// Send a community message
   351  	chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.bob.transport)
   352  
   353  	inputMessage := common.NewMessage()
   354  	inputMessage.ChatId = chat.ID
   355  	inputMessage.Text = "some text"
   356  	inputMessage.CommunityID = community.IDString()
   357  
   358  	err = s.bob.SaveChat(chat)
   359  	s.Require().NoError(err)
   360  	_, err = s.bob.SendChatMessage(context.Background(), inputMessage)
   361  	s.Require().NoError(err)
   362  
   363  	// Pull message and make sure org is received
   364  	err = tt.RetryWithBackOff(func() error {
   365  		response, err = s.alice.RetrieveAll()
   366  		if err != nil {
   367  			return err
   368  		}
   369  		if len(response.Communities()) == 0 {
   370  			return errors.New("community not received")
   371  		}
   372  		return nil
   373  	})
   374  
   375  	s.Require().NoError(err)
   376  	communities, err := s.alice.Communities()
   377  	s.Require().NoError(err)
   378  	s.Require().Len(communities, 1)
   379  	s.Require().Len(response.Communities(), 1)
   380  	s.Require().Len(response.Messages(), 1)
   381  	s.Require().Equal(community.IDString(), response.Messages()[0].CommunityID)
   382  
   383  	// We join the org
   384  	response, err = s.alice.JoinCommunity(ctx, community.ID(), false)
   385  	s.Require().NoError(err)
   386  	s.Require().NotNil(response)
   387  	s.Require().Len(response.Communities(), 1)
   388  	s.Require().True(response.Communities()[0].Joined())
   389  	s.Require().True(response.Communities()[0].JoinedAt() > 0)
   390  	s.Require().Len(response.Chats(), 2)
   391  	s.Require().Len(response.Communities()[0].Categories(), 1)
   392  	s.Require().Len(response.notifications, 1)
   393  	for _, notification := range response.notifications {
   394  		s.Require().Equal(notification.Title, community.Name())
   395  		s.Require().EqualValues(notification.BodyType, localnotifications.CategoryCommunityJoined)
   396  		s.Require().EqualValues(notification.Category, localnotifications.CategoryCommunityJoined)
   397  	}
   398  
   399  	var categoryID string
   400  	for k := range response.Communities()[0].Categories() {
   401  		categoryID = k
   402  	}
   403  
   404  	// The chat should be created
   405  
   406  	found := false
   407  	for _, createdChat := range response.Chats() {
   408  		if orgChat.Identity.DisplayName == createdChat.Name {
   409  			found = true
   410  			s.Require().Equal(community.IDString(), createdChat.CommunityID)
   411  			s.Require().Equal(orgChat.Identity.DisplayName, createdChat.Name)
   412  			s.Require().Equal(orgChat.Identity.Emoji, createdChat.Emoji)
   413  			s.Require().NotEmpty(createdChat.ID)
   414  			s.Require().Equal(ChatTypeCommunityChat, createdChat.ChatType)
   415  			s.Require().Equal(categoryID, createdChat.CategoryID)
   416  			s.Require().True(createdChat.Active)
   417  			s.Require().NotEmpty(createdChat.Timestamp)
   418  			s.Require().True(strings.HasPrefix(createdChat.ID, community.IDString()))
   419  		}
   420  	}
   421  	s.Require().True(found)
   422  
   423  	// Create another org chat
   424  	orgChat = &protobuf.CommunityChat{
   425  		Permissions: &protobuf.CommunityPermissions{
   426  			Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
   427  		},
   428  		Identity: &protobuf.ChatIdentity{
   429  			DisplayName: "status-core-ui",
   430  			Emoji:       "👍",
   431  			Description: "status-core-ui community chat",
   432  		},
   433  	}
   434  	response, err = s.bob.CreateCommunityChat(community.ID(), orgChat)
   435  	s.Require().NoError(err)
   436  	s.Require().NotNil(response)
   437  	s.Require().Len(response.Communities(), 1)
   438  	s.Require().Len(response.Chats(), 1)
   439  
   440  	var actualChat *Chat
   441  	// Pull message, this time it should be received as advertised automatically
   442  	err = tt.RetryWithBackOff(func() error {
   443  		response, err = s.alice.RetrieveAll()
   444  		if err != nil {
   445  			return err
   446  		}
   447  		if len(response.Communities()) != 1 {
   448  			return errors.New("community not received")
   449  		}
   450  
   451  		for _, c := range response.Chats() {
   452  			if c.Name == orgChat.Identity.DisplayName {
   453  				actualChat = c
   454  				return nil
   455  			}
   456  		}
   457  		return errors.New("chat not found")
   458  	})
   459  
   460  	s.Require().NoError(err)
   461  	communities, err = s.alice.Communities()
   462  	s.Require().NoError(err)
   463  	s.Require().Len(communities, 1)
   464  	s.Require().Len(response.Communities(), 1)
   465  	s.Require().NotNil(actualChat)
   466  	s.Require().Equal(community.IDString(), actualChat.CommunityID)
   467  	s.Require().Equal(orgChat.Identity.DisplayName, actualChat.Name)
   468  	s.Require().Equal(orgChat.Identity.Emoji, actualChat.Emoji)
   469  	s.Require().NotEmpty(actualChat.ID)
   470  	s.Require().Equal(ChatTypeCommunityChat, actualChat.ChatType)
   471  	s.Require().True(actualChat.Active)
   472  	s.Require().NotEmpty(actualChat.Timestamp)
   473  	s.Require().True(strings.HasPrefix(actualChat.ID, community.IDString()))
   474  
   475  	// We leave the org
   476  	response, err = s.alice.LeaveCommunity(community.ID())
   477  	s.Require().NoError(err)
   478  	s.Require().NotNil(response)
   479  	s.Require().Len(response.Communities(), 1)
   480  	s.Require().False(response.Communities()[0].Joined())
   481  	s.Require().Len(response.RemovedChats(), 3)
   482  }
   483  
   484  func (s *MessengerCommunitiesSuite) createCommunity() (*communities.Community, *Chat) {
   485  	return createCommunity(&s.Suite, s.owner)
   486  }
   487  
   488  func (s *MessengerCommunitiesSuite) advertiseCommunityTo(community *communities.Community, owner *Messenger, user *Messenger) {
   489  	advertiseCommunityTo(&s.Suite, community, owner, user)
   490  }
   491  
   492  func (s *MessengerCommunitiesSuite) TestCommunityContactCodeAdvertisement() {
   493  	// add bob's profile keypair
   494  	bobProfileKp := accounts.GetProfileKeypairForTest(true, false, false)
   495  	bobProfileKp.KeyUID = s.bob.account.KeyUID
   496  	bobProfileKp.Accounts[0].KeyUID = s.bob.account.KeyUID
   497  
   498  	err := s.bob.settings.SaveOrUpdateKeypair(bobProfileKp)
   499  	s.Require().NoError(err)
   500  
   501  	// create community and make bob and alice join to it
   502  	community, _ := s.createCommunity()
   503  	advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.bob)
   504  	advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice)
   505  
   506  	s.joinCommunity(community, s.owner, s.bob)
   507  	s.joinCommunity(community, s.owner, s.alice)
   508  
   509  	// Trigger ContactCodeAdvertisement
   510  	err = s.bob.SetDisplayName("bobby")
   511  	s.Require().NoError(err)
   512  	err = s.bob.SetBio("I like P2P chats")
   513  	s.Require().NoError(err)
   514  
   515  	// Ensure alice receives bob's ContactCodeAdvertisement
   516  	err = tt.RetryWithBackOff(func() error {
   517  		response, err := s.alice.RetrieveAll()
   518  		if err != nil {
   519  			return err
   520  		}
   521  		if len(response.Contacts) == 0 {
   522  			return errors.New("no contacts in response")
   523  		}
   524  		if response.Contacts[0].DisplayName != "bobby" {
   525  			return errors.New("display name was not updated")
   526  		}
   527  		if response.Contacts[0].Bio != "I like P2P chats" {
   528  			return errors.New("bio was not updated")
   529  		}
   530  		return nil
   531  	})
   532  	s.Require().NoError(err)
   533  }
   534  
   535  func (s *MessengerCommunitiesSuite) TestPostToCommunityChat() {
   536  	community, chat := s.createCommunity()
   537  
   538  	chatID := chat.ID
   539  	inputMessage := common.NewMessage()
   540  	inputMessage.ChatId = chatID
   541  	inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN
   542  	inputMessage.Text = "some text"
   543  
   544  	ctx := context.Background()
   545  
   546  	s.advertiseCommunityTo(community, s.owner, s.alice)
   547  
   548  	// Send message without even spectating fails
   549  	_, err := s.alice.SendChatMessage(ctx, inputMessage)
   550  	s.Require().Error(err)
   551  
   552  	// Sending a message without joining fails
   553  	_, err = s.alice.SpectateCommunity(community.ID())
   554  	s.Require().NoError(err)
   555  	_, err = s.alice.SendChatMessage(ctx, inputMessage)
   556  	s.Require().Error(err)
   557  
   558  	// Sending should work now
   559  	s.joinCommunity(community, s.owner, s.alice)
   560  	_, err = s.alice.SendChatMessage(ctx, inputMessage)
   561  	s.Require().NoError(err)
   562  
   563  	var response *MessengerResponse
   564  	// Pull message and make sure org is received
   565  	err = tt.RetryWithBackOff(func() error {
   566  		response, err = s.owner.RetrieveAll()
   567  		if err != nil {
   568  			return err
   569  		}
   570  		if len(response.messages) == 0 {
   571  			return errors.New("message not received")
   572  		}
   573  		return nil
   574  	})
   575  
   576  	s.Require().NoError(err)
   577  	s.Require().Len(response.Messages(), 1)
   578  	s.Require().Equal(inputMessage.Text, response.Messages()[0].Text)
   579  	s.Require().Equal(s.alice.account.GetCustomizationColor(), response.Contacts[0].CustomizationColor)
   580  
   581  	// check if response contains the chat we're interested in
   582  	// we use this instead of checking just the length of the chat because
   583  	// a CommunityDescription message might be received in the meantime due to syncing
   584  	// hence response.Chats() might contain the general chat, and the new chat;
   585  	// or only the new chat if the CommunityDescription message has not arrived
   586  	found := false
   587  	for _, chat := range response.Chats() {
   588  		if chat.ID == chatID {
   589  			found = true
   590  		}
   591  	}
   592  	s.Require().True(found)
   593  }
   594  
   595  func (s *MessengerCommunitiesSuite) TestPinMessageInCommunityChat() {
   596  	ctx := context.Background()
   597  
   598  	// Create a community
   599  	description := &requests.CreateCommunity{
   600  		Membership:                  protobuf.CommunityPermissions_AUTO_ACCEPT,
   601  		Name:                        "status",
   602  		Color:                       "#ffffff",
   603  		Description:                 "status community description",
   604  		PinMessageAllMembersEnabled: true,
   605  	}
   606  
   607  	response, err := s.owner.CreateCommunity(description, true)
   608  	s.Require().NoError(err)
   609  	s.Require().NotNil(response)
   610  	s.Require().Len(response.Communities(), 1)
   611  
   612  	community := response.Communities()[0]
   613  	s.Require().NotNil(community)
   614  	s.Require().Equal(community.AllowsAllMembersToPinMessage(), true)
   615  
   616  	// Create a community chat
   617  	orgChat := &protobuf.CommunityChat{
   618  		Permissions: &protobuf.CommunityPermissions{
   619  			Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
   620  		},
   621  		Identity: &protobuf.ChatIdentity{
   622  			DisplayName: "status-core",
   623  			Emoji:       "😎",
   624  			Description: "status-core community chat",
   625  		},
   626  	}
   627  	response, err = s.owner.CreateCommunityChat(community.ID(), orgChat)
   628  	s.Require().NoError(err)
   629  	s.Require().NotNil(response)
   630  	s.Require().Len(response.Communities(), 1)
   631  	s.Require().Len(response.Chats(), 1)
   632  	chat := response.Chats()[0]
   633  	s.Require().NotNil(chat)
   634  
   635  	s.advertiseCommunityTo(community, s.owner, s.bob)
   636  	s.joinCommunity(community, s.owner, s.bob)
   637  
   638  	inputMessage := common.NewMessage()
   639  	inputMessage.ChatId = chat.ID
   640  	inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN
   641  	inputMessage.Text = "message to be pinned"
   642  
   643  	sendResponse, err := s.bob.SendChatMessage(ctx, inputMessage)
   644  	s.Require().NoError(err)
   645  	s.Require().Len(sendResponse.Messages(), 1)
   646  
   647  	// bob should be able to pin the message
   648  	pinMessage := common.NewPinMessage()
   649  	pinMessage.ChatId = chat.ID
   650  	pinMessage.MessageId = inputMessage.ID
   651  	pinMessage.Pinned = true
   652  	sendResponse, err = s.bob.SendPinMessage(ctx, pinMessage)
   653  	s.Require().NoError(err)
   654  	s.Require().Len(sendResponse.PinMessages(), 1)
   655  
   656  	// alice does not fully join the community,
   657  	// so she should not be able to send the pin message
   658  	s.advertiseCommunityTo(community, s.owner, s.alice)
   659  	response, err = s.alice.SpectateCommunity(community.ID())
   660  	s.Require().NotNil(response)
   661  	s.Require().NoError(err)
   662  	failedPinMessage := common.NewPinMessage()
   663  	failedPinMessage.ChatId = chat.ID
   664  	failedPinMessage.MessageId = inputMessage.ID
   665  	failedPinMessage.Pinned = true
   666  	sendResponse, err = s.alice.SendPinMessage(ctx, failedPinMessage)
   667  	s.Require().Nil(sendResponse)
   668  	s.Require().Error(err, "can't pin message")
   669  }
   670  
   671  func (s *MessengerCommunitiesSuite) TestImportCommunity() {
   672  	ctx := context.Background()
   673  
   674  	community, _ := s.createCommunity()
   675  
   676  	category := &requests.CreateCommunityCategory{
   677  		CommunityID:  community.ID(),
   678  		CategoryName: "category-name",
   679  		ChatIDs:      []string{},
   680  	}
   681  
   682  	response, err := s.owner.CreateCommunityCategory(category)
   683  	s.Require().NoError(err)
   684  	community = response.Communities()[0]
   685  
   686  	s.advertiseCommunityTo(community, s.owner, s.bob)
   687  	s.joinCommunity(community, s.owner, s.bob)
   688  
   689  	privateKey, err := s.owner.ExportCommunity(community.ID())
   690  	s.Require().NoError(err)
   691  
   692  	_, err = s.alice.ImportCommunity(ctx, privateKey)
   693  	s.Require().NoError(err)
   694  
   695  	newDescription := "new description set post import"
   696  	_, err = s.alice.EditCommunity(&requests.EditCommunity{
   697  		CommunityID: community.ID(),
   698  		CreateCommunity: requests.CreateCommunity{
   699  			Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
   700  			Name:        community.Name(),
   701  			Color:       community.Color(),
   702  			Description: newDescription,
   703  		},
   704  	})
   705  	s.Require().NoError(err)
   706  
   707  	// bob receives new description
   708  	_, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool {
   709  		return len(r.Communities()) > 0 && r.Communities()[0].DescriptionText() == newDescription
   710  	}, "new description not received")
   711  	s.Require().NoError(err)
   712  }
   713  
   714  func (s *MessengerCommunitiesSuite) TestRemovePrivateKey() {
   715  	description := &requests.CreateCommunity{
   716  		Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
   717  		Name:        "status",
   718  		Color:       "#ffffff",
   719  		Description: "status community description",
   720  	}
   721  
   722  	// Create an community chat
   723  	response, err := s.bob.CreateCommunity(description, true)
   724  	s.Require().NoError(err)
   725  	s.Require().NotNil(response)
   726  	s.Require().Len(response.Communities(), 1)
   727  
   728  	community := response.Communities()[0]
   729  	s.Require().True(community.IsControlNode())
   730  	s.Require().True(community.IsControlNode())
   731  
   732  	response, err = s.bob.RemovePrivateKey(community.ID())
   733  	s.Require().NoError(err)
   734  	s.Require().Len(response.Communities(), 1)
   735  
   736  	community = response.Communities()[0]
   737  	s.Require().True(community.IsOwner())
   738  	s.Require().False(community.IsControlNode())
   739  }
   740  
   741  func (s *MessengerCommunitiesSuite) TestRolesAfterImportCommunity() {
   742  	ctx := context.Background()
   743  
   744  	description := &requests.CreateCommunity{
   745  		Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
   746  		Name:        "status",
   747  		Color:       "#ffffff",
   748  		Description: "status community description",
   749  	}
   750  
   751  	// Create a community chat
   752  	response, err := s.bob.CreateCommunity(description, true)
   753  	s.Require().NoError(err)
   754  	s.Require().NotNil(response)
   755  	s.Require().Len(response.Communities(), 1)
   756  	s.Require().Len(response.CommunitiesSettings(), 1)
   757  	s.Require().True(response.Communities()[0].Joined())
   758  	s.Require().True(response.Communities()[0].IsControlNode())
   759  	s.Require().True(response.Communities()[0].IsMemberOwner(&s.bob.identity.PublicKey))
   760  	s.Require().False(response.Communities()[0].IsMemberOwner(&s.alice.identity.PublicKey))
   761  
   762  	community := response.Communities()[0]
   763  	communitySettings := response.CommunitiesSettings()[0]
   764  
   765  	s.Require().Equal(communitySettings.CommunityID, community.IDString())
   766  	s.Require().Equal(communitySettings.HistoryArchiveSupportEnabled, false)
   767  
   768  	category := &requests.CreateCommunityCategory{
   769  		CommunityID:  community.ID(),
   770  		CategoryName: "category-name",
   771  		ChatIDs:      []string{},
   772  	}
   773  
   774  	response, err = s.bob.CreateCommunityCategory(category)
   775  	s.Require().NoError(err)
   776  	community = response.Communities()[0]
   777  
   778  	privateKey, err := s.bob.ExportCommunity(community.ID())
   779  	s.Require().NoError(err)
   780  
   781  	response, err = s.alice.ImportCommunity(ctx, privateKey)
   782  	s.Require().NoError(err)
   783  	s.Require().True(response.Communities()[0].IsMemberOwner(&s.alice.identity.PublicKey))
   784  }
   785  
   786  func (s *MessengerCommunitiesSuite) TestRequestAccess() {
   787  	ctx := context.Background()
   788  
   789  	description := &requests.CreateCommunity{
   790  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
   791  		Name:        "status",
   792  		Color:       "#ffffff",
   793  		Description: "status community description",
   794  	}
   795  
   796  	// Create an community chat
   797  	response, err := s.bob.CreateCommunity(description, true)
   798  	s.Require().NoError(err)
   799  	s.Require().NotNil(response)
   800  	s.Require().Len(response.Communities(), 1)
   801  
   802  	community := response.Communities()[0]
   803  
   804  	chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport)
   805  
   806  	s.Require().NoError(s.bob.SaveChat(chat))
   807  
   808  	message := buildTestMessage(*chat)
   809  	message.CommunityID = community.IDString()
   810  
   811  	// We send a community link to alice
   812  	response, err = s.bob.SendChatMessage(ctx, message)
   813  	s.Require().NoError(err)
   814  	s.Require().NotNil(response)
   815  
   816  	// Retrieve community link & community
   817  	err = tt.RetryWithBackOff(func() error {
   818  		response, err = s.alice.RetrieveAll()
   819  		if err != nil {
   820  			return err
   821  		}
   822  		if len(response.Communities()) == 0 {
   823  			return errors.New("message not received")
   824  		}
   825  		return nil
   826  	})
   827  
   828  	s.Require().NoError(err)
   829  
   830  	request := s.createRequestToJoinCommunity(community.ID(), s.alice)
   831  	// We try to join the org
   832  	response, err = s.alice.RequestToJoinCommunity(request)
   833  	s.Require().NoError(err)
   834  	s.Require().NotNil(response)
   835  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
   836  
   837  	s.Require().Len(response.ActivityCenterNotifications(), 1)
   838  
   839  	notification := response.ActivityCenterNotifications()[0]
   840  	s.Require().NotNil(notification)
   841  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
   842  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
   843  	s.Require().Equal(notification.Read, true)
   844  	s.Require().Equal(notification.Accepted, false)
   845  	s.Require().Equal(notification.Dismissed, false)
   846  
   847  	requestToJoin1 := response.RequestsToJoinCommunity()[0]
   848  	s.Require().NotNil(requestToJoin1)
   849  	s.Require().Equal(community.ID(), requestToJoin1.CommunityID)
   850  	s.Require().True(requestToJoin1.Our)
   851  	s.Require().NotEmpty(requestToJoin1.ID)
   852  	s.Require().NotEmpty(requestToJoin1.Clock)
   853  	s.Require().Equal(requestToJoin1.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
   854  	s.Require().Equal(requestToJoin1.CustomizationColor, s.alice.account.GetCustomizationColor())
   855  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State)
   856  
   857  	// Make sure clock is not empty
   858  	s.Require().NotEmpty(requestToJoin1.Clock)
   859  
   860  	s.Require().Len(response.Communities(), 1)
   861  	s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin1.Clock)
   862  
   863  	// pull all communities to make sure we set RequestedToJoinAt
   864  
   865  	allCommunities, err := s.alice.Communities()
   866  	s.Require().NoError(err)
   867  	s.Require().Len(allCommunities, 1)
   868  	s.Require().Equal(allCommunities[0].ID(), community.ID())
   869  	s.Require().Equal(allCommunities[0].RequestedToJoinAt(), requestToJoin1.Clock)
   870  
   871  	// pull to make sure it has been saved
   872  	requestsToJoin, err := s.alice.MyPendingRequestsToJoin()
   873  	s.Require().NoError(err)
   874  	s.Require().Len(requestsToJoin, 1)
   875  
   876  	// Make sure the requests are fetched also by community
   877  	requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID())
   878  	s.Require().NoError(err)
   879  	s.Require().Len(requestsToJoin, 1)
   880  
   881  	// Retrieve request to join
   882  	err = tt.RetryWithBackOff(func() error {
   883  		response, err = s.bob.RetrieveAll()
   884  		if err != nil {
   885  			return err
   886  		}
   887  		if len(response.RequestsToJoinCommunity()) == 0 {
   888  			return errors.New("request to join community not received")
   889  		}
   890  		return nil
   891  	})
   892  	s.Require().NoError(err)
   893  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
   894  
   895  	requestToJoin2 := response.RequestsToJoinCommunity()[0]
   896  
   897  	s.Require().NotNil(requestToJoin2)
   898  	s.Require().Equal(community.ID(), requestToJoin2.CommunityID)
   899  	s.Require().False(requestToJoin2.Our)
   900  	s.Require().NotEmpty(requestToJoin2.ID)
   901  	s.Require().NotEmpty(requestToJoin2.Clock)
   902  	s.Require().Equal(requestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
   903  	s.Require().Equal(requestToJoin2.CustomizationColor, s.alice.account.GetCustomizationColor())
   904  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin2.State)
   905  
   906  	s.Require().Equal(requestToJoin1.ID, requestToJoin2.ID)
   907  
   908  	s.Require().Len(response.ActivityCenterNotifications(), 1)
   909  
   910  	notification = response.ActivityCenterNotifications()[0]
   911  	s.Require().NotNil(notification)
   912  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
   913  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
   914  	s.Require().Equal(notification.Read, false)
   915  	s.Require().Equal(notification.Accepted, false)
   916  	s.Require().Equal(notification.Dismissed, false)
   917  
   918  	// Accept request
   919  
   920  	acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: requestToJoin1.ID}
   921  
   922  	response, err = s.bob.AcceptRequestToJoinCommunity(acceptRequestToJoin)
   923  	s.Require().NoError(err)
   924  	s.Require().NotNil(response)
   925  
   926  	s.Require().Len(response.Communities(), 1)
   927  
   928  	updatedCommunity := response.Communities()[0]
   929  
   930  	s.Require().NotNil(updatedCommunity)
   931  	s.Require().True(updatedCommunity.HasMember(&s.alice.identity.PublicKey))
   932  
   933  	s.Require().Len(response.ActivityCenterNotifications(), 1)
   934  
   935  	notification = response.ActivityCenterNotifications()[0]
   936  	s.Require().NotNil(notification)
   937  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
   938  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusAccepted)
   939  	s.Require().Equal(notification.Read, true)
   940  	s.Require().Equal(notification.Accepted, true)
   941  	s.Require().Equal(notification.Dismissed, false)
   942  
   943  	// Pull message and make sure org is received
   944  	err = tt.RetryWithBackOff(func() error {
   945  		response, err = s.alice.RetrieveAll()
   946  		if err != nil {
   947  			return err
   948  		}
   949  		if len(response.Communities()) == 0 {
   950  			return errors.New("community not received")
   951  		}
   952  		return nil
   953  	})
   954  
   955  	s.Require().NoError(err)
   956  	s.Require().NotNil(response)
   957  
   958  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
   959  	s.Require().Equal(communities.RequestToJoinStateAccepted, response.RequestsToJoinCommunity()[0].State)
   960  
   961  	s.Require().Len(response.ActivityCenterNotifications(), 1)
   962  
   963  	notification = response.ActivityCenterNotifications()[0]
   964  	s.Require().NotNil(notification)
   965  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
   966  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusAccepted)
   967  	s.Require().Equal(notification.Read, false)
   968  	s.Require().Equal(notification.Accepted, false)
   969  	s.Require().Equal(notification.Dismissed, false)
   970  
   971  	s.Require().Len(response.Communities(), 1)
   972  	aliceCommunity := response.Communities()[0]
   973  
   974  	s.Require().Equal(community.ID(), aliceCommunity.ID())
   975  	s.Require().True(aliceCommunity.HasMember(&s.alice.identity.PublicKey))
   976  
   977  	// Community should be joined at this point
   978  	s.Require().True(aliceCommunity.Joined())
   979  
   980  	// Make sure the requests are not pending on either sides
   981  	requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID())
   982  	s.Require().NoError(err)
   983  	s.Require().Len(requestsToJoin, 0)
   984  
   985  	requestsToJoin, err = s.alice.MyPendingRequestsToJoin()
   986  	s.Require().NoError(err)
   987  	s.Require().Len(requestsToJoin, 0)
   988  
   989  }
   990  
   991  func (s *MessengerCommunitiesSuite) TestDeletePendingRequestAccess() {
   992  	ctx := context.Background()
   993  
   994  	description := &requests.CreateCommunity{
   995  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
   996  		Name:        "status",
   997  		Color:       "#ffffff",
   998  		Description: "status community description",
   999  	}
  1000  
  1001  	// Bob creates a community
  1002  	response, err := s.bob.CreateCommunity(description, true)
  1003  	s.Require().NoError(err)
  1004  	s.Require().NotNil(response)
  1005  	s.Require().Len(response.Communities(), 1)
  1006  
  1007  	community := response.Communities()[0]
  1008  
  1009  	chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport)
  1010  
  1011  	s.Require().NoError(s.bob.SaveChat(chat))
  1012  
  1013  	message := buildTestMessage(*chat)
  1014  	message.CommunityID = community.IDString()
  1015  
  1016  	// Bob sends the community link to Alice
  1017  	response, err = s.bob.SendChatMessage(ctx, message)
  1018  	s.Require().NoError(err)
  1019  	s.Require().NotNil(response)
  1020  
  1021  	// Retrieve community link & community for Alice
  1022  	err = tt.RetryWithBackOff(func() error {
  1023  		response, err = s.alice.RetrieveAll()
  1024  		if err != nil {
  1025  			return err
  1026  		}
  1027  		if len(response.Communities()) == 0 {
  1028  			return errors.New("message not received")
  1029  		}
  1030  		return nil
  1031  	})
  1032  
  1033  	s.Require().NoError(err)
  1034  
  1035  	// Alice request to join community
  1036  	request := s.createRequestToJoinCommunity(community.ID(), s.alice)
  1037  	response, err = s.alice.RequestToJoinCommunity(request)
  1038  	s.Require().NoError(err)
  1039  	s.Require().NotNil(response)
  1040  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1041  
  1042  	requestToJoin := response.RequestsToJoinCommunity()[0]
  1043  	s.Require().NotNil(requestToJoin)
  1044  	s.Require().Equal(community.ID(), requestToJoin.CommunityID)
  1045  	s.Require().NotEmpty(requestToJoin.ID)
  1046  	s.Require().NotEmpty(requestToJoin.Clock)
  1047  	s.Require().Equal(requestToJoin.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  1048  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin.State)
  1049  
  1050  	s.Require().Len(response.Communities(), 1)
  1051  	s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin.Clock)
  1052  
  1053  	// updating request clock by 8 days back
  1054  	requestTime := uint64(time.Now().AddDate(0, 0, -8).Unix())
  1055  	err = s.alice.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime)
  1056  	s.Require().NoError(err)
  1057  
  1058  	// pull to make sure it has been saved
  1059  	requestsToJoin, err := s.alice.MyPendingRequestsToJoin()
  1060  	s.Require().NoError(err)
  1061  	s.Require().Len(requestsToJoin, 1)
  1062  
  1063  	requestToJoin = requestsToJoin[0]
  1064  	s.Require().Equal(requestToJoin.Clock, requestTime)
  1065  
  1066  	// Make sure the requests are fetched also by community
  1067  	requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID())
  1068  	s.Require().NoError(err)
  1069  	s.Require().Len(requestsToJoin, 1)
  1070  
  1071  	// Retrieve request to join
  1072  	bobRetrieveAll := func() (*MessengerResponse, error) {
  1073  		return s.bob.RetrieveAll()
  1074  	}
  1075  	err = tt.RetryWithBackOff(func() error {
  1076  		response, err = bobRetrieveAll()
  1077  		if err != nil {
  1078  			return err
  1079  		}
  1080  
  1081  		if len(response.RequestsToJoinCommunity()) == 0 {
  1082  			return errors.New("request to join community not received")
  1083  		}
  1084  
  1085  		// updating request clock by 8 days back
  1086  		requestToJoin := response.RequestsToJoinCommunity()[0]
  1087  		err = s.bob.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime)
  1088  		if err != nil {
  1089  			return err
  1090  		}
  1091  
  1092  		if len(response.ActivityCenterNotifications()) == 0 {
  1093  			return errors.New("request to join community notification not added in activity center")
  1094  		}
  1095  		return nil
  1096  	})
  1097  	s.Require().NoError(err)
  1098  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1099  
  1100  	// Check activity center notification for Bob
  1101  	fetchActivityCenterNotificationsForAdmin := func() (*ActivityCenterPaginationResponse, error) {
  1102  		return s.bob.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
  1103  			Cursor:        "",
  1104  			Limit:         10,
  1105  			ActivityTypes: []ActivityCenterType{},
  1106  			ReadType:      ActivityCenterQueryParamsReadUnread,
  1107  		})
  1108  	}
  1109  	notifications, err := fetchActivityCenterNotificationsForAdmin()
  1110  	s.Require().NoError(err)
  1111  	s.Require().Len(notifications.Notifications, 1)
  1112  
  1113  	notification := notifications.Notifications[0]
  1114  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
  1115  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
  1116  
  1117  	// Delete pending request to join
  1118  	response, err = s.alice.CheckAndDeletePendingRequestToJoinCommunity(ctx, true)
  1119  	s.Require().NoError(err)
  1120  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1121  	s.Require().Len(response.ActivityCenterNotifications(), 1)
  1122  
  1123  	requestToJoin = response.RequestsToJoinCommunity()[0]
  1124  	s.Require().True(requestToJoin.Deleted)
  1125  
  1126  	notification = response.ActivityCenterNotifications()[0]
  1127  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
  1128  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusIdle)
  1129  
  1130  	response, err = s.bob.CheckAndDeletePendingRequestToJoinCommunity(ctx, true)
  1131  	s.Require().NoError(err)
  1132  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1133  	s.Require().Len(response.ActivityCenterNotifications(), 1)
  1134  
  1135  	requestToJoin = response.RequestsToJoinCommunity()[0]
  1136  	s.Require().True(requestToJoin.Deleted)
  1137  
  1138  	notification = response.ActivityCenterNotifications()[0]
  1139  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
  1140  	s.Require().True(notification.Deleted)
  1141  
  1142  	// Alice request to join community
  1143  	request = s.createRequestToJoinCommunity(community.ID(), s.alice)
  1144  	response, err = s.alice.RequestToJoinCommunity(request)
  1145  	s.Require().NoError(err)
  1146  	s.Require().NotNil(response)
  1147  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1148  
  1149  	aliceRequestToJoin := response.RequestsToJoinCommunity()[0]
  1150  
  1151  	// Retrieve request to join and Check activity center notification for Bob
  1152  	err = tt.RetryWithBackOff(func() error {
  1153  		response, err = bobRetrieveAll()
  1154  		if err != nil {
  1155  			return err
  1156  		}
  1157  		// NOTE: we might receive multiple requests to join in case of re-transmissions
  1158  		// because request to join are hard deleted from the database, we can't check
  1159  		// whether that's an old one or a new one. So here we test for the specific id
  1160  
  1161  		for _, r := range response.RequestsToJoinCommunity() {
  1162  			if bytes.Equal(r.ID, aliceRequestToJoin.ID) {
  1163  				return nil
  1164  			}
  1165  		}
  1166  		return errors.New("request to join not found")
  1167  	})
  1168  	s.Require().NoError(err)
  1169  
  1170  	// Check activity center notification for Bob
  1171  	notifications, err = fetchActivityCenterNotificationsForAdmin()
  1172  
  1173  	s.Require().NoError(err)
  1174  	s.Require().Len(notifications.Notifications, 1)
  1175  
  1176  	notification = notifications.Notifications[0]
  1177  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
  1178  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
  1179  
  1180  }
  1181  
  1182  func (s *MessengerCommunitiesSuite) TestDeletePendingRequestAccessWithDeclinedState() {
  1183  	ctx := context.Background()
  1184  
  1185  	description := &requests.CreateCommunity{
  1186  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  1187  		Name:        "status",
  1188  		Color:       "#ffffff",
  1189  		Description: "status community description",
  1190  	}
  1191  
  1192  	// Bob creates a community
  1193  	response, err := s.bob.CreateCommunity(description, true)
  1194  	s.Require().NoError(err)
  1195  	s.Require().NotNil(response)
  1196  	s.Require().Len(response.Communities(), 1)
  1197  
  1198  	community := response.Communities()[0]
  1199  
  1200  	chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport)
  1201  
  1202  	s.Require().NoError(s.bob.SaveChat(chat))
  1203  
  1204  	message := buildTestMessage(*chat)
  1205  	message.CommunityID = community.IDString()
  1206  
  1207  	// Bob sends the community link to Alice
  1208  	response, err = s.bob.SendChatMessage(ctx, message)
  1209  	s.Require().NoError(err)
  1210  	s.Require().NotNil(response)
  1211  
  1212  	// Retrieve community link & community for Alice
  1213  	err = tt.RetryWithBackOff(func() error {
  1214  		response, err = s.alice.RetrieveAll()
  1215  		if err != nil {
  1216  			return err
  1217  		}
  1218  		if len(response.Communities()) == 0 {
  1219  			return errors.New("message not received")
  1220  		}
  1221  		return nil
  1222  	})
  1223  
  1224  	s.Require().NoError(err)
  1225  
  1226  	// Alice request to join community
  1227  	request := s.createRequestToJoinCommunity(community.ID(), s.alice)
  1228  	response, err = s.alice.RequestToJoinCommunity(request)
  1229  	s.Require().NoError(err)
  1230  	s.Require().NotNil(response)
  1231  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1232  
  1233  	notification := response.ActivityCenterNotifications()[0]
  1234  	s.Require().NotNil(notification)
  1235  	s.Require().NotEmpty(notification.ID)
  1236  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
  1237  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
  1238  	s.Require().Equal(notification.Deleted, false)
  1239  	s.Require().Equal(notification.Read, true)
  1240  
  1241  	requestToJoin := response.RequestsToJoinCommunity()[0]
  1242  	s.Require().NotNil(requestToJoin)
  1243  	s.Require().Equal(community.ID(), requestToJoin.CommunityID)
  1244  	s.Require().NotEmpty(requestToJoin.ID)
  1245  	s.Require().NotEmpty(requestToJoin.Clock)
  1246  	s.Require().Equal(requestToJoin.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  1247  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin.State)
  1248  
  1249  	s.Require().Len(response.Communities(), 1)
  1250  	s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin.Clock)
  1251  
  1252  	// Alice deletes activity center notification
  1253  	var updatedAt uint64 = 99
  1254  	_, err = s.alice.MarkActivityCenterNotificationsDeleted(ctx, []types.HexBytes{notification.ID}, updatedAt, true)
  1255  	s.Require().NoError(err)
  1256  
  1257  	// Check activity center notification for Bob after deleting
  1258  	notifications, err := s.alice.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
  1259  		Cursor:        "",
  1260  		Limit:         10,
  1261  		ActivityTypes: []ActivityCenterType{},
  1262  		ReadType:      ActivityCenterQueryParamsReadUnread,
  1263  	})
  1264  	s.Require().NoError(err)
  1265  	s.Require().Len(notifications.Notifications, 0)
  1266  
  1267  	// updating request clock by 8 days back
  1268  	requestTime := uint64(time.Now().AddDate(0, 0, -8).Unix())
  1269  	err = s.alice.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime)
  1270  	s.Require().NoError(err)
  1271  
  1272  	// pull to make sure it has been saved
  1273  	requestsToJoin, err := s.alice.MyPendingRequestsToJoin()
  1274  	s.Require().NoError(err)
  1275  	s.Require().Len(requestsToJoin, 1)
  1276  
  1277  	requestToJoin = requestsToJoin[0]
  1278  	s.Require().Equal(requestToJoin.Clock, requestTime)
  1279  
  1280  	// Make sure the requests are fetched also by community
  1281  	requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID())
  1282  	s.Require().NoError(err)
  1283  	s.Require().Len(requestsToJoin, 1)
  1284  
  1285  	bobRetrieveAll := func() (*MessengerResponse, error) {
  1286  		return s.bob.RetrieveAll()
  1287  	}
  1288  
  1289  	// Retrieve request to join
  1290  	err = tt.RetryWithBackOff(func() error {
  1291  		response, err = bobRetrieveAll()
  1292  		if err != nil {
  1293  			return err
  1294  		}
  1295  
  1296  		if len(response.RequestsToJoinCommunity()) == 0 {
  1297  			return errors.New("request to join community not received")
  1298  		}
  1299  
  1300  		// updating request clock by 8 days back
  1301  		requestToJoin := response.RequestsToJoinCommunity()[0]
  1302  		err = s.bob.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime)
  1303  		if err != nil {
  1304  			return err
  1305  		}
  1306  
  1307  		if len(response.ActivityCenterNotifications()) == 0 {
  1308  			return errors.New("request to join community notification not added in activity center")
  1309  		}
  1310  		return nil
  1311  	})
  1312  	s.Require().NoError(err)
  1313  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1314  
  1315  	// Check activity center notification for Bob
  1316  	fetchActivityCenterNotificationsForAdmin := func() (*ActivityCenterPaginationResponse, error) {
  1317  		return s.bob.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
  1318  			Cursor:        "",
  1319  			Limit:         10,
  1320  			ActivityTypes: []ActivityCenterType{},
  1321  			ReadType:      ActivityCenterQueryParamsReadUnread,
  1322  		})
  1323  	}
  1324  
  1325  	notifications, err = fetchActivityCenterNotificationsForAdmin()
  1326  	s.Require().NoError(err)
  1327  	s.Require().Len(notifications.Notifications, 1)
  1328  
  1329  	notification = notifications.Notifications[0]
  1330  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
  1331  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
  1332  
  1333  	// Check if admin sees requests correctly
  1334  	requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID())
  1335  	s.Require().NoError(err)
  1336  	s.Require().Len(requestsToJoin, 1)
  1337  
  1338  	requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID())
  1339  	s.Require().NoError(err)
  1340  	s.Require().Len(requestsToJoin, 0)
  1341  
  1342  	// Decline request
  1343  	declinedRequestToJoin := &requests.DeclineRequestToJoinCommunity{ID: requestToJoin.ID}
  1344  	response, err = s.bob.DeclineRequestToJoinCommunity(declinedRequestToJoin)
  1345  	s.Require().NoError(err)
  1346  	s.Require().NotNil(response)
  1347  	s.Require().Len(response.ActivityCenterNotifications(), 1)
  1348  
  1349  	notification = response.ActivityCenterNotifications()[0]
  1350  	s.Require().NotNil(notification)
  1351  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
  1352  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusDeclined)
  1353  	s.Require().Equal(notification.Read, true)
  1354  	s.Require().Equal(notification.Accepted, false)
  1355  	s.Require().Equal(notification.Dismissed, true)
  1356  
  1357  	// Check if admin sees requests correctly
  1358  	requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID())
  1359  	s.Require().NoError(err)
  1360  	s.Require().Len(requestsToJoin, 0)
  1361  
  1362  	requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID())
  1363  	s.Require().NoError(err)
  1364  	s.Require().Len(requestsToJoin, 1)
  1365  
  1366  	// Bob deletes activity center notification
  1367  	updatedAt++
  1368  	_, err = s.bob.MarkActivityCenterNotificationsDeleted(ctx, []types.HexBytes{notification.ID}, updatedAt, true)
  1369  	s.Require().NoError(err)
  1370  
  1371  	// Check activity center notification for Bob after deleting
  1372  	notifications, err = fetchActivityCenterNotificationsForAdmin()
  1373  	s.Require().NoError(err)
  1374  	s.Require().Len(notifications.Notifications, 0)
  1375  
  1376  	// Delete pending request to join
  1377  	response, err = s.alice.CheckAndDeletePendingRequestToJoinCommunity(ctx, true)
  1378  	s.Require().NoError(err)
  1379  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1380  
  1381  	requestToJoin = response.RequestsToJoinCommunity()[0]
  1382  	s.Require().True(requestToJoin.Deleted)
  1383  
  1384  	notification = response.ActivityCenterNotifications()[0]
  1385  	s.Require().NotNil(notification)
  1386  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
  1387  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusIdle)
  1388  	s.Require().Equal(notification.Read, false)
  1389  	s.Require().Equal(notification.Deleted, false)
  1390  
  1391  	notificationState := response.ActivityCenterState()
  1392  	s.Require().False(notificationState.HasSeen)
  1393  
  1394  	// Alice request to join community
  1395  	request = s.createRequestToJoinCommunity(community.ID(), s.alice)
  1396  	response, err = s.alice.RequestToJoinCommunity(request)
  1397  	s.Require().NoError(err)
  1398  	s.Require().NotNil(response)
  1399  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1400  
  1401  	// Retrieve request to join and Check activity center notification for Bob
  1402  	err = tt.RetryWithBackOff(func() error {
  1403  		response, err = bobRetrieveAll()
  1404  		if err != nil {
  1405  			return err
  1406  		}
  1407  
  1408  		if len(response.RequestsToJoinCommunity()) == 0 {
  1409  			return errors.New("request to join community not received")
  1410  		}
  1411  
  1412  		if len(response.ActivityCenterNotifications()) == 0 {
  1413  			return errors.New("request to join community notification not added in activity center")
  1414  		}
  1415  
  1416  		return nil
  1417  	})
  1418  	s.Require().NoError(err)
  1419  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1420  
  1421  	// Check activity center notification for Bob
  1422  	notifications, err = fetchActivityCenterNotificationsForAdmin()
  1423  
  1424  	s.Require().NoError(err)
  1425  	s.Require().Len(notifications.Notifications, 1)
  1426  
  1427  	notification = notifications.Notifications[0]
  1428  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
  1429  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
  1430  	s.Require().False(notification.Deleted)
  1431  
  1432  }
  1433  
  1434  func (s *MessengerCommunitiesSuite) TestCancelRequestAccess() {
  1435  	ctx := context.Background()
  1436  
  1437  	description := &requests.CreateCommunity{
  1438  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  1439  		Name:        "status",
  1440  		Color:       "#ffffff",
  1441  		Description: "status community description",
  1442  	}
  1443  
  1444  	// Create an community chat
  1445  	response, err := s.bob.CreateCommunity(description, true)
  1446  	s.Require().NoError(err)
  1447  	s.Require().NotNil(response)
  1448  	s.Require().Len(response.Communities(), 1)
  1449  
  1450  	community := response.Communities()[0]
  1451  
  1452  	chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport)
  1453  
  1454  	s.Require().NoError(s.bob.SaveChat(chat))
  1455  
  1456  	message := buildTestMessage(*chat)
  1457  	message.CommunityID = community.IDString()
  1458  
  1459  	// We send a community link to alice
  1460  	response, err = s.bob.SendChatMessage(ctx, message)
  1461  	s.Require().NoError(err)
  1462  	s.Require().NotNil(response)
  1463  
  1464  	// Retrieve community link & community
  1465  	err = tt.RetryWithBackOff(func() error {
  1466  		response, err = s.alice.RetrieveAll()
  1467  		if err != nil {
  1468  			return err
  1469  		}
  1470  		if len(response.Communities()) == 0 {
  1471  			return errors.New("message not received")
  1472  		}
  1473  		return nil
  1474  	})
  1475  
  1476  	s.Require().NoError(err)
  1477  
  1478  	request := s.createRequestToJoinCommunity(community.ID(), s.alice)
  1479  	// We try to join the org
  1480  	response, err = s.alice.RequestToJoinCommunity(request)
  1481  	s.Require().NoError(err)
  1482  	s.Require().NotNil(response)
  1483  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1484  
  1485  	requestToJoin1 := response.RequestsToJoinCommunity()[0]
  1486  	s.Require().NotNil(requestToJoin1)
  1487  	s.Require().Equal(community.ID(), requestToJoin1.CommunityID)
  1488  	s.Require().True(requestToJoin1.Our)
  1489  	s.Require().NotEmpty(requestToJoin1.ID)
  1490  	s.Require().NotEmpty(requestToJoin1.Clock)
  1491  	s.Require().Equal(requestToJoin1.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  1492  	s.Require().Equal(requestToJoin1.CustomizationColor, s.alice.account.GetCustomizationColor())
  1493  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State)
  1494  
  1495  	// Make sure clock is not empty
  1496  	s.Require().NotEmpty(requestToJoin1.Clock)
  1497  
  1498  	s.Require().Len(response.Communities(), 1)
  1499  	s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin1.Clock)
  1500  
  1501  	// pull all communities to make sure we set RequestedToJoinAt
  1502  
  1503  	allCommunities, err := s.alice.Communities()
  1504  	s.Require().NoError(err)
  1505  	s.Require().Len(allCommunities, 1)
  1506  	s.Require().Equal(allCommunities[0].ID(), community.ID())
  1507  	s.Require().Equal(allCommunities[0].RequestedToJoinAt(), requestToJoin1.Clock)
  1508  
  1509  	// pull to make sure it has been saved
  1510  	requestsToJoin, err := s.alice.MyPendingRequestsToJoin()
  1511  	s.Require().NoError(err)
  1512  	s.Require().Len(requestsToJoin, 1)
  1513  
  1514  	// Make sure the requests are fetched also by community
  1515  	requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID())
  1516  	s.Require().NoError(err)
  1517  	s.Require().Len(requestsToJoin, 1)
  1518  
  1519  	// Retrieve request to join
  1520  	err = tt.RetryWithBackOff(func() error {
  1521  		response, err = s.bob.RetrieveAll()
  1522  		if err != nil {
  1523  			return err
  1524  		}
  1525  		if len(response.RequestsToJoinCommunity()) == 0 {
  1526  			return errors.New("request to join community not received")
  1527  		}
  1528  		if len(response.ActivityCenterNotifications()) == 0 {
  1529  			return errors.New("request to join community notification not added in activity center")
  1530  		}
  1531  		return nil
  1532  	})
  1533  	s.Require().NoError(err)
  1534  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1535  
  1536  	requestToJoin2 := response.RequestsToJoinCommunity()[0]
  1537  
  1538  	s.Require().NotNil(requestToJoin2)
  1539  	s.Require().Equal(community.ID(), requestToJoin2.CommunityID)
  1540  	s.Require().False(requestToJoin2.Our)
  1541  	s.Require().NotEmpty(requestToJoin2.ID)
  1542  	s.Require().NotEmpty(requestToJoin2.Clock)
  1543  	s.Require().Equal(requestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  1544  	s.Require().Equal(requestToJoin2.CustomizationColor, s.alice.account.GetCustomizationColor())
  1545  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin2.State)
  1546  
  1547  	s.Require().Equal(requestToJoin1.ID, requestToJoin2.ID)
  1548  
  1549  	// Cancel request to join community
  1550  	requestsToJoin, err = s.alice.MyPendingRequestsToJoin()
  1551  	s.Require().NoError(err)
  1552  	s.Require().Len(requestsToJoin, 1)
  1553  
  1554  	requestToJoin := requestsToJoin[0]
  1555  
  1556  	requestToCancel := &requests.CancelRequestToJoinCommunity{ID: requestToJoin.ID}
  1557  	response, err = s.alice.CancelRequestToJoinCommunity(ctx, requestToCancel)
  1558  	s.Require().NoError(err)
  1559  	s.Require().NotNil(response)
  1560  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1561  	s.Require().Equal(communities.RequestToJoinStateCanceled, response.RequestsToJoinCommunity()[0].State)
  1562  
  1563  	// pull to make sure it has been saved
  1564  	cancelRequestsToJoin, err := s.alice.MyCanceledRequestsToJoin()
  1565  	s.Require().NoError(err)
  1566  	s.Require().Len(cancelRequestsToJoin, 1)
  1567  	s.Require().Equal(cancelRequestsToJoin[0].State, communities.RequestToJoinStateCanceled)
  1568  
  1569  	// Retrieve cancel request to join
  1570  	err = tt.RetryWithBackOff(func() error {
  1571  		response, err = s.bob.RetrieveAll()
  1572  		if err != nil {
  1573  			return err
  1574  		}
  1575  		if len(response.RequestsToJoinCommunity()) == 0 {
  1576  			return errors.New("request to join community not received")
  1577  		}
  1578  		return nil
  1579  	})
  1580  	s.Require().NoError(err)
  1581  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1582  
  1583  	s.Require().NoError(err)
  1584  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1585  
  1586  	// Retrieve activity center notifications for admin to make sure the request notification is deleted
  1587  	notifications, err := s.bob.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
  1588  		Cursor:        "",
  1589  		Limit:         10,
  1590  		ActivityTypes: []ActivityCenterType{},
  1591  		ReadType:      ActivityCenterQueryParamsReadUnread,
  1592  	})
  1593  
  1594  	s.Require().NoError(err)
  1595  	s.Require().Len(notifications.Notifications, 0)
  1596  
  1597  	cancelRequestToJoin2 := response.RequestsToJoinCommunity()[0]
  1598  
  1599  	s.Require().NotNil(cancelRequestToJoin2)
  1600  	s.Require().Equal(community.ID(), cancelRequestToJoin2.CommunityID)
  1601  	s.Require().False(cancelRequestToJoin2.Our)
  1602  	s.Require().NotEmpty(cancelRequestToJoin2.ID)
  1603  	s.Require().NotEmpty(cancelRequestToJoin2.Clock)
  1604  	s.Require().Equal(cancelRequestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  1605  	s.Require().Equal(cancelRequestToJoin2.CustomizationColor, s.alice.account.GetCustomizationColor())
  1606  	s.Require().Equal(communities.RequestToJoinStateCanceled, cancelRequestToJoin2.State)
  1607  }
  1608  
  1609  func (s *MessengerCommunitiesSuite) TestRequestAccessAgain() {
  1610  	description := &requests.CreateCommunity{
  1611  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  1612  		Name:        "status",
  1613  		Color:       "#ffffff",
  1614  		Description: "status community description",
  1615  	}
  1616  
  1617  	// Create an community chat
  1618  	response, err := s.bob.CreateCommunity(description, true)
  1619  	s.Require().NoError(err)
  1620  	s.Require().NotNil(response)
  1621  	s.Require().Len(response.Communities(), 1)
  1622  
  1623  	community := response.Communities()[0]
  1624  
  1625  	s.advertiseCommunityTo(community, s.bob, s.alice)
  1626  
  1627  	request := s.createRequestToJoinCommunity(community.ID(), s.alice)
  1628  	// We try to join the org
  1629  	response, err = s.alice.RequestToJoinCommunity(request)
  1630  	s.Require().NoError(err)
  1631  	s.Require().NotNil(response)
  1632  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1633  
  1634  	s.Require().Len(response.ActivityCenterNotifications(), 1)
  1635  
  1636  	notification := response.ActivityCenterNotifications()[0]
  1637  	s.Require().NotNil(notification)
  1638  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
  1639  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
  1640  	s.Require().Equal(notification.Read, true)
  1641  	s.Require().Equal(notification.Accepted, false)
  1642  	s.Require().Equal(notification.Dismissed, false)
  1643  
  1644  	requestToJoin1 := response.RequestsToJoinCommunity()[0]
  1645  	s.Require().NotNil(requestToJoin1)
  1646  	s.Require().Equal(community.ID(), requestToJoin1.CommunityID)
  1647  	s.Require().True(requestToJoin1.Our)
  1648  	s.Require().NotEmpty(requestToJoin1.ID)
  1649  	s.Require().NotEmpty(requestToJoin1.Clock)
  1650  	s.Require().Equal(requestToJoin1.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  1651  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State)
  1652  
  1653  	// Make sure clock is not empty
  1654  	s.Require().NotEmpty(requestToJoin1.Clock)
  1655  
  1656  	s.Require().Len(response.Communities(), 1)
  1657  	s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin1.Clock)
  1658  
  1659  	// pull all communities to make sure we set RequestedToJoinAt
  1660  
  1661  	allCommunities, err := s.alice.Communities()
  1662  	s.Require().NoError(err)
  1663  	s.Require().Len(allCommunities, 1)
  1664  	s.Require().Equal(allCommunities[0].ID(), community.ID())
  1665  	s.Require().Equal(allCommunities[0].RequestedToJoinAt(), requestToJoin1.Clock)
  1666  
  1667  	// pull to make sure it has been saved
  1668  	requestsToJoin, err := s.alice.MyPendingRequestsToJoin()
  1669  	s.Require().NoError(err)
  1670  	s.Require().Len(requestsToJoin, 1)
  1671  
  1672  	// Make sure the requests are fetched also by community
  1673  	requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID())
  1674  	s.Require().NoError(err)
  1675  	s.Require().Len(requestsToJoin, 1)
  1676  
  1677  	// Retrieve request to join
  1678  	err = tt.RetryWithBackOff(func() error {
  1679  		response, err = s.bob.RetrieveAll()
  1680  		if err != nil {
  1681  			return err
  1682  		}
  1683  		if len(response.RequestsToJoinCommunity()) == 0 {
  1684  			return errors.New("request to join community not received")
  1685  		}
  1686  		return nil
  1687  	})
  1688  	s.Require().NoError(err)
  1689  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1690  
  1691  	requestToJoin2 := response.RequestsToJoinCommunity()[0]
  1692  
  1693  	s.Require().NotNil(requestToJoin2)
  1694  	s.Require().Equal(community.ID(), requestToJoin2.CommunityID)
  1695  	s.Require().False(requestToJoin2.Our)
  1696  	s.Require().NotEmpty(requestToJoin2.ID)
  1697  	s.Require().NotEmpty(requestToJoin2.Clock)
  1698  	s.Require().Equal(requestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  1699  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin2.State)
  1700  
  1701  	s.Require().Equal(requestToJoin1.ID, requestToJoin2.ID)
  1702  
  1703  	// Check that a notification is been added to messenger
  1704  
  1705  	notifications := response.Notifications()
  1706  	s.Require().Len(notifications, 1)
  1707  	s.Require().NotEqual(notifications[0].ID.Hex(), "0x0000000000000000000000000000000000000000000000000000000000000000")
  1708  
  1709  	// Accept request
  1710  
  1711  	acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: requestToJoin1.ID}
  1712  
  1713  	response, err = s.bob.AcceptRequestToJoinCommunity(acceptRequestToJoin)
  1714  	s.Require().NoError(err)
  1715  	s.Require().NotNil(response)
  1716  
  1717  	s.Require().Len(response.ActivityCenterNotifications(), 1)
  1718  
  1719  	notification = response.ActivityCenterNotifications()[0]
  1720  	s.Require().NotNil(notification)
  1721  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
  1722  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusAccepted)
  1723  	s.Require().Equal(notification.Read, true)
  1724  	s.Require().Equal(notification.Accepted, true)
  1725  	s.Require().Equal(notification.Dismissed, false)
  1726  
  1727  	s.Require().Len(response.Communities(), 1)
  1728  
  1729  	updatedCommunity := response.Communities()[0]
  1730  
  1731  	s.Require().NotNil(updatedCommunity)
  1732  	s.Require().True(updatedCommunity.HasMember(&s.alice.identity.PublicKey))
  1733  
  1734  	// Pull message and make sure org is received
  1735  	err = tt.RetryWithBackOff(func() error {
  1736  		response, err = s.alice.RetrieveAll()
  1737  		if err != nil {
  1738  			return err
  1739  		}
  1740  		if len(response.Communities()) == 0 {
  1741  			return errors.New("community not received")
  1742  		}
  1743  		return nil
  1744  	})
  1745  
  1746  	s.Require().NoError(err)
  1747  	s.Require().NotNil(response)
  1748  
  1749  	s.Require().Len(response.Communities(), 1)
  1750  
  1751  	aliceCommunity := response.Communities()[0]
  1752  
  1753  	s.Require().Equal(community.ID(), aliceCommunity.ID())
  1754  	s.Require().True(aliceCommunity.HasMember(&s.alice.identity.PublicKey))
  1755  
  1756  	// Community should be joined at this point
  1757  	s.Require().True(aliceCommunity.Joined())
  1758  
  1759  	// Make sure the requests are not pending on either sides
  1760  	requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID())
  1761  	s.Require().NoError(err)
  1762  	s.Require().Len(requestsToJoin, 0)
  1763  
  1764  	requestsToJoin, err = s.alice.MyPendingRequestsToJoin()
  1765  	s.Require().NoError(err)
  1766  	s.Require().Len(requestsToJoin, 0)
  1767  
  1768  	// We request again
  1769  	request2 := s.createRequestToJoinCommunity(community.ID(), s.alice)
  1770  	// We try to join the org, it should error as we are already a member
  1771  	response, err = s.alice.RequestToJoinCommunity(request2)
  1772  	s.Require().Error(err)
  1773  
  1774  	// We kick the member
  1775  	response, err = s.bob.RemoveUserFromCommunity(
  1776  		community.ID(),
  1777  		common.PubkeyToHex(&s.alice.identity.PublicKey),
  1778  	)
  1779  	s.Require().NoError(err)
  1780  	s.Require().NotNil(response)
  1781  	s.Require().Len(response.Communities(), 1)
  1782  
  1783  	community = response.Communities()[0]
  1784  	s.Require().False(community.HasMember(&s.alice.identity.PublicKey))
  1785  
  1786  	// Alice should then be removed
  1787  	err = tt.RetryWithBackOff(func() error {
  1788  		response, err = s.alice.RetrieveAll()
  1789  		if err != nil {
  1790  			return err
  1791  		}
  1792  		if len(response.Communities()) == 0 {
  1793  			return errors.New("community not received")
  1794  		}
  1795  		if len(response.ActivityCenterNotifications()) == 0 {
  1796  			return errors.New("activity center notification not received")
  1797  		}
  1798  		if response.ActivityCenterState().HasSeen {
  1799  			return errors.New("activity center seen state is incorrect")
  1800  		}
  1801  		return nil
  1802  	})
  1803  
  1804  	// Check we got AC notification for Alice
  1805  	aliceNotifications, err := s.alice.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
  1806  		Cursor:        "",
  1807  		Limit:         10,
  1808  		ActivityTypes: []ActivityCenterType{ActivityCenterNotificationTypeCommunityKicked},
  1809  		ReadType:      ActivityCenterQueryParamsReadUnread,
  1810  	},
  1811  	)
  1812  	s.Require().NoError(err)
  1813  	s.Require().Len(aliceNotifications.Notifications, 1)
  1814  	s.Require().Equal(community.IDString(), aliceNotifications.Notifications[0].CommunityID)
  1815  
  1816  	s.Require().NoError(err)
  1817  	s.Require().NotNil(response)
  1818  
  1819  	s.Require().Len(response.Communities(), 1)
  1820  
  1821  	aliceCommunity = response.Communities()[0]
  1822  
  1823  	s.Require().Equal(community.ID(), aliceCommunity.ID())
  1824  	s.Require().False(aliceCommunity.HasMember(&s.alice.identity.PublicKey))
  1825  
  1826  	// Alice can request access again
  1827  	request3 := s.createRequestToJoinCommunity(community.ID(), s.alice)
  1828  	response, err = s.alice.RequestToJoinCommunity(request3)
  1829  	s.Require().NoError(err)
  1830  	s.Require().NotNil(response)
  1831  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1832  
  1833  	requestToJoin3 := response.RequestsToJoinCommunity()[0]
  1834  	s.Require().NotNil(requestToJoin3)
  1835  	s.Require().Equal(community.ID(), requestToJoin3.CommunityID)
  1836  	s.Require().True(requestToJoin3.Our)
  1837  	s.Require().NotEmpty(requestToJoin3.ID)
  1838  	s.Require().NotEmpty(requestToJoin3.Clock)
  1839  	s.Require().Equal(requestToJoin3.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  1840  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin3.State)
  1841  
  1842  	s.Require().Len(response.Communities(), 1)
  1843  	s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin3.Clock)
  1844  
  1845  	s.Require().Len(response.ActivityCenterNotifications(), 1)
  1846  
  1847  	notification = response.ActivityCenterNotifications()[0]
  1848  	s.Require().NotNil(notification)
  1849  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
  1850  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
  1851  
  1852  	// Retrieve request to join
  1853  	response, err = WaitOnMessengerResponse(s.bob,
  1854  		func(r *MessengerResponse) bool {
  1855  			return len(r.RequestsToJoinCommunity()) == 1
  1856  		},
  1857  		"request to join community was never 1",
  1858  	)
  1859  	s.Require().NoError(err)
  1860  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1861  
  1862  	requestToJoin4 := response.RequestsToJoinCommunity()[0]
  1863  
  1864  	s.Require().NotNil(requestToJoin4)
  1865  	s.Require().Equal(community.ID(), requestToJoin4.CommunityID)
  1866  	s.Require().False(requestToJoin4.Our)
  1867  	s.Require().NotEmpty(requestToJoin4.ID)
  1868  	s.Require().NotEmpty(requestToJoin4.Clock)
  1869  	s.Require().Equal(requestToJoin4.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  1870  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin4.State)
  1871  
  1872  	s.Require().Equal(requestToJoin3.ID, requestToJoin4.ID)
  1873  }
  1874  
  1875  func (s *MessengerCommunitiesSuite) TestDeclineAccess() {
  1876  	ctx := context.Background()
  1877  
  1878  	description := &requests.CreateCommunity{
  1879  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  1880  		Name:        "status",
  1881  		Color:       "#ffffff",
  1882  		Description: "status community description",
  1883  	}
  1884  
  1885  	// Create an community chat
  1886  	response, err := s.bob.CreateCommunity(description, true)
  1887  	s.Require().NoError(err)
  1888  	s.Require().NotNil(response)
  1889  	s.Require().Len(response.Communities(), 1)
  1890  
  1891  	community := response.Communities()[0]
  1892  
  1893  	chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport)
  1894  
  1895  	s.Require().NoError(s.bob.SaveChat(chat))
  1896  
  1897  	message := buildTestMessage(*chat)
  1898  	message.CommunityID = community.IDString()
  1899  
  1900  	// We send a community link to alice
  1901  	response, err = s.bob.SendChatMessage(ctx, message)
  1902  	s.Require().NoError(err)
  1903  	s.Require().NotNil(response)
  1904  
  1905  	// Retrieve community link & community
  1906  	err = tt.RetryWithBackOff(func() error {
  1907  		response, err = s.alice.RetrieveAll()
  1908  		if err != nil {
  1909  			return err
  1910  		}
  1911  		if len(response.Communities()) == 0 {
  1912  			return errors.New("message not received")
  1913  		}
  1914  		return nil
  1915  	})
  1916  
  1917  	s.Require().NoError(err)
  1918  
  1919  	request := s.createRequestToJoinCommunity(community.ID(), s.alice)
  1920  	// We try to join the org
  1921  	response, err = s.alice.RequestToJoinCommunity(request)
  1922  	s.Require().NoError(err)
  1923  	s.Require().NotNil(response)
  1924  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1925  
  1926  	requestToJoin1 := response.RequestsToJoinCommunity()[0]
  1927  	s.Require().NotNil(requestToJoin1)
  1928  	s.Require().Equal(community.ID(), requestToJoin1.CommunityID)
  1929  	s.Require().True(requestToJoin1.Our)
  1930  	s.Require().NotEmpty(requestToJoin1.ID)
  1931  	s.Require().NotEmpty(requestToJoin1.Clock)
  1932  	s.Require().Equal(requestToJoin1.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  1933  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State)
  1934  
  1935  	s.Require().Len(response.ActivityCenterNotifications(), 1)
  1936  
  1937  	notification := response.ActivityCenterNotifications()[0]
  1938  	s.Require().NotNil(notification)
  1939  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
  1940  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
  1941  	s.Require().Equal(notification.Read, true)
  1942  	s.Require().Equal(notification.Dismissed, false)
  1943  	s.Require().Equal(notification.Accepted, false)
  1944  
  1945  	// Make sure clock is not empty
  1946  	s.Require().NotEmpty(requestToJoin1.Clock)
  1947  
  1948  	s.Require().Len(response.Communities(), 1)
  1949  	s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin1.Clock)
  1950  
  1951  	// pull all communities to make sure we set RequestedToJoinAt
  1952  
  1953  	allCommunities, err := s.alice.Communities()
  1954  	s.Require().NoError(err)
  1955  	s.Require().Len(allCommunities, 1)
  1956  	s.Require().Equal(allCommunities[0].ID(), community.ID())
  1957  	s.Require().Equal(allCommunities[0].RequestedToJoinAt(), requestToJoin1.Clock)
  1958  
  1959  	// Retrieve request to join
  1960  	err = tt.RetryWithBackOff(func() error {
  1961  		response, err = s.bob.RetrieveAll()
  1962  		if err != nil {
  1963  			return err
  1964  		}
  1965  		if len(response.RequestsToJoinCommunity()) == 0 {
  1966  			return errors.New("request to join community not received")
  1967  		}
  1968  		return nil
  1969  	})
  1970  	s.Require().NoError(err)
  1971  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  1972  
  1973  	// Check if admin sees requests correctly
  1974  	requestsToJoin, err := s.bob.PendingRequestsToJoinForCommunity(community.ID())
  1975  	s.Require().NoError(err)
  1976  	s.Require().Len(requestsToJoin, 1)
  1977  
  1978  	requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID())
  1979  	s.Require().NoError(err)
  1980  	s.Require().Len(requestsToJoin, 0)
  1981  
  1982  	requestToJoin2 := response.RequestsToJoinCommunity()[0]
  1983  
  1984  	s.Require().NotNil(requestToJoin2)
  1985  	s.Require().Equal(community.ID(), requestToJoin2.CommunityID)
  1986  	s.Require().False(requestToJoin2.Our)
  1987  	s.Require().NotEmpty(requestToJoin2.ID)
  1988  	s.Require().NotEmpty(requestToJoin2.Clock)
  1989  	s.Require().Equal(requestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  1990  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin2.State)
  1991  
  1992  	s.Require().Equal(requestToJoin1.ID, requestToJoin2.ID)
  1993  
  1994  	// Decline request
  1995  	declinedRequestToJoin := &requests.DeclineRequestToJoinCommunity{ID: requestToJoin1.ID}
  1996  	response, err = s.bob.DeclineRequestToJoinCommunity(declinedRequestToJoin)
  1997  	s.Require().NoError(err)
  1998  	s.Require().NotNil(response)
  1999  
  2000  	s.Require().Len(response.ActivityCenterNotifications(), 1)
  2001  
  2002  	notification = response.ActivityCenterNotifications()[0]
  2003  	s.Require().NotNil(notification)
  2004  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
  2005  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusDeclined)
  2006  	s.Require().Equal(notification.Read, true)
  2007  	s.Require().Equal(notification.Accepted, false)
  2008  	s.Require().Equal(notification.Dismissed, true)
  2009  
  2010  	// Check if admin sees requests correctly
  2011  	requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID())
  2012  	s.Require().NoError(err)
  2013  	s.Require().Len(requestsToJoin, 0)
  2014  
  2015  	requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID())
  2016  	s.Require().NoError(err)
  2017  	s.Require().Len(requestsToJoin, 1)
  2018  
  2019  	// Accept declined request
  2020  	acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: requestToJoin1.ID}
  2021  	response, err = s.bob.AcceptRequestToJoinCommunity(acceptRequestToJoin)
  2022  	s.Require().NoError(err)
  2023  	s.Require().NotNil(response)
  2024  
  2025  	s.Require().Len(response.Communities(), 1)
  2026  
  2027  	updatedCommunity := response.Communities()[0]
  2028  
  2029  	s.Require().NotNil(updatedCommunity)
  2030  	s.Require().True(updatedCommunity.HasMember(&s.alice.identity.PublicKey))
  2031  
  2032  	s.Require().Len(response.ActivityCenterNotifications(), 1)
  2033  
  2034  	notification = response.ActivityCenterNotifications()[0]
  2035  	s.Require().NotNil(notification)
  2036  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest)
  2037  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusAccepted)
  2038  
  2039  	// Pull message and make sure org is received
  2040  	err = tt.RetryWithBackOff(func() error {
  2041  		response, err = s.alice.RetrieveAll()
  2042  		if err != nil {
  2043  			return err
  2044  		}
  2045  		if len(response.Communities()) == 0 {
  2046  			return errors.New("community not received")
  2047  		}
  2048  		return nil
  2049  	})
  2050  
  2051  	s.Require().NoError(err)
  2052  	s.Require().NotNil(response)
  2053  
  2054  	s.Require().Len(response.Communities(), 1)
  2055  
  2056  	aliceCommunity := response.Communities()[0]
  2057  
  2058  	s.Require().Equal(community.ID(), aliceCommunity.ID())
  2059  	s.Require().True(aliceCommunity.HasMember(&s.alice.identity.PublicKey))
  2060  
  2061  	// Community should be joined at this point
  2062  	s.Require().True(aliceCommunity.Joined())
  2063  
  2064  	// Make sure the requests are not pending on either sides
  2065  	requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID())
  2066  	s.Require().NoError(err)
  2067  	s.Require().Len(requestsToJoin, 0)
  2068  
  2069  	requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID())
  2070  	s.Require().NoError(err)
  2071  	s.Require().Len(requestsToJoin, 0)
  2072  
  2073  	requestsToJoin, err = s.alice.MyPendingRequestsToJoin()
  2074  	s.Require().NoError(err)
  2075  	s.Require().Len(requestsToJoin, 0)
  2076  }
  2077  
  2078  func (s *MessengerCommunitiesSuite) TestLeaveAndRejoinCommunity() {
  2079  	community, _ := s.createCommunity()
  2080  	advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice)
  2081  	advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.bob)
  2082  
  2083  	s.joinCommunity(community, s.owner, s.alice)
  2084  	s.joinCommunity(community, s.owner, s.bob)
  2085  
  2086  	joinedCommunities, err := s.owner.communitiesManager.Joined()
  2087  	s.Require().NoError(err)
  2088  	s.Require().Equal(3, joinedCommunities[0].MembersCount())
  2089  
  2090  	response, err := s.alice.LeaveCommunity(community.ID())
  2091  	s.Require().NoError(err)
  2092  	s.Require().NotNil(response)
  2093  	s.Require().Len(response.Communities(), 1)
  2094  	s.Require().False(response.Communities()[0].Joined())
  2095  
  2096  	// admin should receive alice's request to leave
  2097  	// and then update and advertise community members list accordingly
  2098  
  2099  	verifyCommunityMembers := func(user *Messenger) error {
  2100  		response, err := user.RetrieveAll()
  2101  		if err != nil {
  2102  			return err
  2103  		}
  2104  
  2105  		if len(response.Communities()) == 0 {
  2106  			return errors.New("no communities in response")
  2107  		}
  2108  
  2109  		var communityMembersError error = nil
  2110  
  2111  		if response.Communities()[0].MembersCount() != 2 {
  2112  			communityMembersError = fmt.Errorf("invalid number of members: %d", response.Communities()[0].MembersCount())
  2113  		} else if !response.Communities()[0].HasMember(&s.owner.identity.PublicKey) {
  2114  			communityMembersError = errors.New("admin removed from community")
  2115  		} else if !response.Communities()[0].HasMember(&s.bob.identity.PublicKey) {
  2116  			communityMembersError = errors.New("bob removed from community")
  2117  		} else if response.Communities()[0].HasMember(&s.alice.identity.PublicKey) {
  2118  			communityMembersError = errors.New("alice not removed from community")
  2119  		}
  2120  
  2121  		return communityMembersError
  2122  	}
  2123  	err = tt.RetryWithBackOff(func() error {
  2124  		return verifyCommunityMembers(s.owner)
  2125  	})
  2126  	s.Require().NoError(err)
  2127  	err = tt.RetryWithBackOff(func() error {
  2128  		return verifyCommunityMembers(s.bob)
  2129  	})
  2130  	s.Require().NoError(err)
  2131  
  2132  	joinedCommunities, err = s.owner.communitiesManager.Joined()
  2133  	s.Require().NoError(err)
  2134  	s.Require().Equal(2, joinedCommunities[0].MembersCount())
  2135  
  2136  	chats, err := s.alice.persistence.Chats()
  2137  	s.Require().NoError(err)
  2138  	var numberInactiveChats = 0
  2139  	for i := 0; i < len(chats); i++ {
  2140  		if !chats[i].Active {
  2141  			numberInactiveChats++
  2142  		}
  2143  	}
  2144  	s.Require().Equal(2, numberInactiveChats)
  2145  
  2146  	// alice can rejoin
  2147  	s.joinCommunity(community, s.owner, s.alice)
  2148  
  2149  	joinedCommunities, err = s.owner.communitiesManager.Joined()
  2150  	s.Require().NoError(err)
  2151  	s.Require().Equal(3, joinedCommunities[0].MembersCount())
  2152  
  2153  	chats, err = s.alice.persistence.Chats()
  2154  	s.Require().NoError(err)
  2155  	numberInactiveChats = 0
  2156  	for i := 0; i < len(chats); i++ {
  2157  		if !chats[i].Active {
  2158  			numberInactiveChats++
  2159  		}
  2160  	}
  2161  	s.Require().Equal(1, numberInactiveChats)
  2162  }
  2163  
  2164  func (s *MessengerCommunitiesSuite) TestShareCommunity() {
  2165  	description := &requests.CreateCommunity{
  2166  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  2167  		Name:        "status",
  2168  		Description: "status community description",
  2169  		Color:       "#FFFFFF",
  2170  		Image:       "../_assets/tests/status.png",
  2171  		ImageAx:     0,
  2172  		ImageAy:     0,
  2173  		ImageBx:     256,
  2174  		ImageBy:     256,
  2175  		Banner: images.CroppedImage{
  2176  			ImagePath: "../_assets/tests/IMG_1205.HEIC.jpg",
  2177  			X:         0,
  2178  			Y:         0,
  2179  			Width:     160,
  2180  			Height:    90,
  2181  		},
  2182  	}
  2183  
  2184  	response, err := s.owner.CreateCommunity(description, true)
  2185  
  2186  	s.Require().NoError(err)
  2187  	s.Require().NotNil(response)
  2188  	s.Require().Len(response.Communities(), 1)
  2189  	s.Require().Len(response.Chats(), 1)
  2190  	community := response.Communities()[0]
  2191  
  2192  	inputMessageText := "Come on alice, You'll like it here!"
  2193  	// Alice shares community with Bob
  2194  	response, err = s.owner.ShareCommunity(&requests.ShareCommunity{
  2195  		CommunityID:   community.ID(),
  2196  		Users:         []types.HexBytes{common.PubkeyToHexBytes(&s.alice.identity.PublicKey)},
  2197  		InviteMessage: inputMessageText,
  2198  	})
  2199  
  2200  	s.Require().NoError(err)
  2201  	s.Require().NotNil(response)
  2202  	s.Require().Len(response.Messages(), 1)
  2203  	sentMessageText := response.Messages()[0].Text
  2204  
  2205  	_, err = WaitOnMessengerResponse(s.alice, func(r *MessengerResponse) bool {
  2206  		return len(r.Messages()) > 0
  2207  	}, "Messages not received")
  2208  
  2209  	communityURL := response.Messages()[0].UnfurledStatusLinks.GetUnfurledStatusLinks()[0].Url
  2210  	s.Require().NoError(err)
  2211  	s.Require().Len(response.Messages(), 1)
  2212  	s.Require().Equal(fmt.Sprintf("%s\n%s", inputMessageText, communityURL), sentMessageText)
  2213  	s.Require().NotNil(response.Messages()[0].UnfurledStatusLinks.GetUnfurledStatusLinks()[0].GetCommunity().CommunityId)
  2214  }
  2215  
  2216  func (s *MessengerCommunitiesSuite) TestShareCommunityWithPreviousMember() {
  2217  	description := &requests.CreateCommunity{
  2218  		Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
  2219  		Name:        "status",
  2220  		Color:       "#ffffff",
  2221  		Description: "status community description",
  2222  	}
  2223  
  2224  	// Create an community chat
  2225  	response, err := s.bob.CreateCommunity(description, true)
  2226  	s.Require().NoError(err)
  2227  	s.Require().NotNil(response)
  2228  	s.Require().Len(response.Communities(), 1)
  2229  
  2230  	community := response.Communities()[0]
  2231  
  2232  	orgChat := &protobuf.CommunityChat{
  2233  		Permissions: &protobuf.CommunityPermissions{
  2234  			Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
  2235  		},
  2236  		Identity: &protobuf.ChatIdentity{
  2237  			DisplayName: "status-core",
  2238  			Emoji:       "😎",
  2239  			Description: "status-core community chat",
  2240  		},
  2241  	}
  2242  	response, err = s.bob.CreateCommunityChat(community.ID(), orgChat)
  2243  	s.Require().NoError(err)
  2244  	s.Require().NotNil(response)
  2245  	s.Require().Len(response.Communities(), 1)
  2246  	s.Require().Len(response.Chats(), 1)
  2247  
  2248  	community = response.Communities()[0]
  2249  	communityChat := response.Chats()[0]
  2250  
  2251  	// Add Alice to the community before sharing it
  2252  	_, err = community.AddMember(&s.alice.identity.PublicKey, []protobuf.CommunityMember_Roles{}, community.Clock())
  2253  	s.Require().NoError(err)
  2254  
  2255  	err = s.bob.communitiesManager.SaveCommunity(community)
  2256  	s.Require().NoError(err)
  2257  
  2258  	advertiseCommunityToUserOldWay(&s.Suite, community, s.bob, s.alice)
  2259  
  2260  	// Add bob to contacts so it does not go on activity center
  2261  	bobPk := common.PubkeyToHex(&s.bob.identity.PublicKey)
  2262  	request := &requests.AddContact{ID: bobPk}
  2263  	_, err = s.alice.AddContact(context.Background(), request)
  2264  	s.Require().NoError(err)
  2265  
  2266  	// Alice should have the Joined status for the community
  2267  	communityInResponse := response.Communities()[0]
  2268  	s.Require().Equal(community.ID(), communityInResponse.ID())
  2269  	s.Require().True(communityInResponse.Joined())
  2270  
  2271  	// Alice is able to receive messages in the community
  2272  	inputMessage := buildTestMessage(*communityChat)
  2273  	sendResponse, err := s.bob.SendChatMessage(context.Background(), inputMessage)
  2274  	messageID := sendResponse.Messages()[0].ID
  2275  	s.NoError(err)
  2276  	s.Require().Len(sendResponse.Messages(), 1)
  2277  
  2278  	response, err = WaitOnMessengerResponse(
  2279  		s.alice,
  2280  		func(r *MessengerResponse) bool { return len(r.messages) > 0 },
  2281  		"no messages",
  2282  	)
  2283  	s.Require().NoError(err)
  2284  	s.Require().Len(response.Chats(), 1)
  2285  	s.Require().Len(response.Messages(), 1)
  2286  	s.Require().Equal(messageID, response.Messages()[0].ID)
  2287  }
  2288  
  2289  func (s *MessengerCommunitiesSuite) TestBanUser() {
  2290  	community, _ := s.createCommunity()
  2291  
  2292  	s.advertiseCommunityTo(community, s.owner, s.alice)
  2293  	s.joinCommunity(community, s.owner, s.alice)
  2294  
  2295  	response, err := s.owner.BanUserFromCommunity(
  2296  		context.Background(),
  2297  		&requests.BanUserFromCommunity{
  2298  			CommunityID: community.ID(),
  2299  			User:        common.PubkeyToHexBytes(&s.alice.identity.PublicKey),
  2300  		},
  2301  	)
  2302  	s.Require().NoError(err)
  2303  	s.Require().NotNil(response)
  2304  	s.Require().Len(response.Communities(), 1)
  2305  
  2306  	community = response.Communities()[0]
  2307  	s.Require().False(community.HasMember(&s.alice.identity.PublicKey))
  2308  	s.Require().True(community.IsBanned(&s.alice.identity.PublicKey))
  2309  	s.Require().Len(community.PendingAndBannedMembers(), 1)
  2310  	s.Require().Equal(community.PendingAndBannedMembers()[s.alice.IdentityPublicKeyString()], communities.CommunityMemberBanned)
  2311  
  2312  	response, err = WaitOnMessengerResponse(
  2313  		s.alice,
  2314  		func(r *MessengerResponse) bool {
  2315  			return len(r.Communities()) == 1 &&
  2316  				len(r.Communities()[0].PendingAndBannedMembers()) == 1 &&
  2317  				community.PendingAndBannedMembers()[s.alice.IdentityPublicKeyString()] == communities.CommunityMemberBanned &&
  2318  				r.Communities()[0].IsBanned(&s.alice.identity.PublicKey) &&
  2319  				len(r.ActivityCenterNotifications()) == 1 &&
  2320  				!r.ActivityCenterState().HasSeen &&
  2321  				!r.Communities()[0].Spectated() &&
  2322  				!r.Communities()[0].Joined()
  2323  
  2324  		},
  2325  		"no message about alice ban",
  2326  	)
  2327  
  2328  	s.Require().NoError(err)
  2329  	s.Require().NotNil(response)
  2330  
  2331  	// Check we got ban AC notification for Alice
  2332  	aliceNotifications, err := s.alice.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
  2333  		Cursor:        "",
  2334  		Limit:         10,
  2335  		ActivityTypes: []ActivityCenterType{ActivityCenterNotificationTypeCommunityBanned},
  2336  		ReadType:      ActivityCenterQueryParamsReadUnread,
  2337  	},
  2338  	)
  2339  	s.Require().NoError(err)
  2340  	s.Require().Len(aliceNotifications.Notifications, 1)
  2341  	s.Require().Equal(community.IDString(), aliceNotifications.Notifications[0].CommunityID)
  2342  
  2343  	response, err = s.owner.UnbanUserFromCommunity(
  2344  		&requests.UnbanUserFromCommunity{
  2345  			CommunityID: community.ID(),
  2346  			User:        common.PubkeyToHexBytes(&s.alice.identity.PublicKey),
  2347  		},
  2348  	)
  2349  	s.Require().NoError(err)
  2350  	s.Require().NotNil(response)
  2351  	s.Require().Len(response.Communities(), 1)
  2352  
  2353  	community = response.Communities()[0]
  2354  	s.Require().False(community.HasMember(&s.alice.identity.PublicKey))
  2355  	s.Require().False(community.IsBanned(&s.alice.identity.PublicKey))
  2356  	s.Require().Len(community.PendingAndBannedMembers(), 0)
  2357  
  2358  	response, err = WaitOnMessengerResponse(
  2359  		s.alice,
  2360  		func(r *MessengerResponse) bool {
  2361  			return len(r.Communities()) == 1 &&
  2362  				len(r.Communities()[0].PendingAndBannedMembers()) == 0 &&
  2363  				!r.Communities()[0].IsBanned(&s.alice.identity.PublicKey) &&
  2364  				len(r.ActivityCenterNotifications()) == 1 && !r.ActivityCenterState().HasSeen
  2365  		},
  2366  		"no message about alice unban",
  2367  	)
  2368  
  2369  	s.Require().NoError(err)
  2370  	s.Require().NotNil(response)
  2371  	s.Require().Len(response.Communities(), 1)
  2372  	s.Require().False(response.Communities()[0].Joined())
  2373  
  2374  	// Check we got unban AC notification for Alice
  2375  	aliceNotifications, err = s.alice.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
  2376  		Cursor:        "",
  2377  		Limit:         10,
  2378  		ActivityTypes: []ActivityCenterType{ActivityCenterNotificationTypeCommunityUnbanned},
  2379  		ReadType:      ActivityCenterQueryParamsReadUnread,
  2380  	},
  2381  	)
  2382  	s.Require().NoError(err)
  2383  	s.Require().Len(aliceNotifications.Notifications, 1)
  2384  	s.Require().Equal(community.IDString(), aliceNotifications.Notifications[0].CommunityID)
  2385  }
  2386  
  2387  func (s *MessengerCommunitiesSuite) createOtherDevice(m1 *Messenger) *Messenger {
  2388  	userPk := m1.IdentityPublicKeyString()
  2389  	addresses, exists := s.accountsTestData[userPk]
  2390  	s.Require().True(exists)
  2391  	password, exists := s.accountsPasswords[userPk]
  2392  	s.Require().True(exists)
  2393  	m2 := s.newMessengerWithKey(m1.identity, password, addresses)
  2394  
  2395  	tcs, err := m2.communitiesManager.All()
  2396  	s.Require().NoError(err, "m2.communitiesManager.All")
  2397  	s.Len(tcs, 0, "Must have 0 communities")
  2398  
  2399  	// Pair devices
  2400  	metadata := &multidevice.InstallationMetadata{
  2401  		Name:       "other-device",
  2402  		DeviceType: "other-device-type",
  2403  	}
  2404  	err = m2.SetInstallationMetadata(m2.installationID, metadata)
  2405  	s.Require().NoError(err)
  2406  
  2407  	_, err = m2.Start()
  2408  	s.Require().NoError(err)
  2409  
  2410  	return m2
  2411  }
  2412  
  2413  func (s *MessengerCommunitiesSuite) TestSyncCommunitySettings() {
  2414  	// Create new device
  2415  	alicesOtherDevice := s.createOtherDevice(s.alice)
  2416  	PairDevices(&s.Suite, alicesOtherDevice, s.alice)
  2417  
  2418  	// Create a community
  2419  	createCommunityReq := &requests.CreateCommunity{
  2420  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  2421  		Name:        "new community",
  2422  		Color:       "#000000",
  2423  		Description: "new community description",
  2424  	}
  2425  
  2426  	mr, err := s.alice.CreateCommunity(createCommunityReq, true)
  2427  	s.Require().NoError(err, "s.alice.CreateCommunity")
  2428  	var newCommunity *communities.Community
  2429  	for _, com := range mr.Communities() {
  2430  		if com.Name() == createCommunityReq.Name {
  2431  			newCommunity = com
  2432  		}
  2433  	}
  2434  	s.Require().NotNil(newCommunity)
  2435  
  2436  	// Check that Alice has community settings
  2437  	cs, err := s.alice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID())
  2438  	s.Require().NoError(err, "communitiesManager.GetCommunitySettingsByID")
  2439  	s.NotNil(cs, "Must have community settings")
  2440  
  2441  	// Wait for the message to reach its destination
  2442  	err = tt.RetryWithBackOff(func() error {
  2443  		_, err = alicesOtherDevice.RetrieveAll()
  2444  		if err != nil {
  2445  			return err
  2446  		}
  2447  
  2448  		// Do we have new synced community settings?
  2449  		syncedSettings, err := alicesOtherDevice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID())
  2450  		if err != nil || syncedSettings == nil {
  2451  			return fmt.Errorf("community with sync not received %w", err)
  2452  		}
  2453  		return nil
  2454  	})
  2455  	s.Require().NoError(err)
  2456  
  2457  	tcs, err := alicesOtherDevice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID())
  2458  	s.Require().NoError(err)
  2459  
  2460  	// Check the community settings on their device matched the community settings on Alice's device
  2461  	s.Equal(cs.CommunityID, tcs.CommunityID)
  2462  	s.Equal(cs.HistoryArchiveSupportEnabled, tcs.HistoryArchiveSupportEnabled)
  2463  }
  2464  
  2465  func (s *MessengerCommunitiesSuite) TestSyncCommunitySettings_EditCommunity() {
  2466  	// Create new device
  2467  	alicesOtherDevice := s.createOtherDevice(s.alice)
  2468  	PairDevices(&s.Suite, alicesOtherDevice, s.alice)
  2469  
  2470  	// Create a community
  2471  	createCommunityReq := &requests.CreateCommunity{
  2472  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  2473  		Name:        "new community",
  2474  		Color:       "#000000",
  2475  		Description: "new community description",
  2476  	}
  2477  
  2478  	mr, err := s.alice.CreateCommunity(createCommunityReq, true)
  2479  	s.Require().NoError(err, "s.alice.CreateCommunity")
  2480  	var newCommunity *communities.Community
  2481  	for _, com := range mr.Communities() {
  2482  		if com.Name() == createCommunityReq.Name {
  2483  			newCommunity = com
  2484  		}
  2485  	}
  2486  	s.Require().NotNil(newCommunity)
  2487  
  2488  	// Check that Alice has community settings
  2489  	cs, err := s.alice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID())
  2490  	s.Require().NoError(err, "communitiesManager.GetCommunitySettingsByID")
  2491  	s.NotNil(cs, "Must have community settings")
  2492  
  2493  	// Wait for the message to reach its destination
  2494  	err = tt.RetryWithBackOff(func() error {
  2495  		_, err = alicesOtherDevice.RetrieveAll()
  2496  		if err != nil {
  2497  			return err
  2498  		}
  2499  
  2500  		// Do we have new synced community settings?
  2501  		syncedSettings, err := alicesOtherDevice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID())
  2502  		if err != nil || syncedSettings == nil {
  2503  			return fmt.Errorf("community settings with sync not received %w", err)
  2504  		}
  2505  		return nil
  2506  	})
  2507  	s.Require().NoError(err)
  2508  
  2509  	tcs, err := alicesOtherDevice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID())
  2510  	s.Require().NoError(err)
  2511  
  2512  	// Check the community settings on their device matched the community settings on Alice's device
  2513  	s.Equal(cs.CommunityID, tcs.CommunityID)
  2514  	s.Equal(cs.HistoryArchiveSupportEnabled, tcs.HistoryArchiveSupportEnabled)
  2515  
  2516  	req := createCommunityReq
  2517  	req.HistoryArchiveSupportEnabled = true
  2518  	editCommunityReq := &requests.EditCommunity{
  2519  		CommunityID:     newCommunity.ID(),
  2520  		CreateCommunity: *req,
  2521  	}
  2522  
  2523  	mr, err = s.alice.EditCommunity(editCommunityReq)
  2524  	s.Require().NoError(err, "s.alice.EditCommunity")
  2525  	var editedCommunity *communities.Community
  2526  	for _, com := range mr.Communities() {
  2527  		if com.Name() == createCommunityReq.Name {
  2528  			editedCommunity = com
  2529  		}
  2530  	}
  2531  	s.Require().NotNil(editedCommunity)
  2532  
  2533  	// Wait a bit for sync messages to reach destination
  2534  	time.Sleep(1 * time.Second)
  2535  	err = tt.RetryWithBackOff(func() error {
  2536  		_, err = alicesOtherDevice.RetrieveAll()
  2537  		if err != nil {
  2538  			return err
  2539  		}
  2540  		return nil
  2541  	})
  2542  	s.Require().NoError(err)
  2543  
  2544  	tcs, err = alicesOtherDevice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID())
  2545  	s.Require().NoError(err)
  2546  
  2547  	// Check the community settings on their device matched the community settings on Alice's device
  2548  	s.Equal(cs.CommunityID, tcs.CommunityID)
  2549  	s.Equal(req.HistoryArchiveSupportEnabled, tcs.HistoryArchiveSupportEnabled)
  2550  }
  2551  
  2552  // TestSyncCommunity tests basic sync functionality between 2 Messengers
  2553  func (s *MessengerCommunitiesSuite) TestSyncCommunity() {
  2554  
  2555  	// Create new device
  2556  	alicesOtherDevice := s.createOtherDevice(s.alice)
  2557  	PairDevices(&s.Suite, alicesOtherDevice, s.alice)
  2558  
  2559  	// Create a community
  2560  	createCommunityReq := &requests.CreateCommunity{
  2561  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  2562  		Name:        "new community",
  2563  		Color:       "#000000",
  2564  		Description: "new community description",
  2565  	}
  2566  
  2567  	mr, err := s.alice.CreateCommunity(createCommunityReq, true)
  2568  	s.Require().NoError(err, "s.alice.CreateCommunity")
  2569  	var newCommunity *communities.Community
  2570  	for _, com := range mr.Communities() {
  2571  		if com.Name() == createCommunityReq.Name {
  2572  			newCommunity = com
  2573  		}
  2574  	}
  2575  	s.Require().NotNil(newCommunity)
  2576  
  2577  	// Check that Alice has 1 community
  2578  	cs, err := s.alice.communitiesManager.All()
  2579  	s.Require().NoError(err, "communitiesManager.All")
  2580  	s.Len(cs, 1, "Must have 1 community")
  2581  
  2582  	// Wait for the message to reach its destination
  2583  	err = tt.RetryWithBackOff(func() error {
  2584  		_, err = alicesOtherDevice.RetrieveAll()
  2585  		if err != nil {
  2586  			return err
  2587  		}
  2588  
  2589  		// Do we have a new synced community?
  2590  		_, err = alicesOtherDevice.communitiesManager.GetSyncedRawCommunity(newCommunity.ID())
  2591  		if err != nil {
  2592  			return fmt.Errorf("community with sync not received %w", err)
  2593  		}
  2594  
  2595  		return nil
  2596  	})
  2597  	s.Require().NoError(err)
  2598  
  2599  	// Count the number of communities in their device
  2600  	tcs, err := alicesOtherDevice.communitiesManager.All()
  2601  	s.Require().NoError(err)
  2602  	s.Len(tcs, 1, "There must be 1 community")
  2603  
  2604  	s.logger.Debug("", zap.Any("tcs", tcs))
  2605  
  2606  	// Get the new community from their db
  2607  	tnc, err := alicesOtherDevice.communitiesManager.GetByID(newCommunity.ID())
  2608  	s.Require().NoError(err)
  2609  
  2610  	// Check the community on their device matched the new community on Alice's device
  2611  	s.Equal(newCommunity.ID(), tnc.ID())
  2612  	s.Equal(newCommunity.Name(), tnc.Name())
  2613  	s.Equal(newCommunity.DescriptionText(), tnc.DescriptionText())
  2614  	s.Equal(newCommunity.IDString(), tnc.IDString())
  2615  
  2616  	// Private Key for synced community should be null
  2617  	s.Require().NotNil(newCommunity.PrivateKey())
  2618  	s.Require().Nil(tnc.PrivateKey())
  2619  
  2620  	s.Equal(newCommunity.PublicKey(), tnc.PublicKey())
  2621  	s.Equal(newCommunity.Verified(), tnc.Verified())
  2622  	s.Equal(newCommunity.Muted(), tnc.Muted())
  2623  	s.Equal(newCommunity.Joined(), tnc.Joined())
  2624  	s.Equal(newCommunity.Spectated(), tnc.Spectated())
  2625  
  2626  	s.True(newCommunity.IsControlNode())
  2627  	s.True(newCommunity.IsOwner())
  2628  
  2629  	// Even though synced device have the private key, it is not the control node
  2630  	// There can be only one control node
  2631  	s.False(tnc.IsControlNode())
  2632  	s.True(tnc.IsOwner())
  2633  }
  2634  
  2635  func (s *MessengerCommunitiesSuite) TestSyncCommunity_EncryptionKeys() {
  2636  	// Create new device
  2637  	ownersOtherDevice := s.createOtherDevice(s.owner)
  2638  	defer TearDownMessenger(&s.Suite, ownersOtherDevice)
  2639  
  2640  	PairDevices(&s.Suite, ownersOtherDevice, s.owner)
  2641  
  2642  	community, chat := s.createCommunity()
  2643  	s.owner.communitiesManager.RekeyInterval = 1 * time.Hour
  2644  
  2645  	{ // ensure both community and channel are encrypted
  2646  		permissionRequest := requests.CreateCommunityTokenPermission{
  2647  			CommunityID: community.ID(),
  2648  			Type:        protobuf.CommunityTokenPermission_BECOME_MEMBER,
  2649  			TokenCriteria: []*protobuf.TokenCriteria{
  2650  				&protobuf.TokenCriteria{
  2651  					Type:              protobuf.CommunityTokenType_ERC20,
  2652  					ContractAddresses: map[uint64]string{testChainID1: "0x123"},
  2653  					Symbol:            "TEST",
  2654  					AmountInWei:       "100000000000000000000",
  2655  					Decimals:          uint64(18),
  2656  				},
  2657  			},
  2658  		}
  2659  		_, err := s.owner.CreateCommunityTokenPermission(&permissionRequest)
  2660  		s.Require().NoError(err)
  2661  
  2662  		channelPermissionRequest := requests.CreateCommunityTokenPermission{
  2663  			CommunityID: community.ID(),
  2664  			Type:        protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
  2665  			TokenCriteria: []*protobuf.TokenCriteria{
  2666  				&protobuf.TokenCriteria{
  2667  					Type:              protobuf.CommunityTokenType_ERC20,
  2668  					ContractAddresses: map[uint64]string{testChainID1: "0x123"},
  2669  					Symbol:            "TEST",
  2670  					AmountInWei:       "100000000000000000000",
  2671  					Decimals:          uint64(18),
  2672  				},
  2673  			},
  2674  			ChatIds: []string{chat.ID},
  2675  		}
  2676  
  2677  		_, err = s.owner.CreateCommunityTokenPermission(&channelPermissionRequest)
  2678  		s.Require().NoError(err)
  2679  	}
  2680  
  2681  	getKeysCount := func(m *Messenger) (communityKeysCount int, channelKeysCount int) {
  2682  		keys, err := m.encryptor.GetAllHRKeys(community.ID())
  2683  		s.Require().NoError(err)
  2684  		if keys != nil {
  2685  			communityKeysCount = len(keys.Keys)
  2686  		}
  2687  
  2688  		channelKeys, err := m.encryptor.GetAllHRKeys([]byte(community.IDString() + chat.CommunityChatID()))
  2689  		s.Require().NoError(err)
  2690  		if channelKeys != nil {
  2691  			channelKeysCount = len(channelKeys.Keys)
  2692  		}
  2693  		return
  2694  	}
  2695  
  2696  	communityKeysCount, channelKeysCount := getKeysCount(s.owner)
  2697  	s.Require().GreaterOrEqual(communityKeysCount, 1)
  2698  	s.Require().GreaterOrEqual(channelKeysCount, 1)
  2699  
  2700  	// ensure both community and channel keys are synced
  2701  	_, err := WaitOnMessengerResponse(ownersOtherDevice, func(mr *MessengerResponse) bool {
  2702  		communityKeysCount, channelKeysCount := getKeysCount(s.owner)
  2703  		syncedCommunityKeysCount, syncedChannelKeysCount := getKeysCount(ownersOtherDevice)
  2704  
  2705  		return communityKeysCount == syncedCommunityKeysCount && channelKeysCount == syncedChannelKeysCount
  2706  	}, "keys not synced")
  2707  	s.Require().NoError(err)
  2708  }
  2709  
  2710  // TestSyncCommunity_RequestToJoin tests more complex pairing and syncing scenario where one paired device
  2711  // makes a request to join a community
  2712  func (s *MessengerCommunitiesSuite) TestSyncCommunity_RequestToJoin() {
  2713  	// Set Alice's installation metadata
  2714  	aim := &multidevice.InstallationMetadata{
  2715  		Name:       "alice's-device",
  2716  		DeviceType: "alice's-device-type",
  2717  	}
  2718  	err := s.alice.SetInstallationMetadata(s.alice.installationID, aim)
  2719  	s.Require().NoError(err)
  2720  
  2721  	// Create Alice's other device
  2722  	alicesOtherDevice := s.createOtherDevice(s.alice)
  2723  
  2724  	// Pair alice's two devices
  2725  	PairDevices(&s.Suite, alicesOtherDevice, s.alice)
  2726  	PairDevices(&s.Suite, s.alice, alicesOtherDevice)
  2727  
  2728  	// Check bob the admin has 0 community
  2729  	tcs2, err := s.bob.communitiesManager.All()
  2730  	s.Require().NoError(err, "admin.communitiesManager.All")
  2731  	s.Len(tcs2, 0, "Must have 0 communities")
  2732  
  2733  	// Bob the admin creates a community
  2734  	createCommunityReq := &requests.CreateCommunity{
  2735  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  2736  		Name:        "new community",
  2737  		Color:       "#000000",
  2738  		Description: "new community description",
  2739  	}
  2740  	mr, err := s.bob.CreateCommunity(createCommunityReq, true)
  2741  	s.Require().NoError(err, "CreateCommunity")
  2742  	s.Require().NotNil(mr)
  2743  	s.Len(mr.Communities(), 1)
  2744  
  2745  	community := mr.Communities()[0]
  2746  
  2747  	// Check that admin has 1 community
  2748  	acs, err := s.bob.communitiesManager.All()
  2749  	s.Require().NoError(err, "communitiesManager.All")
  2750  	s.Len(acs, 1, "Must have 1 communities")
  2751  
  2752  	// Check that Alice has 0 communities on either device
  2753  	cs, err := s.alice.communitiesManager.All()
  2754  	s.Require().NoError(err, "communitiesManager.All")
  2755  	s.Len(cs, 0, "Must have 0 communities")
  2756  
  2757  	tcs1, err := alicesOtherDevice.communitiesManager.All()
  2758  	s.Require().NoError(err, "alicesOtherDevice.communitiesManager.All")
  2759  	s.Len(tcs1, 0, "Must have 0 communities")
  2760  
  2761  	// Bob the admin opens up a 1-1 chat with alice
  2762  	chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport)
  2763  	s.Require().NoError(s.bob.SaveChat(chat))
  2764  
  2765  	// Bob the admin shares with Alice, via public chat, an invite link to the new community
  2766  	message := buildTestMessage(*chat)
  2767  	message.CommunityID = community.IDString()
  2768  	response, err := s.bob.SendChatMessage(context.Background(), message)
  2769  	s.Require().NoError(err)
  2770  	s.Require().NotNil(response)
  2771  
  2772  	// Retrieve community link & community
  2773  	err = tt.RetryWithBackOff(func() error {
  2774  		response, err = s.alice.RetrieveAll()
  2775  		if err != nil {
  2776  			return err
  2777  		}
  2778  		if len(response.Communities()) == 0 {
  2779  			return errors.New("no communities received from 1-1")
  2780  		}
  2781  		return nil
  2782  	})
  2783  	s.Require().NoError(err)
  2784  
  2785  	// Check that alice now has 1 community
  2786  	cs, err = s.alice.communitiesManager.All()
  2787  	s.Require().NoError(err, "communitiesManager.All")
  2788  	s.Len(cs, 1, "Must have 1 community")
  2789  	for _, c := range cs {
  2790  		s.False(c.Joined(), "Must not have joined the community")
  2791  	}
  2792  
  2793  	// Alice requests to join the new community
  2794  	request := s.createRequestToJoinCommunity(community.ID(), s.alice)
  2795  	response, err = s.alice.RequestToJoinCommunity(request)
  2796  	s.Require().NoError(err)
  2797  	s.Require().NotNil(response)
  2798  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  2799  
  2800  	s.Require().Len(response.ActivityCenterNotifications(), 1)
  2801  
  2802  	notification := response.ActivityCenterNotifications()[0]
  2803  	s.Require().NotNil(notification)
  2804  	s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
  2805  	s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
  2806  
  2807  	aRtj := response.RequestsToJoinCommunity()[0]
  2808  	s.Require().NotNil(aRtj)
  2809  	s.Equal(community.ID(), aRtj.CommunityID)
  2810  	s.True(aRtj.Our)
  2811  	s.Require().NotEmpty(aRtj.ID)
  2812  	s.Require().NotEmpty(aRtj.Clock)
  2813  	s.Equal(aRtj.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  2814  	s.Equal(aRtj.CustomizationColor, s.alice.account.GetCustomizationColor())
  2815  	s.Equal(communities.RequestToJoinStatePending, aRtj.State)
  2816  
  2817  	// Make sure clock is not empty
  2818  	s.Require().NotEmpty(aRtj.Clock)
  2819  
  2820  	s.Len(response.Communities(), 1)
  2821  	s.Equal(response.Communities()[0].RequestedToJoinAt(), aRtj.Clock)
  2822  
  2823  	// pull all communities to make sure we set RequestedToJoinAt
  2824  	allCommunities, err := s.alice.Communities()
  2825  	s.Require().NoError(err)
  2826  	s.Len(allCommunities, 1)
  2827  	s.Require().Equal(allCommunities[0].ID(), community.ID())
  2828  	s.Require().Equal(allCommunities[0].RequestedToJoinAt(), aRtj.Clock)
  2829  
  2830  	// pull to make sure it has been saved
  2831  	requestsToJoin, err := s.alice.MyPendingRequestsToJoin()
  2832  	s.Require().NoError(err)
  2833  	s.Len(requestsToJoin, 1)
  2834  
  2835  	// Make sure the requests are fetched also by community
  2836  	requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID())
  2837  	s.Require().NoError(err)
  2838  	s.Len(requestsToJoin, 1)
  2839  
  2840  	// Alice's other device retrieves sync message from the join
  2841  	err = tt.RetryWithBackOff(func() error {
  2842  		response, err = alicesOtherDevice.RetrieveAll()
  2843  		if err != nil {
  2844  			return err
  2845  		}
  2846  
  2847  		// Do we have a new synced community?
  2848  		_, err = alicesOtherDevice.communitiesManager.GetSyncedRawCommunity(community.ID())
  2849  		if err != nil {
  2850  			return fmt.Errorf("community with sync not received %w", err)
  2851  		}
  2852  
  2853  		// Do we have a new pending request to join for the new community
  2854  		requestsToJoin, err = alicesOtherDevice.PendingRequestsToJoinForCommunity(community.ID())
  2855  		if err != nil {
  2856  			return err
  2857  		}
  2858  		if len(requestsToJoin) == 0 {
  2859  			return errors.New("no requests to join")
  2860  		}
  2861  
  2862  		return nil
  2863  	})
  2864  	s.Require().NoError(err)
  2865  	s.Len(response.Communities(), 1)
  2866  
  2867  	// Get the pending requests to join for the new community on alicesOtherDevice
  2868  	requestsToJoin, err = alicesOtherDevice.PendingRequestsToJoinForCommunity(community.ID())
  2869  	s.Require().NoError(err)
  2870  	s.Len(requestsToJoin, 1)
  2871  
  2872  	// Check request to join on alicesOtherDevice matches the RTJ on alice
  2873  	aodRtj := requestsToJoin[0]
  2874  	s.Equal(aRtj.PublicKey, aodRtj.PublicKey)
  2875  	s.Equal(aRtj.ID, aodRtj.ID)
  2876  	s.Equal(aRtj.CommunityID, aodRtj.CommunityID)
  2877  	s.Equal(aRtj.Clock, aodRtj.Clock)
  2878  	s.Equal(aRtj.ENSName, aodRtj.ENSName)
  2879  	s.Equal(aRtj.ChatID, aodRtj.ChatID)
  2880  	s.Equal(aRtj.State, aodRtj.State)
  2881  	s.Equal(aRtj.CustomizationColor, aodRtj.CustomizationColor)
  2882  
  2883  	// Bob the admin retrieves request to join
  2884  	err = tt.RetryWithBackOff(func() error {
  2885  		response, err = s.bob.RetrieveAll()
  2886  		if err != nil {
  2887  			return err
  2888  		}
  2889  		if len(response.RequestsToJoinCommunity()) == 0 {
  2890  			return errors.New("request to join community not received")
  2891  		}
  2892  		return nil
  2893  	})
  2894  	s.Require().NoError(err)
  2895  	s.Len(response.RequestsToJoinCommunity(), 1)
  2896  
  2897  	// Check that bob the admin's newly received request to join matches what we expect
  2898  	bobRtj := response.RequestsToJoinCommunity()[0]
  2899  	s.Require().NotNil(bobRtj)
  2900  	s.Equal(community.ID(), bobRtj.CommunityID)
  2901  	s.False(bobRtj.Our)
  2902  	s.Require().NotEmpty(bobRtj.ID)
  2903  	s.Require().NotEmpty(bobRtj.Clock)
  2904  	s.Equal(bobRtj.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  2905  	s.Equal(bobRtj.CustomizationColor, s.alice.account.GetCustomizationColor())
  2906  	s.Equal(communities.RequestToJoinStatePending, bobRtj.State)
  2907  
  2908  	s.Equal(aRtj.PublicKey, bobRtj.PublicKey)
  2909  	s.Equal(aRtj.ID, bobRtj.ID)
  2910  	s.Equal(aRtj.CommunityID, bobRtj.CommunityID)
  2911  	s.Equal(aRtj.Clock, bobRtj.Clock)
  2912  	s.Equal(aRtj.ENSName, bobRtj.ENSName)
  2913  	s.Equal(aRtj.ChatID, bobRtj.ChatID)
  2914  	s.Equal(aRtj.State, bobRtj.State)
  2915  	s.Equal(aRtj.CustomizationColor, bobRtj.CustomizationColor)
  2916  }
  2917  
  2918  func (s *MessengerCommunitiesSuite) TestSyncCommunity_Leave() {
  2919  	// Set Alice's installation metadata
  2920  	aim := &multidevice.InstallationMetadata{
  2921  		Name:       "alice's-device",
  2922  		DeviceType: "alice's-device-type",
  2923  	}
  2924  	err := s.alice.SetInstallationMetadata(s.alice.installationID, aim)
  2925  	s.Require().NoError(err)
  2926  
  2927  	// Create Alice's other device
  2928  	alicesOtherDevice := s.createOtherDevice(s.alice)
  2929  
  2930  	// Pair alice's two devices
  2931  	PairDevices(&s.Suite, alicesOtherDevice, s.alice)
  2932  	PairDevices(&s.Suite, s.alice, alicesOtherDevice)
  2933  
  2934  	// Check bob the admin has only zero community
  2935  	tcs2, err := s.bob.communitiesManager.All()
  2936  	s.Require().NoError(err, "admin.communitiesManager.All")
  2937  	s.Len(tcs2, 0, "Must have 0 communities")
  2938  
  2939  	// Bob the admin creates a community
  2940  	createCommunityReq := &requests.CreateCommunity{
  2941  		Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
  2942  		Name:        "new community",
  2943  		Color:       "#000000",
  2944  		Description: "new community description",
  2945  	}
  2946  	mr, err := s.bob.CreateCommunity(createCommunityReq, true)
  2947  	s.Require().NoError(err, "CreateCommunity")
  2948  	s.Require().NotNil(mr)
  2949  	s.Len(mr.Communities(), 1)
  2950  
  2951  	community := mr.Communities()[0]
  2952  
  2953  	// Check that admin has 1 community
  2954  	acs, err := s.bob.communitiesManager.All()
  2955  	s.Require().NoError(err, "communitiesManager.All")
  2956  	s.Len(acs, 1, "Must have 1 community")
  2957  
  2958  	// Check that Alice has 0 community on either device
  2959  	cs, err := s.alice.communitiesManager.All()
  2960  	s.Require().NoError(err, "communitiesManager.All")
  2961  	s.Len(cs, 0, "Must have 0 communities")
  2962  
  2963  	tcs1, err := alicesOtherDevice.communitiesManager.All()
  2964  	s.Require().NoError(err, "alicesOtherDevice.communitiesManager.All")
  2965  	s.Len(tcs1, 0, "Must have 0 communities")
  2966  
  2967  	// Bob the admin opens up a 1-1 chat with alice
  2968  	chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport)
  2969  	s.Require().NoError(s.bob.SaveChat(chat))
  2970  
  2971  	// Bob the admin shares with Alice, via public chat, an invite link to the new community
  2972  	message := buildTestMessage(*chat)
  2973  	message.CommunityID = community.IDString()
  2974  	response, err := s.bob.SendChatMessage(context.Background(), message)
  2975  	s.Require().NoError(err)
  2976  	s.Require().NotNil(response)
  2977  
  2978  	// Retrieve community link & community
  2979  	err = tt.RetryWithBackOff(func() error {
  2980  		response, err = s.alice.RetrieveAll()
  2981  		if err != nil {
  2982  			return err
  2983  		}
  2984  		if len(response.Communities()) == 0 {
  2985  			return errors.New("no communities received from 1-1")
  2986  		}
  2987  		return nil
  2988  	})
  2989  	s.Require().NoError(err)
  2990  
  2991  	// Check that alice now has 1 community
  2992  	cs, err = s.alice.communitiesManager.All()
  2993  	s.Require().NoError(err, "communitiesManager.All")
  2994  	s.Len(cs, 1, "Must have 1 community")
  2995  	for _, c := range cs {
  2996  		s.False(c.Joined(), "Must not have joined the community")
  2997  	}
  2998  
  2999  	// alice joins the community
  3000  	mr, err = s.alice.JoinCommunity(context.Background(), community.ID(), false)
  3001  	s.Require().NoError(err, "s.alice.JoinCommunity")
  3002  	s.Require().NotNil(mr)
  3003  	s.Len(mr.Communities(), 1)
  3004  	aCom := mr.Communities()[0]
  3005  
  3006  	// Check that the joined community has the correct values
  3007  	s.Equal(community.ID(), aCom.ID())
  3008  	s.Equal(community.Clock(), aCom.Clock())
  3009  	s.Equal(community.PublicKey(), aCom.PublicKey())
  3010  
  3011  	// Check alicesOtherDevice receives the sync join message
  3012  	err = tt.RetryWithBackOff(func() error {
  3013  		response, err = alicesOtherDevice.RetrieveAll()
  3014  		if err != nil {
  3015  			return err
  3016  		}
  3017  
  3018  		// Do we have a new synced community?
  3019  		_, err = alicesOtherDevice.communitiesManager.GetSyncedRawCommunity(community.ID())
  3020  		if err != nil {
  3021  			return fmt.Errorf("community with sync not received %w", err)
  3022  		}
  3023  
  3024  		return nil
  3025  	})
  3026  	s.Require().NoError(err)
  3027  	s.Len(response.Communities(), 1, "")
  3028  
  3029  	aoCom := mr.Communities()[0]
  3030  	s.Equal(aCom, aoCom)
  3031  }
  3032  
  3033  func (s *MessengerCommunitiesSuite) TestSyncCommunity_ImportCommunity() {
  3034  	// Owner creates community
  3035  	community, _ := s.createCommunity()
  3036  	s.Require().True(community.IsControlNode())
  3037  
  3038  	// New device is created & paired
  3039  	ownersOtherDevice := s.createOtherDevice(s.owner)
  3040  	PairDevices(&s.Suite, ownersOtherDevice, s.owner)
  3041  	PairDevices(&s.Suite, s.owner, ownersOtherDevice)
  3042  
  3043  	privateKey, err := s.owner.ExportCommunity(community.ID())
  3044  	s.Require().NoError(err)
  3045  
  3046  	// New device imports the community (before it is received via sync message)
  3047  	ctx := context.Background()
  3048  	response, err := ownersOtherDevice.ImportCommunity(ctx, privateKey)
  3049  	s.Require().NoError(err)
  3050  	s.Require().Len(response.Communities(), 1)
  3051  	s.Require().Equal(community.IDString(), response.Communities()[0].IDString())
  3052  	// New device becomes the control node
  3053  	s.Require().True(response.Communities()[0].IsControlNode())
  3054  
  3055  	// Old device is no longer the control node
  3056  	_, err = WaitOnMessengerResponse(s.owner, func(response *MessengerResponse) bool {
  3057  		if len(response.Communities()) != 1 {
  3058  			return false
  3059  		}
  3060  		c := response.Communities()[0]
  3061  		return c.IDString() == community.IDString() && !c.IsControlNode()
  3062  	}, "community not synced")
  3063  	s.Require().NoError(err)
  3064  }
  3065  
  3066  func (s *MessengerCommunitiesSuite) TestSyncCommunity_OutdatedDescription() {
  3067  	community, _ := s.createCommunity()
  3068  	s.advertiseCommunityTo(community, s.owner, s.alice)
  3069  
  3070  	// Spectate community
  3071  	_, err := s.alice.SpectateCommunity(community.ID())
  3072  	s.Require().NoError(err)
  3073  
  3074  	// Update alice's community reference
  3075  	community, err = s.alice.communitiesManager.GetByID(community.ID())
  3076  	s.Require().NoError(err)
  3077  	s.Require().False(community.Joined())
  3078  	s.Require().True(community.Spectated())
  3079  
  3080  	// Create sync message for later
  3081  	syncCommunityMsg, err := s.alice.buildSyncInstallationCommunity(community, 1)
  3082  	s.Require().NoError(err)
  3083  	s.Require().False(syncCommunityMsg.Joined)
  3084  	s.Require().True(syncCommunityMsg.Spectated)
  3085  
  3086  	// Join community
  3087  	s.joinCommunity(community, s.owner, s.alice)
  3088  
  3089  	// Update owner's community reference
  3090  	community, err = s.owner.GetCommunityByID(community.ID())
  3091  	s.Require().NoError(err)
  3092  	s.Require().True(community.HasMember(s.alice.IdentityPublicKey()))
  3093  
  3094  	// Create another device
  3095  	aliceOtherDevice := s.createOtherDevice(s.alice)
  3096  	defer TearDownMessenger(&s.Suite, aliceOtherDevice)
  3097  
  3098  	// Make other device receive community
  3099  	advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, aliceOtherDevice)
  3100  	community, err = aliceOtherDevice.GetCommunityByID(community.ID())
  3101  	s.Require().NoError(err)
  3102  	s.Require().True(community.Joined())
  3103  
  3104  	// Then make other device handle sync message with outdated community description
  3105  	messageState := aliceOtherDevice.buildMessageState()
  3106  	err = aliceOtherDevice.handleSyncInstallationCommunity(messageState, syncCommunityMsg)
  3107  	s.Require().NoError(err)
  3108  
  3109  	// Then community should not be left
  3110  	community, err = aliceOtherDevice.communitiesManager.GetByID(community.ID())
  3111  	s.Require().NoError(err)
  3112  	s.Require().True(community.Joined())
  3113  	s.Require().False(community.Spectated())
  3114  }
  3115  
  3116  func (s *MessengerCommunitiesSuite) TestSetMutePropertyOnChatsByCategory() {
  3117  	// Create a community
  3118  	createCommunityReq := &requests.CreateCommunity{
  3119  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  3120  		Name:        "new community",
  3121  		Color:       "#000000",
  3122  		Description: "new community description",
  3123  	}
  3124  
  3125  	mr, err := s.alice.CreateCommunity(createCommunityReq, true)
  3126  	s.Require().NoError(err, "s.alice.CreateCommunity")
  3127  	var newCommunity *communities.Community
  3128  	for _, com := range mr.Communities() {
  3129  		if com.Name() == createCommunityReq.Name {
  3130  			newCommunity = com
  3131  		}
  3132  	}
  3133  	s.Require().NotNil(newCommunity)
  3134  
  3135  	orgChat1 := &protobuf.CommunityChat{
  3136  		Permissions: &protobuf.CommunityPermissions{
  3137  			Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
  3138  		},
  3139  		Identity: &protobuf.ChatIdentity{
  3140  			DisplayName: "status-core",
  3141  			Emoji:       "😎",
  3142  			Description: "status-core community chat",
  3143  		},
  3144  	}
  3145  
  3146  	orgChat2 := &protobuf.CommunityChat{
  3147  		Permissions: &protobuf.CommunityPermissions{
  3148  			Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
  3149  		},
  3150  		Identity: &protobuf.ChatIdentity{
  3151  			DisplayName: "status-core2",
  3152  			Emoji:       "😎",
  3153  			Description: "status-core community chat2",
  3154  		},
  3155  	}
  3156  
  3157  	mr, err = s.alice.CreateCommunityChat(newCommunity.ID(), orgChat1)
  3158  	s.Require().NoError(err)
  3159  	s.Require().NotNil(mr)
  3160  	s.Require().Len(mr.Communities(), 1)
  3161  	s.Require().Len(mr.Chats(), 1)
  3162  
  3163  	mr, err = s.alice.CreateCommunityChat(newCommunity.ID(), orgChat2)
  3164  	s.Require().NoError(err)
  3165  	s.Require().NotNil(mr)
  3166  	s.Require().Len(mr.Communities(), 1)
  3167  	s.Require().Len(mr.Chats(), 1)
  3168  
  3169  	var chatIds []string
  3170  	for k := range newCommunity.Chats() {
  3171  		chatIds = append(chatIds, k)
  3172  	}
  3173  	category := &requests.CreateCommunityCategory{
  3174  		CommunityID:  newCommunity.ID(),
  3175  		CategoryName: "category-name",
  3176  		ChatIDs:      chatIds,
  3177  	}
  3178  
  3179  	mr, err = s.alice.CreateCommunityCategory(category)
  3180  	s.Require().NoError(err)
  3181  	s.Require().NotNil(mr)
  3182  	s.Require().Len(mr.Communities(), 1)
  3183  	s.Require().Len(mr.Communities()[0].Categories(), 1)
  3184  
  3185  	var categoryID string
  3186  	for k := range mr.Communities()[0].Categories() {
  3187  		categoryID = k
  3188  	}
  3189  
  3190  	err = s.alice.SetMutePropertyOnChatsByCategory(&requests.MuteCategory{
  3191  		CommunityID: newCommunity.IDString(),
  3192  		CategoryID:  categoryID,
  3193  		MutedType:   MuteTillUnmuted,
  3194  	}, true)
  3195  	s.Require().NoError(err)
  3196  
  3197  	for _, chat := range s.alice.Chats() {
  3198  		if chat.CategoryID == categoryID {
  3199  			s.Require().True(chat.Muted)
  3200  		}
  3201  	}
  3202  
  3203  	err = s.alice.SetMutePropertyOnChatsByCategory(&requests.MuteCategory{
  3204  		CommunityID: newCommunity.IDString(),
  3205  		CategoryID:  categoryID,
  3206  		MutedType:   Unmuted,
  3207  	}, false)
  3208  	s.Require().NoError(err)
  3209  
  3210  	for _, chat := range s.alice.Chats() {
  3211  		s.Require().False(chat.Muted)
  3212  	}
  3213  }
  3214  
  3215  func (s *MessengerCommunitiesSuite) TestCheckCommunitiesToUnmute() {
  3216  	// Create a community
  3217  	createCommunityReq := &requests.CreateCommunity{
  3218  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  3219  		Name:        "new community",
  3220  		Color:       "#000000",
  3221  		Description: "new community description",
  3222  	}
  3223  
  3224  	mr, err := s.alice.CreateCommunity(createCommunityReq, true)
  3225  	s.Require().NoError(err, "s.alice.CreateCommunity")
  3226  	var newCommunity *communities.Community
  3227  	for _, com := range mr.Communities() {
  3228  		if com.Name() == createCommunityReq.Name {
  3229  			newCommunity = com
  3230  		}
  3231  	}
  3232  	s.Require().NotNil(newCommunity)
  3233  
  3234  	currTime, err := time.Parse(time.RFC3339, time.Now().Add(-time.Hour).Format(time.RFC3339))
  3235  	s.Require().NoError(err)
  3236  
  3237  	err = s.alice.communitiesManager.SetMuted(newCommunity.ID(), true)
  3238  	s.Require().NoError(err, "SetMuted to community")
  3239  
  3240  	err = s.alice.communitiesManager.MuteCommunityTill(newCommunity.ID(), currTime)
  3241  	s.Require().NoError(err, "SetMuteTill to community")
  3242  
  3243  	response, err := s.alice.CheckCommunitiesToUnmute()
  3244  	s.Require().NoError(err)
  3245  	s.Require().Len(response.Communities(), 1, "CheckCommunitiesToUnmute should unmute the community")
  3246  
  3247  	community, err := s.alice.communitiesManager.GetByID(newCommunity.ID())
  3248  	s.Require().NoError(err)
  3249  	s.Require().False(community.Muted())
  3250  
  3251  }
  3252  
  3253  func (s *MessengerCommunitiesSuite) TestCommunityNotInDB() {
  3254  	community, err := s.alice.communitiesManager.GetByID([]byte("0x123"))
  3255  	s.Require().ErrorIs(err, communities.ErrOrgNotFound)
  3256  	s.Require().Nil(community)
  3257  }
  3258  
  3259  func (s *MessengerCommunitiesSuite) TestMuteAllCommunityChats() {
  3260  	// Create a community
  3261  	createCommunityReq := &requests.CreateCommunity{
  3262  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  3263  		Name:        "new community",
  3264  		Color:       "#000000",
  3265  		Description: "new community description",
  3266  	}
  3267  
  3268  	mr, err := s.alice.CreateCommunity(createCommunityReq, true)
  3269  	s.Require().NoError(err, "s.alice.CreateCommunity")
  3270  	var newCommunity *communities.Community
  3271  	for _, com := range mr.Communities() {
  3272  		if com.Name() == createCommunityReq.Name {
  3273  			newCommunity = com
  3274  		}
  3275  	}
  3276  	s.Require().NotNil(newCommunity)
  3277  
  3278  	orgChat1 := &protobuf.CommunityChat{
  3279  		Permissions: &protobuf.CommunityPermissions{
  3280  			Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
  3281  		},
  3282  		Identity: &protobuf.ChatIdentity{
  3283  			DisplayName: "status-core",
  3284  			Emoji:       "😎",
  3285  			Description: "status-core community chat",
  3286  		},
  3287  	}
  3288  
  3289  	orgChat2 := &protobuf.CommunityChat{
  3290  		Permissions: &protobuf.CommunityPermissions{
  3291  			Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
  3292  		},
  3293  		Identity: &protobuf.ChatIdentity{
  3294  			DisplayName: "status-core2",
  3295  			Emoji:       "😎",
  3296  			Description: "status-core community chat2",
  3297  		},
  3298  	}
  3299  
  3300  	mr, err = s.alice.CreateCommunityChat(newCommunity.ID(), orgChat1)
  3301  	s.Require().NoError(err)
  3302  	s.Require().NotNil(mr)
  3303  	s.Require().Len(mr.Communities(), 1)
  3304  	s.Require().Len(mr.Chats(), 1)
  3305  
  3306  	mr, err = s.alice.CreateCommunityChat(newCommunity.ID(), orgChat2)
  3307  	s.Require().NoError(err)
  3308  	s.Require().NotNil(mr)
  3309  	s.Require().Len(mr.Communities(), 1)
  3310  	s.Require().Len(mr.Chats(), 1)
  3311  
  3312  	muteDuration, err := s.alice.MuteDuration(MuteFor15Min)
  3313  	s.Require().NoError(err)
  3314  
  3315  	time, err := s.alice.MuteAllCommunityChats(&requests.MuteCommunity{
  3316  		CommunityID: newCommunity.ID(),
  3317  		MutedType:   MuteFor15Min,
  3318  	})
  3319  	s.Require().NoError(err)
  3320  	s.Require().NotNil(time)
  3321  
  3322  	aliceCommunity, err := s.alice.GetCommunityByID(newCommunity.ID())
  3323  	s.Require().NoError(err)
  3324  	s.Require().True(aliceCommunity.Muted())
  3325  
  3326  	for _, chat := range s.alice.Chats() {
  3327  		if chat.CommunityID == newCommunity.IDString() {
  3328  			s.Require().True(chat.Muted)
  3329  			s.Require().Equal(chat.MuteTill, muteDuration)
  3330  		}
  3331  	}
  3332  
  3333  	for _, chat := range s.alice.Chats() {
  3334  		if chat.CommunityID == newCommunity.IDString() {
  3335  			err = s.alice.UnmuteChat(chat.ID)
  3336  			s.Require().NoError(err)
  3337  			s.Require().False(chat.Muted)
  3338  			break
  3339  		}
  3340  	}
  3341  
  3342  	aliceCommunity, err = s.alice.GetCommunityByID(newCommunity.ID())
  3343  	s.Require().NoError(err)
  3344  	s.Require().False(aliceCommunity.Muted())
  3345  
  3346  	time, err = s.alice.UnMuteAllCommunityChats(newCommunity.IDString())
  3347  	s.Require().NoError(err)
  3348  	s.Require().NotNil(time)
  3349  	s.Require().False(newCommunity.Muted())
  3350  
  3351  	for _, chat := range s.alice.Chats() {
  3352  		s.Require().False(chat.Muted)
  3353  	}
  3354  
  3355  }
  3356  
  3357  func (s *MessengerCommunitiesSuite) TestExtractDiscordChannelsAndCategories() {
  3358  
  3359  	tmpFile, err := ioutil.TempFile(os.TempDir(), "discord-channel-")
  3360  	s.Require().NoError(err)
  3361  	defer os.Remove(tmpFile.Name())
  3362  
  3363  	discordMessage := &protobuf.DiscordMessage{
  3364  		Id:              "1234",
  3365  		Type:            "Default",
  3366  		Timestamp:       "2022-07-26T14:20:17.305+00:00",
  3367  		TimestampEdited: "",
  3368  		Content:         "Some discord message",
  3369  		Author: &protobuf.DiscordMessageAuthor{
  3370  			Id:            "123",
  3371  			Name:          "TestAuthor",
  3372  			Discriminator: "456",
  3373  			Nickname:      "",
  3374  			AvatarUrl:     "",
  3375  		},
  3376  	}
  3377  
  3378  	messages := make([]*protobuf.DiscordMessage, 0)
  3379  	messages = append(messages, discordMessage)
  3380  
  3381  	exportedDiscordData := &discord.ExportedData{
  3382  		Channel: discord.Channel{
  3383  			ID:           "12345",
  3384  			CategoryName: "test-category",
  3385  			CategoryID:   "6789",
  3386  			Name:         "test-channel",
  3387  			Description:  "This is a channel topic",
  3388  			FilePath:     tmpFile.Name(),
  3389  		},
  3390  		Messages: messages,
  3391  	}
  3392  
  3393  	data, err := json.Marshal(exportedDiscordData)
  3394  	s.Require().NoError(err)
  3395  
  3396  	err = os.WriteFile(tmpFile.Name(), data, 0666) // nolint: gosec
  3397  	s.Require().NoError(err)
  3398  
  3399  	files := make([]string, 0)
  3400  	files = append(files, tmpFile.Name())
  3401  	mr, errs := s.bob.ExtractDiscordChannelsAndCategories(files)
  3402  	s.Require().Len(errs, 0)
  3403  
  3404  	s.Require().Len(mr.DiscordCategories, 1)
  3405  	s.Require().Len(mr.DiscordChannels, 1)
  3406  	s.Require().Equal(mr.DiscordOldestMessageTimestamp, int(1658845217))
  3407  }
  3408  
  3409  func (s *MessengerCommunitiesSuite) TestExtractDiscordChannelsAndCategories_WithErrors() {
  3410  
  3411  	tmpFile, err := ioutil.TempFile(os.TempDir(), "discord-channel-2")
  3412  	s.Require().NoError(err)
  3413  	defer os.Remove(tmpFile.Name())
  3414  
  3415  	exportedDiscordData := &discord.ExportedData{
  3416  		Channel: discord.Channel{
  3417  			ID:           "12345",
  3418  			CategoryName: "test-category",
  3419  			CategoryID:   "6789",
  3420  			Name:         "test-channel",
  3421  			Description:  "This is a channel topic",
  3422  			FilePath:     tmpFile.Name(),
  3423  		},
  3424  		Messages: make([]*protobuf.DiscordMessage, 0),
  3425  	}
  3426  
  3427  	data, err := json.Marshal(exportedDiscordData)
  3428  	s.Require().NoError(err)
  3429  
  3430  	err = os.WriteFile(tmpFile.Name(), data, 0666) // nolint: gosec
  3431  	s.Require().NoError(err)
  3432  
  3433  	files := make([]string, 0)
  3434  	files = append(files, tmpFile.Name())
  3435  	_, errs := s.bob.ExtractDiscordChannelsAndCategories(files)
  3436  	// Expecting 1 errors since there are no messages to be extracted
  3437  	s.Require().Len(errs, 1)
  3438  }
  3439  
  3440  func (s *MessengerCommunitiesSuite) TestCommunityBanUserRequestToJoin() {
  3441  	community, _ := s.createCommunity()
  3442  
  3443  	s.advertiseCommunityTo(community, s.owner, s.alice)
  3444  	s.joinCommunity(community, s.owner, s.alice)
  3445  
  3446  	response, err := s.owner.BanUserFromCommunity(
  3447  		context.Background(),
  3448  		&requests.BanUserFromCommunity{
  3449  			CommunityID: community.ID(),
  3450  			User:        common.PubkeyToHexBytes(&s.alice.identity.PublicKey),
  3451  		},
  3452  	)
  3453  	s.Require().NoError(err)
  3454  	s.Require().NotNil(response)
  3455  	s.Require().Len(response.Communities(), 1)
  3456  
  3457  	community = response.Communities()[0]
  3458  	s.Require().False(community.HasMember(&s.alice.identity.PublicKey))
  3459  	s.Require().True(community.IsBanned(&s.alice.identity.PublicKey))
  3460  
  3461  	response, err = WaitOnMessengerResponse(
  3462  		s.alice,
  3463  		func(r *MessengerResponse) bool { return len(r.communities) > 0 },
  3464  		"no communities",
  3465  	)
  3466  
  3467  	s.Require().NoError(err)
  3468  	s.Require().Len(response.Communities(), 1)
  3469  
  3470  	request := s.createRequestToJoinCommunity(community.ID(), s.alice)
  3471  	// We try to join the org
  3472  	rtj := s.alice.communitiesManager.CreateRequestToJoin(request, s.alice.account.GetCustomizationColor())
  3473  
  3474  	s.Require().NoError(err)
  3475  
  3476  	displayName, err := s.alice.settings.DisplayName()
  3477  	s.Require().NoError(err)
  3478  
  3479  	requestToJoinProto := &protobuf.CommunityRequestToJoin{
  3480  		Clock:            rtj.Clock,
  3481  		EnsName:          rtj.ENSName,
  3482  		DisplayName:      displayName,
  3483  		CommunityId:      community.ID(),
  3484  		RevealedAccounts: rtj.RevealedAccounts,
  3485  	}
  3486  
  3487  	s.Require().NoError(err)
  3488  
  3489  	messageState := s.owner.buildMessageState()
  3490  	messageState.CurrentMessageState = &CurrentMessageState{}
  3491  
  3492  	messageState.CurrentMessageState.PublicKey = &s.alice.identity.PublicKey
  3493  
  3494  	statusMessage := v1protocol.StatusMessage{}
  3495  	statusMessage.TransportLayer.Dst = community.PublicKey()
  3496  	err = s.owner.HandleCommunityRequestToJoin(messageState, requestToJoinProto, &statusMessage)
  3497  
  3498  	s.Require().ErrorContains(err, "can't request access")
  3499  }
  3500  
  3501  func (s *MessengerCommunitiesSuite) TestCommunityMaxNumberOfMembers() {
  3502  	john := s.newMessenger("johnPassword", []string{"0x0765400000000000000000000000000000000000"})
  3503  	_, err := john.Start()
  3504  	s.Require().NoError(err)
  3505  
  3506  	defer TearDownMessenger(&s.Suite, john)
  3507  
  3508  	// Bring back the original values
  3509  	defer communities.SetMaxNbMembers(5000)
  3510  	defer communities.SetMaxNbPendingRequestedMembers(100)
  3511  
  3512  	community, _ := s.createCommunity()
  3513  
  3514  	communities.SetMaxNbMembers(2)
  3515  	communities.SetMaxNbPendingRequestedMembers(1)
  3516  
  3517  	s.advertiseCommunityTo(community, s.owner, s.alice)
  3518  	s.advertiseCommunityTo(community, s.owner, s.bob)
  3519  	s.advertiseCommunityTo(community, s.owner, john)
  3520  
  3521  	// Alice joins the community correctly
  3522  	s.joinCommunity(community, s.owner, s.alice)
  3523  
  3524  	// Bob also tries to join, but he will be put in the requests to join to approve and won't join
  3525  	request := s.createRequestToJoinCommunity(community.ID(), s.bob)
  3526  	response, err := s.bob.RequestToJoinCommunity(request)
  3527  	s.Require().NoError(err)
  3528  	s.Require().NotNil(response)
  3529  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  3530  	requestID := response.RequestsToJoinCommunity()[0].ID
  3531  
  3532  	response, err = WaitOnMessengerResponse(
  3533  		s.owner,
  3534  		func(r *MessengerResponse) bool {
  3535  			for _, req := range r.RequestsToJoinCommunity() {
  3536  				if reflect.DeepEqual(req.ID, requestID) {
  3537  					return true
  3538  				}
  3539  			}
  3540  			return false
  3541  		},
  3542  		"no request to join",
  3543  	)
  3544  	s.Require().NoError(err)
  3545  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  3546  	s.Require().Equal(communities.RequestToJoinStatePending, response.RequestsToJoinCommunity()[0].State)
  3547  
  3548  	// We confirm that there are still 2 members only and the access setting is now manual
  3549  	updatedCommunity, err := s.owner.communitiesManager.GetByID(community.ID())
  3550  	s.Require().NoError(err)
  3551  	s.Require().Len(updatedCommunity.Members(), 2)
  3552  	s.Require().Equal(protobuf.CommunityPermissions_MANUAL_ACCEPT, updatedCommunity.Permissions().Access)
  3553  
  3554  	// John also tries to join, but he his request will be ignored as it exceeds the max number of pending requests
  3555  	requestJohn := s.createRequestToJoinCommunity(community.ID(), john)
  3556  	response, err = john.RequestToJoinCommunity(requestJohn)
  3557  	s.Require().NoError(err)
  3558  	s.Require().NotNil(response)
  3559  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  3560  	requestJohnID := response.RequestsToJoinCommunity()[0].ID
  3561  
  3562  	_, err = WaitOnMessengerResponse(
  3563  		s.owner,
  3564  		func(r *MessengerResponse) bool {
  3565  			for _, req := range r.RequestsToJoinCommunity() {
  3566  				if reflect.DeepEqual(req.ID, requestJohnID) {
  3567  					return true
  3568  				}
  3569  			}
  3570  			return false
  3571  		},
  3572  		"no request to join",
  3573  	)
  3574  	s.Require().Error(err)
  3575  }
  3576  
  3577  func (s *MessengerCommunitiesSuite) TestHandleImport() {
  3578  	community, chat := s.createCommunity()
  3579  
  3580  	s.advertiseCommunityTo(community, s.owner, s.alice)
  3581  	s.joinCommunity(community, s.owner, s.alice)
  3582  
  3583  	// Check that there are no messages in the chat at first
  3584  	chat, err := s.alice.persistence.Chat(chat.ID)
  3585  	s.Require().NoError(err)
  3586  	s.Require().NotNil(chat)
  3587  	s.Require().Equal(0, int(chat.UnviewedMessagesCount))
  3588  
  3589  	// Create an message that will be imported
  3590  	testMessage := protobuf.ChatMessage{
  3591  		Text:        "abc123",
  3592  		ChatId:      chat.ID,
  3593  		ContentType: protobuf.ChatMessage_TEXT_PLAIN,
  3594  		MessageType: protobuf.MessageType_COMMUNITY_CHAT,
  3595  		Clock:       1,
  3596  		Timestamp:   1,
  3597  	}
  3598  	encodedPayload, err := proto.Marshal(&testMessage)
  3599  	s.Require().NoError(err)
  3600  	wrappedPayload, err := v1protocol.WrapMessageV1(
  3601  		encodedPayload,
  3602  		protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
  3603  		s.owner.identity,
  3604  	)
  3605  	s.Require().NoError(err)
  3606  
  3607  	message := &types.Message{}
  3608  	message.Sig = crypto.FromECDSAPub(&s.owner.identity.PublicKey)
  3609  	message.Payload = wrappedPayload
  3610  
  3611  	filter := s.alice.transport.FilterByChatID(chat.ID)
  3612  	importedMessages := make(map[transport.Filter][]*types.Message, 0)
  3613  
  3614  	importedMessages[*filter] = append(importedMessages[*filter], message)
  3615  
  3616  	// Import that message
  3617  	err = s.alice.handleImportedMessages(importedMessages)
  3618  	s.Require().NoError(err)
  3619  
  3620  	// Get the chat again and see that there is still no unread message because we don't count import messages
  3621  	chat, err = s.alice.persistence.Chat(chat.ID)
  3622  	s.Require().NoError(err)
  3623  	s.Require().NotNil(chat)
  3624  	s.Require().Equal(0, int(chat.UnviewedMessagesCount))
  3625  }
  3626  
  3627  func (s *MessengerCommunitiesSuite) TestGetCommunityIdFromKey() {
  3628  	publicKey := "0x029e4777ce55f20373db33546c8681a082bd181d665c87e18d4306766de9302b53"
  3629  	privateKey := "0x3f932031cb5f94ba7eb8ab4c824c3677973ab01fde65d1b89e0b3f470003a2cd"
  3630  
  3631  	// Public key returns the same
  3632  	communityID := GetCommunityIDFromKey(publicKey)
  3633  	s.Require().Equal(communityID, publicKey)
  3634  
  3635  	// Private key returns the public key
  3636  	communityID = GetCommunityIDFromKey(privateKey)
  3637  	s.Require().Equal(communityID, publicKey)
  3638  }
  3639  
  3640  type testPermissionChecker struct {
  3641  }
  3642  
  3643  func (t *testPermissionChecker) CheckPermissionToJoin(*communities.Community, []gethcommon.Address) (*communities.CheckPermissionToJoinResponse, error) {
  3644  	return &communities.CheckPermissionsResponse{Satisfied: true}, nil
  3645  
  3646  }
  3647  
  3648  func (t *testPermissionChecker) CheckPermissions(permissionsParsedData *communities.PreParsedCommunityPermissionsData, accountsAndChainIDs []*communities.AccountChainIDsCombination, shortcircuit bool) (*communities.CheckPermissionsResponse, error) {
  3649  	return &communities.CheckPermissionsResponse{Satisfied: true}, nil
  3650  }
  3651  
  3652  func (t *testPermissionChecker) CheckPermissionsWithPreFetchedData(permissionsParsedData *communities.PreParsedCommunityPermissionsData, accountsAndChainIDs []*communities.AccountChainIDsCombination, shortcircuit bool, collectiblesOwners communities.CollectiblesOwners) (*communities.CheckPermissionsResponse, error) {
  3653  	return &communities.CheckPermissionsResponse{Satisfied: true}, nil
  3654  }
  3655  
  3656  func (s *MessengerCommunitiesSuite) TestStartCommunityRekeyLoop() {
  3657  	community, chat := createEncryptedCommunity(&s.Suite, s.owner)
  3658  	s.Require().True(community.Encrypted())
  3659  	s.Require().True(community.ChannelEncrypted(chat.CommunityChatID()))
  3660  
  3661  	s.mockPermissionCheckerForAllMessenger()
  3662  
  3663  	s.advertiseCommunityTo(community, s.owner, s.bob)
  3664  	s.advertiseCommunityTo(community, s.owner, s.alice)
  3665  	s.joinCommunity(community, s.owner, s.bob)
  3666  	s.joinCommunity(community, s.owner, s.alice)
  3667  
  3668  	// Check keys in the database
  3669  	communityKeys, err := s.owner.sender.GetKeysForGroup(community.ID())
  3670  	s.Require().NoError(err)
  3671  	communityKeyCount := len(communityKeys)
  3672  
  3673  	channelKeys, err := s.owner.sender.GetKeysForGroup([]byte(chat.ID))
  3674  	s.Require().NoError(err)
  3675  	channelKeyCount := len(channelKeys)
  3676  
  3677  	// Check that rekeying is occurring by counting the number of keyIDs in the encryptor's DB
  3678  	// This test could be flaky, as the rekey function may not be finished before RekeyInterval * 2 has passed
  3679  	for i := 0; i < 5; i++ {
  3680  		time.Sleep(s.owner.communitiesManager.RekeyInterval * 2)
  3681  		communityKeys, err = s.owner.sender.GetKeysForGroup(community.ID())
  3682  		s.Require().NoError(err)
  3683  		s.Require().Greater(len(communityKeys), communityKeyCount)
  3684  		communityKeyCount = len(communityKeys)
  3685  
  3686  		channelKeys, err = s.owner.sender.GetKeysForGroup([]byte(chat.ID))
  3687  		s.Require().NoError(err)
  3688  		s.Require().Greater(len(channelKeys), channelKeyCount)
  3689  		channelKeyCount = len(channelKeys)
  3690  	}
  3691  }
  3692  
  3693  func (s *MessengerCommunitiesSuite) TestCommunityRekeyAfterBan() {
  3694  	s.owner.communitiesManager.RekeyInterval = 500 * time.Minute
  3695  
  3696  	// Create a new community
  3697  	response, err := s.owner.CreateCommunity(
  3698  		&requests.CreateCommunity{
  3699  			Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
  3700  			Name:        "status",
  3701  			Color:       "#57a7e5",
  3702  			Description: "status community description",
  3703  		},
  3704  		true,
  3705  	)
  3706  	s.Require().NoError(err)
  3707  	s.Require().NotNil(response)
  3708  	s.Require().Len(response.Communities(), 1)
  3709  	s.Require().Len(response.Communities()[0].Members(), 1)
  3710  
  3711  	// Check community is present in the DB and has default values we care about
  3712  	c, err := s.owner.GetCommunityByID(response.Communities()[0].ID())
  3713  	s.Require().NoError(err)
  3714  	s.Require().False(c.Encrypted())
  3715  	// TODO some check that there are no keys for the community. Alt for s.Require().Zero(c.RekeyedAt().Unix())
  3716  
  3717  	_, err = s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{
  3718  		CommunityID: c.ID(),
  3719  		Type:        protobuf.CommunityTokenPermission_BECOME_MEMBER,
  3720  		TokenCriteria: []*protobuf.TokenCriteria{{
  3721  			ContractAddresses: map[uint64]string{3: "0x933"},
  3722  			Type:              protobuf.CommunityTokenType_ERC20,
  3723  			Symbol:            "STT",
  3724  			Name:              "Status Test Token",
  3725  			AmountInWei:       "10000000000000000000",
  3726  			Decimals:          18,
  3727  		}},
  3728  	})
  3729  	s.Require().NoError(err)
  3730  
  3731  	c, err = s.owner.GetCommunityByID(c.ID())
  3732  	s.Require().NoError(err)
  3733  	s.Require().True(c.Encrypted())
  3734  
  3735  	s.advertiseCommunityTo(c, s.owner, s.bob)
  3736  	s.advertiseCommunityTo(c, s.owner, s.alice)
  3737  
  3738  	s.mockPermissionCheckerForAllMessenger()
  3739  
  3740  	s.joinCommunity(c, s.owner, s.bob)
  3741  	s.joinCommunity(c, s.owner, s.alice)
  3742  
  3743  	// Check the Alice and Bob are members of the community
  3744  	c, err = s.owner.GetCommunityByID(c.ID())
  3745  	s.Require().NoError(err)
  3746  	s.Require().True(c.HasMember(&s.alice.identity.PublicKey))
  3747  	s.Require().True(c.HasMember(&s.bob.identity.PublicKey))
  3748  
  3749  	// Make sure at least one key makes it to alice
  3750  	response, err = WaitOnMessengerResponse(s.alice,
  3751  		func(r *MessengerResponse) bool {
  3752  			keys, err := s.alice.encryptor.GetKeysForGroup(response.Communities()[0].ID())
  3753  			if err != nil || len(keys) != 1 {
  3754  				return false
  3755  			}
  3756  			return true
  3757  
  3758  		},
  3759  		"alice does not have enough keys",
  3760  	)
  3761  	s.Require().NoError(err)
  3762  
  3763  	response, err = s.owner.BanUserFromCommunity(context.Background(), &requests.BanUserFromCommunity{
  3764  		CommunityID: c.ID(),
  3765  		User:        common.PubkeyToHexBytes(&s.bob.identity.PublicKey),
  3766  	})
  3767  	s.Require().NoError(err)
  3768  	s.Require().Len(response.Communities(), 1)
  3769  
  3770  	s.Require().False(response.Communities()[0].HasMember(&s.bob.identity.PublicKey))
  3771  
  3772  	// Check bob has been banned
  3773  	response, err = WaitOnMessengerResponse(s.alice,
  3774  		func(r *MessengerResponse) bool {
  3775  			return len(r.Communities()) == 1 && !r.Communities()[0].HasMember(&s.bob.identity.PublicKey)
  3776  
  3777  		},
  3778  		"alice didn't receive updated description",
  3779  	)
  3780  	s.Require().NoError(err)
  3781  
  3782  	response, err = WaitOnMessengerResponse(s.alice,
  3783  		func(r *MessengerResponse) bool {
  3784  			keys, err := s.alice.encryptor.GetKeysForGroup(response.Communities()[0].ID())
  3785  			if err != nil || len(keys) < 2 {
  3786  				return false
  3787  			}
  3788  			return true
  3789  
  3790  		},
  3791  		"alice hasn't received updated key",
  3792  	)
  3793  	s.Require().NoError(err)
  3794  }
  3795  
  3796  func (s *MessengerCommunitiesSuite) TestCommunityRekeyAfterBanDisableCompatibility() {
  3797  	common.RekeyCompatibility = false
  3798  	s.owner.communitiesManager.RekeyInterval = 500 * time.Minute
  3799  
  3800  	// Create a new community
  3801  	response, err := s.owner.CreateCommunity(
  3802  		&requests.CreateCommunity{
  3803  			Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
  3804  			Name:        "status",
  3805  			Color:       "#57a7e5",
  3806  			Description: "status community description",
  3807  		},
  3808  		true,
  3809  	)
  3810  	s.Require().NoError(err)
  3811  	s.Require().NotNil(response)
  3812  	s.Require().Len(response.Communities(), 1)
  3813  
  3814  	// Check community is present in the DB and has default values we care about
  3815  	c, err := s.owner.GetCommunityByID(response.Communities()[0].ID())
  3816  	s.Require().NoError(err)
  3817  	s.Require().False(c.Encrypted())
  3818  	// TODO some check that there are no keys for the community. Alt for s.Require().Zero(c.RekeyedAt().Unix())
  3819  
  3820  	_, err = s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{
  3821  		CommunityID: c.ID(),
  3822  		Type:        protobuf.CommunityTokenPermission_BECOME_MEMBER,
  3823  		TokenCriteria: []*protobuf.TokenCriteria{{
  3824  			ContractAddresses: map[uint64]string{3: "0x933"},
  3825  			Type:              protobuf.CommunityTokenType_ERC20,
  3826  			Symbol:            "STT",
  3827  			Name:              "Status Test Token",
  3828  			AmountInWei:       "10000000000000000000",
  3829  			Decimals:          18,
  3830  		}},
  3831  	})
  3832  	s.Require().NoError(err)
  3833  
  3834  	c, err = s.owner.GetCommunityByID(c.ID())
  3835  	s.Require().NoError(err)
  3836  	s.Require().True(c.Encrypted())
  3837  
  3838  	s.advertiseCommunityTo(c, s.owner, s.bob)
  3839  	s.advertiseCommunityTo(c, s.owner, s.alice)
  3840  
  3841  	s.mockPermissionCheckerForAllMessenger()
  3842  
  3843  	s.joinCommunity(c, s.owner, s.bob)
  3844  	s.joinCommunity(c, s.owner, s.alice)
  3845  
  3846  	// Check the Alice and Bob are members of the community
  3847  	c, err = s.owner.GetCommunityByID(c.ID())
  3848  	s.Require().NoError(err)
  3849  	s.Require().True(c.HasMember(&s.alice.identity.PublicKey))
  3850  	s.Require().True(c.HasMember(&s.bob.identity.PublicKey))
  3851  
  3852  	// Make sure at least one key makes it to alice
  3853  	response, err = WaitOnMessengerResponse(s.alice,
  3854  		func(r *MessengerResponse) bool {
  3855  			keys, err := s.alice.encryptor.GetKeysForGroup(response.Communities()[0].ID())
  3856  			if err != nil || len(keys) != 1 {
  3857  				return false
  3858  			}
  3859  			return true
  3860  
  3861  		},
  3862  		"alice does not have enough keys",
  3863  	)
  3864  	s.Require().NoError(err)
  3865  
  3866  	response, err = s.owner.BanUserFromCommunity(context.Background(), &requests.BanUserFromCommunity{
  3867  		CommunityID: c.ID(),
  3868  		User:        common.PubkeyToHexBytes(&s.bob.identity.PublicKey),
  3869  	})
  3870  	s.Require().NoError(err)
  3871  	s.Require().Len(response.Communities(), 1)
  3872  
  3873  	s.Require().False(response.Communities()[0].HasMember(&s.bob.identity.PublicKey))
  3874  
  3875  	// Check bob has been banned
  3876  	response, err = WaitOnMessengerResponse(s.alice,
  3877  		func(r *MessengerResponse) bool {
  3878  			return len(r.Communities()) == 1 && !r.Communities()[0].HasMember(&s.bob.identity.PublicKey)
  3879  
  3880  		},
  3881  		"alice didn't receive updated description",
  3882  	)
  3883  	s.Require().NoError(err)
  3884  
  3885  	response, err = WaitOnMessengerResponse(s.alice,
  3886  		func(r *MessengerResponse) bool {
  3887  			keys, err := s.alice.encryptor.GetKeysForGroup(response.Communities()[0].ID())
  3888  			if err != nil || len(keys) < 2 {
  3889  				return false
  3890  			}
  3891  			return true
  3892  
  3893  		},
  3894  		"alice hasn't received updated key",
  3895  	)
  3896  	s.Require().NoError(err)
  3897  }
  3898  
  3899  func (s *MessengerCommunitiesSuite) TestRetrieveBigCommunity() {
  3900  	bigEmoji := make([]byte, 4*1024*1024) // 4 MB
  3901  	description := &requests.CreateCommunity{
  3902  		Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
  3903  		Name:        "status",
  3904  		Color:       "#ffffff",
  3905  		Description: "status community description",
  3906  		Emoji:       string(bigEmoji),
  3907  	}
  3908  
  3909  	// checks that private messages are segmented
  3910  	// (community is advertised through `SendPrivate`)
  3911  	response, err := s.owner.CreateCommunity(description, true)
  3912  	s.Require().NoError(err)
  3913  	s.Require().NotNil(response)
  3914  	s.Require().Len(response.Communities(), 1)
  3915  	community := response.Communities()[0]
  3916  
  3917  	s.advertiseCommunityTo(community, s.owner, s.alice)
  3918  	s.joinCommunity(community, s.owner, s.alice)
  3919  
  3920  	// checks that public messages are segmented
  3921  	// (community is advertised through `SendPublic`)
  3922  	updatedDescription := "status updated community description"
  3923  	_, err = s.owner.EditCommunity(&requests.EditCommunity{
  3924  		CommunityID: community.ID(),
  3925  		CreateCommunity: requests.CreateCommunity{
  3926  			Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
  3927  			Name:        "status",
  3928  			Color:       "#ffffff",
  3929  			Description: updatedDescription,
  3930  			Emoji:       string(bigEmoji),
  3931  		},
  3932  	})
  3933  	s.Require().NoError(err)
  3934  
  3935  	// alice receives updated description
  3936  	_, err = WaitOnMessengerResponse(s.alice, func(r *MessengerResponse) bool {
  3937  		return len(r.Communities()) > 0 && r.Communities()[0].DescriptionText() == updatedDescription
  3938  	}, "updated description not received")
  3939  	s.Require().NoError(err)
  3940  }
  3941  
  3942  func (s *MessengerCommunitiesSuite) TestRequestAndCancelCommunityAdminOffline() {
  3943  	ctx := context.Background()
  3944  
  3945  	community, _ := s.createCommunity()
  3946  	s.advertiseCommunityTo(community, s.owner, s.alice)
  3947  
  3948  	request := s.createRequestToJoinCommunity(community.ID(), s.alice)
  3949  	// We try to join the org
  3950  	response, err := s.alice.RequestToJoinCommunity(request)
  3951  	s.Require().NoError(err)
  3952  	s.Require().NotNil(response)
  3953  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  3954  
  3955  	requestToJoin1 := response.RequestsToJoinCommunity()[0]
  3956  	s.Require().NotNil(requestToJoin1)
  3957  	s.Require().Equal(community.ID(), requestToJoin1.CommunityID)
  3958  	s.Require().True(requestToJoin1.Our)
  3959  	s.Require().NotEmpty(requestToJoin1.ID)
  3960  	s.Require().NotEmpty(requestToJoin1.Clock)
  3961  	s.Require().Equal(requestToJoin1.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  3962  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State)
  3963  
  3964  	messageState := s.alice.buildMessageState()
  3965  	messageState.CurrentMessageState = &CurrentMessageState{}
  3966  
  3967  	messageState.CurrentMessageState.PublicKey = &s.alice.identity.PublicKey
  3968  
  3969  	statusMessage := v1protocol.StatusMessage{}
  3970  	statusMessage.TransportLayer.Dst = community.PublicKey()
  3971  
  3972  	requestToJoinProto := &protobuf.CommunityRequestToJoin{
  3973  		Clock:            requestToJoin1.Clock,
  3974  		EnsName:          requestToJoin1.ENSName,
  3975  		DisplayName:      "Alice",
  3976  		CommunityId:      community.ID(),
  3977  		RevealedAccounts: requestToJoin1.RevealedAccounts,
  3978  	}
  3979  
  3980  	err = s.owner.HandleCommunityRequestToJoin(messageState, requestToJoinProto, &statusMessage)
  3981  	s.Require().NoError(err)
  3982  	ownerCommunity, err := s.owner.GetCommunityByID(community.ID())
  3983  	// Check Alice has successfully joined at owner side, Because message order was correct
  3984  	s.Require().True(ownerCommunity.HasMember(s.alice.IdentityPublicKey()))
  3985  	s.Require().NoError(err)
  3986  
  3987  	s.Require().Len(response.Communities(), 1)
  3988  	s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin1.Clock)
  3989  
  3990  	// pull all communities to make sure we set RequestedToJoinAt
  3991  
  3992  	allCommunities, err := s.alice.Communities()
  3993  	s.Require().NoError(err)
  3994  	s.Require().Len(allCommunities, 1)
  3995  	s.Require().Equal(allCommunities[0].ID(), community.ID())
  3996  	s.Require().Equal(allCommunities[0].RequestedToJoinAt(), requestToJoin1.Clock)
  3997  
  3998  	// pull to make sure it has been saved
  3999  	requestsToJoin, err := s.alice.MyPendingRequestsToJoin()
  4000  	s.Require().NoError(err)
  4001  	s.Require().Len(requestsToJoin, 1)
  4002  
  4003  	// Make sure the requests are fetched also by community
  4004  	requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID())
  4005  	s.Require().NoError(err)
  4006  	s.Require().Len(requestsToJoin, 1)
  4007  
  4008  	requestToJoin2 := response.RequestsToJoinCommunity()[0]
  4009  
  4010  	s.Require().NotNil(requestToJoin2)
  4011  	s.Require().Equal(community.ID(), requestToJoin2.CommunityID)
  4012  	s.Require().NotEmpty(requestToJoin2.ID)
  4013  	s.Require().NotEmpty(requestToJoin2.Clock)
  4014  	s.Require().Equal(requestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  4015  	s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin2.State)
  4016  
  4017  	s.Require().Equal(requestToJoin1.ID, requestToJoin2.ID)
  4018  
  4019  	requestToCancel := &requests.CancelRequestToJoinCommunity{ID: requestToJoin1.ID}
  4020  	response, err = s.alice.CancelRequestToJoinCommunity(ctx, requestToCancel)
  4021  	s.Require().NoError(err)
  4022  	s.Require().NotNil(response)
  4023  	s.Require().Len(response.RequestsToJoinCommunity(), 1)
  4024  	s.Require().Equal(communities.RequestToJoinStateCanceled, response.RequestsToJoinCommunity()[0].State)
  4025  
  4026  	messageState = s.alice.buildMessageState()
  4027  	messageState.CurrentMessageState = &CurrentMessageState{}
  4028  
  4029  	messageState.CurrentMessageState.PublicKey = &s.alice.identity.PublicKey
  4030  
  4031  	statusMessage.TransportLayer.Dst = community.PublicKey()
  4032  
  4033  	requestToJoinCancelProto := &protobuf.CommunityRequestToJoinResponse{
  4034  		CommunityId: community.ID(),
  4035  		Clock:       requestToJoin1.Clock + 1,
  4036  		Accepted:    true,
  4037  	}
  4038  
  4039  	err = s.alice.HandleCommunityRequestToJoinResponse(messageState, requestToJoinCancelProto, &statusMessage)
  4040  	s.Require().NoError(err)
  4041  	aliceJoinedCommunities, err := s.alice.JoinedCommunities()
  4042  	s.Require().NoError(err)
  4043  	// Make sure on Alice side she hasn't joined any communities
  4044  	s.Require().Empty(aliceJoinedCommunities)
  4045  
  4046  	// pull to make sure it has been saved
  4047  	myRequestToJoinId := communities.CalculateRequestID(s.alice.IdentityPublicKeyString(), community.ID())
  4048  	canceledRequestToJoin, err := s.alice.communitiesManager.GetRequestToJoin(myRequestToJoinId)
  4049  	s.Require().NoError(err)
  4050  	s.Require().NotNil(canceledRequestToJoin)
  4051  	s.Require().Equal(canceledRequestToJoin.State, communities.RequestToJoinStateCanceled)
  4052  
  4053  	s.Require().NoError(err)
  4054  
  4055  	messageState = s.alice.buildMessageState()
  4056  	messageState.CurrentMessageState = &CurrentMessageState{}
  4057  
  4058  	messageState.CurrentMessageState.PublicKey = &s.alice.identity.PublicKey
  4059  
  4060  	statusMessage.TransportLayer.Dst = community.PublicKey()
  4061  
  4062  	requestToJoinResponseProto := &protobuf.CommunityRequestToJoinResponse{
  4063  		Clock:       canceledRequestToJoin.Clock,
  4064  		CommunityId: community.ID(),
  4065  		Accepted:    true,
  4066  	}
  4067  
  4068  	err = s.alice.HandleCommunityRequestToJoinResponse(messageState, requestToJoinResponseProto, &statusMessage)
  4069  	s.Require().NoError(err)
  4070  	// Make sure alice is NOT a member of the community that she cancelled her request to join to
  4071  	s.Require().False(community.HasMember(s.alice.IdentityPublicKey()))
  4072  	// Make sure there are no AC notifications for Alice
  4073  	aliceNotifications, err := s.alice.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
  4074  		Cursor:        "",
  4075  		Limit:         10,
  4076  		ActivityTypes: []ActivityCenterType{},
  4077  		ReadType:      ActivityCenterQueryParamsReadUnread,
  4078  	})
  4079  	s.Require().NoError(err)
  4080  	s.Require().Len(aliceNotifications.Notifications, 0)
  4081  
  4082  	// Retrieve activity center notifications for admin to make sure the request notification is deleted
  4083  	notifications, err := s.owner.ActivityCenterNotifications(ActivityCenterNotificationsRequest{
  4084  		Cursor:        "",
  4085  		Limit:         10,
  4086  		ActivityTypes: []ActivityCenterType{},
  4087  		ReadType:      ActivityCenterQueryParamsReadUnread,
  4088  	})
  4089  
  4090  	s.Require().NoError(err)
  4091  	s.Require().Len(notifications.Notifications, 0)
  4092  	cancelRequestToJoin2 := response.RequestsToJoinCommunity()[0]
  4093  	s.Require().NotNil(cancelRequestToJoin2)
  4094  	s.Require().Equal(community.ID(), cancelRequestToJoin2.CommunityID)
  4095  	s.Require().False(cancelRequestToJoin2.Our)
  4096  	s.Require().NotEmpty(cancelRequestToJoin2.ID)
  4097  	s.Require().NotEmpty(cancelRequestToJoin2.Clock)
  4098  	s.Require().Equal(cancelRequestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey))
  4099  }
  4100  
  4101  func (s *MessengerCommunitiesSuite) TestCommunityLastOpenedAt() {
  4102  	community, _ := s.createCommunity()
  4103  	s.advertiseCommunityTo(community, s.owner, s.alice)
  4104  	s.joinCommunity(community, s.owner, s.alice)
  4105  
  4106  	// Mock frontend triggering communityUpdateLastOpenedAt
  4107  	lastOpenedAt1, err := s.alice.CommunityUpdateLastOpenedAt(community.IDString())
  4108  	s.Require().NoError(err)
  4109  
  4110  	// Check lastOpenedAt was updated
  4111  	s.Require().True(lastOpenedAt1 > 0)
  4112  
  4113  	// Nap for a bit
  4114  	time.Sleep(time.Second)
  4115  
  4116  	// Check lastOpenedAt was successfully updated twice
  4117  	lastOpenedAt2, err := s.alice.CommunityUpdateLastOpenedAt(community.IDString())
  4118  	s.Require().NoError(err)
  4119  
  4120  	s.Require().True(lastOpenedAt2 > lastOpenedAt1)
  4121  }
  4122  
  4123  func (s *MessengerCommunitiesSuite) TestSyncCommunityLastOpenedAt() {
  4124  	// Create new device
  4125  	alicesOtherDevice := s.createOtherDevice(s.alice)
  4126  	PairDevices(&s.Suite, alicesOtherDevice, s.alice)
  4127  
  4128  	// Create a community
  4129  	createCommunityReq := &requests.CreateCommunity{
  4130  		Membership:  protobuf.CommunityPermissions_MANUAL_ACCEPT,
  4131  		Name:        "new community",
  4132  		Color:       "#000000",
  4133  		Description: "new community description",
  4134  	}
  4135  
  4136  	mr, err := s.alice.CreateCommunity(createCommunityReq, true)
  4137  	s.Require().NoError(err, "s.alice.CreateCommunity")
  4138  	var newCommunity *communities.Community
  4139  	for _, com := range mr.Communities() {
  4140  		if com.Name() == createCommunityReq.Name {
  4141  			newCommunity = com
  4142  		}
  4143  	}
  4144  	s.Require().NotNil(newCommunity)
  4145  
  4146  	// Mock frontend triggering communityUpdateLastOpenedAt
  4147  	lastOpenedAt, err := s.alice.CommunityUpdateLastOpenedAt(newCommunity.IDString())
  4148  	s.Require().NoError(err)
  4149  
  4150  	// Check lastOpenedAt was updated
  4151  	s.Require().True(lastOpenedAt > 0)
  4152  
  4153  	err = tt.RetryWithBackOff(func() error {
  4154  		_, err = alicesOtherDevice.RetrieveAll()
  4155  		if err != nil {
  4156  			return err
  4157  		}
  4158  		// Do we have a new synced community?
  4159  		_, err := alicesOtherDevice.communitiesManager.GetSyncedRawCommunity(newCommunity.ID())
  4160  		if err != nil {
  4161  			return fmt.Errorf("community with sync not received %w", err)
  4162  		}
  4163  
  4164  		return nil
  4165  	})
  4166  	otherDeviceCommunity, err := alicesOtherDevice.communitiesManager.GetByID(newCommunity.ID())
  4167  	s.Require().NoError(err)
  4168  	s.Require().True(otherDeviceCommunity.LastOpenedAt() > 0)
  4169  }
  4170  
  4171  func (s *MessengerCommunitiesSuite) TestBanUserAndDeleteAllUserMessages() {
  4172  	community, _ := s.createCommunity()
  4173  
  4174  	orgChat := &protobuf.CommunityChat{
  4175  		Permissions: &protobuf.CommunityPermissions{
  4176  			Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
  4177  		},
  4178  		Identity: &protobuf.ChatIdentity{
  4179  			DisplayName: "chat test delete messages",
  4180  			Emoji:       "😎",
  4181  			Description: "status-core community chat",
  4182  		},
  4183  	}
  4184  	response, err := s.owner.CreateCommunityChat(community.ID(), orgChat)
  4185  	s.Require().NoError(err)
  4186  	s.Require().NotNil(response)
  4187  	s.Require().Len(response.Communities(), 1)
  4188  	s.Require().Len(response.Chats(), 1)
  4189  
  4190  	community = response.Communities()[0]
  4191  	communityChat := response.Chats()[0]
  4192  
  4193  	s.advertiseCommunityTo(community, s.owner, s.alice)
  4194  	s.joinCommunity(community, s.owner, s.alice)
  4195  
  4196  	inputMessage := buildTestMessage(*communityChat)
  4197  
  4198  	sendResponse, err := s.alice.SendChatMessage(context.Background(), inputMessage)
  4199  	s.NoError(err)
  4200  	s.Require().NotNil(sendResponse)
  4201  	s.Require().Len(sendResponse.Messages(), 1)
  4202  	messageID := sendResponse.Messages()[0].ID
  4203  
  4204  	response, err = WaitOnMessengerResponse(
  4205  		s.owner,
  4206  		func(r *MessengerResponse) bool {
  4207  			if len(r.Messages()) == 0 {
  4208  				return false
  4209  			}
  4210  
  4211  			for _, message := range r.Messages() {
  4212  				if message.ID == messageID {
  4213  					return true
  4214  				}
  4215  			}
  4216  			return false
  4217  		},
  4218  		"no messages",
  4219  	)
  4220  	s.Require().NoError(err)
  4221  	s.Require().Len(response.Messages(), 1)
  4222  	s.Require().Equal(messageID, response.Messages()[0].ID)
  4223  
  4224  	response, err = s.owner.BanUserFromCommunity(
  4225  		context.Background(),
  4226  		&requests.BanUserFromCommunity{
  4227  			CommunityID:       community.ID(),
  4228  			User:              common.PubkeyToHexBytes(&s.alice.identity.PublicKey),
  4229  			DeleteAllMessages: true,
  4230  		},
  4231  	)
  4232  
  4233  	s.Require().NoError(err)
  4234  	s.Require().NotNil(response)
  4235  	s.Require().Len(response.Communities(), 1)
  4236  	s.Require().Len(response.Messages(), 0)
  4237  	s.Require().Len(response.RemovedMessages(), 0)
  4238  	s.Require().Len(response.DeletedMessages(), 1)
  4239  	// we are removing last message, so we must get chat update too
  4240  
  4241  	community = response.Communities()[0]
  4242  	s.Require().False(community.HasMember(&s.alice.identity.PublicKey))
  4243  	s.Require().True(community.IsBanned(&s.alice.identity.PublicKey))
  4244  	s.Require().Len(community.PendingAndBannedMembers(), 1)
  4245  	s.Require().Equal(community.PendingAndBannedMembers()[s.alice.IdentityPublicKeyString()], communities.CommunityMemberBanWithAllMessagesDelete)
  4246  
  4247  	response, err = WaitOnMessengerResponse(
  4248  		s.alice,
  4249  		func(r *MessengerResponse) bool {
  4250  			return r != nil && len(r.DeletedMessages()) > 0
  4251  		},
  4252  		"no removed message for alice",
  4253  	)
  4254  
  4255  	s.Require().NoError(err)
  4256  	s.Require().NotNil(response)
  4257  	s.Require().Len(response.Communities(), 1)
  4258  	s.Require().Len(response.Messages(), 0)
  4259  	s.Require().Len(response.ActivityCenterNotifications(), 1)
  4260  	s.Require().Len(response.RemovedMessages(), 0)
  4261  	s.Require().Len(response.DeletedMessages(), 1)
  4262  	// we are removing last message, so we must get chat update too
  4263  
  4264  	community = response.Communities()[0]
  4265  	s.Require().False(community.HasMember(&s.alice.identity.PublicKey))
  4266  	s.Require().True(community.IsBanned(&s.alice.identity.PublicKey))
  4267  	s.Require().Len(community.PendingAndBannedMembers(), 1)
  4268  	s.Require().Equal(community.PendingAndBannedMembers()[s.alice.IdentityPublicKeyString()], communities.CommunityMemberBanWithAllMessagesDelete)
  4269  	s.Require().False(community.Joined())
  4270  	s.Require().False(community.Spectated())
  4271  }
  4272  
  4273  func (s *MessengerCommunitiesSuite) TestIsDisplayNameDupeOfCommunityMember() {
  4274  	community, _ := s.createCommunity()
  4275  	advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice)
  4276  
  4277  	s.joinCommunity(community, s.owner, s.alice)
  4278  
  4279  	result, err := s.alice.IsDisplayNameDupeOfCommunityMember("Charlie")
  4280  	s.Require().NoError(err)
  4281  	s.Require().True(result)
  4282  
  4283  	result, err = s.alice.IsDisplayNameDupeOfCommunityMember("Alice")
  4284  	s.Require().NoError(err)
  4285  	s.Require().True(result)
  4286  
  4287  	result, err = s.alice.IsDisplayNameDupeOfCommunityMember("Bobby")
  4288  	s.Require().NoError(err)
  4289  	s.Require().False(result)
  4290  }
  4291  
  4292  func (s *MessengerCommunitiesSuite) sendImageToCommunity(sender *Messenger, chatID string) *common.Message {
  4293  	ctx := context.Background()
  4294  	messageToSend := common.NewMessage()
  4295  	messageToSend.ChatId = chatID
  4296  	messageToSend.ContentType = protobuf.ChatMessage_IMAGE
  4297  
  4298  	// base64 image
  4299  	encodedB64Image := "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII"
  4300  	decodedBytes, _ := base64.StdEncoding.DecodeString(encodedB64Image)
  4301  
  4302  	messageToSend.Payload = &protobuf.ChatMessage_Image{
  4303  		Image: &protobuf.ImageMessage{
  4304  			Format:  1, // PNG
  4305  			Payload: decodedBytes,
  4306  		},
  4307  	}
  4308  
  4309  	response, err := sender.SendChatMessage(ctx, messageToSend)
  4310  	s.Require().NoError(err)
  4311  	s.Require().Len(response.Messages(), 1)
  4312  	sentMessage := response.Messages()[0]
  4313  
  4314  	receivers := []*Messenger{s.alice, s.bob, s.owner}
  4315  	for _, receiver := range receivers {
  4316  		if receiver == sender {
  4317  			continue
  4318  		}
  4319  		_, err = WaitOnMessengerResponse(receiver, func(response *MessengerResponse) bool {
  4320  			return len(response.Messages()) == 1 && response.Messages()[0].ID == sentMessage.ID
  4321  		}, "receiver did not receive message")
  4322  		s.Require().NoError(err)
  4323  	}
  4324  	return sentMessage
  4325  }
  4326  
  4327  func (s *MessengerCommunitiesSuite) TestSerializedCommunities() {
  4328  	addMediaServer := func(messenger *Messenger) {
  4329  		mediaServer, err := server.NewMediaServer(messenger.database, nil, nil, nil)
  4330  		s.Require().NoError(err)
  4331  		s.Require().NoError(mediaServer.Start())
  4332  		messenger.SetMediaServer(mediaServer)
  4333  	}
  4334  	addMediaServer(s.owner)
  4335  
  4336  	community, _ := s.createCommunity()
  4337  	// update community description
  4338  	description := community.Description()
  4339  	identImageName := "small"
  4340  	identImagePayload := []byte("123")
  4341  	description.Identity = &protobuf.ChatIdentity{
  4342  		Images: map[string]*protobuf.IdentityImage{
  4343  			identImageName: {
  4344  				Payload: identImagePayload,
  4345  			},
  4346  		},
  4347  	}
  4348  	// #nosec G101
  4349  	tokenImageInBase64 := "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/wcAAwAB/tdgBUgAAAAASUVORK5CYII="
  4350  	description.CommunityTokensMetadata = []*protobuf.CommunityTokenMetadata{
  4351  		{
  4352  			Image:  tokenImageInBase64,
  4353  			Symbol: "STT",
  4354  		},
  4355  	}
  4356  	description.Clock = description.Clock + 1
  4357  	community.Edit(description)
  4358  	s.Require().NoError(s.owner.communitiesManager.SaveCommunity(community))
  4359  
  4360  	// check edit was successful
  4361  	b, err := s.owner.communitiesManager.GetByID(community.ID())
  4362  	s.Require().NoError(err)
  4363  	s.Require().NotNil(b)
  4364  	s.Len(b.Description().CommunityTokensMetadata, 1)
  4365  	s.Equal(tokenImageInBase64, b.Description().CommunityTokensMetadata[0].Image)
  4366  	s.Len(b.Description().Identity.Images, 1)
  4367  	s.Equal(identImagePayload, b.Description().Identity.Images[identImageName].Payload)
  4368  
  4369  	c, err := s.owner.Communities()
  4370  	s.Require().NoError(err)
  4371  	s.Require().Len(c, 1)
  4372  	d, err := json.Marshal(c)
  4373  	s.Require().NoError(err)
  4374  
  4375  	type Image struct {
  4376  		Uri string `json:"uri"`
  4377  	}
  4378  	var communityData []struct {
  4379  		Images                  map[string]Image `json:"images"`
  4380  		CommunityTokensMetadata []struct {
  4381  			Image  string `json:"image"`
  4382  			Symbol string `json:"symbol"`
  4383  		} `json:"communityTokensMetadata"`
  4384  	}
  4385  	err = json.Unmarshal(d, &communityData)
  4386  	s.Require().NoError(err)
  4387  	// Check community description image
  4388  	s.Require().NotEmpty(communityData[0].Images[identImageName])
  4389  	image := communityData[0].Images[identImageName]
  4390  	s.T().Log(fmt.Sprintf("Image URL (%s):", identImageName), image)
  4391  	e, err := s.fetchImage(image.Uri)
  4392  	s.Require().NoError(err)
  4393  	s.Require().Equal(identImagePayload, e)
  4394  
  4395  	imageUrlWithoutCommunityID, err := s.removeUrlParam(image.Uri, "communityID")
  4396  	s.Require().NoError(err)
  4397  	e, err = s.fetchImage(imageUrlWithoutCommunityID)
  4398  	s.Require().NoError(err)
  4399  	s.Require().Len(e, 0)
  4400  
  4401  	imageUrlWithWrongCommunityID, err := s.updateUrlParam(image.Uri, "communityID", "0x0")
  4402  	s.Require().NoError(err)
  4403  	e, err = s.fetchImage(imageUrlWithWrongCommunityID)
  4404  	s.Require().NoError(err)
  4405  	s.Require().Len(e, 0)
  4406  
  4407  	// Check communityTokensMetadata image
  4408  	s.Require().NotEmpty(communityData[0].CommunityTokensMetadata)
  4409  	tokenImageUrl := communityData[0].CommunityTokensMetadata[0].Image
  4410  	s.T().Log("Community Token Metadata Image:", tokenImageUrl)
  4411  	s.T().Log("Community Token Metadata Symbol:", communityData[0].CommunityTokensMetadata[0].Symbol)
  4412  	f, err := s.fetchImage(tokenImageUrl)
  4413  	s.Require().NoError(err)
  4414  	tokenImagePayload, err := images.GetPayloadFromURI(tokenImageInBase64)
  4415  	s.Require().NoError(err)
  4416  	s.Require().Equal(tokenImagePayload, f)
  4417  
  4418  	tokenImageUrlWithoutCommunityID, err := s.removeUrlParam(tokenImageUrl, "communityID")
  4419  	s.Require().NoError(err)
  4420  	f, err = s.fetchImage(tokenImageUrlWithoutCommunityID)
  4421  	s.Require().NoError(err)
  4422  	s.Require().Len(f, 0)
  4423  
  4424  	tokenImageUrlWithWrongCommunityID, err := s.updateUrlParam(tokenImageUrl, "communityID", "0x0")
  4425  	s.Require().NoError(err)
  4426  	f, err = s.fetchImage(tokenImageUrlWithWrongCommunityID)
  4427  	s.Require().NoError(err)
  4428  	s.Require().Len(f, 0)
  4429  
  4430  	tokenImageUrlWithoutSymbol, err := s.removeUrlParam(tokenImageUrl, "symbol")
  4431  	s.Require().NoError(err)
  4432  	f, err = s.fetchImage(tokenImageUrlWithoutSymbol)
  4433  	s.Require().NoError(err)
  4434  	s.Require().Len(f, 0)
  4435  
  4436  	tokenImageUrlWithWrongSymbol, err := s.updateUrlParam(tokenImageUrl, "symbol", "WRONG")
  4437  	s.Require().NoError(err)
  4438  	f, err = s.fetchImage(tokenImageUrlWithWrongSymbol)
  4439  	s.Require().NoError(err)
  4440  	s.Require().Len(f, 0)
  4441  }
  4442  
  4443  func (s *MessengerCommunitiesSuite) updateUrlParam(rawURL string, name, val string) (string, error) {
  4444  	parsedURL, err := url.Parse(rawURL)
  4445  	if err != nil {
  4446  		return "", err
  4447  	}
  4448  
  4449  	queryParams := parsedURL.Query()
  4450  	queryParams.Set(name, val)
  4451  	parsedURL.RawQuery = queryParams.Encode()
  4452  	return parsedURL.String(), nil
  4453  }
  4454  
  4455  func (s *MessengerCommunitiesSuite) removeUrlParam(rawURL, name string) (string, error) {
  4456  	parsedURL, err := url.Parse(rawURL)
  4457  	if err != nil {
  4458  		return "", err
  4459  	}
  4460  
  4461  	queryParams := parsedURL.Query()
  4462  	queryParams.Del(name)
  4463  	parsedURL.RawQuery = queryParams.Encode()
  4464  	return parsedURL.String(), nil
  4465  }
  4466  
  4467  func (s *MessengerCommunitiesSuite) fetchImage(fullURL string) ([]byte, error) {
  4468  	req, err := http.NewRequest("GET", fullURL, nil)
  4469  	if err != nil {
  4470  		return nil, err
  4471  	}
  4472  
  4473  	client := &http.Client{
  4474  		Transport: &http.Transport{
  4475  			TLSClientConfig: &tls.Config{
  4476  				MinVersion:         tls.VersionTLS12,
  4477  				InsecureSkipVerify: true, // nolint: gosec
  4478  			},
  4479  		},
  4480  	}
  4481  
  4482  	resp, err := client.Do(req)
  4483  	if err != nil {
  4484  		return nil, err
  4485  	}
  4486  	defer resp.Body.Close()
  4487  	return ioutil.ReadAll(resp.Body)
  4488  }
  4489  
  4490  func (s *MessengerCommunitiesSuite) TestMemberMessagesHasImageLink() {
  4491  	// GIVEN
  4492  	addMediaServer := func(messenger *Messenger) {
  4493  		mediaServer, err := server.NewMediaServer(messenger.database, nil, nil, nil)
  4494  		s.Require().NoError(err)
  4495  		s.Require().NoError(mediaServer.Start())
  4496  		messenger.SetMediaServer(mediaServer)
  4497  	}
  4498  	addMediaServer(s.alice)
  4499  	addMediaServer(s.bob)
  4500  	addMediaServer(s.owner)
  4501  	community, communityChat := s.createCommunity()
  4502  
  4503  	advertiseCommunityTo(&s.Suite, community, s.owner, s.alice)
  4504  	s.joinCommunity(community, s.owner, s.alice)
  4505  
  4506  	advertiseCommunityTo(&s.Suite, community, s.owner, s.bob)
  4507  	s.joinCommunity(community, s.owner, s.bob)
  4508  
  4509  	// WHEN: alice sends an image message
  4510  	sentMessage := s.sendImageToCommunity(s.alice, communityChat.ID)
  4511  
  4512  	// THEN: everyone see alice message with image link
  4513  	requireMessageWithImage := func(messenger *Messenger, memberPubKey string, communityID string) {
  4514  		storedMessages, err := messenger.GetCommunityMemberAllMessages(
  4515  			&requests.CommunityMemberMessages{
  4516  				CommunityID:     communityID,
  4517  				MemberPublicKey: memberPubKey})
  4518  		s.Require().NoError(err)
  4519  		s.Require().Equal(1, len(storedMessages))
  4520  		memberMessage := storedMessages[0]
  4521  		s.Require().Equal(sentMessage.ID, memberMessage.ID)
  4522  		s.Require().True(strings.HasPrefix(memberMessage.ImageLocalURL, "https://Localhost"))
  4523  	}
  4524  	communityID := community.IDString()
  4525  	alicePubKey := s.alice.IdentityPublicKeyString()
  4526  
  4527  	requireMessageWithImage(s.owner, alicePubKey, communityID)
  4528  	requireMessageWithImage(s.alice, alicePubKey, communityID)
  4529  	requireMessageWithImage(s.bob, alicePubKey, communityID)
  4530  }
  4531  
  4532  func (s *MessengerCommunitiesSuite) TestOpenAndNotJoinedCommunityNewChannelIsNotEmpty() {
  4533  	// Create an open community
  4534  	community, _ := s.createCommunity()
  4535  	s.Require().Len(community.Chats(), 1)
  4536  	s.Require().False(community.Encrypted())
  4537  
  4538  	// Bob joins the community
  4539  	advertiseCommunityTo(&s.Suite, community, s.owner, s.bob)
  4540  	s.joinCommunity(community, s.owner, s.bob)
  4541  
  4542  	// Alice just observes the community
  4543  	advertiseCommunityTo(&s.Suite, community, s.owner, s.alice)
  4544  	_, err := s.alice.SpectateCommunity(community.ID())
  4545  	s.Require().NoError(err)
  4546  
  4547  	aliceCommunity, err := s.alice.GetCommunityByID(community.ID())
  4548  	s.Require().NoError(err)
  4549  	s.Require().Len(aliceCommunity.Chats(), 1)
  4550  
  4551  	// Owner creates a new channel
  4552  	newChannel := &protobuf.CommunityChat{
  4553  		Permissions: &protobuf.CommunityPermissions{
  4554  			Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
  4555  		},
  4556  		Identity: &protobuf.ChatIdentity{
  4557  			DisplayName: "new channel",
  4558  			Emoji:       "",
  4559  			Description: "chat created after joining the community",
  4560  		},
  4561  	}
  4562  
  4563  	response, err := s.owner.CreateCommunityChat(community.ID(), newChannel)
  4564  	s.Require().NoError(err)
  4565  	s.Require().Len(response.CommunityChanges, 1)
  4566  	s.Require().Len(response.CommunityChanges[0].ChatsAdded, 1)
  4567  	s.Require().Len(response.Communities(), 1)
  4568  	s.Require().Len(response.Chats(), 1)
  4569  	s.Require().Len(response.Chats()[0].Members, 2)
  4570  	for _, chat := range response.Communities()[0].Chats() {
  4571  		s.Require().Len(chat.Members, 2)
  4572  	}
  4573  
  4574  	// Check Alice gets the correct member list for a new channel
  4575  	_, err = WaitOnMessengerResponse(
  4576  		s.alice,
  4577  		func(r *MessengerResponse) bool {
  4578  			if len(r.Chats()) == 1 && len(r.Communities()) > 0 {
  4579  				for _, chat := range r.Chats() {
  4580  					s.Require().Len(chat.Members, 2)
  4581  				}
  4582  				for _, chat := range r.Communities()[0].Chats() {
  4583  					s.Require().Len(chat.Members, 2)
  4584  				}
  4585  				return true
  4586  			}
  4587  			return false
  4588  		},
  4589  		"no commiunity message for Alice",
  4590  	)
  4591  	s.Require().NoError(err)
  4592  
  4593  	aliceCommunity, err = s.alice.GetCommunityByID(community.ID())
  4594  	s.Require().NoError(err)
  4595  	s.Require().Len(aliceCommunity.Chats(), 2)
  4596  	for _, chat := range aliceCommunity.Chats() {
  4597  		s.Require().Len(chat.Members, 2)
  4598  	}
  4599  }
  4600  
  4601  func (s *MessengerCommunitiesSuite) sendMention(sender *Messenger, chatID string) *common.Message {
  4602  	ctx := context.Background()
  4603  	messageToSend := common.NewMessage()
  4604  	messageToSend.ChatId = chatID
  4605  	messageToSend.ContentType = protobuf.ChatMessage_TEXT_PLAIN
  4606  	messageToSend.Text = "Hello @" + common.EveryoneMentionTag
  4607  
  4608  	response, err := sender.SendChatMessage(ctx, messageToSend)
  4609  	s.Require().NoError(err)
  4610  	s.Require().Len(response.Messages(), 1)
  4611  	s.Require().True(response.Messages()[0].Mentioned)
  4612  	return response.Messages()[0]
  4613  }
  4614  
  4615  func (s *MessengerCommunitiesSuite) TestAliceDoesNotReceiveMentionWhenSpectating() {
  4616  	// GIVEN: Create an open community
  4617  	community, communityChat := s.createCommunity()
  4618  	community, err := s.owner.GetCommunityByID(community.ID())
  4619  	s.Require().NoError(err)
  4620  	s.Require().Len(community.Chats(), 1)
  4621  	s.Require().False(community.Encrypted())
  4622  
  4623  	// Alice SPECTATES the community
  4624  	advertiseCommunityTo(&s.Suite, community, s.owner, s.alice)
  4625  	_, err = s.alice.SpectateCommunity(community.ID())
  4626  	s.Require().NoError(err)
  4627  
  4628  	aliceCommunity, err := s.alice.GetCommunityByID(community.ID())
  4629  	s.Require().NoError(err)
  4630  	s.Require().Contains(aliceCommunity.ChatIDs(), communityChat.ID)
  4631  
  4632  	// Bob JOINS the community
  4633  	advertiseCommunityTo(&s.Suite, community, s.owner, s.bob)
  4634  	s.joinCommunity(community, s.owner, s.bob)
  4635  
  4636  	// Check Alice gets the updated community
  4637  	_, err = WaitOnMessengerResponse(
  4638  		s.alice,
  4639  		func(r *MessengerResponse) bool {
  4640  			return len(r.Communities()) > 0 && r.Communities()[0].MembersCount() == 2
  4641  		},
  4642  		"no community updates for Alice",
  4643  	)
  4644  	s.Require().NoError(err)
  4645  
  4646  	// WHEN: Bob sends a message to a channel with mention
  4647  	sentMessage := s.sendMention(s.bob, communityChat.ID)
  4648  
  4649  	// THEN: Check Alice gets the message, but no activity center notification
  4650  	_, err = WaitOnMessengerResponse(
  4651  		s.alice,
  4652  		func(r *MessengerResponse) bool {
  4653  			return len(r.Messages()) == 1 && len(r.ActivityCenterNotifications()) == 0 &&
  4654  				r.Messages()[0].ID == sentMessage.ID
  4655  		},
  4656  		"no message for Alice",
  4657  	)
  4658  	s.Require().NoError(err)
  4659  
  4660  	// Alice joins community
  4661  	s.joinCommunity(community, s.owner, s.alice)
  4662  
  4663  	// Bob sends a message with mention
  4664  	sentMessage = s.sendMention(s.bob, communityChat.ID)
  4665  
  4666  	// Check Alice gets the message and activity center notification
  4667  	_, err = WaitOnMessengerResponse(
  4668  		s.alice,
  4669  		func(r *MessengerResponse) bool {
  4670  			return len(r.Messages()) == 1 && len(r.ActivityCenterNotifications()) == 1 &&
  4671  				r.Messages()[0].ID == sentMessage.ID && r.ActivityCenterNotifications()[0].Message.ID == sentMessage.ID &&
  4672  				r.ActivityCenterNotifications()[0].Type == ActivityCenterNotificationTypeMention
  4673  		},
  4674  		"no message for Alice",
  4675  	)
  4676  	s.Require().NoError(err)
  4677  }
  4678  
  4679  // this test simulate the scenario, when we are leaving the community and after the leave
  4680  // receiving outdated COMMUNITY_REQUEST_TO_JOIN_RESPONSE and joining the community again
  4681  func (s *MessengerCommunitiesSuite) TestAliceDidNotProcessOutdatedCommunityRequestToJoinResponse() {
  4682  	community, _ := s.createCommunity()
  4683  
  4684  	advertiseCommunityTo(&s.Suite, community, s.owner, s.alice)
  4685  	s.joinCommunity(community, s.owner, s.alice)
  4686  
  4687  	response, err := s.alice.LeaveCommunity(community.ID())
  4688  	s.Require().NoError(err)
  4689  	s.Require().Len(response.Communities(), 1)
  4690  	s.Require().False(response.Communities()[0].Joined())
  4691  
  4692  	// double-check that alice left the community
  4693  	community, err = s.alice.GetCommunityByID(community.ID())
  4694  	s.Require().NoError(err)
  4695  	s.Require().False(community.Joined())
  4696  
  4697  	// prepare the same request to join response
  4698  	community, err = s.owner.GetCommunityByID(community.ID())
  4699  	s.Require().NoError(err)
  4700  
  4701  	grant, err := community.BuildGrant(s.alice.IdentityPublicKey(), "")
  4702  	s.Require().NoError(err)
  4703  
  4704  	var key *ecdsa.PrivateKey
  4705  	if s.owner.transport.WakuVersion() == 2 {
  4706  		key, err = s.owner.transport.RetrievePubsubTopicKey(community.PubsubTopic())
  4707  		s.Require().NoError(err)
  4708  	}
  4709  
  4710  	encryptedDescription, err := community.EncryptedDescription()
  4711  	s.Require().NoError(err)
  4712  
  4713  	requestToJoinResponse := &protobuf.CommunityRequestToJoinResponse{
  4714  		Clock:                    community.Clock(),
  4715  		Accepted:                 true,
  4716  		CommunityId:              community.ID(),
  4717  		Community:                encryptedDescription,
  4718  		Grant:                    grant,
  4719  		ProtectedTopicPrivateKey: crypto.FromECDSA(key),
  4720  		Shard:                    community.Shard().Protobuffer(),
  4721  	}
  4722  
  4723  	// alice handle duplicated request to join response
  4724  	state := &ReceivedMessageState{
  4725  		Response: &MessengerResponse{},
  4726  		CurrentMessageState: &CurrentMessageState{
  4727  			PublicKey: community.ControlNode(),
  4728  		},
  4729  	}
  4730  
  4731  	err = s.alice.HandleCommunityRequestToJoinResponse(state, requestToJoinResponse, nil)
  4732  	s.Require().Error(err, ErrOutdatedCommunityRequestToJoin)
  4733  
  4734  	// alice receives new request to join when she's already joined
  4735  	// Note: requestToJoinResponse clock is stored as milliseconds, but requestToJoin in database stored
  4736  	// as seconds
  4737  	requestToJoinResponse.Clock = requestToJoinResponse.Clock + 1000
  4738  	err = s.alice.HandleCommunityRequestToJoinResponse(state, requestToJoinResponse, nil)
  4739  	s.Require().NoError(err)
  4740  }
  4741  
  4742  func (s *MessengerCommunitiesSuite) TestIgnoreOutdatedCommunityDescription() {
  4743  	community, _ := s.createCommunity()
  4744  	wrappedDescription1, err := community.ToProtocolMessageBytes()
  4745  	s.Require().NoError(err)
  4746  	signer, description1, err := communities.UnwrapCommunityDescriptionMessage(wrappedDescription1)
  4747  	s.Require().NoError(err)
  4748  
  4749  	_, err = community.AddMember(&s.alice.identity.PublicKey, []protobuf.CommunityMember_Roles{}, community.Clock())
  4750  	s.Require().NoError(err)
  4751  	wrappedDescription2, err := community.ToProtocolMessageBytes()
  4752  	s.Require().NoError(err)
  4753  	_, description2, err := communities.UnwrapCommunityDescriptionMessage(wrappedDescription2)
  4754  	s.Require().NoError(err)
  4755  
  4756  	_, err = community.AddMember(&s.bob.identity.PublicKey, []protobuf.CommunityMember_Roles{}, community.Clock())
  4757  	s.Require().NoError(err)
  4758  	wrappedDescription3, err := community.ToProtocolMessageBytes()
  4759  	s.Require().NoError(err)
  4760  	_, description3, err := communities.UnwrapCommunityDescriptionMessage(wrappedDescription3)
  4761  	s.Require().NoError(err)
  4762  
  4763  	s.Require().Less(description1.Clock, description2.Clock)
  4764  	s.Require().Less(description2.Clock, description3.Clock)
  4765  
  4766  	// Handle first community description
  4767  	{
  4768  		messageState := s.bob.buildMessageState()
  4769  		err = s.bob.handleCommunityDescription(messageState, signer, description1, wrappedDescription1, nil, community.Shard().Protobuffer())
  4770  		s.Require().NoError(err)
  4771  		s.Require().Len(messageState.Response.Communities(), 1)
  4772  		s.Require().Equal(description1.Clock, messageState.Response.Communities()[0].Clock())
  4773  	}
  4774  
  4775  	// Handle third community description
  4776  	{
  4777  		messageState := s.bob.buildMessageState()
  4778  		err = s.bob.handleCommunityDescription(messageState, signer, description3, wrappedDescription3, nil, community.Shard().Protobuffer())
  4779  		s.Require().NoError(err)
  4780  		s.Require().Len(messageState.Response.Communities(), 1)
  4781  		s.Require().Equal(description3.Clock, messageState.Response.Communities()[0].Clock())
  4782  
  4783  		communityFromDB, err := s.bob.communitiesManager.GetByID(community.ID())
  4784  		s.Require().NoError(err)
  4785  		s.Require().Equal(description3.Clock, communityFromDB.Clock())
  4786  		s.Require().Len(communityFromDB.Members(), 3)
  4787  	}
  4788  
  4789  	// Handle second (out of order) community description
  4790  	// It should be ignored
  4791  	{
  4792  		messageState := s.bob.buildMessageState()
  4793  		err = s.bob.handleCommunityDescription(messageState, signer, description2, wrappedDescription2, nil, community.Shard().Protobuffer())
  4794  		s.Require().Len(messageState.Response.Communities(), 0)
  4795  		s.Require().Len(messageState.Response.CommunityChanges, 0)
  4796  		s.Require().ErrorIs(err, communities.ErrInvalidCommunityDescriptionClockOutdated)
  4797  
  4798  		communityFromDB, err := s.bob.communitiesManager.GetByID(community.ID())
  4799  		s.Require().NoError(err)
  4800  		s.Require().Equal(description3.Clock, communityFromDB.Clock())
  4801  		s.Require().Len(communityFromDB.Members(), 3)
  4802  	}
  4803  }
  4804  
  4805  func (s *MessengerCommunitiesSuite) mockPermissionCheckerForAllMessenger() {
  4806  	s.owner.communitiesManager.PermissionChecker = &testPermissionChecker{}
  4807  	s.alice.communitiesManager.PermissionChecker = &testPermissionChecker{}
  4808  	s.bob.communitiesManager.PermissionChecker = &testPermissionChecker{}
  4809  }