2019-11-21 16:19:22 +00:00
|
|
|
package protocol
|
|
|
|
|
|
|
|
import (
|
2020-12-10 10:12:51 +00:00
|
|
|
"bytes"
|
2019-11-21 16:19:22 +00:00
|
|
|
"database/sql"
|
|
|
|
"io/ioutil"
|
|
|
|
"math"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
2020-10-28 13:41:22 +00:00
|
|
|
"time"
|
2019-11-21 16:19:22 +00:00
|
|
|
|
2020-01-02 09:10:19 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2020-09-01 10:34:28 +00:00
|
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
|
|
|
"github.com/status-im/status-go/eth-node/types"
|
2020-09-01 13:27:01 +00:00
|
|
|
"github.com/status-im/status-go/protocol/common"
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
2019-11-21 16:19:22 +00:00
|
|
|
"github.com/status-im/status-go/protocol/sqlite"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestTableUserMessagesAllFieldsCount(t *testing.T) {
|
|
|
|
db := sqlitePersistence{}
|
2020-05-20 12:16:12 +00:00
|
|
|
expected := len(strings.Split(db.tableUserMessagesAllFields(), ","))
|
|
|
|
require.Equal(t, expected, db.tableUserMessagesAllFieldsCount())
|
2019-11-21 16:19:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSaveMessages(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2019-11-21 16:19:22 +00:00
|
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
id := strconv.Itoa(i)
|
|
|
|
err := insertMinimalMessage(p, id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
m, err := p.MessageByID(id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.EqualValues(t, id, m.ID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add replies to messages
Currently replies to messages are handled in status-react.
This causes some issues with the fact that sometimes replies might come
out of order, they might be offloaded to the database etc.
This commit changes the behavior so that status-go always returns the
replies, and in case a reply comes out of order (first the reply, later
the message being replied to), it will include in the messages the
updated message.
It also adds some fields (RTL,Replace,LineCount) to the database which
were not previously saved, resulting in some potential bugs.
The method that we use to pull replies is currently a bit naive, we just
pull all the message again from the database, but has the advantage of
being simple. It will go through performance testing to make sure
performnace are acceptable, if so I think it's reasonable to avoid some
complexity.
2020-04-08 13:42:02 +00:00
|
|
|
func TestMessagesByIDs(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
Add replies to messages
Currently replies to messages are handled in status-react.
This causes some issues with the fact that sometimes replies might come
out of order, they might be offloaded to the database etc.
This commit changes the behavior so that status-go always returns the
replies, and in case a reply comes out of order (first the reply, later
the message being replied to), it will include in the messages the
updated message.
It also adds some fields (RTL,Replace,LineCount) to the database which
were not previously saved, resulting in some potential bugs.
The method that we use to pull replies is currently a bit naive, we just
pull all the message again from the database, but has the advantage of
being simple. It will go through performance testing to make sure
performnace are acceptable, if so I think it's reasonable to avoid some
complexity.
2020-04-08 13:42:02 +00:00
|
|
|
|
|
|
|
var ids []string
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
id := strconv.Itoa(i)
|
|
|
|
err := insertMinimalMessage(p, id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
ids = append(ids, id)
|
|
|
|
|
|
|
|
}
|
|
|
|
m, err := p.MessagesByIDs(ids)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, m, 10)
|
|
|
|
}
|
|
|
|
|
2019-11-21 16:19:22 +00:00
|
|
|
func TestMessageByID(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2019-11-21 16:19:22 +00:00
|
|
|
id := "1"
|
|
|
|
|
|
|
|
err = insertMinimalMessage(p, id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
m, err := p.MessageByID(id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.EqualValues(t, id, m.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMessagesExist(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2019-11-21 16:19:22 +00:00
|
|
|
|
|
|
|
err = insertMinimalMessage(p, "1")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
result, err := p.MessagesExist([]string{"1"})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.True(t, result["1"])
|
|
|
|
|
|
|
|
err = insertMinimalMessage(p, "2")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
result, err = p.MessagesExist([]string{"1", "2", "3"})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.True(t, result["1"])
|
|
|
|
require.True(t, result["2"])
|
|
|
|
require.False(t, result["3"])
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMessageByChatID(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-02-21 14:48:53 +00:00
|
|
|
chatID := testPublicChatID
|
2019-11-21 16:19:22 +00:00
|
|
|
count := 1000
|
|
|
|
pageSize := 50
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
var messages []*common.Message
|
2019-11-21 16:19:22 +00:00
|
|
|
for i := 0; i < count; i++ {
|
2020-09-01 13:27:01 +00:00
|
|
|
messages = append(messages, &common.Message{
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
ID: strconv.Itoa(i),
|
|
|
|
LocalChatID: chatID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{
|
|
|
|
Clock: uint64(i),
|
|
|
|
},
|
|
|
|
From: "me",
|
2019-11-21 16:19:22 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
// Add some other chats.
|
|
|
|
if count%5 == 0 {
|
2020-09-01 13:27:01 +00:00
|
|
|
messages = append(messages, &common.Message{
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
ID: strconv.Itoa(count + i),
|
|
|
|
LocalChatID: "other-chat",
|
|
|
|
ChatMessage: protobuf.ChatMessage{
|
|
|
|
Clock: uint64(i),
|
|
|
|
},
|
|
|
|
|
|
|
|
From: "me",
|
2019-11-21 16:19:22 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add some out-of-order message. Add more than page size.
|
|
|
|
outOfOrderCount := pageSize + 1
|
|
|
|
allCount := count + outOfOrderCount
|
|
|
|
for i := 0; i < pageSize+1; i++ {
|
2020-09-01 13:27:01 +00:00
|
|
|
messages = append(messages, &common.Message{
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
ID: strconv.Itoa(count*2 + i),
|
|
|
|
LocalChatID: chatID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{
|
|
|
|
Clock: uint64(i),
|
|
|
|
},
|
|
|
|
|
|
|
|
From: "me",
|
2019-11-21 16:19:22 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-05-20 12:16:12 +00:00
|
|
|
err = p.SaveMessages(messages)
|
2019-11-21 16:19:22 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
var (
|
2020-09-01 13:27:01 +00:00
|
|
|
result []*common.Message
|
2019-11-21 16:19:22 +00:00
|
|
|
cursor string
|
|
|
|
iter int
|
|
|
|
)
|
|
|
|
for {
|
|
|
|
var (
|
2020-09-01 13:27:01 +00:00
|
|
|
items []*common.Message
|
2019-11-21 16:19:22 +00:00
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
items, cursor, err = p.MessageByChatID(chatID, cursor, pageSize)
|
|
|
|
require.NoError(t, err)
|
|
|
|
result = append(result, items...)
|
|
|
|
|
|
|
|
iter++
|
|
|
|
if len(cursor) == 0 || iter > count {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
require.Equal(t, "", cursor) // for loop should exit because of cursor being empty
|
|
|
|
require.EqualValues(t, math.Ceil(float64(allCount)/float64(pageSize)), iter)
|
|
|
|
require.Equal(t, len(result), allCount)
|
|
|
|
require.True(
|
|
|
|
t,
|
|
|
|
// Verify descending order.
|
|
|
|
sort.SliceIsSorted(result, func(i, j int) bool {
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
return result[i].Clock > result[j].Clock
|
2019-11-21 16:19:22 +00:00
|
|
|
}),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-05-14 21:22:50 +00:00
|
|
|
func TestPinMessageByChatID(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
|
|
|
p := sqlitePersistence{db: db}
|
|
|
|
chatID := "chat-with-pinned-messages"
|
|
|
|
messagesCount := 1000
|
|
|
|
pageSize := 5
|
|
|
|
pinnedMessagesCount := 0
|
|
|
|
|
|
|
|
var messages []*common.Message
|
|
|
|
var pinMessages []*common.PinMessage
|
|
|
|
for i := 0; i < messagesCount; i++ {
|
|
|
|
messages = append(messages, &common.Message{
|
|
|
|
ID: strconv.Itoa(i),
|
|
|
|
LocalChatID: chatID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{
|
|
|
|
Clock: uint64(i),
|
|
|
|
},
|
|
|
|
From: "me",
|
|
|
|
})
|
|
|
|
|
|
|
|
// Pin this message
|
|
|
|
if i%100 == 0 {
|
2021-06-08 15:23:32 +00:00
|
|
|
from := "me"
|
|
|
|
if i == 100 {
|
|
|
|
from = "them"
|
|
|
|
}
|
|
|
|
|
2021-05-14 21:22:50 +00:00
|
|
|
pinMessage := &common.PinMessage{
|
|
|
|
ID: strconv.Itoa(i),
|
|
|
|
LocalChatID: chatID,
|
2021-06-08 15:23:32 +00:00
|
|
|
From: from,
|
2021-05-14 21:22:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pinMessage.MessageId = strconv.Itoa(i)
|
|
|
|
pinMessage.Clock = 111
|
|
|
|
pinMessage.Pinned = true
|
|
|
|
pinMessages = append(pinMessages, pinMessage)
|
|
|
|
pinnedMessagesCount++
|
|
|
|
|
|
|
|
if i%200 == 0 {
|
|
|
|
// unpin a message
|
|
|
|
unpinMessage := &common.PinMessage{
|
|
|
|
ID: strconv.Itoa(i),
|
|
|
|
LocalChatID: chatID,
|
|
|
|
From: "me",
|
|
|
|
}
|
|
|
|
pinMessage.MessageId = strconv.Itoa(i)
|
|
|
|
unpinMessage.Clock = 333
|
|
|
|
unpinMessage.Pinned = false
|
|
|
|
pinMessages = append(pinMessages, unpinMessage)
|
|
|
|
pinnedMessagesCount--
|
|
|
|
|
|
|
|
// pinned before the unpin
|
|
|
|
pinMessage2 := &common.PinMessage{
|
|
|
|
ID: strconv.Itoa(i),
|
|
|
|
LocalChatID: chatID,
|
|
|
|
From: "me",
|
|
|
|
}
|
|
|
|
pinMessage2.MessageId = strconv.Itoa(i)
|
|
|
|
pinMessage2.Clock = 222
|
|
|
|
pinMessage2.Pinned = true
|
|
|
|
pinMessages = append(pinMessages, pinMessage2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add some other chats.
|
|
|
|
if i%5 == 0 {
|
|
|
|
messages = append(messages, &common.Message{
|
|
|
|
ID: strconv.Itoa(messagesCount + i),
|
|
|
|
LocalChatID: "chat-without-pinned-messages",
|
|
|
|
ChatMessage: protobuf.ChatMessage{
|
|
|
|
Clock: uint64(i),
|
|
|
|
},
|
|
|
|
|
|
|
|
From: "me",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = p.SaveMessages(messages)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = p.SavePinMessages(pinMessages)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
var (
|
|
|
|
result []*common.PinnedMessage
|
|
|
|
cursor string
|
|
|
|
iter int
|
|
|
|
)
|
|
|
|
for {
|
|
|
|
var (
|
|
|
|
items []*common.PinnedMessage
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
items, cursor, err = p.PinnedMessageByChatID(chatID, cursor, pageSize)
|
|
|
|
require.NoError(t, err)
|
|
|
|
result = append(result, items...)
|
|
|
|
|
|
|
|
iter++
|
|
|
|
if len(cursor) == 0 || iter > messagesCount {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 15:23:32 +00:00
|
|
|
|
2021-05-14 21:22:50 +00:00
|
|
|
require.Equal(t, "", cursor) // for loop should exit because of cursor being empty
|
|
|
|
require.EqualValues(t, pinnedMessagesCount, len(result))
|
|
|
|
require.EqualValues(t, math.Ceil(float64(pinnedMessagesCount)/float64(pageSize)), iter)
|
|
|
|
require.True(
|
|
|
|
t,
|
|
|
|
// Verify descending order.
|
|
|
|
sort.SliceIsSorted(result, func(i, j int) bool {
|
2021-06-08 15:23:32 +00:00
|
|
|
return result[i].Message.Clock > result[j].Message.Clock
|
2021-05-14 21:22:50 +00:00
|
|
|
}),
|
|
|
|
)
|
2021-06-08 15:23:32 +00:00
|
|
|
|
|
|
|
require.Equal(t, "them", result[len(result)-1].PinnedBy)
|
|
|
|
for i := 0; i < len(result)-1; i++ {
|
|
|
|
require.Equal(t, "me", result[i].PinnedBy)
|
|
|
|
}
|
2021-05-14 21:22:50 +00:00
|
|
|
}
|
|
|
|
|
2019-11-21 16:19:22 +00:00
|
|
|
func TestMessageReplies(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-02-21 14:48:53 +00:00
|
|
|
chatID := testPublicChatID
|
2020-09-01 13:27:01 +00:00
|
|
|
message1 := &common.Message{
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
ID: "id-1",
|
|
|
|
LocalChatID: chatID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{
|
|
|
|
Text: "content-1",
|
|
|
|
Clock: uint64(1),
|
|
|
|
},
|
|
|
|
From: "1",
|
2019-11-21 16:19:22 +00:00
|
|
|
}
|
2020-09-01 13:27:01 +00:00
|
|
|
message2 := &common.Message{
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
ID: "id-2",
|
|
|
|
LocalChatID: chatID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{
|
|
|
|
Text: "content-2",
|
|
|
|
Clock: uint64(2),
|
|
|
|
ResponseTo: "id-1",
|
|
|
|
},
|
|
|
|
|
|
|
|
From: "2",
|
2019-11-21 16:19:22 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
message3 := &common.Message{
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
ID: "id-3",
|
|
|
|
LocalChatID: chatID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{
|
|
|
|
Text: "content-3",
|
|
|
|
Clock: uint64(3),
|
|
|
|
ResponseTo: "non-existing",
|
|
|
|
},
|
|
|
|
From: "3",
|
2019-11-21 16:19:22 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
messages := []*common.Message{message1, message2, message3}
|
2019-11-21 16:19:22 +00:00
|
|
|
|
2020-05-20 12:16:12 +00:00
|
|
|
err = p.SaveMessages(messages)
|
2019-11-21 16:19:22 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
retrievedMessages, _, err := p.MessageByChatID(chatID, "", 10)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
require.Equal(t, "non-existing", retrievedMessages[0].ResponseTo)
|
2019-11-21 16:19:22 +00:00
|
|
|
require.Nil(t, retrievedMessages[0].QuotedMessage)
|
|
|
|
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
require.Equal(t, "id-1", retrievedMessages[1].ResponseTo)
|
2020-09-01 13:27:01 +00:00
|
|
|
require.Equal(t, &common.QuotedMessage{From: "1", Text: "content-1"}, retrievedMessages[1].QuotedMessage)
|
2019-11-21 16:19:22 +00:00
|
|
|
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
require.Equal(t, "", retrievedMessages[2].ResponseTo)
|
2019-11-21 16:19:22 +00:00
|
|
|
require.Nil(t, retrievedMessages[2].QuotedMessage)
|
|
|
|
}
|
|
|
|
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
func TestMessageByChatIDWithTheSameClocks(t *testing.T) {
|
2019-11-21 16:19:22 +00:00
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-02-21 14:48:53 +00:00
|
|
|
chatID := testPublicChatID
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
clockValues := []uint64{10, 10, 9, 9, 9, 11, 12, 11, 100000, 6, 4, 5, 5, 5, 5}
|
2019-11-21 16:19:22 +00:00
|
|
|
count := len(clockValues)
|
|
|
|
pageSize := 2
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
var messages []*common.Message
|
2019-11-21 16:19:22 +00:00
|
|
|
|
|
|
|
for i, clock := range clockValues {
|
2020-09-01 13:27:01 +00:00
|
|
|
messages = append(messages, &common.Message{
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
ID: strconv.Itoa(i),
|
|
|
|
LocalChatID: chatID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{
|
|
|
|
Clock: clock,
|
|
|
|
},
|
|
|
|
From: "me",
|
2019-11-21 16:19:22 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-05-20 12:16:12 +00:00
|
|
|
err = p.SaveMessages(messages)
|
2019-11-21 16:19:22 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
var (
|
2020-09-01 13:27:01 +00:00
|
|
|
result []*common.Message
|
2019-11-21 16:19:22 +00:00
|
|
|
cursor string
|
|
|
|
iter int
|
|
|
|
)
|
|
|
|
for {
|
|
|
|
var (
|
2020-09-01 13:27:01 +00:00
|
|
|
items []*common.Message
|
2019-11-21 16:19:22 +00:00
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
items, cursor, err = p.MessageByChatID(chatID, cursor, pageSize)
|
|
|
|
require.NoError(t, err)
|
|
|
|
result = append(result, items...)
|
|
|
|
|
|
|
|
iter++
|
|
|
|
if cursor == "" || iter > count {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
require.Empty(t, cursor) // for loop should exit because of cursor being empty
|
|
|
|
require.Len(t, result, count)
|
|
|
|
// Verify the order.
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
expectedClocks := make([]uint64, len(clockValues))
|
|
|
|
copy(expectedClocks, clockValues)
|
|
|
|
sort.Slice(expectedClocks, func(i, j int) bool {
|
|
|
|
return expectedClocks[i] > expectedClocks[j]
|
2019-11-21 16:19:22 +00:00
|
|
|
})
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
resultClocks := make([]uint64, 0, len(clockValues))
|
2019-11-21 16:19:22 +00:00
|
|
|
for _, m := range result {
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
resultClocks = append(resultClocks, m.Clock)
|
2019-11-21 16:19:22 +00:00
|
|
|
}
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
require.EqualValues(t, expectedClocks, resultClocks)
|
2019-11-21 16:19:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestDeleteMessageByID(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2019-11-21 16:19:22 +00:00
|
|
|
id := "1"
|
|
|
|
|
|
|
|
err = insertMinimalMessage(p, id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
m, err := p.MessageByID(id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, id, m.ID)
|
|
|
|
|
|
|
|
err = p.DeleteMessage(m.ID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
_, err = p.MessageByID(id)
|
|
|
|
require.EqualError(t, err, "record not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDeleteMessagesByChatID(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2019-11-21 16:19:22 +00:00
|
|
|
|
|
|
|
err = insertMinimalMessage(p, "1")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = insertMinimalMessage(p, "2")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
m, _, err := p.MessageByChatID(testPublicChatID, "", 10)
|
2019-11-21 16:19:22 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 2, len(m))
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
err = p.DeleteMessagesByChatID(testPublicChatID)
|
2019-11-21 16:19:22 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
m, _, err = p.MessageByChatID(testPublicChatID, "", 10)
|
2019-11-21 16:19:22 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 0, len(m))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMarkMessageSeen(t *testing.T) {
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
chatID := "test-chat"
|
2019-11-21 16:19:22 +00:00
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2019-11-21 16:19:22 +00:00
|
|
|
id := "1"
|
|
|
|
|
|
|
|
err = insertMinimalMessage(p, id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
m, err := p.MessageByID(id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, m.Seen)
|
|
|
|
|
2021-08-31 08:49:39 +00:00
|
|
|
count, countWithMention, err := p.MarkMessagesSeen(chatID, []string{m.ID})
|
2019-11-21 16:19:22 +00:00
|
|
|
require.NoError(t, err)
|
2020-04-06 12:08:53 +00:00
|
|
|
require.Equal(t, uint64(1), count)
|
2021-08-31 08:49:39 +00:00
|
|
|
require.Equal(t, uint64(0), countWithMention)
|
2019-11-21 16:19:22 +00:00
|
|
|
|
|
|
|
m, err = p.MessageByID(id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, m.Seen)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUpdateMessageOutgoingStatus(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2019-11-21 16:19:22 +00:00
|
|
|
id := "1"
|
|
|
|
|
|
|
|
err = insertMinimalMessage(p, id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = p.UpdateMessageOutgoingStatus(id, "new-status")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
m, err := p.MessageByID(id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "new-status", m.OutgoingStatus)
|
|
|
|
}
|
|
|
|
|
2020-12-15 14:43:41 +00:00
|
|
|
func TestMessagesIDsByType(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-12-15 14:43:41 +00:00
|
|
|
|
|
|
|
ids, err := p.RawMessagesIDsByType(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Empty(t, ids)
|
|
|
|
|
|
|
|
err = p.SaveRawMessage(minimalRawMessage("chat-message-id", protobuf.ApplicationMetadataMessage_CHAT_MESSAGE))
|
|
|
|
require.NoError(t, err)
|
|
|
|
ids, err = p.RawMessagesIDsByType(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 1, len(ids))
|
|
|
|
require.Equal(t, "chat-message-id", ids[0])
|
|
|
|
|
|
|
|
ids, err = p.RawMessagesIDsByType(protobuf.ApplicationMetadataMessage_EMOJI_REACTION)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Empty(t, ids)
|
|
|
|
|
|
|
|
err = p.SaveRawMessage(minimalRawMessage("emoji-message-id", protobuf.ApplicationMetadataMessage_EMOJI_REACTION))
|
|
|
|
require.NoError(t, err)
|
|
|
|
ids, err = p.RawMessagesIDsByType(protobuf.ApplicationMetadataMessage_EMOJI_REACTION)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 1, len(ids))
|
|
|
|
require.Equal(t, "emoji-message-id", ids[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestExpiredEmojiReactionsIDs(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-12-15 14:43:41 +00:00
|
|
|
|
|
|
|
ids, err := p.ExpiredEmojiReactionsIDs(emojiResendMaxCount)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Empty(t, ids)
|
|
|
|
|
|
|
|
//save expired chat message
|
|
|
|
rawChatMessage := minimalRawMessage("chat-message-id", protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
|
|
|
|
rawChatMessage.Sent = false
|
|
|
|
err = p.SaveRawMessage(rawChatMessage)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
//make sure it doesn't apper in an expired emoji reactions list
|
|
|
|
ids, err = p.ExpiredEmojiReactionsIDs(emojiResendMaxCount)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Empty(t, ids)
|
|
|
|
|
|
|
|
//save expired emoji message
|
|
|
|
rawEmojiReaction := minimalRawMessage("emoji-message-id", protobuf.ApplicationMetadataMessage_EMOJI_REACTION)
|
|
|
|
rawEmojiReaction.Sent = false
|
|
|
|
err = p.SaveRawMessage(rawEmojiReaction)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
//make sure it appered in expired emoji reactions list
|
|
|
|
ids, err = p.ExpiredEmojiReactionsIDs(emojiResendMaxCount)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 1, len(ids))
|
|
|
|
|
|
|
|
//save non-expired emoji reaction
|
|
|
|
rawEmojiReaction2 := minimalRawMessage("emoji-message-id2", protobuf.ApplicationMetadataMessage_EMOJI_REACTION)
|
|
|
|
rawEmojiReaction2.Sent = true
|
|
|
|
err = p.SaveRawMessage(rawEmojiReaction2)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
//make sure it didn't appear in expired emoji reactions list
|
|
|
|
ids, err = p.ExpiredEmojiReactionsIDs(emojiResendMaxCount)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 1, len(ids))
|
|
|
|
}
|
|
|
|
|
2020-07-27 15:57:01 +00:00
|
|
|
func TestPersistenceEmojiReactions(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-07-27 15:57:01 +00:00
|
|
|
// reverse order as we use DESC
|
|
|
|
id1 := "1"
|
|
|
|
id2 := "2"
|
|
|
|
id3 := "3"
|
|
|
|
|
|
|
|
from1 := "from-1"
|
|
|
|
from2 := "from-2"
|
|
|
|
from3 := "from-3"
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
chatID := testPublicChatID
|
2020-07-27 15:57:01 +00:00
|
|
|
|
|
|
|
err = insertMinimalMessage(p, id1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = insertMinimalMessage(p, id2)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = insertMinimalMessage(p, id3)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Insert normal emoji reaction
|
|
|
|
require.NoError(t, p.SaveEmojiReaction(&EmojiReaction{
|
|
|
|
EmojiReaction: protobuf.EmojiReaction{
|
|
|
|
Clock: 1,
|
|
|
|
MessageId: id3,
|
|
|
|
ChatId: chatID,
|
|
|
|
Type: protobuf.EmojiReaction_SAD,
|
|
|
|
},
|
2020-07-28 07:53:32 +00:00
|
|
|
LocalChatID: chatID,
|
|
|
|
From: from1,
|
2020-07-27 15:57:01 +00:00
|
|
|
}))
|
|
|
|
|
|
|
|
// Insert retracted emoji reaction
|
|
|
|
require.NoError(t, p.SaveEmojiReaction(&EmojiReaction{
|
|
|
|
EmojiReaction: protobuf.EmojiReaction{
|
|
|
|
Clock: 1,
|
|
|
|
MessageId: id3,
|
|
|
|
ChatId: chatID,
|
|
|
|
Type: protobuf.EmojiReaction_SAD,
|
|
|
|
Retracted: true,
|
|
|
|
},
|
2020-07-28 07:53:32 +00:00
|
|
|
LocalChatID: chatID,
|
|
|
|
From: from2,
|
2020-07-27 15:57:01 +00:00
|
|
|
}))
|
|
|
|
|
|
|
|
// Insert retracted emoji reaction out of pagination
|
|
|
|
require.NoError(t, p.SaveEmojiReaction(&EmojiReaction{
|
|
|
|
EmojiReaction: protobuf.EmojiReaction{
|
|
|
|
Clock: 1,
|
|
|
|
MessageId: id1,
|
|
|
|
ChatId: chatID,
|
|
|
|
Type: protobuf.EmojiReaction_SAD,
|
|
|
|
},
|
2020-07-28 07:53:32 +00:00
|
|
|
LocalChatID: chatID,
|
|
|
|
From: from2,
|
2020-07-27 15:57:01 +00:00
|
|
|
}))
|
|
|
|
|
|
|
|
// Insert retracted emoji reaction out of pagination
|
|
|
|
require.NoError(t, p.SaveEmojiReaction(&EmojiReaction{
|
|
|
|
EmojiReaction: protobuf.EmojiReaction{
|
|
|
|
Clock: 1,
|
|
|
|
MessageId: id1,
|
|
|
|
ChatId: chatID,
|
|
|
|
Type: protobuf.EmojiReaction_SAD,
|
|
|
|
},
|
2020-07-28 07:53:32 +00:00
|
|
|
LocalChatID: chatID,
|
|
|
|
From: from3,
|
|
|
|
}))
|
|
|
|
|
|
|
|
// Wrong local chat id
|
|
|
|
require.NoError(t, p.SaveEmojiReaction(&EmojiReaction{
|
|
|
|
EmojiReaction: protobuf.EmojiReaction{
|
|
|
|
Clock: 1,
|
|
|
|
MessageId: id1,
|
|
|
|
ChatId: chatID,
|
|
|
|
Type: protobuf.EmojiReaction_LOVE,
|
|
|
|
},
|
|
|
|
LocalChatID: "wrong-chat-id",
|
|
|
|
From: from3,
|
2020-07-27 15:57:01 +00:00
|
|
|
}))
|
|
|
|
|
|
|
|
reactions, err := p.EmojiReactionsByChatID(chatID, "", 1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, reactions, 1)
|
|
|
|
require.Equal(t, id3, reactions[0].MessageId)
|
|
|
|
|
|
|
|
// Try with a cursor
|
|
|
|
_, cursor, err := p.MessageByChatID(chatID, "", 1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
reactions, err = p.EmojiReactionsByChatID(chatID, cursor, 2)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, reactions, 2)
|
|
|
|
require.Equal(t, id1, reactions[0].MessageId)
|
|
|
|
require.Equal(t, id1, reactions[1].MessageId)
|
|
|
|
}
|
|
|
|
|
2019-11-21 16:19:22 +00:00
|
|
|
func openTestDB() (*sql.DB, error) {
|
|
|
|
dbPath, err := ioutil.TempFile("", "")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return sqlite.Open(dbPath.Name(), "")
|
|
|
|
}
|
|
|
|
|
2021-03-09 12:48:15 +00:00
|
|
|
func insertMinimalMessage(p *sqlitePersistence, id string) error {
|
2020-09-01 13:27:01 +00:00
|
|
|
return p.SaveMessages([]*common.Message{{
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
ID: id,
|
2020-09-01 13:27:01 +00:00
|
|
|
LocalChatID: testPublicChatID,
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
ChatMessage: protobuf.ChatMessage{Text: "some-text"},
|
|
|
|
From: "me",
|
2019-11-21 16:19:22 +00:00
|
|
|
}})
|
|
|
|
}
|
2020-07-30 06:26:19 +00:00
|
|
|
|
2020-12-15 14:43:41 +00:00
|
|
|
func minimalRawMessage(id string, messageType protobuf.ApplicationMetadataMessage_Type) *common.RawMessage {
|
|
|
|
return &common.RawMessage{
|
|
|
|
ID: id,
|
|
|
|
LocalChatID: "test-chat",
|
|
|
|
MessageType: messageType,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-30 06:26:19 +00:00
|
|
|
// Regression test making sure that if audio_duration_ms is null, no error is thrown
|
|
|
|
func TestMessagesAudioDurationMsNull(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-07-30 06:26:19 +00:00
|
|
|
id := "message-id-1"
|
|
|
|
|
|
|
|
err = insertMinimalMessage(p, id)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
_, err = p.db.Exec("UPDATE user_messages SET audio_duration_ms = NULL")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
m, err := p.MessagesByIDs([]string{id})
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, m, 1)
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
m, _, err = p.MessageByChatID(testPublicChatID, "", 10)
|
2020-07-30 06:26:19 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, m, 1)
|
|
|
|
}
|
2020-08-17 06:37:18 +00:00
|
|
|
|
|
|
|
func TestSaveChat(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-08-17 06:37:18 +00:00
|
|
|
|
|
|
|
chat := CreatePublicChat("test-chat", &testTimeSource{})
|
2020-09-01 13:27:01 +00:00
|
|
|
chat.LastMessage = &common.Message{}
|
2021-01-11 10:32:51 +00:00
|
|
|
err = p.SaveChat(*chat)
|
2020-08-17 06:37:18 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
retrievedChat, err := p.Chat(chat.ID)
|
|
|
|
require.NoError(t, err)
|
2021-01-11 10:32:51 +00:00
|
|
|
require.Equal(t, chat, retrievedChat)
|
2020-08-17 06:37:18 +00:00
|
|
|
}
|
2020-09-01 10:34:28 +00:00
|
|
|
|
|
|
|
func TestSaveMentions(t *testing.T) {
|
2020-09-01 13:27:01 +00:00
|
|
|
chatID := testPublicChatID
|
2020-09-01 10:34:28 +00:00
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-09-01 10:34:28 +00:00
|
|
|
|
|
|
|
key, err := crypto.GenerateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
pkString := types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey))
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
message := common.Message{
|
2020-09-01 10:34:28 +00:00
|
|
|
ID: "1",
|
|
|
|
LocalChatID: chatID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{Text: "some-text"},
|
|
|
|
From: "me",
|
|
|
|
Mentions: []string{pkString},
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
err = p.SaveMessages([]*common.Message{&message})
|
2020-09-01 10:34:28 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
retrievedMessages, _, err := p.MessageByChatID(chatID, "", 10)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, retrievedMessages, 1)
|
|
|
|
require.Len(t, retrievedMessages[0].Mentions, 1)
|
|
|
|
require.Equal(t, retrievedMessages[0].Mentions, message.Mentions)
|
2020-10-28 13:18:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSqlitePersistence_GetWhenChatIdentityLastPublished(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-10-28 13:41:22 +00:00
|
|
|
|
2020-11-24 23:16:19 +00:00
|
|
|
chatID := "0xabcd1234"
|
2020-12-10 10:12:51 +00:00
|
|
|
hash := []byte{0x1}
|
2020-10-28 13:41:22 +00:00
|
|
|
now := time.Now().Unix()
|
2020-10-28 13:18:24 +00:00
|
|
|
|
2020-12-10 10:12:51 +00:00
|
|
|
err = p.SaveWhenChatIdentityLastPublished(chatID, hash)
|
2020-10-28 13:18:24 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-12-10 10:12:51 +00:00
|
|
|
ts, actualHash, err := p.GetWhenChatIdentityLastPublished(chatID)
|
2020-10-28 13:18:24 +00:00
|
|
|
require.NoError(t, err)
|
2020-09-01 10:34:28 +00:00
|
|
|
|
2020-10-28 13:59:51 +00:00
|
|
|
// Check that the save happened in the last 2 seconds
|
2020-12-16 18:28:34 +00:00
|
|
|
diff := ts - now
|
2020-10-28 13:41:22 +00:00
|
|
|
require.LessOrEqual(t, diff, int64(2))
|
2020-10-28 13:59:51 +00:00
|
|
|
|
2020-12-10 10:12:51 +00:00
|
|
|
require.True(t, bytes.Equal(hash, actualHash))
|
|
|
|
|
2020-10-28 13:59:51 +00:00
|
|
|
// Require unsaved values to be zero
|
2020-12-10 10:12:51 +00:00
|
|
|
ts2, actualHash2, err := p.GetWhenChatIdentityLastPublished("0xdeadbeef")
|
2020-10-28 13:59:51 +00:00
|
|
|
require.NoError(t, err)
|
2020-12-16 18:28:34 +00:00
|
|
|
require.Exactly(t, int64(0), ts2)
|
2020-12-10 10:12:51 +00:00
|
|
|
require.Nil(t, actualHash2)
|
2020-09-01 10:34:28 +00:00
|
|
|
}
|
2020-10-27 17:35:28 +00:00
|
|
|
|
2020-12-15 15:28:05 +00:00
|
|
|
func TestSaveContactIdentityImage(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-12-15 15:28:05 +00:00
|
|
|
|
|
|
|
key, err := crypto.GenerateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
contactID := types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey))
|
|
|
|
|
|
|
|
err = p.SaveContact(&Contact{ID: contactID}, nil)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jpegType := []byte{0xff, 0xd8, 0xff, 0x1}
|
|
|
|
identityImages := make(map[string]*protobuf.IdentityImage)
|
|
|
|
identityImages["large"] = &protobuf.IdentityImage{
|
|
|
|
Payload: jpegType,
|
|
|
|
SourceType: protobuf.IdentityImage_RAW_PAYLOAD,
|
|
|
|
ImageType: protobuf.ImageType_PNG,
|
|
|
|
}
|
|
|
|
|
|
|
|
identityImages["small"] = &protobuf.IdentityImage{
|
|
|
|
Payload: jpegType,
|
|
|
|
SourceType: protobuf.IdentityImage_RAW_PAYLOAD,
|
|
|
|
ImageType: protobuf.ImageType_PNG,
|
|
|
|
}
|
|
|
|
|
|
|
|
images := &protobuf.ChatIdentity{
|
|
|
|
Clock: 1,
|
|
|
|
Images: identityImages,
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := p.SaveContactChatIdentity(contactID, images)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, result)
|
|
|
|
|
|
|
|
// Save again same clock, it should return false
|
|
|
|
result, err = p.SaveContactChatIdentity(contactID, images)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, result)
|
|
|
|
|
|
|
|
contacts, err := p.Contacts()
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, contacts, 1)
|
|
|
|
|
|
|
|
require.Len(t, contacts[0].Images, 2)
|
|
|
|
}
|
|
|
|
|
2020-10-27 17:35:28 +00:00
|
|
|
func TestSaveLinks(t *testing.T) {
|
|
|
|
chatID := testPublicChatID
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-10-27 17:35:28 +00:00
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
message := common.Message{
|
|
|
|
ID: "1",
|
|
|
|
LocalChatID: chatID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{Text: "some-text"},
|
|
|
|
From: "me",
|
|
|
|
Links: []string{"https://github.com/status-im/status-react"},
|
|
|
|
}
|
|
|
|
|
|
|
|
err = p.SaveMessages([]*common.Message{&message})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
retrievedMessages, _, err := p.MessageByChatID(chatID, "", 10)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, retrievedMessages, 1)
|
|
|
|
require.Len(t, retrievedMessages[0].Links, 1)
|
|
|
|
require.Equal(t, retrievedMessages[0].Links, message.Links)
|
|
|
|
|
|
|
|
}
|
2020-12-07 15:13:39 +00:00
|
|
|
|
|
|
|
func TestHideMessage(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-12-07 15:13:39 +00:00
|
|
|
chatID := testPublicChatID
|
|
|
|
message := &common.Message{
|
|
|
|
ID: "id-1",
|
|
|
|
LocalChatID: chatID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{
|
|
|
|
Text: "content-1",
|
|
|
|
Clock: uint64(1),
|
|
|
|
},
|
|
|
|
From: "1",
|
|
|
|
}
|
|
|
|
|
|
|
|
messages := []*common.Message{message}
|
|
|
|
|
|
|
|
err = p.SaveMessages(messages)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = p.HideMessage(message.ID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
var actualHidden, actualSeen bool
|
|
|
|
err = p.db.QueryRow("SELECT hide, seen FROM user_messages WHERE id = ?", message.ID).Scan(&actualHidden, &actualSeen)
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, actualHidden)
|
|
|
|
require.True(t, actualSeen)
|
|
|
|
}
|
2020-12-22 10:49:25 +00:00
|
|
|
|
|
|
|
func TestDeactivatePublicChat(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-12-22 10:49:25 +00:00
|
|
|
publicChatID := "public-chat-id"
|
|
|
|
var currentClockValue uint64 = 10
|
|
|
|
|
|
|
|
timesource := &testTimeSource{}
|
|
|
|
lastMessage := common.Message{
|
|
|
|
ID: "0x01",
|
|
|
|
LocalChatID: publicChatID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{Text: "some-text"},
|
|
|
|
From: "me",
|
|
|
|
}
|
|
|
|
lastMessage.Clock = 20
|
|
|
|
|
|
|
|
require.NoError(t, p.SaveMessages([]*common.Message{&lastMessage}))
|
|
|
|
|
|
|
|
publicChat := CreatePublicChat(publicChatID, timesource)
|
|
|
|
publicChat.LastMessage = &lastMessage
|
|
|
|
publicChat.UnviewedMessagesCount = 1
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
err = p.DeactivateChat(publicChat, currentClockValue)
|
2020-12-22 10:49:25 +00:00
|
|
|
|
|
|
|
// It does not set deleted at for a public chat
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, uint64(0), publicChat.DeletedAtClockValue)
|
|
|
|
|
|
|
|
// It sets the lastMessage to nil
|
|
|
|
require.Nil(t, publicChat.LastMessage)
|
|
|
|
|
|
|
|
// It sets unviewed messages count
|
|
|
|
require.Equal(t, uint(0), publicChat.UnviewedMessagesCount)
|
|
|
|
|
|
|
|
// It sets active as false
|
|
|
|
require.False(t, publicChat.Active)
|
|
|
|
|
|
|
|
// It deletes messages
|
|
|
|
messages, _, err := p.MessageByChatID(publicChatID, "", 10)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, messages, 0)
|
|
|
|
|
|
|
|
// Reload chat to make sure it has been save
|
|
|
|
dbChat, err := p.Chat(publicChatID)
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, dbChat)
|
|
|
|
|
|
|
|
// Same checks on the chat pulled from the db
|
|
|
|
// It does not set deleted at for a public chat
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, uint64(0), dbChat.DeletedAtClockValue)
|
|
|
|
|
|
|
|
// It sets the lastMessage to nil
|
|
|
|
require.Nil(t, dbChat.LastMessage)
|
|
|
|
|
|
|
|
// It sets unviewed messages count
|
|
|
|
require.Equal(t, uint(0), dbChat.UnviewedMessagesCount)
|
|
|
|
|
|
|
|
// It sets active as false
|
|
|
|
require.False(t, dbChat.Active)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDeactivateOneToOneChat(t *testing.T) {
|
|
|
|
key, err := crypto.GenerateKey()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
pkString := types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey))
|
|
|
|
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
2021-03-09 12:48:15 +00:00
|
|
|
p := NewSQLitePersistence(db)
|
2020-12-22 10:49:25 +00:00
|
|
|
var currentClockValue uint64 = 10
|
|
|
|
|
|
|
|
timesource := &testTimeSource{}
|
|
|
|
|
|
|
|
chat := CreateOneToOneChat(pkString, &key.PublicKey, timesource)
|
|
|
|
|
|
|
|
lastMessage := common.Message{
|
|
|
|
ID: "0x01",
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
ChatMessage: protobuf.ChatMessage{Text: "some-text"},
|
|
|
|
From: "me",
|
|
|
|
}
|
|
|
|
lastMessage.Clock = 20
|
|
|
|
|
|
|
|
require.NoError(t, p.SaveMessages([]*common.Message{&lastMessage}))
|
|
|
|
|
|
|
|
chat.LastMessage = &lastMessage
|
|
|
|
chat.UnviewedMessagesCount = 1
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
err = p.DeactivateChat(chat, currentClockValue)
|
2020-12-22 10:49:25 +00:00
|
|
|
|
|
|
|
// It does set deleted at for a public chat
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotEqual(t, uint64(0), chat.DeletedAtClockValue)
|
|
|
|
|
|
|
|
// It sets the lastMessage to nil
|
|
|
|
require.Nil(t, chat.LastMessage)
|
|
|
|
|
|
|
|
// It sets unviewed messages count
|
|
|
|
require.Equal(t, uint(0), chat.UnviewedMessagesCount)
|
|
|
|
|
|
|
|
// It sets active as false
|
|
|
|
require.False(t, chat.Active)
|
|
|
|
|
|
|
|
// It deletes messages
|
|
|
|
messages, _, err := p.MessageByChatID(chat.ID, "", 10)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, messages, 0)
|
|
|
|
|
|
|
|
// Reload chat to make sure it has been save
|
|
|
|
dbChat, err := p.Chat(chat.ID)
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, dbChat)
|
|
|
|
|
|
|
|
// Same checks on the chat pulled from the db
|
|
|
|
// It does set deleted at for a public chat
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotEqual(t, uint64(0), dbChat.DeletedAtClockValue)
|
|
|
|
|
|
|
|
// It sets the lastMessage to nil
|
|
|
|
require.Nil(t, dbChat.LastMessage)
|
|
|
|
|
|
|
|
// It sets unviewed messages count
|
|
|
|
require.Equal(t, uint(0), dbChat.UnviewedMessagesCount)
|
|
|
|
|
|
|
|
// It sets active as false
|
|
|
|
require.False(t, dbChat.Active)
|
|
|
|
}
|
2021-03-09 12:48:15 +00:00
|
|
|
|
|
|
|
func TestConfirmations(t *testing.T) {
|
|
|
|
dataSyncID1 := []byte("datsync-id-1")
|
|
|
|
dataSyncID2 := []byte("datsync-id-2")
|
|
|
|
dataSyncID3 := []byte("datsync-id-3")
|
|
|
|
dataSyncID4 := []byte("datsync-id-3")
|
|
|
|
|
|
|
|
messageID1 := []byte("message-id-1")
|
|
|
|
messageID2 := []byte("message-id-2")
|
|
|
|
|
|
|
|
publicKey1 := []byte("pk-1")
|
|
|
|
publicKey2 := []byte("pk-2")
|
|
|
|
publicKey3 := []byte("pk-3")
|
|
|
|
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
|
|
|
p := NewSQLitePersistence(db)
|
|
|
|
|
|
|
|
confirmation1 := &common.RawMessageConfirmation{
|
|
|
|
DataSyncID: dataSyncID1,
|
|
|
|
MessageID: messageID1,
|
|
|
|
PublicKey: publicKey1,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Same datasyncID and same messageID, different pubkey
|
|
|
|
confirmation2 := &common.RawMessageConfirmation{
|
|
|
|
DataSyncID: dataSyncID2,
|
|
|
|
MessageID: messageID1,
|
|
|
|
PublicKey: publicKey2,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Different datasyncID and same messageID, different pubkey
|
|
|
|
confirmation3 := &common.RawMessageConfirmation{
|
|
|
|
DataSyncID: dataSyncID3,
|
|
|
|
MessageID: messageID1,
|
|
|
|
PublicKey: publicKey3,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Same dataSyncID, different messageID
|
|
|
|
confirmation4 := &common.RawMessageConfirmation{
|
|
|
|
DataSyncID: dataSyncID4,
|
|
|
|
MessageID: messageID2,
|
|
|
|
PublicKey: publicKey1,
|
|
|
|
}
|
|
|
|
|
|
|
|
require.NoError(t, p.InsertPendingConfirmation(confirmation1))
|
|
|
|
require.NoError(t, p.InsertPendingConfirmation(confirmation2))
|
|
|
|
require.NoError(t, p.InsertPendingConfirmation(confirmation3))
|
|
|
|
require.NoError(t, p.InsertPendingConfirmation(confirmation4))
|
|
|
|
|
|
|
|
// We confirm the first datasync message, no confirmations
|
|
|
|
messageID, err := p.MarkAsConfirmed(dataSyncID1, false)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Nil(t, messageID)
|
|
|
|
|
|
|
|
// We confirm the second datasync message, no confirmations
|
|
|
|
messageID, err = p.MarkAsConfirmed(dataSyncID2, false)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Nil(t, messageID)
|
|
|
|
|
|
|
|
// We confirm the third datasync message, messageID1 should be confirmed
|
|
|
|
messageID, err = p.MarkAsConfirmed(dataSyncID3, false)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, messageID, types.HexBytes(messageID1))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestConfirmationsAtLeastOne(t *testing.T) {
|
|
|
|
dataSyncID1 := []byte("datsync-id-1")
|
|
|
|
dataSyncID2 := []byte("datsync-id-2")
|
|
|
|
dataSyncID3 := []byte("datsync-id-3")
|
|
|
|
|
|
|
|
messageID1 := []byte("message-id-1")
|
|
|
|
|
|
|
|
publicKey1 := []byte("pk-1")
|
|
|
|
publicKey2 := []byte("pk-2")
|
|
|
|
publicKey3 := []byte("pk-3")
|
|
|
|
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
|
|
|
p := NewSQLitePersistence(db)
|
|
|
|
|
|
|
|
confirmation1 := &common.RawMessageConfirmation{
|
|
|
|
DataSyncID: dataSyncID1,
|
|
|
|
MessageID: messageID1,
|
|
|
|
PublicKey: publicKey1,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Same datasyncID and same messageID, different pubkey
|
|
|
|
confirmation2 := &common.RawMessageConfirmation{
|
|
|
|
DataSyncID: dataSyncID2,
|
|
|
|
MessageID: messageID1,
|
|
|
|
PublicKey: publicKey2,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Different datasyncID and same messageID, different pubkey
|
|
|
|
confirmation3 := &common.RawMessageConfirmation{
|
|
|
|
DataSyncID: dataSyncID3,
|
|
|
|
MessageID: messageID1,
|
|
|
|
PublicKey: publicKey3,
|
|
|
|
}
|
|
|
|
|
|
|
|
require.NoError(t, p.InsertPendingConfirmation(confirmation1))
|
|
|
|
require.NoError(t, p.InsertPendingConfirmation(confirmation2))
|
|
|
|
require.NoError(t, p.InsertPendingConfirmation(confirmation3))
|
|
|
|
|
|
|
|
// We confirm the first datasync message, messageID1 and 3 should be confirmed
|
|
|
|
messageID, err := p.MarkAsConfirmed(dataSyncID1, true)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, messageID)
|
|
|
|
require.Equal(t, types.HexBytes(messageID1), messageID)
|
|
|
|
}
|
2021-04-07 12:57:14 +00:00
|
|
|
|
|
|
|
func TestActivityCenterPersistence(t *testing.T) {
|
|
|
|
nID1 := types.HexBytes([]byte("1"))
|
|
|
|
nID2 := types.HexBytes([]byte("2"))
|
|
|
|
nID3 := types.HexBytes([]byte("3"))
|
|
|
|
nID4 := types.HexBytes([]byte("4"))
|
|
|
|
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
|
|
|
p := NewSQLitePersistence(db)
|
|
|
|
|
|
|
|
chat := CreatePublicChat("test-chat", &testTimeSource{})
|
|
|
|
message := &common.Message{}
|
|
|
|
message.Text = "sample text"
|
|
|
|
chat.LastMessage = message
|
|
|
|
err = p.SaveChat(*chat)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
notification := &ActivityCenterNotification{
|
|
|
|
ID: nID1,
|
|
|
|
Type: ActivityCenterNotificationTypeNewOneToOne,
|
|
|
|
ChatID: chat.ID,
|
|
|
|
Timestamp: 1,
|
|
|
|
}
|
|
|
|
err = p.SaveActivityCenterNotification(notification)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
cursor, notifications, err := p.ActivityCenterNotifications("", 2)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Empty(t, cursor)
|
|
|
|
require.Len(t, notifications, 1)
|
|
|
|
require.Equal(t, chat.ID, notifications[0].ChatID)
|
|
|
|
require.Equal(t, message, notifications[0].LastMessage)
|
|
|
|
|
|
|
|
// Add another notification
|
|
|
|
|
|
|
|
notification = &ActivityCenterNotification{
|
|
|
|
ID: nID2,
|
|
|
|
Type: ActivityCenterNotificationTypeNewOneToOne,
|
|
|
|
Timestamp: 2,
|
|
|
|
}
|
|
|
|
err = p.SaveActivityCenterNotification(notification)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
cursor, notifications, err = p.ActivityCenterNotifications("", 1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, notifications, 1)
|
|
|
|
require.NotEmpty(t, cursor)
|
|
|
|
require.Equal(t, nID2, notifications[0].ID)
|
|
|
|
|
|
|
|
// fetch next pagination
|
|
|
|
|
|
|
|
cursor, notifications, err = p.ActivityCenterNotifications(cursor, 1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, notifications, 1)
|
|
|
|
require.Empty(t, cursor)
|
|
|
|
require.False(t, notifications[0].Read)
|
|
|
|
require.Equal(t, nID1, notifications[0].ID)
|
|
|
|
|
|
|
|
// Check count
|
|
|
|
count, err := p.UnreadActivityCenterNotificationsCount()
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, uint64(2), count)
|
|
|
|
|
2021-06-11 16:47:53 +00:00
|
|
|
// Mark first one as read
|
|
|
|
require.NoError(t, p.MarkActivityCenterNotificationsRead([]types.HexBytes{nID1}))
|
|
|
|
count, err = p.UnreadActivityCenterNotificationsCount()
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, uint64(1), count)
|
|
|
|
|
2021-09-24 10:57:15 +00:00
|
|
|
// Mark first one as unread
|
|
|
|
require.NoError(t, p.MarkActivityCenterNotificationsUnread([]types.HexBytes{nID1}))
|
|
|
|
count, err = p.UnreadActivityCenterNotificationsCount()
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, uint64(2), count)
|
|
|
|
|
2021-06-11 16:47:53 +00:00
|
|
|
// Mark all read
|
2021-04-07 12:57:14 +00:00
|
|
|
require.NoError(t, p.MarkAllActivityCenterNotificationsRead())
|
|
|
|
_, notifications, err = p.ActivityCenterNotifications(cursor, 2)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, notifications, 2)
|
|
|
|
require.Empty(t, cursor)
|
|
|
|
require.True(t, notifications[0].Read)
|
|
|
|
require.True(t, notifications[1].Read)
|
|
|
|
|
|
|
|
// Check count
|
|
|
|
count, err = p.UnreadActivityCenterNotificationsCount()
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, uint64(0), count)
|
|
|
|
|
|
|
|
// Mark first one as accepted
|
|
|
|
|
|
|
|
notifications, err = p.AcceptActivityCenterNotifications([]types.HexBytes{nID1})
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, notifications, 1)
|
|
|
|
_, notifications, err = p.ActivityCenterNotifications("", 2)
|
|
|
|
require.NoError(t, err)
|
|
|
|
// It should not be returned anymore
|
|
|
|
require.Len(t, notifications, 1)
|
|
|
|
|
|
|
|
// Mark last one as dismissed
|
|
|
|
require.NoError(t, p.DismissActivityCenterNotifications([]types.HexBytes{nID2}))
|
|
|
|
_, notifications, err = p.ActivityCenterNotifications("", 2)
|
|
|
|
require.NoError(t, err)
|
|
|
|
// Dismissed notifications should not be returned
|
|
|
|
require.Len(t, notifications, 0)
|
|
|
|
|
|
|
|
// Insert new notification
|
|
|
|
notification = &ActivityCenterNotification{
|
|
|
|
ID: nID3,
|
|
|
|
Type: ActivityCenterNotificationTypeNewOneToOne,
|
|
|
|
Timestamp: 3,
|
|
|
|
}
|
|
|
|
err = p.SaveActivityCenterNotification(notification)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Mark all as accepted
|
|
|
|
notifications, err = p.AcceptAllActivityCenterNotifications()
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, notifications, 1)
|
|
|
|
|
|
|
|
_, notifications, err = p.ActivityCenterNotifications("", 2)
|
|
|
|
require.NoError(t, err)
|
|
|
|
// It should not return those
|
|
|
|
require.Len(t, notifications, 0)
|
|
|
|
|
|
|
|
// Insert new notification
|
|
|
|
notification = &ActivityCenterNotification{
|
|
|
|
ID: nID4,
|
|
|
|
Type: ActivityCenterNotificationTypeNewOneToOne,
|
|
|
|
Timestamp: 4,
|
|
|
|
}
|
|
|
|
err = p.SaveActivityCenterNotification(notification)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Mark all as dismissed
|
|
|
|
require.NoError(t, p.DismissAllActivityCenterNotifications())
|
|
|
|
_, notifications, err = p.ActivityCenterNotifications("", 2)
|
|
|
|
require.NoError(t, err)
|
|
|
|
// It should not return those
|
|
|
|
require.Len(t, notifications, 0)
|
|
|
|
}
|
2021-06-01 12:13:17 +00:00
|
|
|
|
|
|
|
func TestSaveCommunityChat(t *testing.T) {
|
|
|
|
db, err := openTestDB()
|
|
|
|
require.NoError(t, err)
|
|
|
|
p := NewSQLitePersistence(db)
|
|
|
|
|
|
|
|
identity := &protobuf.ChatIdentity{
|
|
|
|
DisplayName: "community-chat-name",
|
|
|
|
Description: "community-chat-name-description",
|
|
|
|
}
|
|
|
|
permissions := &protobuf.CommunityPermissions{
|
|
|
|
Access: protobuf.CommunityPermissions_NO_MEMBERSHIP,
|
|
|
|
}
|
|
|
|
|
|
|
|
communityChat := &protobuf.CommunityChat{
|
|
|
|
Identity: identity,
|
|
|
|
Permissions: permissions,
|
|
|
|
}
|
|
|
|
|
|
|
|
chat := CreateCommunityChat("test-or-gid", "test-chat-id", communityChat, &testTimeSource{})
|
|
|
|
chat.LastMessage = &common.Message{}
|
|
|
|
err = p.SaveChat(*chat)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
retrievedChat, err := p.Chat(chat.ID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, chat, retrievedChat)
|
|
|
|
}
|