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

     1  package protocol
     2  
     3  import (
     4  	"bytes"
     5  	"database/sql"
     6  	"fmt"
     7  	"math"
     8  	"sort"
     9  	"strconv"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/status-im/status-go/appdatabase"
    17  	"github.com/status-im/status-go/eth-node/crypto"
    18  	"github.com/status-im/status-go/eth-node/types"
    19  	"github.com/status-im/status-go/protocol/common"
    20  	"github.com/status-im/status-go/protocol/protobuf"
    21  	"github.com/status-im/status-go/protocol/sqlite"
    22  	"github.com/status-im/status-go/t/helpers"
    23  )
    24  
    25  func TestTableUserMessagesAllFieldsCount(t *testing.T) {
    26  	db := sqlitePersistence{}
    27  	expected := len(strings.Split(db.tableUserMessagesAllFields(), ","))
    28  	require.Equal(t, expected, db.tableUserMessagesAllFieldsCount())
    29  }
    30  
    31  func TestSaveMessages(t *testing.T) {
    32  	db, err := openTestDB()
    33  	require.NoError(t, err)
    34  	p := newSQLitePersistence(db)
    35  
    36  	for i := 0; i < 10; i++ {
    37  		id := strconv.Itoa(i)
    38  		err := insertMinimalMessage(p, id)
    39  		require.NoError(t, err)
    40  
    41  		m, err := p.MessageByID(id)
    42  		require.NoError(t, err)
    43  		require.EqualValues(t, id, m.ID)
    44  	}
    45  }
    46  
    47  func TestMessagesByIDs(t *testing.T) {
    48  	db, err := openTestDB()
    49  	require.NoError(t, err)
    50  	p := newSQLitePersistence(db)
    51  
    52  	var ids []string
    53  	for i := 0; i < 10; i++ {
    54  		id := strconv.Itoa(i)
    55  		err := insertMinimalMessage(p, id)
    56  		require.NoError(t, err)
    57  		ids = append(ids, id)
    58  
    59  	}
    60  	m, err := p.MessagesByIDs(ids)
    61  	require.NoError(t, err)
    62  	require.Len(t, m, 10)
    63  }
    64  
    65  func TestMessagesByIDs_WithDiscordMessagesPayload(t *testing.T) {
    66  	db, err := openTestDB()
    67  	require.NoError(t, err)
    68  	p := newSQLitePersistence(db)
    69  
    70  	var ids []string
    71  	for i := 0; i < 10; i++ {
    72  		id := strconv.Itoa(i)
    73  		err := insertMinimalMessage(p, id)
    74  		require.NoError(t, err)
    75  		err = insertMinimalDiscordMessage(p, id, id)
    76  		require.NoError(t, err)
    77  		ids = append(ids, id)
    78  	}
    79  
    80  	m, err := p.MessagesByIDs(ids)
    81  	require.NoError(t, err)
    82  	require.Len(t, m, 10)
    83  
    84  	for _, _m := range m {
    85  		require.NotNil(t, _m.GetDiscordMessage())
    86  	}
    87  }
    88  
    89  func TestMessageByID(t *testing.T) {
    90  	db, err := openTestDB()
    91  	require.NoError(t, err)
    92  	p := newSQLitePersistence(db)
    93  	id := "1"
    94  
    95  	err = insertMinimalMessage(p, id)
    96  	require.NoError(t, err)
    97  
    98  	m, err := p.MessageByID(id)
    99  	require.NoError(t, err)
   100  	require.EqualValues(t, id, m.ID)
   101  }
   102  
   103  func TestMessageByID_WithDiscordMessagePayload(t *testing.T) {
   104  
   105  	db, err := openTestDB()
   106  	require.NoError(t, err)
   107  	p := newSQLitePersistence(db)
   108  	id := "1"
   109  	discordMessageID := "2"
   110  
   111  	err = insertMinimalDiscordMessage(p, id, discordMessageID)
   112  	require.NoError(t, err)
   113  
   114  	m, err := p.MessageByID(id)
   115  	require.NoError(t, err)
   116  	require.EqualValues(t, id, m.ID)
   117  	require.NotNil(t, m.GetDiscordMessage())
   118  	require.EqualValues(t, discordMessageID, m.GetDiscordMessage().Id)
   119  	require.EqualValues(t, "2", m.GetDiscordMessage().Author.Id)
   120  }
   121  
   122  func TestMessageByID_WithDiscordMessageAttachmentPayload(t *testing.T) {
   123  
   124  	db, err := openTestDB()
   125  	require.NoError(t, err)
   126  	p := newSQLitePersistence(db)
   127  	id := "1"
   128  	discordMessageID := "2"
   129  
   130  	err = insertDiscordMessageWithAttachments(p, id, discordMessageID)
   131  	require.NoError(t, err)
   132  
   133  	m, err := p.MessageByID(id)
   134  	require.NoError(t, err)
   135  	require.EqualValues(t, id, m.ID)
   136  
   137  	dm := m.GetDiscordMessage()
   138  	require.NotNil(t, dm)
   139  	require.EqualValues(t, discordMessageID, dm.Id)
   140  
   141  	require.NotNil(t, dm.Attachments)
   142  	require.Len(t, dm.Attachments, 2)
   143  }
   144  
   145  func TestMessagesExist(t *testing.T) {
   146  	db, err := openTestDB()
   147  	require.NoError(t, err)
   148  	p := newSQLitePersistence(db)
   149  
   150  	err = insertMinimalMessage(p, "1")
   151  	require.NoError(t, err)
   152  
   153  	result, err := p.MessagesExist([]string{"1"})
   154  	require.NoError(t, err)
   155  
   156  	require.True(t, result["1"])
   157  
   158  	err = insertMinimalMessage(p, "2")
   159  	require.NoError(t, err)
   160  
   161  	result, err = p.MessagesExist([]string{"1", "2", "3"})
   162  	require.NoError(t, err)
   163  
   164  	require.True(t, result["1"])
   165  	require.True(t, result["2"])
   166  	require.False(t, result["3"])
   167  }
   168  
   169  func TestMessageByChatID(t *testing.T) {
   170  	db, err := openTestDB()
   171  	require.NoError(t, err)
   172  	p := newSQLitePersistence(db)
   173  	chatID := testPublicChatID
   174  	count := 1000
   175  	pageSize := 50
   176  
   177  	var messages []*common.Message
   178  	for i := 0; i < count; i++ {
   179  		messages = append(messages, &common.Message{
   180  			ID:          strconv.Itoa(i),
   181  			LocalChatID: chatID,
   182  			ChatMessage: &protobuf.ChatMessage{
   183  				Clock: uint64(i),
   184  			},
   185  			From: testPK,
   186  		})
   187  
   188  		// Add some other chats.
   189  		if count%5 == 0 {
   190  			messages = append(messages, &common.Message{
   191  				ID:          strconv.Itoa(count + i),
   192  				LocalChatID: "other-chat",
   193  				ChatMessage: &protobuf.ChatMessage{
   194  					Clock: uint64(i),
   195  				},
   196  
   197  				From: testPK,
   198  			})
   199  		}
   200  	}
   201  
   202  	// Add some out-of-order message. Add more than page size.
   203  	outOfOrderCount := pageSize + 1
   204  	allCount := count + outOfOrderCount
   205  	for i := 0; i < pageSize+1; i++ {
   206  		messages = append(messages, &common.Message{
   207  			ID:          strconv.Itoa(count*2 + i),
   208  			LocalChatID: chatID,
   209  			ChatMessage: &protobuf.ChatMessage{
   210  				Clock: uint64(i),
   211  			},
   212  
   213  			From: testPK,
   214  		})
   215  	}
   216  
   217  	err = p.SaveMessages(messages)
   218  	require.NoError(t, err)
   219  
   220  	var (
   221  		result []*common.Message
   222  		cursor string
   223  		iter   int
   224  	)
   225  	for {
   226  		var (
   227  			items []*common.Message
   228  			err   error
   229  		)
   230  
   231  		items, cursor, err = p.MessageByChatID(chatID, cursor, pageSize)
   232  		require.NoError(t, err)
   233  		result = append(result, items...)
   234  
   235  		iter++
   236  		if len(cursor) == 0 || iter > count {
   237  			break
   238  		}
   239  	}
   240  	require.Equal(t, "", cursor) // for loop should exit because of cursor being empty
   241  	require.EqualValues(t, math.Ceil(float64(allCount)/float64(pageSize)), iter)
   242  	require.Equal(t, len(result), allCount)
   243  	require.True(
   244  		t,
   245  		// Verify descending order.
   246  		sort.SliceIsSorted(result, func(i, j int) bool {
   247  			return result[i].Clock > result[j].Clock
   248  		}),
   249  	)
   250  }
   251  
   252  func TestFirstUnseenMessageIDByChatID(t *testing.T) {
   253  	db, err := openTestDB()
   254  	require.NoError(t, err)
   255  	p := newSQLitePersistence(db)
   256  
   257  	messageID, err := p.FirstUnseenMessageID(testPublicChatID)
   258  	require.NoError(t, err)
   259  	require.Equal(t, "", messageID)
   260  
   261  	err = p.SaveMessages([]*common.Message{
   262  		{
   263  			ID:          "1",
   264  			LocalChatID: testPublicChatID,
   265  			ChatMessage: &protobuf.ChatMessage{
   266  				Clock: 1,
   267  				Text:  "some-text"},
   268  			From: testPK,
   269  			Seen: true,
   270  		},
   271  		{
   272  			ID:          "2",
   273  			LocalChatID: testPublicChatID,
   274  			ChatMessage: &protobuf.ChatMessage{
   275  				Clock: 2,
   276  				Text:  "some-text"},
   277  			From: testPK,
   278  			Seen: false,
   279  		},
   280  		{
   281  			ID:          "3",
   282  			LocalChatID: testPublicChatID,
   283  			ChatMessage: &protobuf.ChatMessage{
   284  				Clock: 3,
   285  				Text:  "some-text"},
   286  			From: testPK,
   287  			Seen: false,
   288  		},
   289  	})
   290  	require.NoError(t, err)
   291  
   292  	messageID, err = p.FirstUnseenMessageID(testPublicChatID)
   293  	require.NoError(t, err)
   294  	require.Equal(t, "2", messageID)
   295  }
   296  
   297  func TestLatestMessageByChatID(t *testing.T) {
   298  	db, err := openTestDB()
   299  	require.NoError(t, err)
   300  	p := newSQLitePersistence(db)
   301  
   302  	var ids []string
   303  	for i := 0; i < 10; i++ {
   304  		id := strconv.Itoa(i)
   305  		err := insertMinimalMessage(p, id)
   306  		require.NoError(t, err)
   307  		ids = append(ids, id)
   308  	}
   309  
   310  	id := strconv.Itoa(10)
   311  	err = insertMinimalDeletedMessage(p, id)
   312  	require.NoError(t, err)
   313  	ids = append(ids, id)
   314  
   315  	id = strconv.Itoa(11)
   316  	err = insertMinimalDeletedForMeMessage(p, id)
   317  	require.NoError(t, err)
   318  	ids = append(ids, id)
   319  
   320  	m, err := p.LatestMessageByChatID(testPublicChatID)
   321  	require.NoError(t, err)
   322  	require.Equal(t, m[0].ID, ids[9])
   323  }
   324  
   325  func TestOldestMessageWhisperTimestampByChatID(t *testing.T) {
   326  	db, err := openTestDB()
   327  	require.NoError(t, err)
   328  	p := newSQLitePersistence(db)
   329  	chatID := testPublicChatID
   330  
   331  	_, hasMessage, err := p.OldestMessageWhisperTimestampByChatID(chatID)
   332  	require.NoError(t, err)
   333  	require.False(t, hasMessage)
   334  
   335  	var messages []*common.Message
   336  	for i := 0; i < 10; i++ {
   337  		messages = append(messages, &common.Message{
   338  			ID:          strconv.Itoa(i),
   339  			LocalChatID: chatID,
   340  			ChatMessage: &protobuf.ChatMessage{
   341  				Clock: uint64(i),
   342  			},
   343  			WhisperTimestamp: uint64(i + 10),
   344  			From:             testPK,
   345  		})
   346  	}
   347  
   348  	err = p.SaveMessages(messages)
   349  	require.NoError(t, err)
   350  
   351  	timestamp, hasMessage, err := p.OldestMessageWhisperTimestampByChatID(chatID)
   352  	require.NoError(t, err)
   353  	require.True(t, hasMessage)
   354  	require.Equal(t, uint64(10), timestamp)
   355  }
   356  
   357  func TestPinMessageByChatID(t *testing.T) {
   358  	db, err := openTestDB()
   359  	require.NoError(t, err)
   360  	p := sqlitePersistence{db: db}
   361  	chatID := "chat-with-pinned-messages"
   362  	messagesCount := 1000
   363  	pageSize := 5
   364  	pinnedMessagesCount := 0
   365  
   366  	var messages []*common.Message
   367  	var pinMessages []*common.PinMessage
   368  	for i := 0; i < messagesCount; i++ {
   369  		messages = append(messages, &common.Message{
   370  			ID:          strconv.Itoa(i),
   371  			LocalChatID: chatID,
   372  			ChatMessage: &protobuf.ChatMessage{
   373  				Clock: uint64(i),
   374  			},
   375  			From: testPK,
   376  		})
   377  
   378  		// Pin this message
   379  		if i%100 == 0 {
   380  			from := testPK
   381  			if i == 100 {
   382  				from = "them"
   383  			}
   384  
   385  			pinMessage := common.NewPinMessage()
   386  			pinMessage.ID = strconv.Itoa(i)
   387  			pinMessage.LocalChatID = chatID
   388  			pinMessage.From = from
   389  
   390  			pinMessage.MessageId = strconv.Itoa(i)
   391  			pinMessage.Clock = 111
   392  			pinMessage.Pinned = true
   393  			pinMessages = append(pinMessages, pinMessage)
   394  			pinnedMessagesCount++
   395  
   396  			if i%200 == 0 {
   397  				// unpin a message
   398  				unpinMessage := common.NewPinMessage()
   399  
   400  				unpinMessage.ID = strconv.Itoa(i)
   401  				unpinMessage.LocalChatID = chatID
   402  				unpinMessage.From = testPK
   403  
   404  				pinMessage.MessageId = strconv.Itoa(i)
   405  				unpinMessage.Clock = 333
   406  				unpinMessage.Pinned = false
   407  				pinMessages = append(pinMessages, unpinMessage)
   408  				pinnedMessagesCount--
   409  
   410  				// pinned before the unpin
   411  				pinMessage2 := common.NewPinMessage()
   412  				pinMessage2.ID = strconv.Itoa(i)
   413  				pinMessage2.LocalChatID = chatID
   414  				pinMessage2.From = testPK
   415  
   416  				pinMessage2.MessageId = strconv.Itoa(i)
   417  				pinMessage2.Clock = 222
   418  				pinMessage2.Pinned = true
   419  				pinMessages = append(pinMessages, pinMessage2)
   420  			}
   421  		}
   422  
   423  		// Add some other chats.
   424  		if i%5 == 0 {
   425  			messages = append(messages, &common.Message{
   426  				ID:          strconv.Itoa(messagesCount + i),
   427  				LocalChatID: "chat-without-pinned-messages",
   428  				ChatMessage: &protobuf.ChatMessage{
   429  					Clock: uint64(i),
   430  				},
   431  
   432  				From: testPK,
   433  			})
   434  		}
   435  	}
   436  
   437  	err = p.SaveMessages(messages)
   438  	require.NoError(t, err)
   439  
   440  	err = p.SavePinMessages(pinMessages)
   441  	require.NoError(t, err)
   442  
   443  	var (
   444  		result []*common.PinnedMessage
   445  		cursor string
   446  		iter   int
   447  	)
   448  	for {
   449  		var (
   450  			items []*common.PinnedMessage
   451  			err   error
   452  		)
   453  
   454  		items, cursor, err = p.PinnedMessageByChatID(chatID, cursor, pageSize)
   455  		require.NoError(t, err)
   456  		result = append(result, items...)
   457  
   458  		iter++
   459  		if len(cursor) == 0 || iter > messagesCount {
   460  			break
   461  		}
   462  	}
   463  
   464  	require.Equal(t, "", cursor) // for loop should exit because of cursor being empty
   465  	require.EqualValues(t, pinnedMessagesCount, len(result))
   466  	require.EqualValues(t, math.Ceil(float64(pinnedMessagesCount)/float64(pageSize)), iter)
   467  	require.True(
   468  		t,
   469  		// Verify descending order.
   470  		sort.SliceIsSorted(result, func(i, j int) bool {
   471  			return result[i].Message.Clock > result[j].Message.Clock
   472  		}),
   473  	)
   474  
   475  	require.Equal(t, "them", result[len(result)-1].PinnedBy)
   476  	for i := 0; i < len(result)-1; i++ {
   477  		require.Equal(t, testPK, result[i].PinnedBy)
   478  	}
   479  }
   480  
   481  func TestMessageReplies(t *testing.T) {
   482  	db, err := openTestDB()
   483  	require.NoError(t, err)
   484  	p := newSQLitePersistence(db)
   485  	chatID := testPublicChatID
   486  	message1 := &common.Message{
   487  		ID:          "id-1",
   488  		LocalChatID: chatID,
   489  		ChatMessage: &protobuf.ChatMessage{
   490  			Text:  "content-1",
   491  			Clock: uint64(1),
   492  		},
   493  		From: "1",
   494  	}
   495  	message2 := &common.Message{
   496  		ID:          "id-2",
   497  		LocalChatID: chatID,
   498  		ChatMessage: &protobuf.ChatMessage{
   499  			Text:       "content-2",
   500  			Clock:      uint64(2),
   501  			ResponseTo: "id-1",
   502  		},
   503  
   504  		From: "2",
   505  	}
   506  
   507  	message3 := &common.Message{
   508  		ID:          "id-3",
   509  		LocalChatID: chatID,
   510  		ChatMessage: &protobuf.ChatMessage{
   511  			Text:       "content-3",
   512  			Clock:      uint64(3),
   513  			ResponseTo: "non-existing",
   514  		},
   515  		From: "3",
   516  	}
   517  
   518  	// Message that is deleted
   519  	message4 := &common.Message{
   520  		ID:          "id-4",
   521  		LocalChatID: chatID,
   522  		Deleted:     true,
   523  		ChatMessage: &protobuf.ChatMessage{
   524  			Text:  "content-4",
   525  			Clock: uint64(4),
   526  		},
   527  		From: "2",
   528  	}
   529  
   530  	// Message replied to a deleted message. It will not have QuotedMessage info
   531  	message5 := &common.Message{
   532  		ID:          "id-5",
   533  		LocalChatID: chatID,
   534  		ChatMessage: &protobuf.ChatMessage{
   535  			Text:       "content-4",
   536  			Clock:      uint64(5),
   537  			ResponseTo: "id-4",
   538  		},
   539  		From: "3",
   540  	}
   541  
   542  	// messages := []*common.Message{message1, message2, message3}
   543  	messages := []*common.Message{message1, message2, message3, message4, message5}
   544  
   545  	err = p.SaveMessages(messages)
   546  	require.NoError(t, err)
   547  
   548  	retrievedMessages, _, err := p.MessageByChatID(chatID, "", 10)
   549  	require.NoError(t, err)
   550  
   551  	require.Equal(t, "non-existing", retrievedMessages[2].ResponseTo)
   552  	require.Nil(t, retrievedMessages[2].QuotedMessage)
   553  
   554  	require.Equal(t, "id-1", retrievedMessages[3].ResponseTo)
   555  	require.Equal(t, &common.QuotedMessage{ID: "id-1", From: "1", Text: "content-1"}, retrievedMessages[3].QuotedMessage)
   556  
   557  	require.Equal(t, "", retrievedMessages[4].ResponseTo)
   558  	require.Nil(t, retrievedMessages[4].QuotedMessage)
   559  
   560  	// We have a ResponseTo, but no QuotedMessage only gives the ID, From, and Deleted
   561  	require.Equal(t, "id-4", retrievedMessages[0].ResponseTo)
   562  	require.Equal(t, &common.QuotedMessage{ID: "id-4", Deleted: true, From: "2"}, retrievedMessages[0].QuotedMessage)
   563  }
   564  
   565  func TestMessageByChatIDWithTheSameClocks(t *testing.T) {
   566  	db, err := openTestDB()
   567  	require.NoError(t, err)
   568  	p := newSQLitePersistence(db)
   569  	chatID := testPublicChatID
   570  	clockValues := []uint64{10, 10, 9, 9, 9, 11, 12, 11, 100000, 6, 4, 5, 5, 5, 5}
   571  	count := len(clockValues)
   572  	pageSize := 2
   573  
   574  	var messages []*common.Message
   575  
   576  	for i, clock := range clockValues {
   577  		messages = append(messages, &common.Message{
   578  			ID:          strconv.Itoa(i),
   579  			LocalChatID: chatID,
   580  			ChatMessage: &protobuf.ChatMessage{
   581  				Clock: clock,
   582  			},
   583  			From: testPK,
   584  		})
   585  	}
   586  
   587  	err = p.SaveMessages(messages)
   588  	require.NoError(t, err)
   589  
   590  	var (
   591  		result []*common.Message
   592  		cursor string
   593  		iter   int
   594  	)
   595  	for {
   596  		var (
   597  			items []*common.Message
   598  			err   error
   599  		)
   600  
   601  		items, cursor, err = p.MessageByChatID(chatID, cursor, pageSize)
   602  		require.NoError(t, err)
   603  		result = append(result, items...)
   604  
   605  		iter++
   606  		if cursor == "" || iter > count {
   607  			break
   608  		}
   609  	}
   610  	require.Empty(t, cursor) // for loop should exit because of cursor being empty
   611  	require.Len(t, result, count)
   612  	// Verify the order.
   613  	expectedClocks := make([]uint64, len(clockValues))
   614  	copy(expectedClocks, clockValues)
   615  	sort.Slice(expectedClocks, func(i, j int) bool {
   616  		return expectedClocks[i] > expectedClocks[j]
   617  	})
   618  	resultClocks := make([]uint64, 0, len(clockValues))
   619  	for _, m := range result {
   620  		resultClocks = append(resultClocks, m.Clock)
   621  	}
   622  	require.EqualValues(t, expectedClocks, resultClocks)
   623  }
   624  
   625  func TestDeleteMessageByID(t *testing.T) {
   626  	db, err := openTestDB()
   627  	require.NoError(t, err)
   628  	p := newSQLitePersistence(db)
   629  	id := "1"
   630  
   631  	err = insertMinimalMessage(p, id)
   632  	require.NoError(t, err)
   633  
   634  	m, err := p.MessageByID(id)
   635  	require.NoError(t, err)
   636  	require.Equal(t, id, m.ID)
   637  
   638  	err = p.DeleteMessage(m.ID)
   639  	require.NoError(t, err)
   640  
   641  	_, err = p.MessageByID(id)
   642  	require.EqualError(t, err, "record not found")
   643  }
   644  
   645  func TestDeleteMessagesByChatID(t *testing.T) {
   646  	db, err := openTestDB()
   647  	require.NoError(t, err)
   648  	p := newSQLitePersistence(db)
   649  
   650  	err = insertMinimalMessage(p, "1")
   651  	require.NoError(t, err)
   652  
   653  	err = insertMinimalMessage(p, "2")
   654  	require.NoError(t, err)
   655  
   656  	m, _, err := p.MessageByChatID(testPublicChatID, "", 10)
   657  	require.NoError(t, err)
   658  	require.Equal(t, 2, len(m))
   659  
   660  	err = p.DeleteMessagesByChatID(testPublicChatID)
   661  	require.NoError(t, err)
   662  
   663  	m, _, err = p.MessageByChatID(testPublicChatID, "", 10)
   664  	require.NoError(t, err)
   665  	require.Equal(t, 0, len(m))
   666  
   667  }
   668  
   669  func TestMarkMessageSeen(t *testing.T) {
   670  	chatID := "test-chat"
   671  	db, err := openTestDB()
   672  	require.NoError(t, err)
   673  	p := newSQLitePersistence(db)
   674  	id := "1"
   675  
   676  	err = insertMinimalMessage(p, id)
   677  	require.NoError(t, err)
   678  
   679  	m, err := p.MessageByID(id)
   680  	require.NoError(t, err)
   681  	require.False(t, m.Seen)
   682  
   683  	count, countWithMention, err := p.MarkMessagesSeen(chatID, []string{m.ID})
   684  	require.NoError(t, err)
   685  	require.Equal(t, uint64(1), count)
   686  	require.Equal(t, uint64(0), countWithMention)
   687  
   688  	m, err = p.MessageByID(id)
   689  	require.NoError(t, err)
   690  	require.True(t, m.Seen)
   691  }
   692  
   693  func TestUpdateMessageOutgoingStatus(t *testing.T) {
   694  	db, err := openTestDB()
   695  	require.NoError(t, err)
   696  	p := newSQLitePersistence(db)
   697  	id := "1"
   698  
   699  	err = insertMinimalMessage(p, id)
   700  	require.NoError(t, err)
   701  
   702  	err = p.UpdateMessageOutgoingStatus(id, "new-status")
   703  	require.NoError(t, err)
   704  
   705  	m, err := p.MessageByID(id)
   706  	require.NoError(t, err)
   707  	require.Equal(t, "new-status", m.OutgoingStatus)
   708  }
   709  
   710  func TestMessagesIDsByType(t *testing.T) {
   711  	db, err := openTestDB()
   712  	require.NoError(t, err)
   713  	p := newSQLitePersistence(db)
   714  
   715  	ids, err := p.RawMessagesIDsByType(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
   716  	require.NoError(t, err)
   717  	require.Empty(t, ids)
   718  
   719  	err = p.SaveRawMessage(minimalRawMessage("chat-message-id", protobuf.ApplicationMetadataMessage_CHAT_MESSAGE))
   720  	require.NoError(t, err)
   721  	ids, err = p.RawMessagesIDsByType(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
   722  	require.NoError(t, err)
   723  	require.Equal(t, 1, len(ids))
   724  	require.Equal(t, "chat-message-id", ids[0])
   725  
   726  	ids, err = p.RawMessagesIDsByType(protobuf.ApplicationMetadataMessage_EMOJI_REACTION)
   727  	require.NoError(t, err)
   728  	require.Empty(t, ids)
   729  
   730  	err = p.SaveRawMessage(minimalRawMessage("emoji-message-id", protobuf.ApplicationMetadataMessage_EMOJI_REACTION))
   731  	require.NoError(t, err)
   732  	ids, err = p.RawMessagesIDsByType(protobuf.ApplicationMetadataMessage_EMOJI_REACTION)
   733  	require.NoError(t, err)
   734  	require.Equal(t, 1, len(ids))
   735  	require.Equal(t, "emoji-message-id", ids[0])
   736  }
   737  
   738  func TestExpiredMessagesIDs(t *testing.T) {
   739  	messageResendMaxCount := 30
   740  	db, err := openTestDB()
   741  	require.NoError(t, err)
   742  	p := newSQLitePersistence(db)
   743  
   744  	ids, err := p.ExpiredMessagesIDs(messageResendMaxCount)
   745  	require.NoError(t, err)
   746  	require.Empty(t, ids)
   747  
   748  	//save expired emoji message
   749  	rawEmojiReaction := minimalRawMessage("emoji-message-id", protobuf.ApplicationMetadataMessage_EMOJI_REACTION)
   750  	rawEmojiReaction.Sent = false
   751  	err = p.SaveRawMessage(rawEmojiReaction)
   752  	require.NoError(t, err)
   753  
   754  	//make sure it appered in expired emoji reactions list
   755  	ids, err = p.ExpiredMessagesIDs(messageResendMaxCount)
   756  	require.NoError(t, err)
   757  	require.Equal(t, 1, len(ids))
   758  
   759  	//save non-expired emoji reaction
   760  	rawEmojiReaction2 := minimalRawMessage("emoji-message-id2", protobuf.ApplicationMetadataMessage_EMOJI_REACTION)
   761  	rawEmojiReaction2.Sent = true
   762  	err = p.SaveRawMessage(rawEmojiReaction2)
   763  	require.NoError(t, err)
   764  
   765  	//make sure it didn't appear in expired emoji reactions list
   766  	ids, err = p.ExpiredMessagesIDs(messageResendMaxCount)
   767  	require.NoError(t, err)
   768  	require.Equal(t, 1, len(ids))
   769  }
   770  
   771  func TestDontOverwriteSentStatus(t *testing.T) {
   772  	db, err := openTestDB()
   773  	require.NoError(t, err)
   774  	p := newSQLitePersistence(db)
   775  
   776  	//save rawMessage
   777  	rawMessage := minimalRawMessage("chat-message-id", protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
   778  	rawMessage.Sent = true
   779  	err = p.SaveRawMessage(rawMessage)
   780  	require.NoError(t, err)
   781  
   782  	rawMessage.Sent = false
   783  	err = p.SaveRawMessage(rawMessage)
   784  	require.NoError(t, err)
   785  
   786  	m, err := p.RawMessageByID(rawMessage.ID)
   787  	require.NoError(t, err)
   788  
   789  	require.True(t, m.Sent)
   790  }
   791  
   792  func TestPersistenceEmojiReactions(t *testing.T) {
   793  	db, err := openTestDB()
   794  	require.NoError(t, err)
   795  	p := newSQLitePersistence(db)
   796  	// reverse order as we use DESC
   797  	id1 := "1"
   798  	id2 := "2"
   799  	id3 := "3"
   800  
   801  	from1 := "from-1"
   802  	from2 := "from-2"
   803  	from3 := "from-3"
   804  
   805  	chatID := testPublicChatID
   806  
   807  	err = insertMinimalMessage(p, id1)
   808  	require.NoError(t, err)
   809  
   810  	err = insertMinimalMessage(p, id2)
   811  	require.NoError(t, err)
   812  
   813  	err = insertMinimalMessage(p, id3)
   814  	require.NoError(t, err)
   815  
   816  	// Insert normal emoji reaction
   817  	require.NoError(t, p.SaveEmojiReaction(&EmojiReaction{
   818  		EmojiReaction: &protobuf.EmojiReaction{
   819  			Clock:     1,
   820  			MessageId: id3,
   821  			ChatId:    chatID,
   822  			Type:      protobuf.EmojiReaction_SAD,
   823  		},
   824  		LocalChatID: chatID,
   825  		From:        from1,
   826  	}))
   827  
   828  	// Insert retracted emoji reaction
   829  	require.NoError(t, p.SaveEmojiReaction(&EmojiReaction{
   830  		EmojiReaction: &protobuf.EmojiReaction{
   831  			Clock:     1,
   832  			MessageId: id3,
   833  			ChatId:    chatID,
   834  			Type:      protobuf.EmojiReaction_SAD,
   835  			Retracted: true,
   836  		},
   837  		LocalChatID: chatID,
   838  		From:        from2,
   839  	}))
   840  
   841  	// Insert retracted emoji reaction out of pagination
   842  	require.NoError(t, p.SaveEmojiReaction(&EmojiReaction{
   843  		EmojiReaction: &protobuf.EmojiReaction{
   844  			Clock:     1,
   845  			MessageId: id1,
   846  			ChatId:    chatID,
   847  			Type:      protobuf.EmojiReaction_SAD,
   848  		},
   849  		LocalChatID: chatID,
   850  		From:        from2,
   851  	}))
   852  
   853  	// Insert retracted emoji reaction out of pagination
   854  	require.NoError(t, p.SaveEmojiReaction(&EmojiReaction{
   855  		EmojiReaction: &protobuf.EmojiReaction{
   856  			Clock:     1,
   857  			MessageId: id1,
   858  			ChatId:    chatID,
   859  			Type:      protobuf.EmojiReaction_SAD,
   860  		},
   861  		LocalChatID: chatID,
   862  		From:        from3,
   863  	}))
   864  
   865  	// Wrong local chat id
   866  	require.NoError(t, p.SaveEmojiReaction(&EmojiReaction{
   867  		EmojiReaction: &protobuf.EmojiReaction{
   868  			Clock:     1,
   869  			MessageId: id1,
   870  			ChatId:    chatID,
   871  			Type:      protobuf.EmojiReaction_LOVE,
   872  		},
   873  		LocalChatID: "wrong-chat-id",
   874  		From:        from3,
   875  	}))
   876  
   877  	reactions, err := p.EmojiReactionsByChatID(chatID, "", 1)
   878  	require.NoError(t, err)
   879  	require.Len(t, reactions, 1)
   880  	require.Equal(t, id3, reactions[0].MessageId)
   881  
   882  	// Try with a cursor
   883  	_, cursor, err := p.MessageByChatID(chatID, "", 1)
   884  	require.NoError(t, err)
   885  
   886  	reactions, err = p.EmojiReactionsByChatID(chatID, cursor, 2)
   887  	require.NoError(t, err)
   888  	require.Len(t, reactions, 2)
   889  	require.Equal(t, id1, reactions[0].MessageId)
   890  	require.Equal(t, id1, reactions[1].MessageId)
   891  }
   892  
   893  func openTestDB() (*sql.DB, error) {
   894  	db, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
   895  	if err != nil {
   896  		return nil, err
   897  	}
   898  
   899  	return db, sqlite.Migrate(db)
   900  }
   901  
   902  func insertMinimalMessage(p *sqlitePersistence, id string) error {
   903  	return p.SaveMessages([]*common.Message{{
   904  		ID:          id,
   905  		LocalChatID: testPublicChatID,
   906  		ChatMessage: &protobuf.ChatMessage{Text: "some-text"},
   907  		From:        testPK,
   908  	}})
   909  }
   910  
   911  func insertMinimalDeletedMessage(p *sqlitePersistence, id string) error {
   912  	return p.SaveMessages([]*common.Message{{
   913  		ID:          id,
   914  		Deleted:     true,
   915  		LocalChatID: testPublicChatID,
   916  		ChatMessage: &protobuf.ChatMessage{Text: "some-text"},
   917  		From:        testPK,
   918  	}})
   919  }
   920  
   921  func insertMinimalDeletedForMeMessage(p *sqlitePersistence, id string) error {
   922  	return p.SaveMessages([]*common.Message{{
   923  		ID:           id,
   924  		DeletedForMe: true,
   925  		LocalChatID:  testPublicChatID,
   926  		ChatMessage:  &protobuf.ChatMessage{Text: "some-text"},
   927  		From:         testPK,
   928  	}})
   929  }
   930  
   931  func insertDiscordMessageWithAttachments(p *sqlitePersistence, id string, discordMessageID string) error {
   932  	err := insertMinimalDiscordMessage(p, id, discordMessageID)
   933  	if err != nil {
   934  		return err
   935  	}
   936  
   937  	attachment := &protobuf.DiscordMessageAttachment{
   938  		Id:        "1",
   939  		MessageId: discordMessageID,
   940  		Url:       "https://does-not-exist.com",
   941  		Payload:   []byte{1, 2, 3, 4},
   942  	}
   943  
   944  	attachment2 := &protobuf.DiscordMessageAttachment{
   945  		Id:        "2",
   946  		MessageId: discordMessageID,
   947  		Url:       "https://does-not-exist.com",
   948  		Payload:   []byte{5, 6, 7, 8},
   949  	}
   950  
   951  	return p.SaveDiscordMessageAttachments([]*protobuf.DiscordMessageAttachment{
   952  		attachment,
   953  		attachment2,
   954  	})
   955  }
   956  
   957  func insertMinimalDiscordMessage(p *sqlitePersistence, id string, discordMessageID string) error {
   958  	discordMessage := &protobuf.DiscordMessage{
   959  		Id:        discordMessageID,
   960  		Type:      "Default",
   961  		Timestamp: "123456",
   962  		Content:   "This is the message",
   963  		Author: &protobuf.DiscordMessageAuthor{
   964  			Id: "2",
   965  		},
   966  		Reference: &protobuf.DiscordMessageReference{},
   967  	}
   968  
   969  	err := p.SaveDiscordMessage(discordMessage)
   970  	if err != nil {
   971  		return err
   972  	}
   973  
   974  	return p.SaveMessages([]*common.Message{{
   975  		ID:          id,
   976  		LocalChatID: testPublicChatID,
   977  		From:        testPK,
   978  		ChatMessage: &protobuf.ChatMessage{
   979  			Text:        "some-text",
   980  			ContentType: protobuf.ChatMessage_DISCORD_MESSAGE,
   981  			ChatId:      testPublicChatID,
   982  			Payload: &protobuf.ChatMessage_DiscordMessage{
   983  				DiscordMessage: discordMessage,
   984  			},
   985  		},
   986  	}})
   987  }
   988  
   989  func minimalRawMessage(id string, messageType protobuf.ApplicationMetadataMessage_Type) *common.RawMessage {
   990  	return &common.RawMessage{
   991  		ID:          id,
   992  		LocalChatID: "test-chat",
   993  		MessageType: messageType,
   994  	}
   995  }
   996  
   997  // Regression test making sure that if audio_duration_ms is null, no error is thrown
   998  func TestMessagesAudioDurationMsNull(t *testing.T) {
   999  	db, err := openTestDB()
  1000  	require.NoError(t, err)
  1001  	p := newSQLitePersistence(db)
  1002  	id := "message-id-1"
  1003  
  1004  	err = insertMinimalMessage(p, id)
  1005  	require.NoError(t, err)
  1006  
  1007  	_, err = p.db.Exec("UPDATE user_messages SET audio_duration_ms = NULL")
  1008  	require.NoError(t, err)
  1009  
  1010  	m, err := p.MessagesByIDs([]string{id})
  1011  	require.NoError(t, err)
  1012  	require.Len(t, m, 1)
  1013  
  1014  	m, _, err = p.MessageByChatID(testPublicChatID, "", 10)
  1015  	require.NoError(t, err)
  1016  	require.Len(t, m, 1)
  1017  }
  1018  
  1019  func TestSaveChat(t *testing.T) {
  1020  	db, err := openTestDB()
  1021  	require.NoError(t, err)
  1022  	p := newSQLitePersistence(db)
  1023  
  1024  	chat := CreatePublicChat("test-chat", &testTimeSource{})
  1025  	chat.LastMessage = common.NewMessage()
  1026  	err = p.SaveChat(*chat)
  1027  	require.NoError(t, err)
  1028  
  1029  	retrievedChat, err := p.Chat(chat.ID)
  1030  	require.NoError(t, err)
  1031  	require.Equal(t, chat, retrievedChat)
  1032  }
  1033  
  1034  func TestSaveMentions(t *testing.T) {
  1035  	chatID := testPublicChatID
  1036  	db, err := openTestDB()
  1037  	require.NoError(t, err)
  1038  	p := newSQLitePersistence(db)
  1039  
  1040  	key, err := crypto.GenerateKey()
  1041  	require.NoError(t, err)
  1042  
  1043  	pkString := types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey))
  1044  
  1045  	message := common.Message{
  1046  		ID:          "1",
  1047  		LocalChatID: chatID,
  1048  		ChatMessage: &protobuf.ChatMessage{Text: "some-text"},
  1049  		From:        testPK,
  1050  		Mentions:    []string{pkString},
  1051  	}
  1052  
  1053  	err = p.SaveMessages([]*common.Message{&message})
  1054  	require.NoError(t, err)
  1055  
  1056  	retrievedMessages, _, err := p.MessageByChatID(chatID, "", 10)
  1057  	require.NoError(t, err)
  1058  	require.Len(t, retrievedMessages, 1)
  1059  	require.Len(t, retrievedMessages[0].Mentions, 1)
  1060  	require.Equal(t, retrievedMessages[0].Mentions, message.Mentions)
  1061  }
  1062  
  1063  func TestSqlitePersistence_GetWhenChatIdentityLastPublished(t *testing.T) {
  1064  	db, err := openTestDB()
  1065  	require.NoError(t, err)
  1066  	p := newSQLitePersistence(db)
  1067  
  1068  	chatID := "0xabcd1234"
  1069  	hash := []byte{0x1}
  1070  	now := time.Now().Unix()
  1071  
  1072  	err = p.SaveWhenChatIdentityLastPublished(chatID, hash)
  1073  	require.NoError(t, err)
  1074  
  1075  	ts, actualHash, err := p.GetWhenChatIdentityLastPublished(chatID)
  1076  	require.NoError(t, err)
  1077  
  1078  	// Check that the save happened in the last 2 seconds
  1079  	diff := ts - now
  1080  	require.LessOrEqual(t, diff, int64(2))
  1081  
  1082  	require.True(t, bytes.Equal(hash, actualHash))
  1083  
  1084  	// Require unsaved values to be zero
  1085  	ts2, actualHash2, err := p.GetWhenChatIdentityLastPublished("0xdeadbeef")
  1086  	require.NoError(t, err)
  1087  	require.Exactly(t, int64(0), ts2)
  1088  	require.Nil(t, actualHash2)
  1089  }
  1090  
  1091  func TestContactBioPersistence(t *testing.T) {
  1092  	db, err := openTestDB()
  1093  	require.NoError(t, err)
  1094  	p := newSQLitePersistence(db)
  1095  
  1096  	key, err := crypto.GenerateKey()
  1097  	require.NoError(t, err)
  1098  
  1099  	contactID := types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey))
  1100  	contactBio := "A contact bio description"
  1101  
  1102  	err = p.SaveContact(&Contact{ID: contactID, Bio: contactBio}, nil)
  1103  	require.NoError(t, err)
  1104  
  1105  	contacts, err := p.Contacts()
  1106  	require.NoError(t, err)
  1107  	require.Len(t, contacts, 1)
  1108  	require.Equal(t, contactBio, contacts[0].Bio)
  1109  }
  1110  
  1111  func TestContactBioPersistenceDefaults(t *testing.T) {
  1112  	db, err := openTestDB()
  1113  	require.NoError(t, err)
  1114  	p := newSQLitePersistence(db)
  1115  
  1116  	key, err := crypto.GenerateKey()
  1117  	require.NoError(t, err)
  1118  
  1119  	contactID := types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey))
  1120  
  1121  	err = p.SaveContact(&Contact{ID: contactID}, nil)
  1122  	require.NoError(t, err)
  1123  
  1124  	contacts, err := p.Contacts()
  1125  	require.NoError(t, err)
  1126  	require.Len(t, contacts, 1)
  1127  	require.Equal(t, "", contacts[0].Bio)
  1128  }
  1129  
  1130  func TestUpdateContactChatIdentity(t *testing.T) {
  1131  	db, err := openTestDB()
  1132  	require.NoError(t, err)
  1133  	p := newSQLitePersistence(db)
  1134  
  1135  	key, err := crypto.GenerateKey()
  1136  	require.NoError(t, err)
  1137  
  1138  	contactID := types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey))
  1139  
  1140  	err = p.SaveContact(&Contact{ID: contactID}, nil)
  1141  	require.NoError(t, err)
  1142  
  1143  	jpegType := []byte{0xff, 0xd8, 0xff, 0x1}
  1144  	identityImages := make(map[string]*protobuf.IdentityImage)
  1145  	identityImages["large"] = &protobuf.IdentityImage{
  1146  		Payload:     jpegType,
  1147  		SourceType:  protobuf.IdentityImage_RAW_PAYLOAD,
  1148  		ImageFormat: protobuf.ImageFormat_PNG,
  1149  	}
  1150  
  1151  	identityImages["small"] = &protobuf.IdentityImage{
  1152  		Payload:     jpegType,
  1153  		SourceType:  protobuf.IdentityImage_RAW_PAYLOAD,
  1154  		ImageFormat: protobuf.ImageFormat_PNG,
  1155  	}
  1156  
  1157  	toArrayOfPointers := func(array []protobuf.SocialLink) (result []*protobuf.SocialLink) {
  1158  		result = make([]*protobuf.SocialLink, len(array))
  1159  		for i := range array {
  1160  			result[i] = &array[i]
  1161  		}
  1162  		return
  1163  	}
  1164  
  1165  	chatIdentity := &protobuf.ChatIdentity{
  1166  		Clock:  1,
  1167  		Images: identityImages,
  1168  		SocialLinks: toArrayOfPointers([]protobuf.SocialLink{
  1169  			{
  1170  				Text: "Personal Site",
  1171  				Url:  "status.im",
  1172  			},
  1173  			{
  1174  				Text: "Twitter",
  1175  				Url:  "Status_ico",
  1176  			},
  1177  		}),
  1178  	}
  1179  
  1180  	clockUpdated, imagesUpdated, err := p.UpdateContactChatIdentity(contactID, chatIdentity)
  1181  	require.NoError(t, err)
  1182  	require.True(t, clockUpdated)
  1183  	require.True(t, imagesUpdated)
  1184  
  1185  	// Save again same clock and data
  1186  	clockUpdated, imagesUpdated, err = p.UpdateContactChatIdentity(contactID, chatIdentity)
  1187  	require.NoError(t, err)
  1188  	require.False(t, clockUpdated)
  1189  	require.False(t, imagesUpdated)
  1190  
  1191  	// Save again newer clock and no images
  1192  	chatIdentity.Clock = 2
  1193  	chatIdentity.Images = make(map[string]*protobuf.IdentityImage)
  1194  	clockUpdated, imagesUpdated, err = p.UpdateContactChatIdentity(contactID, chatIdentity)
  1195  	require.NoError(t, err)
  1196  	require.True(t, clockUpdated)
  1197  	require.True(t, imagesUpdated)
  1198  
  1199  	contacts, err := p.Contacts()
  1200  	require.NoError(t, err)
  1201  	require.Len(t, contacts, 1)
  1202  
  1203  	require.Len(t, contacts[0].Images, 0)
  1204  }
  1205  
  1206  func TestRemovedProfileImage(t *testing.T) {
  1207  	db, err := openTestDB()
  1208  	require.NoError(t, err)
  1209  	p := newSQLitePersistence(db)
  1210  
  1211  	key, err := crypto.GenerateKey()
  1212  	require.NoError(t, err)
  1213  
  1214  	contactID := types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey))
  1215  
  1216  	err = p.SaveContact(&Contact{ID: contactID}, nil)
  1217  	require.NoError(t, err)
  1218  
  1219  	jpegType := []byte{0xff, 0xd8, 0xff, 0x1}
  1220  	identityImages := make(map[string]*protobuf.IdentityImage)
  1221  	identityImages["large"] = &protobuf.IdentityImage{
  1222  		Payload:     jpegType,
  1223  		SourceType:  protobuf.IdentityImage_RAW_PAYLOAD,
  1224  		ImageFormat: protobuf.ImageFormat_PNG,
  1225  	}
  1226  
  1227  	identityImages["small"] = &protobuf.IdentityImage{
  1228  		Payload:     jpegType,
  1229  		SourceType:  protobuf.IdentityImage_RAW_PAYLOAD,
  1230  		ImageFormat: protobuf.ImageFormat_PNG,
  1231  	}
  1232  
  1233  	chatIdentity := &protobuf.ChatIdentity{
  1234  		Clock:  1,
  1235  		Images: identityImages,
  1236  	}
  1237  
  1238  	clockUpdated, imagesUpdated, err := p.UpdateContactChatIdentity(contactID, chatIdentity)
  1239  	require.NoError(t, err)
  1240  	require.True(t, clockUpdated)
  1241  	require.True(t, imagesUpdated)
  1242  
  1243  	contacts, err := p.Contacts()
  1244  	require.NoError(t, err)
  1245  	require.Len(t, contacts, 1)
  1246  	require.Len(t, contacts[0].Images, 2)
  1247  
  1248  	emptyChatIdentity := &protobuf.ChatIdentity{
  1249  		Clock:  1,
  1250  		Images: nil,
  1251  	}
  1252  
  1253  	clockUpdated, imagesUpdated, err = p.UpdateContactChatIdentity(contactID, emptyChatIdentity)
  1254  	require.NoError(t, err)
  1255  	require.False(t, clockUpdated)
  1256  	require.True(t, imagesUpdated)
  1257  
  1258  	contacts, err = p.Contacts()
  1259  	require.NoError(t, err)
  1260  	require.Len(t, contacts, 1)
  1261  	require.Len(t, contacts[0].Images, 0)
  1262  }
  1263  
  1264  func TestSaveLinks(t *testing.T) {
  1265  	chatID := testPublicChatID
  1266  	db, err := openTestDB()
  1267  	require.NoError(t, err)
  1268  	p := newSQLitePersistence(db)
  1269  
  1270  	require.NoError(t, err)
  1271  
  1272  	message := common.Message{
  1273  		ID:          "1",
  1274  		LocalChatID: chatID,
  1275  		ChatMessage: &protobuf.ChatMessage{Text: "some-text"},
  1276  		From:        testPK,
  1277  		Links:       []string{"https://github.com/status-im/status-mobile"},
  1278  	}
  1279  
  1280  	err = p.SaveMessages([]*common.Message{&message})
  1281  	require.NoError(t, err)
  1282  
  1283  	retrievedMessages, _, err := p.MessageByChatID(chatID, "", 10)
  1284  	require.NoError(t, err)
  1285  	require.Len(t, retrievedMessages, 1)
  1286  	require.Len(t, retrievedMessages[0].Links, 1)
  1287  	require.Equal(t, retrievedMessages[0].Links, message.Links)
  1288  }
  1289  
  1290  func TestSaveWithUnfurledLinks(t *testing.T) {
  1291  	db, err := openTestDB()
  1292  	require.NoError(t, err)
  1293  	p := newSQLitePersistence(db)
  1294  	require.NoError(t, err)
  1295  
  1296  	chatID := testPublicChatID
  1297  	message := common.Message{
  1298  		ID:          "1",
  1299  		LocalChatID: chatID,
  1300  		From:        testPK,
  1301  		ChatMessage: &protobuf.ChatMessage{
  1302  			Text: "some-text",
  1303  			UnfurledLinks: []*protobuf.UnfurledLink{
  1304  				{
  1305  					Type:             protobuf.UnfurledLink_LINK,
  1306  					Url:              "https://github.com",
  1307  					Title:            "Build software better, together",
  1308  					Description:      "GitHub is where people build software.",
  1309  					ThumbnailPayload: []byte("abc"),
  1310  				},
  1311  				{
  1312  					Type:             protobuf.UnfurledLink_LINK,
  1313  					Url:              "https://www.youtube.com/watch?v=mzOyYtfXkb0",
  1314  					Title:            "Status Town Hall #67 - 12 October 2020",
  1315  					Description:      "",
  1316  					ThumbnailPayload: []byte("def"),
  1317  				},
  1318  			},
  1319  		},
  1320  	}
  1321  
  1322  	err = p.SaveMessages([]*common.Message{&message})
  1323  	require.NoError(t, err)
  1324  
  1325  	mgs, _, err := p.MessageByChatID(chatID, "", 10)
  1326  	require.NoError(t, err)
  1327  	require.Len(t, mgs, 1)
  1328  	require.Len(t, mgs[0].UnfurledLinks, 2)
  1329  	require.Equal(t, mgs[0].UnfurledLinks, message.UnfurledLinks)
  1330  }
  1331  
  1332  func TestHideMessage(t *testing.T) {
  1333  	db, err := openTestDB()
  1334  	require.NoError(t, err)
  1335  	p := newSQLitePersistence(db)
  1336  	chatID := testPublicChatID
  1337  	message := &common.Message{
  1338  		ID:          "id-1",
  1339  		LocalChatID: chatID,
  1340  		ChatMessage: &protobuf.ChatMessage{
  1341  			Text:  "content-1",
  1342  			Clock: uint64(1),
  1343  		},
  1344  		From: "1",
  1345  	}
  1346  
  1347  	messages := []*common.Message{message}
  1348  
  1349  	err = p.SaveMessages(messages)
  1350  	require.NoError(t, err)
  1351  
  1352  	err = p.HideMessage(message.ID)
  1353  	require.NoError(t, err)
  1354  
  1355  	var actualHidden, actualSeen bool
  1356  	err = p.db.QueryRow("SELECT hide, seen FROM user_messages WHERE id = ?", message.ID).Scan(&actualHidden, &actualSeen)
  1357  
  1358  	require.NoError(t, err)
  1359  	require.True(t, actualHidden)
  1360  	require.True(t, actualSeen)
  1361  }
  1362  
  1363  func TestDeactivatePublicChat(t *testing.T) {
  1364  	db, err := openTestDB()
  1365  	require.NoError(t, err)
  1366  	p := newSQLitePersistence(db)
  1367  	publicChatID := "public-chat-id"
  1368  	var currentClockValue uint64 = 10
  1369  
  1370  	timesource := &testTimeSource{}
  1371  	lastMessage := common.Message{
  1372  		ID:          "0x01",
  1373  		LocalChatID: publicChatID,
  1374  		ChatMessage: &protobuf.ChatMessage{Text: "some-text"},
  1375  		From:        testPK,
  1376  	}
  1377  	lastMessage.Clock = 20
  1378  
  1379  	require.NoError(t, p.SaveMessages([]*common.Message{&lastMessage}))
  1380  
  1381  	publicChat := CreatePublicChat(publicChatID, timesource)
  1382  	publicChat.LastMessage = &lastMessage
  1383  	publicChat.UnviewedMessagesCount = 1
  1384  
  1385  	err = p.DeactivateChat(publicChat, currentClockValue, true)
  1386  
  1387  	// It does not set deleted at for a public chat
  1388  	require.NoError(t, err)
  1389  	require.Equal(t, uint64(0), publicChat.DeletedAtClockValue)
  1390  
  1391  	// It sets the lastMessage to nil
  1392  	require.Nil(t, publicChat.LastMessage)
  1393  
  1394  	// It sets unviewed messages count
  1395  	require.Equal(t, uint(0), publicChat.UnviewedMessagesCount)
  1396  
  1397  	// It sets active as false
  1398  	require.False(t, publicChat.Active)
  1399  
  1400  	// It deletes messages
  1401  	messages, _, err := p.MessageByChatID(publicChatID, "", 10)
  1402  	require.NoError(t, err)
  1403  	require.Len(t, messages, 0)
  1404  
  1405  	// Reload chat to make sure it has been save
  1406  	dbChat, err := p.Chat(publicChatID)
  1407  
  1408  	require.NoError(t, err)
  1409  	require.NotNil(t, dbChat)
  1410  
  1411  	// Same checks on the chat pulled from the db
  1412  	// It does not set deleted at for a public chat
  1413  	require.NoError(t, err)
  1414  	require.Equal(t, uint64(0), dbChat.DeletedAtClockValue)
  1415  
  1416  	// It sets the lastMessage to nil
  1417  	require.Nil(t, dbChat.LastMessage)
  1418  
  1419  	// It sets unviewed messages count
  1420  	require.Equal(t, uint(0), dbChat.UnviewedMessagesCount)
  1421  
  1422  	// It sets active as false
  1423  	require.False(t, dbChat.Active)
  1424  }
  1425  
  1426  func TestDeactivateOneToOneChat(t *testing.T) {
  1427  	key, err := crypto.GenerateKey()
  1428  	require.NoError(t, err)
  1429  
  1430  	pkString := types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey))
  1431  
  1432  	db, err := openTestDB()
  1433  	require.NoError(t, err)
  1434  	p := newSQLitePersistence(db)
  1435  	var currentClockValue uint64 = 10
  1436  
  1437  	timesource := &testTimeSource{}
  1438  
  1439  	chat := CreateOneToOneChat(pkString, &key.PublicKey, timesource)
  1440  
  1441  	lastMessage := common.Message{
  1442  		ID:          "0x01",
  1443  		LocalChatID: chat.ID,
  1444  		ChatMessage: &protobuf.ChatMessage{Text: "some-text"},
  1445  		From:        testPK,
  1446  	}
  1447  	lastMessage.Clock = 20
  1448  
  1449  	require.NoError(t, p.SaveMessages([]*common.Message{&lastMessage}))
  1450  
  1451  	chat.LastMessage = &lastMessage
  1452  	chat.UnviewedMessagesCount = 1
  1453  
  1454  	err = p.DeactivateChat(chat, currentClockValue, true)
  1455  
  1456  	// It does set deleted at for a public chat
  1457  	require.NoError(t, err)
  1458  	require.NotEqual(t, uint64(0), chat.DeletedAtClockValue)
  1459  
  1460  	// It sets the lastMessage to nil
  1461  	require.Nil(t, chat.LastMessage)
  1462  
  1463  	// It sets unviewed messages count
  1464  	require.Equal(t, uint(0), chat.UnviewedMessagesCount)
  1465  
  1466  	// It sets active as false
  1467  	require.False(t, chat.Active)
  1468  
  1469  	// It deletes messages
  1470  	messages, _, err := p.MessageByChatID(chat.ID, "", 10)
  1471  	require.NoError(t, err)
  1472  	require.Len(t, messages, 0)
  1473  
  1474  	// Reload chat to make sure it has been save
  1475  	dbChat, err := p.Chat(chat.ID)
  1476  
  1477  	require.NoError(t, err)
  1478  	require.NotNil(t, dbChat)
  1479  
  1480  	// Same checks on the chat pulled from the db
  1481  	// It does set deleted at for a public chat
  1482  	require.NoError(t, err)
  1483  	require.NotEqual(t, uint64(0), dbChat.DeletedAtClockValue)
  1484  
  1485  	// It sets the lastMessage to nil
  1486  	require.Nil(t, dbChat.LastMessage)
  1487  
  1488  	// It sets unviewed messages count
  1489  	require.Equal(t, uint(0), dbChat.UnviewedMessagesCount)
  1490  
  1491  	// It sets active as false
  1492  	require.False(t, dbChat.Active)
  1493  }
  1494  
  1495  func TestConfirmations(t *testing.T) {
  1496  	dataSyncID1 := []byte("datsync-id-1")
  1497  	dataSyncID2 := []byte("datsync-id-2")
  1498  	dataSyncID3 := []byte("datsync-id-3")
  1499  	dataSyncID4 := []byte("datsync-id-3")
  1500  
  1501  	messageID1 := []byte("message-id-1")
  1502  	messageID2 := []byte("message-id-2")
  1503  
  1504  	publicKey1 := []byte("pk-1")
  1505  	publicKey2 := []byte("pk-2")
  1506  	publicKey3 := []byte("pk-3")
  1507  
  1508  	db, err := openTestDB()
  1509  	require.NoError(t, err)
  1510  	p := newSQLitePersistence(db)
  1511  
  1512  	confirmation1 := &common.RawMessageConfirmation{
  1513  		DataSyncID: dataSyncID1,
  1514  		MessageID:  messageID1,
  1515  		PublicKey:  publicKey1,
  1516  	}
  1517  
  1518  	// Same datasyncID and same messageID, different pubkey
  1519  	confirmation2 := &common.RawMessageConfirmation{
  1520  		DataSyncID: dataSyncID2,
  1521  		MessageID:  messageID1,
  1522  		PublicKey:  publicKey2,
  1523  	}
  1524  
  1525  	// Different datasyncID and same messageID, different pubkey
  1526  	confirmation3 := &common.RawMessageConfirmation{
  1527  		DataSyncID: dataSyncID3,
  1528  		MessageID:  messageID1,
  1529  		PublicKey:  publicKey3,
  1530  	}
  1531  
  1532  	// Same dataSyncID, different messageID
  1533  	confirmation4 := &common.RawMessageConfirmation{
  1534  		DataSyncID: dataSyncID4,
  1535  		MessageID:  messageID2,
  1536  		PublicKey:  publicKey1,
  1537  	}
  1538  
  1539  	require.NoError(t, p.InsertPendingConfirmation(confirmation1))
  1540  	require.NoError(t, p.InsertPendingConfirmation(confirmation2))
  1541  	require.NoError(t, p.InsertPendingConfirmation(confirmation3))
  1542  	require.NoError(t, p.InsertPendingConfirmation(confirmation4))
  1543  
  1544  	// We confirm the first datasync message, no confirmations
  1545  	messageID, err := p.MarkAsConfirmed(dataSyncID1, false)
  1546  	require.NoError(t, err)
  1547  	require.Nil(t, messageID)
  1548  
  1549  	// We confirm the second datasync message, no confirmations
  1550  	messageID, err = p.MarkAsConfirmed(dataSyncID2, false)
  1551  	require.NoError(t, err)
  1552  	require.Nil(t, messageID)
  1553  
  1554  	// We confirm the third datasync message, messageID1 should be confirmed
  1555  	messageID, err = p.MarkAsConfirmed(dataSyncID3, false)
  1556  	require.NoError(t, err)
  1557  	require.Equal(t, messageID, types.HexBytes(messageID1))
  1558  }
  1559  
  1560  func TestConfirmationsAtLeastOne(t *testing.T) {
  1561  	dataSyncID1 := []byte("datsync-id-1")
  1562  	dataSyncID2 := []byte("datsync-id-2")
  1563  	dataSyncID3 := []byte("datsync-id-3")
  1564  
  1565  	messageID1 := []byte("message-id-1")
  1566  
  1567  	publicKey1 := []byte("pk-1")
  1568  	publicKey2 := []byte("pk-2")
  1569  	publicKey3 := []byte("pk-3")
  1570  
  1571  	db, err := openTestDB()
  1572  	require.NoError(t, err)
  1573  	p := newSQLitePersistence(db)
  1574  
  1575  	confirmation1 := &common.RawMessageConfirmation{
  1576  		DataSyncID: dataSyncID1,
  1577  		MessageID:  messageID1,
  1578  		PublicKey:  publicKey1,
  1579  	}
  1580  
  1581  	// Same datasyncID and same messageID, different pubkey
  1582  	confirmation2 := &common.RawMessageConfirmation{
  1583  		DataSyncID: dataSyncID2,
  1584  		MessageID:  messageID1,
  1585  		PublicKey:  publicKey2,
  1586  	}
  1587  
  1588  	// Different datasyncID and same messageID, different pubkey
  1589  	confirmation3 := &common.RawMessageConfirmation{
  1590  		DataSyncID: dataSyncID3,
  1591  		MessageID:  messageID1,
  1592  		PublicKey:  publicKey3,
  1593  	}
  1594  
  1595  	require.NoError(t, p.InsertPendingConfirmation(confirmation1))
  1596  	require.NoError(t, p.InsertPendingConfirmation(confirmation2))
  1597  	require.NoError(t, p.InsertPendingConfirmation(confirmation3))
  1598  
  1599  	// We confirm the first datasync message, messageID1 and 3 should be confirmed
  1600  	messageID, err := p.MarkAsConfirmed(dataSyncID1, true)
  1601  	require.NoError(t, err)
  1602  	require.NotNil(t, messageID)
  1603  	require.Equal(t, types.HexBytes(messageID1), messageID)
  1604  }
  1605  
  1606  func TestSaveCommunityChat(t *testing.T) {
  1607  	db, err := openTestDB()
  1608  	require.NoError(t, err)
  1609  	p := newSQLitePersistence(db)
  1610  
  1611  	identity := &protobuf.ChatIdentity{
  1612  		DisplayName:           "community-chat-name",
  1613  		Description:           "community-chat-name-description",
  1614  		FirstMessageTimestamp: 1,
  1615  	}
  1616  	permissions := &protobuf.CommunityPermissions{
  1617  		Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
  1618  	}
  1619  
  1620  	communityChat := &protobuf.CommunityChat{
  1621  		Identity:    identity,
  1622  		Permissions: permissions,
  1623  	}
  1624  
  1625  	chat := CreateCommunityChat("test-or-gid", "test-chat-id", communityChat, &testTimeSource{})
  1626  	chat.LastMessage = common.NewMessage()
  1627  	err = p.SaveChat(*chat)
  1628  	require.NoError(t, err)
  1629  
  1630  	retrievedChat, err := p.Chat(chat.ID)
  1631  	require.NoError(t, err)
  1632  	require.Equal(t, chat, retrievedChat)
  1633  }
  1634  
  1635  func TestSaveDiscordMessageAuthor(t *testing.T) {
  1636  
  1637  	db, err := openTestDB()
  1638  	require.NoError(t, err)
  1639  	p := newSQLitePersistence(db)
  1640  
  1641  	testAuthor := &protobuf.DiscordMessageAuthor{
  1642  		Id:                 "1",
  1643  		Name:               "Testuser",
  1644  		Discriminator:      "1234",
  1645  		Nickname:           "User",
  1646  		AvatarUrl:          "http://example.com/profile.jpg",
  1647  		AvatarImagePayload: []byte{1, 2, 3},
  1648  	}
  1649  
  1650  	require.NoError(t, p.SaveDiscordMessageAuthor(testAuthor))
  1651  
  1652  	exists, err := p.HasDiscordMessageAuthor("1")
  1653  	require.NoError(t, err)
  1654  	require.True(t, exists)
  1655  	author, err := p.GetDiscordMessageAuthorByID("1")
  1656  	require.NoError(t, err)
  1657  	require.Equal(t, author.Id, testAuthor.Id)
  1658  	require.Equal(t, author.Name, testAuthor.Name)
  1659  }
  1660  
  1661  func TestGetDiscordMessageAuthorImagePayloadByID(t *testing.T) {
  1662  	db, err := openTestDB()
  1663  	require.NoError(t, err)
  1664  	p := newSQLitePersistence(db)
  1665  
  1666  	testAuthor := &protobuf.DiscordMessageAuthor{
  1667  		Id:                 "1",
  1668  		Name:               "Testuser",
  1669  		Discriminator:      "1234",
  1670  		Nickname:           "User",
  1671  		AvatarUrl:          "http://example.com/profile.jpg",
  1672  		AvatarImagePayload: []byte{1, 2, 3},
  1673  	}
  1674  
  1675  	require.NoError(t, p.SaveDiscordMessageAuthor(testAuthor))
  1676  
  1677  	payload, err := p.GetDiscordMessageAuthorImagePayloadByID("1")
  1678  	require.NoError(t, err)
  1679  
  1680  	require.Equal(t, testAuthor.AvatarImagePayload, payload)
  1681  }
  1682  
  1683  func TestSaveDiscordMessage(t *testing.T) {
  1684  
  1685  	db, err := openTestDB()
  1686  	require.NoError(t, err)
  1687  	p := newSQLitePersistence(db)
  1688  
  1689  	require.NoError(t, p.SaveDiscordMessage(&protobuf.DiscordMessage{
  1690  		Id:        "1",
  1691  		Type:      "Default",
  1692  		Timestamp: "123456",
  1693  		Content:   "This is the message",
  1694  		Author: &protobuf.DiscordMessageAuthor{
  1695  			Id: "2",
  1696  		},
  1697  		Reference: &protobuf.DiscordMessageReference{},
  1698  	}))
  1699  
  1700  	require.NoError(t, err)
  1701  }
  1702  
  1703  func TestSaveDiscordMessages(t *testing.T) {
  1704  	db, err := openTestDB()
  1705  	require.NoError(t, err)
  1706  	p := newSQLitePersistence(db)
  1707  
  1708  	for i := 0; i < 10; i++ {
  1709  		id := strconv.Itoa(i)
  1710  		err := insertMinimalDiscordMessage(p, id, id)
  1711  		require.NoError(t, err)
  1712  
  1713  		m, err := p.MessageByID(id)
  1714  		require.NoError(t, err)
  1715  		dm := m.GetDiscordMessage()
  1716  		require.NotNil(t, dm)
  1717  		require.EqualValues(t, id, dm.Id)
  1718  		require.EqualValues(t, "2", dm.Author.Id)
  1719  	}
  1720  }
  1721  
  1722  func TestUpdateDiscordMessageAuthorImage(t *testing.T) {
  1723  
  1724  	db, err := openTestDB()
  1725  	require.NoError(t, err)
  1726  	p := newSQLitePersistence(db)
  1727  
  1728  	require.NoError(t, p.SaveDiscordMessageAuthor(&protobuf.DiscordMessageAuthor{
  1729  		Id:            "1",
  1730  		Name:          "Testuser",
  1731  		Discriminator: "1234",
  1732  		Nickname:      "User",
  1733  		AvatarUrl:     "http://example.com/profile.jpg",
  1734  	}))
  1735  
  1736  	exists, err := p.HasDiscordMessageAuthor("1")
  1737  	require.NoError(t, err)
  1738  	require.True(t, exists)
  1739  
  1740  	err = p.UpdateDiscordMessageAuthorImage("1", []byte{0, 1, 2, 3})
  1741  	require.NoError(t, err)
  1742  	payload, err := p.GetDiscordMessageAuthorImagePayloadByID("1")
  1743  	require.NoError(t, err)
  1744  	require.Equal(t, []byte{0, 1, 2, 3}, payload)
  1745  }
  1746  
  1747  func TestSaveHashRatchetMessage(t *testing.T) {
  1748  	db, err := openTestDB()
  1749  	require.NoError(t, err)
  1750  	p := newSQLitePersistence(db)
  1751  
  1752  	groupID1 := []byte("group-id-1")
  1753  	groupID2 := []byte("group-id-2")
  1754  	keyID := []byte("key-id")
  1755  
  1756  	message1 := &types.Message{
  1757  		Hash:      []byte{1},
  1758  		Sig:       []byte{2},
  1759  		TTL:       1,
  1760  		Timestamp: 2,
  1761  		Payload:   []byte{3},
  1762  	}
  1763  
  1764  	require.NoError(t, p.SaveHashRatchetMessage(groupID1, keyID, message1))
  1765  
  1766  	message2 := &types.Message{
  1767  		Hash:      []byte{2},
  1768  		Sig:       []byte{2},
  1769  		TTL:       1,
  1770  		Topic:     types.BytesToTopic([]byte{5}),
  1771  		Timestamp: 2,
  1772  		Payload:   []byte{3},
  1773  		Dst:       []byte{4},
  1774  		P2P:       true,
  1775  	}
  1776  
  1777  	require.NoError(t, p.SaveHashRatchetMessage(groupID2, keyID, message2))
  1778  
  1779  	fetchedMessages, err := p.GetHashRatchetMessages(keyID)
  1780  	require.NoError(t, err)
  1781  	require.NotNil(t, fetchedMessages)
  1782  	require.Len(t, fetchedMessages, 2)
  1783  }
  1784  
  1785  func TestCountActiveChattersInCommunity(t *testing.T) {
  1786  	db, err := openTestDB()
  1787  	require.NoError(t, err)
  1788  	p := newSQLitePersistence(db)
  1789  
  1790  	channel1 := Chat{
  1791  		ID:          "channel1",
  1792  		Name:        "channel1",
  1793  		CommunityID: "testCommunity",
  1794  	}
  1795  
  1796  	channel2 := Chat{
  1797  		ID:          "channel2",
  1798  		Name:        "channel2",
  1799  		CommunityID: "testCommunity",
  1800  	}
  1801  
  1802  	require.NoError(t, p.SaveChat(channel1))
  1803  	require.NoError(t, p.SaveChat(channel2))
  1804  
  1805  	fillChatWithMessages := func(chat *Chat, offset int) {
  1806  		count := 5
  1807  		var messages []*common.Message
  1808  		for i := 0; i < count; i++ {
  1809  			messages = append(messages, &common.Message{
  1810  				ID:          fmt.Sprintf("%smsg%d", chat.Name, i),
  1811  				LocalChatID: chat.ID,
  1812  				ChatMessage: &protobuf.ChatMessage{
  1813  					Clock:     uint64(i),
  1814  					Timestamp: uint64(i + offset),
  1815  				},
  1816  				From: fmt.Sprintf("user%d", i),
  1817  			})
  1818  		}
  1819  		require.NoError(t, p.SaveMessages(messages))
  1820  	}
  1821  
  1822  	// timestamp/user/msgID
  1823  	// channel1: 0/user0/channel1msg0 1/user1/channel1msg1 2/user2/channel1msg2 3/user3/channel1msg3 4/user4/channel1msg4
  1824  	// channel2: 3/user0/channel2msg0 4/user1/channel2msg1 5/user2/channel2msg2 6/user3/channel2msg3 7/user4/channel2msg4
  1825  	fillChatWithMessages(&channel1, 0)
  1826  	fillChatWithMessages(&channel2, 3)
  1827  
  1828  	checker := func(activeAfterTimestamp int64, expected uint) {
  1829  		result, err := p.CountActiveChattersInCommunity("testCommunity", activeAfterTimestamp)
  1830  		require.NoError(t, err)
  1831  		require.Equal(t, expected, result)
  1832  	}
  1833  	checker(0, 5)
  1834  	checker(3, 5)
  1835  	checker(4, 4)
  1836  	checker(5, 3)
  1837  	checker(6, 2)
  1838  	checker(7, 1)
  1839  	checker(8, 0)
  1840  }
  1841  
  1842  func TestDeleteHashRatchetMessage(t *testing.T) {
  1843  	db, err := openTestDB()
  1844  	require.NoError(t, err)
  1845  	p := newSQLitePersistence(db)
  1846  
  1847  	groupID := []byte("group-id")
  1848  	keyID := []byte("key-id")
  1849  
  1850  	message1 := &types.Message{
  1851  		Hash:      []byte{1},
  1852  		Sig:       []byte{2},
  1853  		TTL:       1,
  1854  		Timestamp: 2,
  1855  		Payload:   []byte{3},
  1856  	}
  1857  
  1858  	require.NoError(t, p.SaveHashRatchetMessage(groupID, keyID, message1))
  1859  
  1860  	message2 := &types.Message{
  1861  		Hash:      []byte{2},
  1862  		Sig:       []byte{2},
  1863  		TTL:       1,
  1864  		Topic:     types.BytesToTopic([]byte{5}),
  1865  		Timestamp: 2,
  1866  		Payload:   []byte{3},
  1867  		Dst:       []byte{4},
  1868  		P2P:       true,
  1869  	}
  1870  
  1871  	require.NoError(t, p.SaveHashRatchetMessage(groupID, keyID, message2))
  1872  
  1873  	message3 := &types.Message{
  1874  		Hash:      []byte{3},
  1875  		Sig:       []byte{2},
  1876  		TTL:       1,
  1877  		Topic:     types.BytesToTopic([]byte{5}),
  1878  		Timestamp: 2,
  1879  		Payload:   []byte{3},
  1880  		Dst:       []byte{4},
  1881  		P2P:       true,
  1882  	}
  1883  
  1884  	require.NoError(t, p.SaveHashRatchetMessage(groupID, keyID, message3))
  1885  
  1886  	fetchedMessages, err := p.GetHashRatchetMessages(keyID)
  1887  	require.NoError(t, err)
  1888  	require.NotNil(t, fetchedMessages)
  1889  	require.Len(t, fetchedMessages, 3)
  1890  
  1891  	require.NoError(t, p.DeleteHashRatchetMessages([][]byte{[]byte{1}, []byte{2}}))
  1892  
  1893  	fetchedMessages, err = p.GetHashRatchetMessages(keyID)
  1894  	require.NoError(t, err)
  1895  	require.NotNil(t, fetchedMessages)
  1896  	require.Len(t, fetchedMessages, 1)
  1897  
  1898  }
  1899  
  1900  func TestSaveBridgeMessage(t *testing.T) {
  1901  	db, err := openTestDB()
  1902  	require.NoError(t, err)
  1903  	p := newSQLitePersistence(db)
  1904  
  1905  	require.NoError(t, err)
  1906  
  1907  	bridgeMessage := &protobuf.BridgeMessage{
  1908  		BridgeName:      "discord",
  1909  		UserName:        "joe",
  1910  		Content:         "abc",
  1911  		UserAvatar:      "data:image/png;base64,iVBO...",
  1912  		UserID:          "123",
  1913  		MessageID:       "456",
  1914  		ParentMessageID: "789",
  1915  	}
  1916  
  1917  	const msgID = "123"
  1918  	err = p.SaveMessages([]*common.Message{{
  1919  		ID:          msgID,
  1920  		LocalChatID: testPublicChatID,
  1921  		From:        testPK,
  1922  		ChatMessage: &protobuf.ChatMessage{
  1923  			Text:        "some-text",
  1924  			ContentType: protobuf.ChatMessage_BRIDGE_MESSAGE,
  1925  			ChatId:      testPublicChatID,
  1926  			Payload: &protobuf.ChatMessage_BridgeMessage{
  1927  				BridgeMessage: bridgeMessage,
  1928  			},
  1929  		},
  1930  	}})
  1931  
  1932  	require.NoError(t, err)
  1933  
  1934  	retrievedMessages, _, err := p.MessageByChatID(testPublicChatID, "", 10)
  1935  	require.NoError(t, err)
  1936  	require.Len(t, retrievedMessages, 1)
  1937  	require.Equal(t, "discord", retrievedMessages[0].GetBridgeMessage().BridgeName)
  1938  	require.Equal(t, "joe", retrievedMessages[0].GetBridgeMessage().UserName)
  1939  	require.Equal(t, "abc", retrievedMessages[0].GetBridgeMessage().Content)
  1940  	require.Equal(t, "data:image/png;base64,iVBO...", retrievedMessages[0].GetBridgeMessage().UserAvatar)
  1941  	require.Equal(t, "123", retrievedMessages[0].GetBridgeMessage().UserID)
  1942  	require.Equal(t, "456", retrievedMessages[0].GetBridgeMessage().MessageID)
  1943  	require.Equal(t, "789", retrievedMessages[0].GetBridgeMessage().ParentMessageID)
  1944  }
  1945  
  1946  func insertMinimalBridgeMessage(p *sqlitePersistence, messageID string, bridgeMessageID string, bridgeMessageParentID string) error {
  1947  
  1948  	bridgeMessage := &protobuf.BridgeMessage{
  1949  		BridgeName:      "discord",
  1950  		UserName:        "joe",
  1951  		Content:         "abc",
  1952  		UserAvatar:      "data:image/png;base64,iVBO...",
  1953  		UserID:          "123",
  1954  		MessageID:       bridgeMessageID,
  1955  		ParentMessageID: bridgeMessageParentID,
  1956  	}
  1957  
  1958  	return p.SaveMessages([]*common.Message{{
  1959  		ID:          messageID,
  1960  		LocalChatID: testPublicChatID,
  1961  		From:        testPK,
  1962  		ChatMessage: &protobuf.ChatMessage{
  1963  			Text:        "some-text",
  1964  			ContentType: protobuf.ChatMessage_BRIDGE_MESSAGE,
  1965  			ChatId:      testPublicChatID,
  1966  			Payload: &protobuf.ChatMessage_BridgeMessage{
  1967  				BridgeMessage: bridgeMessage,
  1968  			},
  1969  		},
  1970  	}})
  1971  }
  1972  
  1973  func messageResponseTo(p *sqlitePersistence, messageID string) (string, error) {
  1974  	var responseTo string
  1975  	err := p.db.QueryRow("SELECT response_to FROM user_messages WHERE id = ?", messageID).Scan(&responseTo)
  1976  	return responseTo, err
  1977  }
  1978  
  1979  func TestBridgeMessageReplies(t *testing.T) {
  1980  	db, err := openTestDB()
  1981  	require.NoError(t, err)
  1982  	p := newSQLitePersistence(db)
  1983  
  1984  	require.NoError(t, err)
  1985  
  1986  	err = insertMinimalBridgeMessage(p, "111", "1", "")
  1987  	require.NoError(t, err)
  1988  
  1989  	err = insertMinimalBridgeMessage(p, "222", "2", "1")
  1990  	require.NoError(t, err)
  1991  
  1992  	// "333 is not delivered yet"
  1993  
  1994  	// this is a reply to a message which was not delivered yet
  1995  	err = insertMinimalBridgeMessage(p, "444", "4", "3")
  1996  	require.NoError(t, err)
  1997  
  1998  	// status message "222" should have reply_to = "111"
  1999  	responseTo, err := messageResponseTo(p, "222")
  2000  	require.NoError(t, err)
  2001  	require.Equal(t, "111", responseTo)
  2002  
  2003  	responseTo, err = messageResponseTo(p, "111")
  2004  	require.NoError(t, err)
  2005  	require.Equal(t, "", responseTo)
  2006  
  2007  	responseTo, err = messageResponseTo(p, "444")
  2008  	require.NoError(t, err)
  2009  	require.Equal(t, "", responseTo)
  2010  
  2011  	// receiving message for which "444" is replied to
  2012  	err = insertMinimalBridgeMessage(p, "333", "3", "")
  2013  	require.NoError(t, err)
  2014  
  2015  	responseTo, err = messageResponseTo(p, "333")
  2016  	require.NoError(t, err)
  2017  	require.Equal(t, "", responseTo)
  2018  
  2019  	// now 444 is replied to 333
  2020  	responseTo, err = messageResponseTo(p, "444")
  2021  	require.NoError(t, err)
  2022  	require.Equal(t, "333", responseTo)
  2023  }
  2024  
  2025  func createAndSaveMessage(p *sqlitePersistence, id string, from string, deleted bool, communityID string) error {
  2026  	return p.SaveMessages([]*common.Message{{
  2027  		ID:          id,
  2028  		From:        from,
  2029  		CommunityID: communityID,
  2030  		ChatMessage: &protobuf.ChatMessage{
  2031  			Timestamp: uint64(time.Now().Unix()),
  2032  			Text:      "some-text",
  2033  			ChatId:    testPublicChatID,
  2034  		},
  2035  		Deleted: deleted,
  2036  	}})
  2037  }
  2038  
  2039  func TestGetCommunityMemberMessagesID(t *testing.T) {
  2040  	db, err := openTestDB()
  2041  	require.NoError(t, err)
  2042  	p := newSQLitePersistence(db)
  2043  	testCommunity := "test-community"
  2044  
  2045  	chat := &Chat{
  2046  		ID:          testPublicChatID,
  2047  		CommunityID: testCommunity,
  2048  	}
  2049  
  2050  	err = p.SaveChats([]*Chat{chat})
  2051  	require.NoError(t, err)
  2052  
  2053  	messages, err := p.GetCommunityMemberMessagesToDelete(testPK, testCommunity)
  2054  	require.NoError(t, err)
  2055  	require.Len(t, messages, 0)
  2056  
  2057  	require.NoError(t, createAndSaveMessage(p, "1", testPK, false, testCommunity))
  2058  
  2059  	messages, err = p.GetCommunityMemberMessagesToDelete(testPK, "wrong community")
  2060  	require.NoError(t, err)
  2061  	require.Len(t, messages, 0)
  2062  
  2063  	messages, err = p.GetCommunityMemberMessagesToDelete("wrong user name", testCommunity)
  2064  	require.NoError(t, err)
  2065  	require.Len(t, messages, 0)
  2066  
  2067  	messages, err = p.GetCommunityMemberMessagesToDelete(testPK, testCommunity)
  2068  	require.NoError(t, err)
  2069  	require.Len(t, messages, 1)
  2070  	require.Exactly(t, "1", messages[0].Id)
  2071  	require.Exactly(t, testPublicChatID, messages[0].ChatId)
  2072  
  2073  	require.NoError(t, createAndSaveMessage(p, "2", "another user", false, testCommunity))
  2074  
  2075  	messages, err = p.GetCommunityMemberMessagesToDelete(testPK, testCommunity)
  2076  	require.NoError(t, err)
  2077  	require.Len(t, messages, 1)
  2078  
  2079  	require.NoError(t, createAndSaveMessage(p, "3", testPK, true, testCommunity))
  2080  
  2081  	messages, err = p.GetCommunityMemberMessagesToDelete(testPK, testCommunity)
  2082  	require.NoError(t, err)
  2083  	require.Len(t, messages, 2)
  2084  }
  2085  
  2086  func TestGetCommunityMemberMessages(t *testing.T) {
  2087  	db, err := openTestDB()
  2088  	require.NoError(t, err)
  2089  	p := newSQLitePersistence(db)
  2090  	testCommunity := "test-community"
  2091  
  2092  	chat := &Chat{
  2093  		ID:          testPublicChatID,
  2094  		CommunityID: testCommunity,
  2095  	}
  2096  
  2097  	err = p.SaveChats([]*Chat{chat})
  2098  	require.NoError(t, err)
  2099  
  2100  	messages, err := p.GetCommunityMemberAllMessages(testPK, testCommunity)
  2101  	require.NoError(t, err)
  2102  	require.Len(t, messages, 0)
  2103  
  2104  	require.NoError(t, createAndSaveMessage(p, "1", testPK, false, testCommunity))
  2105  
  2106  	messages, err = p.GetCommunityMemberAllMessages(testPK, "wrong community")
  2107  	require.NoError(t, err)
  2108  	require.Len(t, messages, 0)
  2109  
  2110  	messages, err = p.GetCommunityMemberAllMessages("wrong user name", testCommunity)
  2111  	require.NoError(t, err)
  2112  	require.Len(t, messages, 0)
  2113  
  2114  	messages, err = p.GetCommunityMemberAllMessages(testPK, testCommunity)
  2115  	require.NoError(t, err)
  2116  	require.Len(t, messages, 1)
  2117  	require.Exactly(t, "1", messages[0].ID)
  2118  
  2119  	require.NoError(t, createAndSaveMessage(p, "2", "another user", false, testCommunity))
  2120  
  2121  	messages, err = p.GetCommunityMemberAllMessages(testPK, testCommunity)
  2122  	require.NoError(t, err)
  2123  	require.Len(t, messages, 1)
  2124  
  2125  	require.NoError(t, createAndSaveMessage(p, "3", testPK, true, testCommunity))
  2126  
  2127  	messages, err = p.GetCommunityMemberAllMessages(testPK, testCommunity)
  2128  	require.NoError(t, err)
  2129  	require.Len(t, messages, 2)
  2130  }