github.com/status-im/status-go@v1.1.0/protocol/messenger_offline_test.go (about) 1 package protocol 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/suite" 10 "go.uber.org/zap" 11 12 gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" 13 "github.com/status-im/status-go/eth-node/types" 14 "github.com/status-im/status-go/protocol/common" 15 "github.com/status-im/status-go/protocol/communities" 16 "github.com/status-im/status-go/protocol/protobuf" 17 "github.com/status-im/status-go/protocol/requests" 18 "github.com/status-im/status-go/protocol/tt" 19 ) 20 21 const minimumResendDelay = 500 * time.Millisecond 22 const waitForResentDelay = minimumResendDelay + 100*time.Millisecond 23 24 type MessengerOfflineSuite struct { 25 suite.Suite 26 27 owner *Messenger 28 bob *Messenger 29 alice *Messenger 30 31 ownerWaku types.Waku 32 bobWaku types.Waku 33 aliceWaku types.Waku 34 35 logger *zap.Logger 36 37 mockedBalances communities.BalancesByChain 38 collectiblesManagerMock *CollectiblesManagerMock 39 collectiblesServiceMock *CollectiblesServiceMock 40 accountsTestData map[string][]string 41 accountsPasswords map[string]string 42 } 43 44 func TestMessengerOfflineSuite(t *testing.T) { 45 suite.Run(t, new(MessengerOfflineSuite)) 46 } 47 48 func (s *MessengerOfflineSuite) SetupTest() { 49 s.logger = tt.MustCreateTestLogger() 50 51 s.collectiblesServiceMock = &CollectiblesServiceMock{} 52 s.collectiblesManagerMock = &CollectiblesManagerMock{} 53 s.accountsTestData = make(map[string][]string) 54 s.accountsPasswords = make(map[string]string) 55 56 wakuNodes := CreateWakuV2Network(&s.Suite, s.logger, []string{"owner", "bob", "alice"}) 57 58 ownerLogger := s.logger.With(zap.String("name", "owner")) 59 s.ownerWaku = wakuNodes[0] 60 s.owner = s.newMessenger(s.ownerWaku, ownerLogger, "", []string{}) 61 62 bobLogger := s.logger.With(zap.String("name", "bob")) 63 s.bobWaku = wakuNodes[1] 64 s.bob = s.newMessenger(s.bobWaku, bobLogger, bobPassword, []string{bobAccountAddress}) 65 66 aliceLogger := s.logger.With(zap.String("name", "alice")) 67 s.aliceWaku = wakuNodes[2] 68 s.alice = s.newMessenger(s.aliceWaku, aliceLogger, alicePassword, []string{aliceAddress1}) 69 70 _, err := s.owner.Start() 71 s.Require().NoError(err) 72 _, err = s.bob.Start() 73 s.Require().NoError(err) 74 _, err = s.alice.Start() 75 s.Require().NoError(err) 76 77 s.owner.communitiesManager.RekeyInterval = 50 * time.Millisecond 78 } 79 80 func (s *MessengerOfflineSuite) TearDownTest() { 81 if s.owner != nil { 82 s.Require().NoError(s.owner.Shutdown()) 83 } 84 if s.ownerWaku != nil { 85 s.Require().NoError(gethbridge.GetGethWakuV2From(s.ownerWaku).Stop()) 86 } 87 88 if s.bob != nil { 89 s.Require().NoError(s.bob.Shutdown()) 90 } 91 if s.bobWaku != nil { 92 s.Require().NoError(gethbridge.GetGethWakuV2From(s.bobWaku).Stop()) 93 } 94 if s.alice != nil { 95 s.Require().NoError(s.alice.Shutdown()) 96 } 97 if s.aliceWaku != nil { 98 s.Require().NoError(gethbridge.GetGethWakuV2From(s.aliceWaku).Stop()) 99 } 100 _ = s.logger.Sync() 101 } 102 103 func (s *MessengerOfflineSuite) newMessenger(waku types.Waku, logger *zap.Logger, password string, accounts []string) *Messenger { 104 return newTestCommunitiesMessenger(&s.Suite, waku, testCommunitiesMessengerConfig{ 105 testMessengerConfig: testMessengerConfig{ 106 logger: s.logger, 107 extraOptions: []Option{ 108 WithResendParams(minimumResendDelay, 1), 109 }, 110 }, 111 walletAddresses: accounts, 112 password: password, 113 mockedBalances: &s.mockedBalances, 114 collectiblesManager: s.collectiblesManagerMock, 115 }) 116 } 117 118 func (s *MessengerOfflineSuite) advertiseCommunityTo(community *communities.Community, owner *Messenger, user *Messenger) { 119 advertiseCommunityTo(&s.Suite, community, owner, user) 120 } 121 122 func (s *MessengerOfflineSuite) TestCommunityOfflineEdit() { 123 community, chat := createCommunity(&s.Suite, s.owner) 124 125 chatID := chat.ID 126 inputMessage := common.NewMessage() 127 inputMessage.ChatId = chatID 128 inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN 129 inputMessage.Text = "some text" 130 131 ctx := context.Background() 132 133 s.advertiseCommunityTo(community, s.owner, s.alice) 134 joinCommunity(&s.Suite, community.ID(), s.owner, s.alice, aliceAccountAddress, []string{aliceAddress1}) 135 136 _, err := s.alice.SendChatMessage(ctx, inputMessage) 137 s.Require().NoError(err) 138 s.checkMessageDelivery(ctx, inputMessage) 139 140 // Simulate going offline 141 wakuv2 := gethbridge.GetGethWakuV2From(s.aliceWaku) 142 wakuv2.SkipPublishToTopic(true) 143 144 resp, err := s.alice.SendChatMessage(ctx, inputMessage) 145 messageID := types.Hex2Bytes(resp.Messages()[0].ID) 146 s.Require().NoError(err) 147 148 // Check that message is re-sent once back online 149 wakuv2.SkipPublishToTopic(false) 150 time.Sleep(waitForResentDelay) 151 152 s.checkMessageDelivery(ctx, inputMessage) 153 154 editedText := "some text edited" 155 editedMessage := &requests.EditMessage{ 156 ID: messageID, 157 Text: editedText, 158 } 159 160 wakuv2.SkipPublishToTopic(true) 161 sendResponse, err := s.alice.EditMessage(ctx, editedMessage) 162 s.Require().NotNil(sendResponse) 163 s.Require().NoError(err) 164 165 // Check that message is re-sent once back online 166 wakuv2.SkipPublishToTopic(false) 167 time.Sleep(waitForResentDelay) 168 inputMessage.Text = editedText 169 170 s.checkMessageDelivery(ctx, inputMessage) 171 } 172 173 func (s *MessengerOfflineSuite) checkMessageDelivery(ctx context.Context, inputMessage *common.Message) { 174 var response *MessengerResponse 175 // Pull message and make sure org is received 176 err := tt.RetryWithBackOff(func() error { 177 var err error 178 response, err = s.owner.RetrieveAll() 179 if err != nil { 180 return err 181 } 182 if len(response.messages) == 0 { 183 return errors.New("message not received") 184 } 185 return nil 186 }) 187 188 s.Require().NoError(err) 189 s.Require().Len(response.Messages(), 1) 190 s.Require().Equal(inputMessage.Text, response.Messages()[0].Text) 191 192 // check if response contains the chat we're interested in 193 // we use this instead of checking just the length of the chat because 194 // a CommunityDescription message might be received in the meantime due to syncing 195 // hence response.Chats() might contain the general chat, and the new chat; 196 // or only the new chat if the CommunityDescription message has not arrived 197 found := false 198 for _, chat := range response.Chats() { 199 if chat.ID == inputMessage.ChatId { 200 found = true 201 } 202 } 203 s.Require().True(found) 204 }