267 lines
9.8 KiB
Go
267 lines
9.8 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gotd/td/clock"
|
|
"github.com/gotd/td/tg"
|
|
"go.uber.org/zap/zaptest"
|
|
|
|
botsapp "telesrv/internal/app/bots"
|
|
appchannels "telesrv/internal/app/channels"
|
|
appusers "telesrv/internal/app/users"
|
|
"telesrv/internal/domain"
|
|
"telesrv/internal/store/memory"
|
|
)
|
|
|
|
func TestGroupBotRPCShape(t *testing.T) {
|
|
ctx := context.Background()
|
|
users := memory.NewUserStore()
|
|
botStore := memory.NewBotStore(users)
|
|
dialogs := memory.NewDialogStore()
|
|
messages := memory.NewMessageStore(dialogs)
|
|
bots := botsapp.NewService(users, botStore, messages)
|
|
channelStore := memory.NewChannelStore()
|
|
baseChannels := appchannels.NewService(channelStore, appchannels.WithBotProfileResolver(bots))
|
|
channels := &countingBotParticipantsChannelsService{Service: baseChannels}
|
|
|
|
owner, err := users.Create(ctx, domain.User{AccessHash: 6201, Phone: "15550006201", FirstName: "Owner"})
|
|
if err != nil {
|
|
t.Fatalf("create owner: %v", err)
|
|
}
|
|
friend, err := users.Create(ctx, domain.User{AccessHash: 6202, Phone: "15550006202", FirstName: "Friend"})
|
|
if err != nil {
|
|
t.Fatalf("create friend: %v", err)
|
|
}
|
|
bot, _, err := bots.CreateBot(ctx, owner.ID, "Group Bot", "group_shape_bot")
|
|
if err != nil {
|
|
t.Fatalf("create bot: %v", err)
|
|
}
|
|
if _, err := bots.SetJoinGroups(ctx, bot.ID, false); err != nil {
|
|
t.Fatalf("disable join groups: %v", err)
|
|
}
|
|
|
|
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{
|
|
Users: appusers.NewService(users),
|
|
Channels: channels,
|
|
Bots: bots,
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
ownerCtx := WithUserID(ctx, owner.ID)
|
|
created, err := r.onMessagesCreateChat(ownerCtx, &tg.MessagesCreateChatRequest{
|
|
Users: []tg.InputUserClass{&tg.InputUser{UserID: friend.ID, AccessHash: friend.AccessHash}},
|
|
Title: "Bot Group RPC",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create chat: %v", err)
|
|
}
|
|
channel := created.Updates.(*tg.Updates).Chats[0].(*tg.Channel)
|
|
|
|
blockedUser := r.tgUser(bot)
|
|
if !blockedUser.BotNochats {
|
|
t.Fatalf("tg user bot_nochats = false, want true before invite")
|
|
}
|
|
if _, err := r.onChannelsInviteToChannel(ownerCtx, &tg.ChannelsInviteToChannelRequest{
|
|
Channel: &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
|
|
Users: []tg.InputUserClass{&tg.InputUser{UserID: bot.ID, AccessHash: bot.AccessHash}},
|
|
}); err == nil || !strings.Contains(err.Error(), "BOT_GROUPS_BLOCKED") {
|
|
t.Fatalf("invite blocked bot err = %v, want BOT_GROUPS_BLOCKED", err)
|
|
}
|
|
|
|
if _, err := bots.SetJoinGroups(ctx, bot.ID, true); err != nil {
|
|
t.Fatalf("enable join groups: %v", err)
|
|
}
|
|
if _, err := bots.SetBotCommands(ctx, bot.ID, []domain.BotCommand{{Command: "status", Description: "Show status"}}); err != nil {
|
|
t.Fatalf("set bot commands: %v", err)
|
|
}
|
|
if _, err := r.onChannelsInviteToChannel(ownerCtx, &tg.ChannelsInviteToChannelRequest{
|
|
Channel: &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
|
|
Users: []tg.InputUserClass{&tg.InputUser{UserID: bot.ID, AccessHash: bot.AccessHash}},
|
|
}); err != nil {
|
|
t.Fatalf("invite allowed bot: %v", err)
|
|
}
|
|
|
|
participants, err := r.onChannelsGetParticipants(ownerCtx, &tg.ChannelsGetParticipantsRequest{
|
|
Channel: &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
|
|
Filter: &tg.ChannelParticipantsBots{},
|
|
Limit: 20,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("get bot participants: %v", err)
|
|
}
|
|
list := participants.(*tg.ChannelsChannelParticipants)
|
|
if list.Count != 1 || len(list.Participants) != 1 {
|
|
t.Fatalf("bot participants = count %d len %d, want one", list.Count, len(list.Participants))
|
|
}
|
|
if len(list.Users) != 1 {
|
|
t.Fatalf("bot participants users = %d, want one", len(list.Users))
|
|
}
|
|
listBot := list.Users[0].(*tg.User)
|
|
if !listBot.Bot || listBot.ID != bot.ID || listBot.BotNochats {
|
|
t.Fatalf("participants user = %+v, want allowed bot", listBot)
|
|
}
|
|
|
|
channels.botParticipantCalls = 0
|
|
full, err := r.onChannelsGetFullChannel(ownerCtx, &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash})
|
|
if err != nil {
|
|
t.Fatalf("get full channel: %v", err)
|
|
}
|
|
channelFull := full.FullChat.(*tg.ChannelFull)
|
|
if len(channelFull.BotInfo) != 1 || channelFull.BotInfo[0].UserID != bot.ID {
|
|
t.Fatalf("channel full bot_info = %+v, want bot %d", channelFull.BotInfo, bot.ID)
|
|
}
|
|
if len(channelFull.BotInfo[0].Commands) != 1 || channelFull.BotInfo[0].Commands[0].Command != "status" {
|
|
t.Fatalf("channel full bot commands = %+v, want status", channelFull.BotInfo[0].Commands)
|
|
}
|
|
foundUser := false
|
|
for _, u := range full.Users {
|
|
if got, ok := u.(*tg.User); ok && got.ID == bot.ID && got.Bot {
|
|
foundUser = true
|
|
}
|
|
}
|
|
if !foundUser {
|
|
t.Fatalf("full channel users = %+v, want bot user", full.Users)
|
|
}
|
|
if _, err := r.onChannelsGetFullChannel(ownerCtx, &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash}); err != nil {
|
|
t.Fatalf("get full channel cached: %v", err)
|
|
}
|
|
if channels.botParticipantCalls != 1 {
|
|
t.Fatalf("full channel bot participants calls = %d, want 1", channels.botParticipantCalls)
|
|
}
|
|
}
|
|
|
|
func TestFullChannelBotInfoCacheCachesEmptyResult(t *testing.T) {
|
|
ctx := context.Background()
|
|
users := memory.NewUserStore()
|
|
botStore := memory.NewBotStore(users)
|
|
dialogs := memory.NewDialogStore()
|
|
messages := memory.NewMessageStore(dialogs)
|
|
bots := botsapp.NewService(users, botStore, messages)
|
|
channelStore := memory.NewChannelStore()
|
|
baseChannels := appchannels.NewService(channelStore, appchannels.WithBotProfileResolver(bots))
|
|
channels := &countingBotParticipantsChannelsService{Service: baseChannels}
|
|
|
|
owner, err := users.Create(ctx, domain.User{AccessHash: 6221, Phone: "15550006221", FirstName: "Owner"})
|
|
if err != nil {
|
|
t.Fatalf("create owner: %v", err)
|
|
}
|
|
friend, err := users.Create(ctx, domain.User{AccessHash: 6222, Phone: "15550006222", FirstName: "Friend"})
|
|
if err != nil {
|
|
t.Fatalf("create friend: %v", err)
|
|
}
|
|
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{
|
|
Users: appusers.NewService(users),
|
|
Channels: channels,
|
|
Bots: bots,
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
ownerCtx := WithUserID(ctx, owner.ID)
|
|
created, err := r.onMessagesCreateChat(ownerCtx, &tg.MessagesCreateChatRequest{
|
|
Users: []tg.InputUserClass{&tg.InputUser{UserID: friend.ID, AccessHash: friend.AccessHash}},
|
|
Title: "Empty Bot Group",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create chat: %v", err)
|
|
}
|
|
channel := created.Updates.(*tg.Updates).Chats[0].(*tg.Channel)
|
|
|
|
channels.botParticipantCalls = 0
|
|
first, err := r.onChannelsGetFullChannel(ownerCtx, &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash})
|
|
if err != nil {
|
|
t.Fatalf("get first full channel: %v", err)
|
|
}
|
|
if got := len(first.FullChat.(*tg.ChannelFull).BotInfo); got != 0 {
|
|
t.Fatalf("first full channel bot_info len = %d, want 0", got)
|
|
}
|
|
second, err := r.onChannelsGetFullChannel(ownerCtx, &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash})
|
|
if err != nil {
|
|
t.Fatalf("get second full channel: %v", err)
|
|
}
|
|
if got := len(second.FullChat.(*tg.ChannelFull).BotInfo); got != 0 {
|
|
t.Fatalf("second full channel bot_info len = %d, want 0", got)
|
|
}
|
|
if channels.botParticipantCalls != 1 {
|
|
t.Fatalf("empty full channel bot participants calls = %d, want 1", channels.botParticipantCalls)
|
|
}
|
|
}
|
|
|
|
func TestTGUsersForIDsUsesBatchBotProfiles(t *testing.T) {
|
|
ctx := context.Background()
|
|
users := memory.NewUserStore()
|
|
botStore := memory.NewBotStore(users)
|
|
dialogs := memory.NewDialogStore()
|
|
messages := memory.NewMessageStore(dialogs)
|
|
baseBots := botsapp.NewService(users, botStore, messages)
|
|
bots := &countingBatchBotsService{Service: baseBots}
|
|
|
|
owner, err := users.Create(ctx, domain.User{AccessHash: 6211, Phone: "15550006211", FirstName: "Owner"})
|
|
if err != nil {
|
|
t.Fatalf("create owner: %v", err)
|
|
}
|
|
botA, _, err := baseBots.CreateBot(ctx, owner.ID, "Batch Bot A", "batcha_bot")
|
|
if err != nil {
|
|
t.Fatalf("create bot A: %v", err)
|
|
}
|
|
botB, _, err := baseBots.CreateBot(ctx, owner.ID, "Batch Bot B", "batchb_bot")
|
|
if err != nil {
|
|
t.Fatalf("create bot B: %v", err)
|
|
}
|
|
if _, err := baseBots.SetJoinGroups(ctx, botA.ID, false); err != nil {
|
|
t.Fatalf("disable bot A groups: %v", err)
|
|
}
|
|
if _, err := baseBots.SetInlinePlaceholder(ctx, botB.ID, "Search B"); err != nil {
|
|
t.Fatalf("set bot B inline placeholder: %v", err)
|
|
}
|
|
|
|
r := New(Config{}, Deps{
|
|
Users: appusers.NewService(users),
|
|
Bots: bots,
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
got := r.tgUsersForIDs(ctx, owner.ID, []int64{botA.ID, botB.ID})
|
|
if bots.batchCalls != 1 {
|
|
t.Fatalf("BotInfos calls = %d, want 1", bots.batchCalls)
|
|
}
|
|
if bots.singleCalls != 0 {
|
|
t.Fatalf("BotInfo calls = %d, want 0", bots.singleCalls)
|
|
}
|
|
byID := make(map[int64]*tg.User)
|
|
for _, item := range got {
|
|
if u, ok := item.(*tg.User); ok {
|
|
byID[u.ID] = u
|
|
}
|
|
}
|
|
if u := byID[botA.ID]; u == nil || !u.BotNochats {
|
|
t.Fatalf("bot A user = %+v, want bot_nochats", u)
|
|
}
|
|
if u := byID[botB.ID]; u == nil || u.BotInlinePlaceholder != "Search B" {
|
|
t.Fatalf("bot B user = %+v, want inline placeholder", u)
|
|
}
|
|
}
|
|
|
|
type countingBatchBotsService struct {
|
|
*botsapp.Service
|
|
singleCalls int
|
|
batchCalls int
|
|
}
|
|
|
|
func (s *countingBatchBotsService) BotInfo(ctx context.Context, botUserID int64) (domain.BotProfile, bool, error) {
|
|
s.singleCalls++
|
|
return s.Service.BotInfo(ctx, botUserID)
|
|
}
|
|
|
|
func (s *countingBatchBotsService) BotInfos(ctx context.Context, botUserIDs []int64) (map[int64]domain.BotProfile, error) {
|
|
s.batchCalls++
|
|
return s.Service.BotInfos(ctx, botUserIDs)
|
|
}
|
|
|
|
type countingBotParticipantsChannelsService struct {
|
|
*appchannels.Service
|
|
botParticipantCalls int
|
|
}
|
|
|
|
func (s *countingBotParticipantsChannelsService) GetParticipants(ctx context.Context, userID, channelID int64, filter domain.ChannelParticipantsFilter, offset, limit int) (domain.ChannelParticipantList, error) {
|
|
if filter.Kind == domain.ChannelParticipantsBots {
|
|
s.botParticipantCalls++
|
|
}
|
|
return s.Service.GetParticipants(ctx, userID, channelID, filter, offset, limit)
|
|
}
|