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

     1  package protocol
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/suite"
     8  
     9  	"github.com/status-im/status-go/eth-node/crypto"
    10  	"github.com/status-im/status-go/eth-node/types"
    11  	"github.com/status-im/status-go/protocol/common"
    12  	"github.com/status-im/status-go/protocol/protobuf"
    13  	"github.com/status-im/status-go/protocol/requests"
    14  )
    15  
    16  func TestMessengerEditMessageSuite(t *testing.T) {
    17  	suite.Run(t, new(MessengerEditMessageSuite))
    18  }
    19  
    20  type MessengerEditMessageSuite struct {
    21  	MessengerBaseTestSuite
    22  }
    23  
    24  func (s *MessengerEditMessageSuite) TestEditMessage() {
    25  	theirMessenger := s.newMessenger()
    26  	defer TearDownMessenger(&s.Suite, theirMessenger)
    27  
    28  	theirChat := CreateOneToOneChat("Their 1TO1", &s.privateKey.PublicKey, s.m.transport)
    29  	err := theirMessenger.SaveChat(theirChat)
    30  	s.Require().NoError(err)
    31  
    32  	ourChat := CreateOneToOneChat("Our 1TO1", &theirMessenger.identity.PublicKey, s.m.transport)
    33  	err = s.m.SaveChat(ourChat)
    34  	s.Require().NoError(err)
    35  
    36  	inputMessage := buildTestMessage(*theirChat)
    37  	sendResponse, err := theirMessenger.SendChatMessage(context.Background(), inputMessage)
    38  	s.NoError(err)
    39  	s.Require().Len(sendResponse.Messages(), 1)
    40  
    41  	response, err := WaitOnMessengerResponse(
    42  		s.m,
    43  		func(r *MessengerResponse) bool { return len(r.messages) > 0 },
    44  		"no messages",
    45  	)
    46  	s.Require().NoError(err)
    47  	s.Require().Len(response.Chats(), 1)
    48  	s.Require().Len(response.Messages(), 1)
    49  
    50  	ogMessage := sendResponse.Messages()[0]
    51  
    52  	messageID, err := types.DecodeHex(ogMessage.ID)
    53  	s.Require().NoError(err)
    54  
    55  	editedText := "edited text"
    56  	editedMessage := &requests.EditMessage{
    57  		ID:   messageID,
    58  		Text: editedText,
    59  	}
    60  
    61  	sendResponse, err = theirMessenger.EditMessage(context.Background(), editedMessage)
    62  
    63  	s.Require().NoError(err)
    64  	s.Require().Len(sendResponse.Messages(), 1)
    65  	s.Require().NotEmpty(sendResponse.Messages()[0].EditedAt)
    66  	s.Require().Equal(sendResponse.Messages()[0].Text, editedText)
    67  	s.Require().Len(sendResponse.Chats(), 1)
    68  	s.Require().NotNil(sendResponse.Chats()[0].LastMessage)
    69  	s.Require().NotEmpty(sendResponse.Chats()[0].LastMessage.EditedAt)
    70  
    71  	response, err = WaitOnMessengerResponse(
    72  		s.m,
    73  		func(r *MessengerResponse) bool { return len(r.messages) > 0 },
    74  		"no messages",
    75  	)
    76  	s.Require().NoError(err)
    77  
    78  	s.Require().Len(response.Chats(), 1)
    79  	s.Require().Len(response.Messages(), 1)
    80  	s.Require().NotEmpty(response.Messages()[0].EditedAt)
    81  	s.Require().False(response.Messages()[0].New)
    82  
    83  	// Main instance user attempts to edit the message it received from theirMessenger
    84  	editedMessage = &requests.EditMessage{
    85  		ID:   messageID,
    86  		Text: "edited-again text",
    87  	}
    88  	_, err = s.m.EditMessage(context.Background(), editedMessage)
    89  
    90  	s.Require().Equal(ErrInvalidEditOrDeleteAuthor, err)
    91  }
    92  
    93  func (s *MessengerEditMessageSuite) TestEditBridgeMessage() {
    94  	theirMessenger := s.newMessenger()
    95  	defer TearDownMessenger(&s.Suite, theirMessenger)
    96  
    97  	theirChat := CreateOneToOneChat("Their 1TO1", &s.privateKey.PublicKey, s.m.transport)
    98  	err := theirMessenger.SaveChat(theirChat)
    99  	s.Require().NoError(err)
   100  
   101  	ourChat := CreateOneToOneChat("Our 1TO1", &theirMessenger.identity.PublicKey, s.m.transport)
   102  	err = s.m.SaveChat(ourChat)
   103  	s.Require().NoError(err)
   104  
   105  	bridgeMessage := buildTestMessage(*theirChat)
   106  	bridgeMessage.ContentType = protobuf.ChatMessage_BRIDGE_MESSAGE
   107  	bridgeMessage.Payload = &protobuf.ChatMessage_BridgeMessage{
   108  		BridgeMessage: &protobuf.BridgeMessage{
   109  			BridgeName:      "discord",
   110  			UserName:        "user1",
   111  			UserAvatar:      "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==",
   112  			UserID:          "123",
   113  			Content:         "text1",
   114  			MessageID:       "456",
   115  			ParentMessageID: "789",
   116  		},
   117  	}
   118  
   119  	sendResponse, err := theirMessenger.SendChatMessage(context.Background(), bridgeMessage)
   120  	s.NoError(err)
   121  	s.Require().Len(sendResponse.Messages(), 1)
   122  
   123  	response, err := WaitOnMessengerResponse(
   124  		s.m,
   125  		func(r *MessengerResponse) bool { return len(r.messages) > 0 },
   126  		"no messages",
   127  	)
   128  	s.Require().NoError(err)
   129  	s.Require().Len(response.Chats(), 1)
   130  	s.Require().Len(response.Messages(), 1)
   131  
   132  	messageToEdit := sendResponse.Messages()[0]
   133  
   134  	messageID, err := types.DecodeHex(messageToEdit.ID)
   135  	s.Require().NoError(err)
   136  
   137  	editedText := "edited text"
   138  	editedMessage := &requests.EditMessage{
   139  		ID:          messageID,
   140  		Text:        editedText,
   141  		ContentType: protobuf.ChatMessage_BRIDGE_MESSAGE,
   142  	}
   143  
   144  	sendResponse, err = theirMessenger.EditMessage(context.Background(), editedMessage)
   145  	s.Require().NoError(err)
   146  	s.Require().Len(sendResponse.Messages(), 1)
   147  	s.Require().NotEmpty(sendResponse.Messages()[0].EditedAt)
   148  	s.Require().Equal(sendResponse.Messages()[0].Text, "text-input-message")
   149  	s.Require().Equal(sendResponse.Messages()[0].GetBridgeMessage().Content, editedText)
   150  	s.Require().Len(sendResponse.Chats(), 1)
   151  	s.Require().NotNil(sendResponse.Chats()[0].LastMessage)
   152  	s.Require().NotEmpty(sendResponse.Chats()[0].LastMessage.EditedAt)
   153  
   154  	response, err = WaitOnMessengerResponse(
   155  		s.m,
   156  		func(r *MessengerResponse) bool { return len(r.messages) > 0 },
   157  		"no messages",
   158  	)
   159  	s.Require().NoError(err)
   160  	s.Require().Len(response.Chats(), 1)
   161  	s.Require().Len(response.Messages(), 1)
   162  
   163  	s.Require().NotEmpty(response.Chats()[0].LastMessage.EditedAt)
   164  	s.Require().Equal(response.Messages()[0].GetBridgeMessage().Content, "edited text")
   165  }
   166  
   167  func (s *MessengerEditMessageSuite) TestEditMessageEdgeCases() {
   168  	theirMessenger := s.newMessenger()
   169  	defer TearDownMessenger(&s.Suite, theirMessenger)
   170  
   171  	theirChat := CreateOneToOneChat("Their 1TO1", &s.privateKey.PublicKey, s.m.transport)
   172  	err := theirMessenger.SaveChat(theirChat)
   173  	s.Require().NoError(err)
   174  
   175  	ourChat := CreateOneToOneChat("Our 1TO1", &theirMessenger.identity.PublicKey, s.m.transport)
   176  	err = s.m.SaveChat(ourChat)
   177  	s.Require().NoError(err)
   178  
   179  	inputMessage := buildTestMessage(*theirChat)
   180  	sendResponse, err := theirMessenger.SendChatMessage(context.Background(), inputMessage)
   181  	s.NoError(err)
   182  	s.Require().Len(sendResponse.Messages(), 1)
   183  
   184  	response, err := WaitOnMessengerResponse(
   185  		s.m,
   186  		func(r *MessengerResponse) bool { return len(r.messages) > 0 },
   187  		"no messages",
   188  	)
   189  	s.Require().NoError(err)
   190  	s.Require().Len(response.Chats(), 1)
   191  	s.Require().Len(response.Messages(), 1)
   192  
   193  	chat := response.Chats()[0]
   194  	editedMessage := sendResponse.Messages()[0]
   195  
   196  	newContactKey, err := crypto.GenerateKey()
   197  	s.Require().NoError(err)
   198  	wrongContact, err := BuildContactFromPublicKey(&newContactKey.PublicKey)
   199  	s.Require().NoError(err)
   200  
   201  	editMessage := EditMessage{
   202  		EditMessage: &protobuf.EditMessage{
   203  			Clock:     editedMessage.Clock + 1,
   204  			Text:      "some text",
   205  			MessageId: editedMessage.ID,
   206  			ChatId:    chat.ID,
   207  		},
   208  		From: wrongContact.ID,
   209  	}
   210  
   211  	state := &ReceivedMessageState{
   212  		Response: &MessengerResponse{},
   213  		AllChats: &chatMap{},
   214  	}
   215  	state.AllChats.Store(ourChat.ID, ourChat)
   216  
   217  	err = s.m.handleEditMessage(state, editMessage)
   218  	// It should error as the user can't edit this message
   219  	s.Require().Error(err)
   220  
   221  	// Edit with a newer clock value
   222  
   223  	contact, err := BuildContactFromPublicKey(&theirMessenger.identity.PublicKey)
   224  	s.Require().NoError(err)
   225  
   226  	editMessage = EditMessage{
   227  		EditMessage: &protobuf.EditMessage{
   228  			Clock:       editedMessage.Clock + 2,
   229  			Text:        "some text",
   230  			MessageType: protobuf.MessageType_ONE_TO_ONE,
   231  			MessageId:   editedMessage.ID,
   232  			ChatId:      chat.ID,
   233  		},
   234  		From: contact.ID,
   235  	}
   236  
   237  	err = s.m.handleEditMessage(state, editMessage)
   238  	s.Require().NoError(err)
   239  	// It save the edit
   240  	s.Require().Len(state.Response.Messages(), 1)
   241  	s.Require().Len(state.Response.Chats(), 1)
   242  	s.Require().NotNil(state.Response.Chats()[0].LastMessage)
   243  	s.Require().NotEmpty(state.Response.Chats()[0].LastMessage.EditedAt)
   244  
   245  	editedMessage = state.Response.Messages()[0]
   246  
   247  	// In-between edit
   248  	editMessage = EditMessage{
   249  		EditMessage: &protobuf.EditMessage{
   250  			Clock:       editedMessage.Clock + 1,
   251  			Text:        "some other text",
   252  			MessageType: protobuf.MessageType_ONE_TO_ONE,
   253  			MessageId:   editedMessage.ID,
   254  			ChatId:      chat.ID,
   255  		},
   256  		From: contact.ID,
   257  	}
   258  
   259  	state.Response = &MessengerResponse{}
   260  
   261  	err = s.m.handleEditMessage(state, editMessage)
   262  	// It should error as the user can't edit this message
   263  	s.Require().NoError(err)
   264  	// It discards the edit
   265  	s.Require().Len(state.Response.Messages(), 0)
   266  }
   267  
   268  func (s *MessengerEditMessageSuite) TestEditMessageFirstEditsThenMessage() {
   269  	theirMessenger := s.newMessenger()
   270  	defer TearDownMessenger(&s.Suite, theirMessenger)
   271  
   272  	theirChat := CreateOneToOneChat("Their 1TO1", &s.privateKey.PublicKey, s.m.transport)
   273  	err := theirMessenger.SaveChat(theirChat)
   274  	s.Require().NoError(err)
   275  
   276  	contact, err := BuildContactFromPublicKey(&theirMessenger.identity.PublicKey)
   277  	s.Require().NoError(err)
   278  
   279  	ourChat := CreateOneToOneChat("Our 1TO1", &theirMessenger.identity.PublicKey, s.m.transport)
   280  	err = s.m.SaveChat(ourChat)
   281  	s.Require().NoError(err)
   282  	messageID := "message-id"
   283  
   284  	inputMessage := buildTestMessage(*theirChat)
   285  	inputMessage.Clock = 1
   286  	editMessage := EditMessage{
   287  		EditMessage: &protobuf.EditMessage{
   288  			Clock:       2,
   289  			Text:        "some text",
   290  			MessageType: protobuf.MessageType_ONE_TO_ONE,
   291  			MessageId:   messageID,
   292  			ChatId:      theirChat.ID,
   293  		},
   294  		From: common.PubkeyToHex(&theirMessenger.identity.PublicKey),
   295  	}
   296  	state := &ReceivedMessageState{
   297  		Response: &MessengerResponse{},
   298  	}
   299  
   300  	// Handle edit first
   301  	err = s.m.handleEditMessage(state, editMessage)
   302  	s.Require().NoError(err)
   303  
   304  	// Handle chat message
   305  	response := &MessengerResponse{}
   306  	state = &ReceivedMessageState{
   307  		Response: response,
   308  		CurrentMessageState: &CurrentMessageState{
   309  			MessageID:        messageID,
   310  			WhisperTimestamp: s.m.getTimesource().GetCurrentTime(),
   311  			Contact:          contact,
   312  			PublicKey:        &theirMessenger.identity.PublicKey,
   313  		},
   314  	}
   315  	err = s.m.HandleChatMessage(state, inputMessage.ChatMessage, nil, false)
   316  	s.Require().NoError(err)
   317  	s.Require().Len(response.Messages(), 1)
   318  
   319  	editedMessage := response.Messages()[0]
   320  
   321  	s.Require().Equal(uint64(2), editedMessage.EditedAt)
   322  }
   323  
   324  // Test editing a message on an existing private group chat
   325  func (s *MessengerEditMessageSuite) TestEditGroupChatMessage() {
   326  	theirMessenger := s.newMessenger()
   327  	defer TearDownMessenger(&s.Suite, theirMessenger)
   328  
   329  	response, err := s.m.CreateGroupChatWithMembers(context.Background(), "id", []string{})
   330  	s.NoError(err)
   331  	s.Require().Len(response.Chats(), 1)
   332  
   333  	ourChat := response.Chats()[0]
   334  
   335  	err = s.m.SaveChat(ourChat)
   336  	s.NoError(err)
   337  
   338  	s.Require().NoError(makeMutualContact(s.m, &theirMessenger.identity.PublicKey))
   339  
   340  	members := []string{common.PubkeyToHex(&theirMessenger.identity.PublicKey)}
   341  	_, err = s.m.AddMembersToGroupChat(context.Background(), ourChat.ID, members)
   342  	s.NoError(err)
   343  
   344  	// Retrieve their messages so that the chat is created
   345  	response, err = WaitOnMessengerResponse(
   346  		theirMessenger,
   347  		func(r *MessengerResponse) bool { return len(r.Chats()) > 0 },
   348  		"chat invitation not received",
   349  	)
   350  	s.Require().NoError(err)
   351  	s.Require().Len(response.Chats(), 1)
   352  	s.Require().Len(response.ActivityCenterNotifications(), 1)
   353  	s.Require().False(response.Chats()[0].Active)
   354  
   355  	_, err = theirMessenger.ConfirmJoiningGroup(context.Background(), ourChat.ID)
   356  	s.NoError(err)
   357  
   358  	// Wait for the message to reach its destination
   359  	_, err = WaitOnMessengerResponse(
   360  		s.m,
   361  		func(r *MessengerResponse) bool { return len(r.Chats()) > 0 },
   362  		"no joining group event received",
   363  	)
   364  	s.Require().NoError(err)
   365  
   366  	inputMessage := buildTestMessage(*ourChat)
   367  
   368  	sendResponse, err := theirMessenger.SendChatMessage(context.Background(), inputMessage)
   369  	s.NoError(err)
   370  	s.Require().Len(sendResponse.Messages(), 1)
   371  
   372  	sentMessage := sendResponse.Messages()[0]
   373  
   374  	_, err = WaitOnMessengerResponse(
   375  		s.m,
   376  		func(r *MessengerResponse) bool { return len(r.Messages()) > 0 },
   377  		"no messages",
   378  	)
   379  	s.Require().NoError(err)
   380  
   381  	// Edit message
   382  
   383  	messageID, err := types.DecodeHex(sentMessage.ID)
   384  	s.Require().NoError(err)
   385  
   386  	editedText := "edited text"
   387  	editedMessage := &requests.EditMessage{
   388  		ID:   messageID,
   389  		Text: editedText,
   390  	}
   391  
   392  	_, err = theirMessenger.EditMessage(context.Background(), editedMessage)
   393  
   394  	s.Require().NoError(err)
   395  
   396  	response, err = WaitOnMessengerResponse(
   397  		s.m,
   398  		func(r *MessengerResponse) bool { return len(r.messages) > 0 },
   399  		"no messages",
   400  	)
   401  	s.Require().NoError(err)
   402  	s.Require().Len(response.Chats(), 1)
   403  	s.Require().Len(response.Messages(), 1)
   404  	s.Require().NotEmpty(response.Messages()[0].EditedAt)
   405  	s.Require().False(response.Messages()[0].New)
   406  }
   407  
   408  func (s *MessengerEditMessageSuite) TestEditMessageWithMention() {
   409  	theirMessenger := s.newMessenger()
   410  	defer TearDownMessenger(&s.Suite, theirMessenger)
   411  
   412  	theirChat := CreateOneToOneChat("Their 1TO1", &s.privateKey.PublicKey, s.m.transport)
   413  	err := theirMessenger.SaveChat(theirChat)
   414  	s.Require().NoError(err)
   415  
   416  	ourChat := CreateOneToOneChat("Our 1TO1", &theirMessenger.identity.PublicKey, s.m.transport)
   417  	err = s.m.SaveChat(ourChat)
   418  	s.Require().NoError(err)
   419  
   420  	inputMessage := buildTestMessage(*theirChat)
   421  	// Send first message with no mention
   422  	sendResponse, err := theirMessenger.SendChatMessage(context.Background(), inputMessage)
   423  	s.NoError(err)
   424  	s.Require().Len(sendResponse.Messages(), 1)
   425  
   426  	response, err := WaitOnMessengerResponse(
   427  		s.m,
   428  		func(r *MessengerResponse) bool { return len(r.messages) == 1 },
   429  		"no messages",
   430  	)
   431  	s.Require().NoError(err)
   432  	s.Require().Len(response.Chats(), 1)
   433  	s.Require().Len(response.Messages(), 1)
   434  	// Make sure the message is not marked as Mentioned (chat still counts it because it's 1-1)
   435  	s.Require().False(response.Messages()[0].Mentioned)
   436  	s.Require().Equal(int(response.Chats()[0].UnviewedMessagesCount), 1)
   437  	s.Require().Equal(int(response.Chats()[0].UnviewedMentionsCount), 1)
   438  
   439  	ogMessage := sendResponse.Messages()[0]
   440  
   441  	messageID, err := types.DecodeHex(ogMessage.ID)
   442  	s.Require().NoError(err)
   443  
   444  	// Edit the message and add a mention
   445  	editedText := "edited text @" + common.PubkeyToHex(&s.privateKey.PublicKey)
   446  	editedMessage := &requests.EditMessage{
   447  		ID:   messageID,
   448  		Text: editedText,
   449  	}
   450  
   451  	sendResponse, err = theirMessenger.EditMessage(context.Background(), editedMessage)
   452  
   453  	s.Require().NoError(err)
   454  	s.Require().Len(sendResponse.Messages(), 1)
   455  	s.Require().NotEmpty(sendResponse.Messages()[0].EditedAt)
   456  	s.Require().Equal(sendResponse.Messages()[0].Text, editedText)
   457  	s.Require().Len(sendResponse.Chats(), 1)
   458  	s.Require().NotNil(sendResponse.Chats()[0].LastMessage)
   459  	s.Require().NotEmpty(sendResponse.Chats()[0].LastMessage.EditedAt)
   460  	s.Require().False(sendResponse.Messages()[0].Mentioned) // Sender is still not mentioned
   461  
   462  	response, err = WaitOnMessengerResponse(
   463  		s.m,
   464  		func(r *MessengerResponse) bool { return len(r.messages) == 1 },
   465  		"no messages",
   466  	)
   467  	s.Require().NoError(err)
   468  
   469  	s.Require().Len(response.Chats(), 1)
   470  	s.Require().Len(response.Messages(), 1)
   471  	s.Require().NotEmpty(response.Messages()[0].EditedAt)
   472  	s.Require().False(response.Messages()[0].New)
   473  	// Receiver (us) is now mentioned
   474  	s.Require().True(response.Messages()[0].Mentioned)
   475  	s.Require().Equal(int(response.Chats()[0].UnviewedMessagesCount), 1)
   476  	s.Require().Equal(int(response.Chats()[0].UnviewedMentionsCount), 1)
   477  
   478  	// Edit the message again but remove the mention
   479  	editedText = "edited text no mention"
   480  	editedMessage = &requests.EditMessage{
   481  		ID:   messageID,
   482  		Text: editedText,
   483  	}
   484  
   485  	sendResponse, err = theirMessenger.EditMessage(context.Background(), editedMessage)
   486  
   487  	s.Require().NoError(err)
   488  	s.Require().Len(sendResponse.Messages(), 1)
   489  	s.Require().NotEmpty(sendResponse.Messages()[0].EditedAt)
   490  	s.Require().Equal(sendResponse.Messages()[0].Text, editedText)
   491  	s.Require().Len(sendResponse.Chats(), 1)
   492  	s.Require().NotNil(sendResponse.Chats()[0].LastMessage)
   493  	s.Require().NotEmpty(sendResponse.Chats()[0].LastMessage.EditedAt)
   494  	s.Require().False(sendResponse.Messages()[0].Mentioned) // Sender is still not mentioned
   495  
   496  	response, err = WaitOnMessengerResponse(
   497  		s.m,
   498  		func(r *MessengerResponse) bool { return len(r.messages) == 1 },
   499  		"no messages",
   500  	)
   501  	s.Require().NoError(err)
   502  
   503  	s.Require().Len(response.Chats(), 1)
   504  	s.Require().Len(response.Messages(), 1)
   505  	s.Require().NotEmpty(response.Messages()[0].EditedAt)
   506  	s.Require().False(response.Messages()[0].New)
   507  	// Receiver (us) is no longer mentioned
   508  	s.Require().False(response.Messages()[0].Mentioned)
   509  	s.Require().Equal(int(response.Chats()[0].UnviewedMessagesCount), 1) // We still have an unread message though
   510  	s.Require().Equal(int(response.Chats()[0].UnviewedMentionsCount), 1)
   511  }
   512  
   513  func (s *MessengerEditMessageSuite) TestEditMessageWithLinkPreviews() {
   514  	theirMessenger := s.newMessenger()
   515  	defer TearDownMessenger(&s.Suite, theirMessenger)
   516  
   517  	theirChat := CreateOneToOneChat("Their 1TO1", &s.privateKey.PublicKey, s.m.transport)
   518  	err := theirMessenger.SaveChat(theirChat)
   519  	s.Require().NoError(err)
   520  
   521  	ourChat := CreateOneToOneChat("Our 1TO1", &theirMessenger.identity.PublicKey, s.m.transport)
   522  	err = s.m.SaveChat(ourChat)
   523  	s.Require().NoError(err)
   524  
   525  	inputMessage := buildTestMessage(*theirChat)
   526  
   527  	sendResponse, err := theirMessenger.SendChatMessage(context.Background(), inputMessage)
   528  	s.NoError(err)
   529  	s.Require().Len(sendResponse.Messages(), 1)
   530  
   531  	response, err := WaitOnMessengerResponse(
   532  		s.m,
   533  		func(r *MessengerResponse) bool { return len(r.messages) > 0 },
   534  		"no messages",
   535  	)
   536  	s.Require().NoError(err)
   537  	s.Require().Len(response.Chats(), 1)
   538  	s.Require().Len(response.Messages(), 1)
   539  
   540  	ogMessage := sendResponse.Messages()[0]
   541  
   542  	messageID, err := types.DecodeHex(ogMessage.ID)
   543  	s.Require().NoError(err)
   544  
   545  	contactPublicKey, err := crypto.GenerateKey()
   546  	s.Require().NoError(err)
   547  	contactID := types.EncodeHex(crypto.FromECDSAPub(&contactPublicKey.PublicKey))
   548  
   549  	editedText := "edited text"
   550  	editedMessage := &requests.EditMessage{
   551  		ID:   messageID,
   552  		Text: editedText,
   553  		LinkPreviews: []common.LinkPreview{
   554  			{
   555  				Type:        protobuf.UnfurledLink_LINK,
   556  				Description: "GitHub is where people build software.",
   557  				Hostname:    "github.com",
   558  				Title:       "Build software better, together",
   559  				URL:         "https://github.com",
   560  				Thumbnail: common.LinkPreviewThumbnail{
   561  					Width:   100,
   562  					Height:  200,
   563  					URL:     "http://localhost:9999",
   564  					DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
   565  				}},
   566  		},
   567  		StatusLinkPreviews: []common.StatusLinkPreview{
   568  			{
   569  				URL: "https://status.app/u/TestUrl",
   570  				Contact: &common.StatusContactLinkPreview{
   571  					PublicKey:   contactID,
   572  					DisplayName: "TestDisplayName",
   573  					Description: "Test description",
   574  					Icon: common.LinkPreviewThumbnail{
   575  						Width:   100,
   576  						Height:  200,
   577  						DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
   578  					},
   579  				},
   580  			},
   581  		},
   582  	}
   583  
   584  	sendResponse, err = theirMessenger.EditMessage(context.Background(), editedMessage)
   585  
   586  	s.Require().NoError(err)
   587  	s.Require().Len(sendResponse.Messages(), 1)
   588  	s.Require().Len(sendResponse.Messages()[0].LinkPreviews, 1)
   589  	s.Require().NotNil(sendResponse.Messages()[0].UnfurledStatusLinks)
   590  	s.Require().Len(sendResponse.Messages()[0].UnfurledStatusLinks.UnfurledStatusLinks, 1)
   591  	response, err = WaitOnMessengerResponse(
   592  		s.m,
   593  		func(r *MessengerResponse) bool { return len(r.messages) == 1 },
   594  		"no messages",
   595  	)
   596  	s.Require().NoError(err)
   597  
   598  	s.Require().Len(response.Chats(), 1)
   599  	s.Require().Len(response.Messages(), 1)
   600  
   601  	responseMessage := response.Messages()[0]
   602  	s.Require().NotEmpty(responseMessage.EditedAt)
   603  	s.Require().Len(responseMessage.UnfurledLinks, 1)
   604  	s.Require().NotNil(responseMessage.UnfurledStatusLinks)
   605  	s.Require().Len(responseMessage.UnfurledStatusLinks.UnfurledStatusLinks, 1)
   606  	s.Require().False(responseMessage.New)
   607  }