323 lines
12 KiB
Go
323 lines
12 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"github.com/iamxvbaba/td/clock"
|
|
"github.com/iamxvbaba/td/proto"
|
|
"github.com/iamxvbaba/td/tg"
|
|
"go.uber.org/zap/zaptest"
|
|
appchannels "telesrv/internal/app/channels"
|
|
"telesrv/internal/domain"
|
|
"telesrv/internal/store/memory"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestUpdatesDifferenceIncludesReactionMessageAndUpdate(t *testing.T) {
|
|
const (
|
|
aliceID = int64(1000000001)
|
|
bobID = int64(1000000002)
|
|
)
|
|
reaction := domain.MessageReaction{Type: domain.MessageReactionEmoji, Emoticon: "\U0001f44d"}
|
|
reactions := domain.ChannelMessageReactions{
|
|
CanSeeList: true,
|
|
Results: []domain.ChannelMessageReactionCount{{
|
|
Reaction: reaction,
|
|
Count: 1,
|
|
ChosenOrder: 1,
|
|
}},
|
|
Recent: []domain.ChannelMessagePeerReaction{{
|
|
UserID: bobID,
|
|
Reaction: reaction,
|
|
My: true,
|
|
ChosenOrder: 1,
|
|
Date: 1700000310,
|
|
}},
|
|
}
|
|
msg := domain.Message{
|
|
ID: 68,
|
|
UID: 7001,
|
|
OwnerUserID: aliceID,
|
|
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: bobID},
|
|
From: domain.Peer{Type: domain.PeerTypeUser, ID: aliceID},
|
|
Date: 1700000300,
|
|
Body: "rx",
|
|
Reactions: &reactions,
|
|
}
|
|
got, ok := tgUpdatesDifference(0, domain.UpdateDifference{
|
|
State: domain.UpdateState{Pts: 9, Date: 1700000310},
|
|
Events: []domain.UpdateEvent{{
|
|
UserID: aliceID,
|
|
Type: domain.UpdateEventMessageReactions,
|
|
Pts: 9,
|
|
PtsCount: 1,
|
|
Date: 1700000310,
|
|
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: bobID},
|
|
Message: msg,
|
|
}},
|
|
}).(*tg.UpdatesDifference)
|
|
if !ok {
|
|
t.Fatalf("difference = %T, want *tg.UpdatesDifference", got)
|
|
}
|
|
if len(got.NewMessages) != 1 || len(got.OtherUpdates) != 1 {
|
|
t.Fatalf("difference messages/updates = %d/%d, want 1/1", len(got.NewMessages), len(got.OtherUpdates))
|
|
}
|
|
wireMsg, ok := got.NewMessages[0].(*tg.Message)
|
|
if !ok || wireMsg.ID != msg.ID {
|
|
t.Fatalf("message = %T %+v, want message %d", got.NewMessages[0], got.NewMessages[0], msg.ID)
|
|
}
|
|
msgReactions, ok := wireMsg.GetReactions()
|
|
if !ok || len(msgReactions.Results) != 1 || msgReactions.Results[0].Count != 1 || msgReactions.Results[0].ChosenOrder != 1 {
|
|
t.Fatalf("message reactions = %+v set=%v, want chosen reaction", msgReactions, ok)
|
|
}
|
|
update, ok := got.OtherUpdates[0].(*tg.UpdateMessageReactions)
|
|
if !ok || update.MsgID != msg.ID || len(update.Reactions.Results) != 1 || update.Reactions.Results[0].ChosenOrder != 1 {
|
|
t.Fatalf("reaction update = %T %+v, want update for msg %d", got.OtherUpdates[0], got.OtherUpdates[0], msg.ID)
|
|
}
|
|
}
|
|
|
|
func TestMessagesUpdateSavedReactionTagPersistsAndPushesRefresh(t *testing.T) {
|
|
const userID = int64(1000000001)
|
|
sessions := &captureSessions{}
|
|
r := New(Config{}, Deps{
|
|
Channels: appchannels.NewService(memory.NewChannelStore()),
|
|
Sessions: sessions,
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
|
|
req := &tg.MessagesUpdateSavedReactionTagRequest{
|
|
Reaction: &tg.ReactionEmoji{Emoticon: "\U0001f44d"},
|
|
}
|
|
req.SetTitle("Fav")
|
|
ok, err := r.onMessagesUpdateSavedReactionTag(WithSessionID(WithUserID(context.Background(), userID), 55), req)
|
|
if err != nil || !ok {
|
|
t.Fatalf("update saved reaction tag = %v, %v, want true nil", ok, err)
|
|
}
|
|
|
|
got, err := r.onMessagesGetSavedReactionTags(WithUserID(context.Background(), userID), &tg.MessagesGetSavedReactionTagsRequest{})
|
|
if err != nil {
|
|
t.Fatalf("get saved reaction tags: %v", err)
|
|
}
|
|
page, ok := got.(*tg.MessagesSavedReactionTags)
|
|
if !ok || len(page.Tags) != 1 {
|
|
t.Fatalf("saved reaction tags = %T %+v, want one tag", got, got)
|
|
}
|
|
if emoji, ok := page.Tags[0].Reaction.(*tg.ReactionEmoji); !ok || emoji.Emoticon != "\U0001f44d" || page.Tags[0].Title != "Fav" {
|
|
t.Fatalf("saved reaction tag = %+v, want persisted thumb/Fav", page.Tags[0])
|
|
}
|
|
|
|
push := sessions.snapshot()
|
|
if push.userID != userID || push.sessionID != 55 || push.messageType != proto.MessageFromServer {
|
|
t.Fatalf("push = user %d exclude session %d type %v, want self/exclude/from_server", push.userID, push.sessionID, push.messageType)
|
|
}
|
|
updates, ok := push.message.(*tg.Updates)
|
|
if !ok {
|
|
t.Fatalf("pushed message = %T, want *tg.Updates", push.message)
|
|
}
|
|
if len(updates.Updates) != 1 {
|
|
t.Fatalf("updates = %+v, want one update", updates.Updates)
|
|
}
|
|
if _, ok := updates.Updates[0].(*tg.UpdateSavedReactionTags); !ok {
|
|
t.Fatalf("update = %T, want *tg.UpdateSavedReactionTags", updates.Updates[0])
|
|
}
|
|
}
|
|
|
|
func TestMessagesSendReactionPrivatePeerReturnsReactionUpdate(t *testing.T) {
|
|
const (
|
|
userID = int64(1000000001)
|
|
peerID = int64(1000000002)
|
|
now = int64(1700000200)
|
|
)
|
|
messages := &captureMessages{}
|
|
r := New(Config{}, Deps{Messages: messages}, zaptest.NewLogger(t), fixedClock{now: time.Unix(now, 0)})
|
|
req := &tg.MessagesSendReactionRequest{
|
|
Peer: &tg.InputPeerUser{UserID: peerID, AccessHash: 22},
|
|
MsgID: 7,
|
|
Reaction: []tg.ReactionClass{&tg.ReactionEmoji{Emoticon: "\U0001f44d"}},
|
|
Big: true,
|
|
}
|
|
req.SetReaction(req.Reaction)
|
|
req.SetAddToRecent(true)
|
|
|
|
updates, err := r.onMessagesSendReaction(WithUserID(context.Background(), userID), req)
|
|
if err != nil {
|
|
t.Fatalf("messages.sendReaction private: %v", err)
|
|
}
|
|
if messages.setReactionReq.UserID != userID || messages.setReactionReq.Peer != (domain.Peer{Type: domain.PeerTypeUser, ID: peerID}) || messages.setReactionReq.MessageID != req.MsgID || !messages.setReactionReq.Big || !messages.setReactionReq.AddToRecent {
|
|
t.Fatalf("set reaction req = %+v, want private peer/message context", messages.setReactionReq)
|
|
}
|
|
got := updates.(*tg.Updates).Updates
|
|
if len(got) != 1 {
|
|
t.Fatalf("updates = %+v, want one reaction update", got)
|
|
}
|
|
update, ok := got[0].(*tg.UpdateMessageReactions)
|
|
if !ok {
|
|
t.Fatalf("update = %T, want *tg.UpdateMessageReactions", got[0])
|
|
}
|
|
peer, ok := update.Peer.(*tg.PeerUser)
|
|
if !ok || peer.UserID != peerID || update.MsgID != req.MsgID {
|
|
t.Fatalf("update peer/msg = %+v/%d, want peer %d msg %d", update.Peer, update.MsgID, peerID, req.MsgID)
|
|
}
|
|
if len(update.Reactions.Results) != 1 || update.Reactions.Results[0].Count != 1 || update.Reactions.Results[0].ChosenOrder != 1 {
|
|
t.Fatalf("reaction results = %+v, want one chosen reaction", update.Reactions.Results)
|
|
}
|
|
}
|
|
|
|
func TestMessagesSendReactionPrivatePeerAllowsCustomEmoji(t *testing.T) {
|
|
const (
|
|
userID = int64(1000000001)
|
|
peerID = int64(1000000002)
|
|
customDocumentID = int64(990001)
|
|
)
|
|
messages := &captureMessages{}
|
|
r := New(Config{}, Deps{Messages: messages}, zaptest.NewLogger(t), fixedClock{now: time.Unix(1700000200, 0)})
|
|
req := &tg.MessagesSendReactionRequest{
|
|
Peer: &tg.InputPeerUser{UserID: peerID, AccessHash: 22},
|
|
MsgID: 7,
|
|
}
|
|
req.SetReaction([]tg.ReactionClass{&tg.ReactionCustomEmoji{DocumentID: customDocumentID}})
|
|
|
|
updates, err := r.onMessagesSendReaction(WithUserID(context.Background(), userID), req)
|
|
if err != nil {
|
|
t.Fatalf("messages.sendReaction custom private: %v", err)
|
|
}
|
|
if len(messages.setReactionReq.Reactions) != 1 || messages.setReactionReq.Reactions[0].Type != domain.MessageReactionCustomEmoji || messages.setReactionReq.Reactions[0].DocumentID != customDocumentID {
|
|
t.Fatalf("set reaction req reactions = %+v, want custom document %d", messages.setReactionReq.Reactions, customDocumentID)
|
|
}
|
|
update := updates.(*tg.Updates).Updates[0].(*tg.UpdateMessageReactions)
|
|
reaction, ok := update.Reactions.Results[0].Reaction.(*tg.ReactionCustomEmoji)
|
|
if !ok || reaction.DocumentID != customDocumentID {
|
|
t.Fatalf("update reaction = %T %+v, want custom document %d", update.Reactions.Results[0].Reaction, update.Reactions.Results[0].Reaction, customDocumentID)
|
|
}
|
|
}
|
|
|
|
func TestMessagesSendReactionPrivatePushesViewerLocalMessageID(t *testing.T) {
|
|
const (
|
|
aliceID = int64(1000000001)
|
|
bobID = int64(1000000002)
|
|
now = int64(1700000200)
|
|
)
|
|
reaction := domain.MessageReaction{Type: domain.MessageReactionEmoji, Emoticon: "\U0001f44d"}
|
|
aliceReactions := domain.ChannelMessageReactions{
|
|
CanSeeList: true,
|
|
Results: []domain.ChannelMessageReactionCount{{
|
|
Reaction: reaction,
|
|
Count: 1,
|
|
}},
|
|
Recent: []domain.ChannelMessagePeerReaction{{
|
|
SenderUserID: aliceID,
|
|
UserID: bobID,
|
|
Reaction: reaction,
|
|
Unread: true,
|
|
Date: int(now),
|
|
}},
|
|
}
|
|
bobReactions := domain.ChannelMessageReactions{
|
|
CanSeeList: true,
|
|
Results: []domain.ChannelMessageReactionCount{{
|
|
Reaction: reaction,
|
|
Count: 1,
|
|
ChosenOrder: 1,
|
|
}},
|
|
Recent: []domain.ChannelMessagePeerReaction{{
|
|
UserID: bobID,
|
|
Reaction: reaction,
|
|
My: true,
|
|
ChosenOrder: 1,
|
|
Date: int(now),
|
|
}},
|
|
}
|
|
messages := &captureMessages{
|
|
setReactionRes: domain.PrivateMessageReactionsResult{
|
|
Messages: []domain.Message{
|
|
{
|
|
ID: 68,
|
|
UID: 7001,
|
|
OwnerUserID: aliceID,
|
|
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: bobID},
|
|
From: domain.Peer{Type: domain.PeerTypeUser, ID: aliceID},
|
|
Date: int(now),
|
|
Reactions: &aliceReactions,
|
|
},
|
|
{
|
|
ID: 64,
|
|
UID: 7001,
|
|
OwnerUserID: bobID,
|
|
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: aliceID},
|
|
From: domain.Peer{Type: domain.PeerTypeUser, ID: aliceID},
|
|
Date: int(now),
|
|
Reactions: &bobReactions,
|
|
},
|
|
},
|
|
Reactions: bobReactions,
|
|
},
|
|
}
|
|
sessions := &captureSessions{}
|
|
r := New(Config{}, Deps{Messages: messages, Sessions: sessions}, zaptest.NewLogger(t), fixedClock{now: time.Unix(now, 0)})
|
|
req := &tg.MessagesSendReactionRequest{
|
|
Peer: &tg.InputPeerUser{UserID: aliceID, AccessHash: 11},
|
|
MsgID: 64,
|
|
Reaction: []tg.ReactionClass{&tg.ReactionEmoji{Emoticon: "\U0001f44d"}},
|
|
}
|
|
req.SetReaction(req.Reaction)
|
|
|
|
updates, err := r.onMessagesSendReaction(WithSessionID(WithUserID(context.Background(), bobID), 77), req)
|
|
if err != nil {
|
|
t.Fatalf("messages.sendReaction private: %v", err)
|
|
}
|
|
self := updates.(*tg.Updates).Updates[0].(*tg.UpdateMessageReactions)
|
|
if peer, ok := self.Peer.(*tg.PeerUser); !ok || peer.UserID != aliceID || self.MsgID != 64 {
|
|
t.Fatalf("self update peer/msg = %#v/%d, want alice/msg64", self.Peer, self.MsgID)
|
|
}
|
|
if got := sessions.pushedUserIDs(); len(got) != 2 || got[0] != bobID || got[1] != aliceID {
|
|
t.Fatalf("pushed users = %+v, want bob then alice", got)
|
|
}
|
|
pushed := sessions.snapshot()
|
|
if pushed.userID != aliceID || pushed.sessionID != 77 || pushed.messageType != proto.MessageFromServer {
|
|
t.Fatalf("last push = user %d session %d type %v, want alice/exclude bob/from_server", pushed.userID, pushed.sessionID, pushed.messageType)
|
|
}
|
|
pushedUpdates, ok := pushed.message.(*tg.Updates)
|
|
if !ok || len(pushedUpdates.Updates) != 1 {
|
|
t.Fatalf("pushed message = %T %+v, want one updates container", pushed.message, pushed.message)
|
|
}
|
|
other, ok := pushedUpdates.Updates[0].(*tg.UpdateMessageReactions)
|
|
if !ok {
|
|
t.Fatalf("pushed update = %T, want *tg.UpdateMessageReactions", pushedUpdates.Updates[0])
|
|
}
|
|
peer, ok := other.Peer.(*tg.PeerUser)
|
|
if !ok || peer.UserID != bobID || other.MsgID != 68 {
|
|
t.Fatalf("pushed update peer/msg = %#v/%d, want bob/msg68", other.Peer, other.MsgID)
|
|
}
|
|
if len(other.Reactions.Results) != 1 || other.Reactions.Results[0].Count != 1 || other.Reactions.Results[0].ChosenOrder != 0 {
|
|
t.Fatalf("pushed reaction results = %+v, want one non-chosen reaction", other.Reactions.Results)
|
|
}
|
|
if recent, ok := other.Reactions.GetRecentReactions(); !ok || len(recent) != 1 || !recent[0].Unread || recent[0].My {
|
|
t.Fatalf("pushed recent reactions = %+v set=%v, want one unread non-my reaction", recent, ok)
|
|
}
|
|
}
|
|
|
|
func TestTransientPrivateBigReactionCacheIsBoundedAndExpires(t *testing.T) {
|
|
r := &Router{}
|
|
peer := domain.Peer{Type: domain.PeerTypeUser, ID: 2002}
|
|
r.rememberTransientPrivateBigReaction(1001, peer, 1, 10)
|
|
if !r.shouldSuppressTransientPrivateReactionClear(1001, peer, 1, 12) {
|
|
t.Fatalf("transient big reaction clear should be suppressed inside window")
|
|
}
|
|
if r.shouldSuppressTransientPrivateReactionClear(1001, peer, 1, 14) {
|
|
t.Fatalf("transient big reaction clear should not be suppressed after expiry")
|
|
}
|
|
|
|
var cache transientPrivateBigReactionCache
|
|
for i := 0; i < transientPrivateBigReactionMaxEntries+100; i++ {
|
|
cache.remember(transientPrivateBigReactionKey{
|
|
UserID: 1001,
|
|
PeerID: int64(2000 + i),
|
|
MessageID: i + 1,
|
|
}, 100+i, 1)
|
|
}
|
|
cache.mu.Lock()
|
|
got := len(cache.entries)
|
|
cache.mu.Unlock()
|
|
if got > transientPrivateBigReactionMaxEntries {
|
|
t.Fatalf("transient cache entries = %d, want <= %d", got, transientPrivateBigReactionMaxEntries)
|
|
}
|
|
}
|