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  }