389 lines
15 KiB
Go
389 lines
15 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"github.com/iamxvbaba/td/bin"
|
|
"github.com/iamxvbaba/td/clock"
|
|
"github.com/iamxvbaba/td/tg"
|
|
"go.uber.org/zap/zaptest"
|
|
"strings"
|
|
appchannels "telesrv/internal/app/channels"
|
|
appdialogs "telesrv/internal/app/dialogs"
|
|
appusers "telesrv/internal/app/users"
|
|
"telesrv/internal/domain"
|
|
"telesrv/internal/store/memory"
|
|
"testing"
|
|
)
|
|
|
|
func TestMessagesCreateChatCreatesMegagroupAndDialogsRPC(t *testing.T) {
|
|
ctx := context.Background()
|
|
userStore := memory.NewUserStore()
|
|
owner, err := userStore.Create(ctx, domain.User{AccessHash: 11, Phone: "15550001001", FirstName: "Owner"})
|
|
if err != nil {
|
|
t.Fatalf("create owner: %v", err)
|
|
}
|
|
friend, err := userStore.Create(ctx, domain.User{AccessHash: 22, Phone: "15550001002", FirstName: "Friend"})
|
|
if err != nil {
|
|
t.Fatalf("create friend: %v", err)
|
|
}
|
|
channelStore := memory.NewChannelStore()
|
|
r := New(Config{}, Deps{
|
|
Users: appusers.NewService(userStore),
|
|
Channels: appchannels.NewService(channelStore),
|
|
Dialogs: appdialogs.NewService(memory.NewDialogStore(), channelStore),
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
|
|
invited, err := r.onMessagesCreateChat(WithUserID(ctx, owner.ID), &tg.MessagesCreateChatRequest{
|
|
Users: []tg.InputUserClass{&tg.InputUser{UserID: friend.ID, AccessHash: friend.AccessHash}},
|
|
Title: "E2E Group",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create chat: %v", err)
|
|
}
|
|
updates, ok := invited.Updates.(*tg.Updates)
|
|
if !ok || len(updates.Chats) != 1 {
|
|
t.Fatalf("updates = %T %+v, want one chat", invited.Updates, invited.Updates)
|
|
}
|
|
channel, ok := updates.Chats[0].(*tg.Channel)
|
|
if !ok || !channel.Megagroup || channel.Broadcast {
|
|
t.Fatalf("chat = %#v, want megagroup channel", updates.Chats[0])
|
|
}
|
|
assertDefaultBannedRightsAllowsSend(t, channel)
|
|
if len(updates.Updates) != 4 {
|
|
t.Fatalf("updates len = %d, want create/invite service messages plus channel refreshes", len(updates.Updates))
|
|
}
|
|
newMsg, ok := updates.Updates[0].(*tg.UpdateNewChannelMessage)
|
|
if !ok || newMsg.Pts != 1 || newMsg.PtsCount != 1 {
|
|
t.Fatalf("create update = %#v, want channel pts=1", updates.Updates[0])
|
|
}
|
|
if refresh, ok := updates.Updates[1].(*tg.UpdateChannel); !ok || refresh.ChannelID != channel.ID {
|
|
t.Fatalf("create refresh = %#v, want channel refresh", updates.Updates[1])
|
|
}
|
|
service, ok := newMsg.Message.(*tg.MessageService)
|
|
if !ok {
|
|
t.Fatalf("create message = %T, want service", newMsg.Message)
|
|
}
|
|
if _, ok := service.Action.(*tg.MessageActionChannelCreate); !ok {
|
|
t.Fatalf("service action = %T, want channel create", service.Action)
|
|
}
|
|
inviteMsg, ok := updates.Updates[2].(*tg.UpdateNewChannelMessage)
|
|
if !ok || inviteMsg.Pts != 2 || inviteMsg.PtsCount != 1 {
|
|
t.Fatalf("invite update = %#v, want channel pts=2", updates.Updates[2])
|
|
}
|
|
if refresh, ok := updates.Updates[3].(*tg.UpdateChannel); !ok || refresh.ChannelID != channel.ID {
|
|
t.Fatalf("invite refresh = %#v, want channel refresh", updates.Updates[3])
|
|
}
|
|
inviteService, ok := inviteMsg.Message.(*tg.MessageService)
|
|
if !ok {
|
|
t.Fatalf("invite message = %T, want service", inviteMsg.Message)
|
|
}
|
|
addUser, ok := inviteService.Action.(*tg.MessageActionChatAddUser)
|
|
if !ok || len(addUser.Users) != 1 || addUser.Users[0] != friend.ID {
|
|
t.Fatalf("invite action = %#v, want add friend %d", inviteService.Action, friend.ID)
|
|
}
|
|
if len(updates.Users) != 2 {
|
|
t.Fatalf("updates users len = %d, want owner + friend", len(updates.Users))
|
|
}
|
|
|
|
participants, err := r.onChannelsGetParticipants(WithUserID(ctx, owner.ID), &tg.ChannelsGetParticipantsRequest{
|
|
Channel: &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
|
|
Filter: &tg.ChannelParticipantsRecent{},
|
|
Limit: 10,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("get participants: %v", err)
|
|
}
|
|
participantList, ok := participants.(*tg.ChannelsChannelParticipants)
|
|
if !ok || participantList.Count != 2 || len(participantList.Participants) != 2 || len(participantList.Users) != 2 {
|
|
t.Fatalf("participants = %T %+v, want owner + friend participants/users", participants, participants)
|
|
}
|
|
|
|
req := &tg.MessagesGetDialogsRequest{OffsetPeer: &tg.InputPeerEmpty{}, Limit: 20}
|
|
var b bin.Buffer
|
|
if err := req.Encode(&b); err != nil {
|
|
t.Fatalf("encode get dialogs: %v", err)
|
|
}
|
|
enc, err := r.Dispatch(WithUserID(ctx, owner.ID), [8]byte{}, 0, &b)
|
|
if err != nil {
|
|
t.Fatalf("dispatch get dialogs: %v", err)
|
|
}
|
|
dialogs, ok := enc.(*tg.MessagesDialogs)
|
|
if !ok {
|
|
t.Fatalf("dialogs response = %T, want *tg.MessagesDialogs", enc)
|
|
}
|
|
if len(dialogs.Dialogs) != 1 || len(dialogs.Chats) != 1 || len(dialogs.Messages) != 1 {
|
|
t.Fatalf("dialogs = %+v, want channel dialog/chat/message", dialogs)
|
|
}
|
|
dialog := dialogs.Dialogs[0].(*tg.Dialog)
|
|
if peer, ok := dialog.Peer.(*tg.PeerChannel); !ok || peer.ChannelID != channel.ID {
|
|
t.Fatalf("dialog peer = %#v, want channel %d", dialog.Peer, channel.ID)
|
|
}
|
|
}
|
|
|
|
func TestMessagesCreateChatRejectsEmptyInviteListRPC(t *testing.T) {
|
|
ctx := context.Background()
|
|
userStore := memory.NewUserStore()
|
|
owner, err := userStore.Create(ctx, domain.User{AccessHash: 21, Phone: "15550001021", FirstName: "Owner"})
|
|
if err != nil {
|
|
t.Fatalf("create owner: %v", err)
|
|
}
|
|
r := New(Config{}, Deps{
|
|
Users: appusers.NewService(userStore),
|
|
Channels: appchannels.NewService(memory.NewChannelStore()),
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
|
|
if _, err := r.onMessagesCreateChat(WithUserID(ctx, owner.ID), &tg.MessagesCreateChatRequest{
|
|
Title: "No Invitees",
|
|
}); err == nil || !strings.Contains(err.Error(), "USERS_TOO_FEW") {
|
|
t.Fatalf("create chat without users err = %v, want USERS_TOO_FEW", err)
|
|
}
|
|
}
|
|
|
|
func TestMessagesCreateChatTDesktopReturnsLegacyChatAndAcceptsInputPeerChatRPC(t *testing.T) {
|
|
ctx := context.Background()
|
|
userStore := memory.NewUserStore()
|
|
owner, err := userStore.Create(ctx, domain.User{AccessHash: 31, Phone: "15550001031", FirstName: "Owner"})
|
|
if err != nil {
|
|
t.Fatalf("create owner: %v", err)
|
|
}
|
|
friend, err := userStore.Create(ctx, domain.User{AccessHash: 32, Phone: "15550001032", FirstName: "Friend"})
|
|
if err != nil {
|
|
t.Fatalf("create friend: %v", err)
|
|
}
|
|
channelStore := memory.NewChannelStore()
|
|
r := New(Config{}, Deps{
|
|
Users: appusers.NewService(userStore),
|
|
Channels: appchannels.NewService(channelStore),
|
|
Dialogs: appdialogs.NewService(memory.NewDialogStore(), channelStore),
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
tdCtx := WithClientInfo(WithUserID(ctx, owner.ID), ClientInfo{
|
|
DeviceModel: "Desktop",
|
|
AppVersion: "6.8.4 x64",
|
|
LangPack: "tdesktop",
|
|
})
|
|
|
|
invited, err := r.onMessagesCreateChat(tdCtx, &tg.MessagesCreateChatRequest{
|
|
Users: []tg.InputUserClass{&tg.InputUser{UserID: friend.ID, AccessHash: friend.AccessHash}},
|
|
Title: "TDesktop Group",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create chat: %v", err)
|
|
}
|
|
updates, ok := invited.Updates.(*tg.Updates)
|
|
if !ok || len(updates.Chats) != 2 {
|
|
t.Fatalf("updates = %T %+v, want legacy chat + channel", invited.Updates, invited.Updates)
|
|
}
|
|
legacy, ok := updates.Chats[0].(*tg.Chat)
|
|
if !ok || !legacy.Deactivated {
|
|
t.Fatalf("legacy chat = %#v, want migrated chat", updates.Chats[0])
|
|
}
|
|
assertDefaultBannedRightsAllowsSend(t, legacy)
|
|
channel, ok := updates.Chats[1].(*tg.Channel)
|
|
if !ok || !channel.Megagroup || channel.Broadcast {
|
|
t.Fatalf("channel = %#v, want megagroup channel", updates.Chats[1])
|
|
}
|
|
assertDefaultBannedRightsAllowsSend(t, channel)
|
|
if !channel.Creator {
|
|
t.Fatalf("channel creator flag = false, want true for creator")
|
|
}
|
|
if rights, ok := channel.GetAdminRights(); !ok || !rights.ChangeInfo || !rights.InviteUsers {
|
|
t.Fatalf("channel admin rights = %+v ok=%v, want creator manage rights", rights, ok)
|
|
}
|
|
migrated, ok := legacy.GetMigratedTo()
|
|
if !ok {
|
|
t.Fatalf("legacy chat missing migrated_to")
|
|
}
|
|
migratedTo, ok := migrated.(*tg.InputChannel)
|
|
if !ok || migratedTo.ChannelID != channel.ID || migratedTo.AccessHash != channel.AccessHash {
|
|
t.Fatalf("migrated_to = %#v, want channel %d/%d", migrated, channel.ID, channel.AccessHash)
|
|
}
|
|
|
|
participants, err := r.onChannelsGetParticipants(tdCtx, &tg.ChannelsGetParticipantsRequest{
|
|
Channel: &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
|
|
Filter: &tg.ChannelParticipantsRecent{},
|
|
Limit: 10,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("get participants after create chat: %v", err)
|
|
}
|
|
participantList, ok := participants.(*tg.ChannelsChannelParticipants)
|
|
if !ok || len(participantList.Chats) != 0 {
|
|
t.Fatalf("participants = %T %+v, want no chat side vector", participants, participants)
|
|
}
|
|
if participantList.Count != 2 || len(participantList.Participants) != 2 || len(participantList.Users) != 2 {
|
|
t.Fatalf("participants count/rows/users = %d/%d/%d, want owner + friend",
|
|
participantList.Count, len(participantList.Participants), len(participantList.Users))
|
|
}
|
|
legacyHistoryReq := &tg.MessagesGetHistoryRequest{
|
|
Peer: &tg.InputPeerChat{ChatID: channel.ID},
|
|
Limit: 20,
|
|
}
|
|
var legacyHistoryBuf bin.Buffer
|
|
if err := legacyHistoryReq.Encode(&legacyHistoryBuf); err != nil {
|
|
t.Fatalf("encode legacy history: %v", err)
|
|
}
|
|
legacyHistory, err := r.Dispatch(tdCtx, [8]byte{}, 0, &legacyHistoryBuf)
|
|
if err != nil {
|
|
t.Fatalf("legacy history: %v", err)
|
|
}
|
|
legacyMessages, ok := legacyHistory.(*tg.MessagesMessages)
|
|
if !ok {
|
|
t.Fatalf("legacy history = %T %+v, want *tg.MessagesMessages", legacyHistory, legacyHistory)
|
|
}
|
|
if len(legacyMessages.Messages) != 0 {
|
|
t.Fatalf("legacy history = %T %+v, want empty messages.messages", legacyHistory, legacyHistory)
|
|
}
|
|
|
|
sent, err := r.onMessagesSendMessage(tdCtx, &tg.MessagesSendMessageRequest{
|
|
Peer: &tg.InputPeerChat{ChatID: channel.ID},
|
|
RandomID: 99,
|
|
Message: "via legacy input peer",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("send via inputPeerChat: %v", err)
|
|
}
|
|
sentUpdates, ok := sent.(*tg.Updates)
|
|
if !ok || len(sentUpdates.Updates) < 2 {
|
|
t.Fatalf("send updates = %T %+v, want channel message updates", sent, sent)
|
|
}
|
|
newMsg, ok := sentUpdates.Updates[1].(*tg.UpdateNewChannelMessage)
|
|
if !ok {
|
|
t.Fatalf("send update = %#v, want updateNewChannelMessage", sentUpdates.Updates[1])
|
|
}
|
|
msg, ok := newMsg.Message.(*tg.Message)
|
|
if !ok || msg.Message != "via legacy input peer" {
|
|
t.Fatalf("sent message = %#v, want text channel message", newMsg.Message)
|
|
}
|
|
if peer, ok := msg.PeerID.(*tg.PeerChannel); !ok || peer.ChannelID != channel.ID {
|
|
t.Fatalf("sent peer = %#v, want peerChannel %d", msg.PeerID, channel.ID)
|
|
}
|
|
}
|
|
|
|
func TestMessagesCreateChatDispatchRemembersTDesktopClientInfo(t *testing.T) {
|
|
ctx := context.Background()
|
|
userStore := memory.NewUserStore()
|
|
owner, err := userStore.Create(ctx, domain.User{AccessHash: 41, Phone: "15550001041", FirstName: "Owner"})
|
|
if err != nil {
|
|
t.Fatalf("create owner: %v", err)
|
|
}
|
|
friend, err := userStore.Create(ctx, domain.User{AccessHash: 42, Phone: "15550001042", FirstName: "Friend"})
|
|
if err != nil {
|
|
t.Fatalf("create friend: %v", err)
|
|
}
|
|
channelStore := memory.NewChannelStore()
|
|
sessions := &captureSessions{}
|
|
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{
|
|
Users: appusers.NewService(userStore),
|
|
Channels: appchannels.NewService(channelStore),
|
|
Dialogs: appdialogs.NewService(memory.NewDialogStore(), channelStore),
|
|
Sessions: sessions,
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
|
|
rawAuthKeyID := [8]byte{0x42, 0x01}
|
|
sessionID := int64(77)
|
|
initReq := &tg.InvokeWithLayerRequest{
|
|
Layer: 225,
|
|
Query: &tg.InitConnectionRequest{
|
|
APIID: 111111,
|
|
DeviceModel: "Desktop",
|
|
AppVersion: "6.8.4 x64",
|
|
SystemLangCode: "en",
|
|
LangPack: "tdesktop",
|
|
LangCode: "en",
|
|
Query: &tg.HelpGetConfigRequest{},
|
|
},
|
|
}
|
|
var initBuf bin.Buffer
|
|
if err := initReq.Encode(&initBuf); err != nil {
|
|
t.Fatalf("encode init: %v", err)
|
|
}
|
|
if _, err := r.Dispatch(WithUserID(ctx, owner.ID), rawAuthKeyID, sessionID, &initBuf); err != nil {
|
|
t.Fatalf("dispatch init: %v", err)
|
|
}
|
|
|
|
createReq := &tg.MessagesCreateChatRequest{
|
|
Users: []tg.InputUserClass{&tg.InputUser{UserID: friend.ID, AccessHash: friend.AccessHash}},
|
|
Title: "Dispatch TDesktop Group",
|
|
}
|
|
var createBuf bin.Buffer
|
|
if err := createReq.Encode(&createBuf); err != nil {
|
|
t.Fatalf("encode create chat: %v", err)
|
|
}
|
|
enc, err := r.Dispatch(context.Background(), rawAuthKeyID, sessionID, &createBuf)
|
|
if err != nil {
|
|
t.Fatalf("dispatch create chat: %v", err)
|
|
}
|
|
invited, ok := enc.(*tg.MessagesInvitedUsers)
|
|
if !ok {
|
|
t.Fatalf("create response = %T, want messages.invitedUsers", enc)
|
|
}
|
|
updates, ok := invited.Updates.(*tg.Updates)
|
|
if !ok || len(updates.Chats) != 2 {
|
|
t.Fatalf("updates = %T %+v, want legacy chat + channel", invited.Updates, invited.Updates)
|
|
}
|
|
if legacy, ok := updates.Chats[0].(*tg.Chat); !ok || !legacy.Deactivated {
|
|
t.Fatalf("first chat = %#v, want migrated legacy chat", updates.Chats[0])
|
|
}
|
|
channel, ok := updates.Chats[1].(*tg.Channel)
|
|
if !ok || !channel.Megagroup || !channel.Creator {
|
|
t.Fatalf("second chat = %#v, want creator megagroup channel", updates.Chats[1])
|
|
}
|
|
if len(updates.Updates) != 4 {
|
|
t.Fatalf("updates len = %d, want create/invite service messages plus channel refreshes", len(updates.Updates))
|
|
}
|
|
participants, err := r.onChannelsGetParticipants(WithUserID(ctx, owner.ID), &tg.ChannelsGetParticipantsRequest{
|
|
Channel: &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
|
|
Filter: &tg.ChannelParticipantsRecent{},
|
|
Limit: 10,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("get participants: %v", err)
|
|
}
|
|
list, ok := participants.(*tg.ChannelsChannelParticipants)
|
|
if !ok || list.Count != 2 || len(list.Participants) != 2 || len(list.Users) != 2 {
|
|
t.Fatalf("participants = %T %+v, want owner + friend", participants, participants)
|
|
}
|
|
sessions.mu.Lock()
|
|
pushUserIDs := append([]int64(nil), sessions.pushUserIDs...)
|
|
sessions.mu.Unlock()
|
|
if len(pushUserIDs) != 1 || pushUserIDs[0] != friend.ID {
|
|
t.Fatalf("push user ids = %v, want only invited friend %d", pushUserIDs, friend.ID)
|
|
}
|
|
}
|
|
|
|
func TestMessagesCreateChatSessionWithoutClientInfoReturnsLegacyChat(t *testing.T) {
|
|
ctx := context.Background()
|
|
userStore := memory.NewUserStore()
|
|
owner, err := userStore.Create(ctx, domain.User{AccessHash: 51, Phone: "15550001051", FirstName: "Owner"})
|
|
if err != nil {
|
|
t.Fatalf("create owner: %v", err)
|
|
}
|
|
friend, err := userStore.Create(ctx, domain.User{AccessHash: 52, Phone: "15550001052", FirstName: "Friend"})
|
|
if err != nil {
|
|
t.Fatalf("create friend: %v", err)
|
|
}
|
|
channelStore := memory.NewChannelStore()
|
|
r := New(Config{}, Deps{
|
|
Users: appusers.NewService(userStore),
|
|
Channels: appchannels.NewService(channelStore),
|
|
Dialogs: appdialogs.NewService(memory.NewDialogStore(), channelStore),
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
|
|
invited, err := r.onMessagesCreateChat(WithSessionID(WithUserID(ctx, owner.ID), 99), &tg.MessagesCreateChatRequest{
|
|
Users: []tg.InputUserClass{&tg.InputUser{UserID: friend.ID, AccessHash: friend.AccessHash}},
|
|
Title: "Session Legacy Group",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create chat: %v", err)
|
|
}
|
|
updates, ok := invited.Updates.(*tg.Updates)
|
|
if !ok || len(updates.Chats) != 2 {
|
|
t.Fatalf("updates = %T %+v, want legacy chat + channel", invited.Updates, invited.Updates)
|
|
}
|
|
if legacy, ok := updates.Chats[0].(*tg.Chat); !ok || !legacy.Deactivated {
|
|
t.Fatalf("first chat = %#v, want migrated legacy chat", updates.Chats[0])
|
|
}
|
|
if channel, ok := updates.Chats[1].(*tg.Channel); !ok || !channel.Megagroup || !channel.Creator {
|
|
t.Fatalf("second chat = %#v, want creator megagroup channel", updates.Chats[1])
|
|
}
|
|
}
|