340 lines
13 KiB
Go
340 lines
13 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gotd/td/bin"
|
|
"github.com/gotd/td/clock"
|
|
"github.com/gotd/td/tg"
|
|
"go.uber.org/zap/zaptest"
|
|
|
|
appaccount "telesrv/internal/app/account"
|
|
botsapp "telesrv/internal/app/bots"
|
|
appmessages "telesrv/internal/app/messages"
|
|
apppolls "telesrv/internal/app/polls"
|
|
appusers "telesrv/internal/app/users"
|
|
"telesrv/internal/domain"
|
|
"telesrv/internal/store/memory"
|
|
publicweb "telesrv/internal/web"
|
|
)
|
|
|
|
func newStickerLinkHandler(t *testing.T, files publicweb.StickerSetResolver) http.Handler {
|
|
t.Helper()
|
|
h, err := publicweb.NewHandler(publicweb.Config{StickerSets: files, PublicBaseURL: "https://telesrv.net"})
|
|
if err != nil {
|
|
t.Fatalf("new public Web handler: %v", err)
|
|
}
|
|
return h
|
|
}
|
|
|
|
func TestCustomStickerPackLinkInstallAndSendSmoke(t *testing.T) {
|
|
ctx := context.Background()
|
|
userStore := memory.NewUserStore()
|
|
alice, _ := userStore.Create(ctx, domain.User{AccessHash: 11, Phone: "15550009001", FirstName: "Alice"})
|
|
bob, _ := userStore.Create(ctx, domain.User{AccessHash: 12, Phone: "15550009002", FirstName: "Bob"})
|
|
dialogStore := memory.NewDialogStore()
|
|
messageStore := memory.NewMessageStore(dialogStore)
|
|
pollStore := memory.NewPollStore()
|
|
messageStore.AttachPollStore(pollStore)
|
|
passwordStore := memory.NewPasswordStore()
|
|
files := &fakeFiles{
|
|
docs: map[int64]domain.Document{
|
|
101: {
|
|
ID: 101,
|
|
AccessHash: 1101,
|
|
DCID: 2,
|
|
MimeType: "image/webp",
|
|
Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrSticker}},
|
|
},
|
|
},
|
|
photos: map[int64]domain.Photo{},
|
|
sets: map[domain.StickerSetKind][]domain.StickerSet{},
|
|
}
|
|
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{
|
|
Account: appaccount.NewService(passwordStore, appaccount.WithUserStickerSets(passwordStore)),
|
|
Users: appusers.NewService(userStore),
|
|
Messages: appmessages.NewService(messageStore, dialogStore),
|
|
Files: files,
|
|
Polls: apppolls.NewService(pollStore),
|
|
Sessions: &captureSessions{},
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
|
|
created, err := r.onStickersCreateStickerSet(WithUserID(ctx, alice.ID), &tg.StickersCreateStickerSetRequest{
|
|
UserID: &tg.InputUserSelf{},
|
|
Title: "Alice Fresh Pack",
|
|
ShortName: "alice_fresh_pack",
|
|
Stickers: []tg.InputStickerSetItem{{
|
|
Document: &tg.InputDocument{ID: 101, AccessHash: 1101},
|
|
Emoji: "🙂",
|
|
Keywords: "fresh",
|
|
}},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create sticker set: %v", err)
|
|
}
|
|
createdFull, ok := created.(*tg.MessagesStickerSet)
|
|
if !ok {
|
|
t.Fatalf("created = %T, want *tg.MessagesStickerSet", created)
|
|
}
|
|
|
|
web := newStickerLinkHandler(t, files)
|
|
rr := httptest.NewRecorder()
|
|
web.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/addstickers/alice_fresh_pack", nil))
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("sticker link status = %d body=%q, want 200", rr.Code, rr.Body.String())
|
|
}
|
|
body := rr.Body.String()
|
|
for _, want := range []string{"https://telesrv.net/addstickers/alice_fresh_pack", "telesrv://addstickers?set=alice_fresh_pack"} {
|
|
if !strings.Contains(body, want) {
|
|
t.Fatalf("sticker link body missing %q:\n%s", want, body)
|
|
}
|
|
}
|
|
if strings.Contains(body, `window.location.href = "tg://`) {
|
|
t.Fatalf("sticker link must auto-open telesrv://, not tg://:\n%s", body)
|
|
}
|
|
|
|
preview, err := r.onMessagesGetStickerSet(WithUserID(ctx, bob.ID), &tg.MessagesGetStickerSetRequest{
|
|
Stickerset: &tg.InputStickerSetShortName{ShortName: "alice_fresh_pack"},
|
|
Hash: 0,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("bob preview sticker set: %v", err)
|
|
}
|
|
previewFull, ok := preview.(*tg.MessagesStickerSet)
|
|
if !ok || previewFull.Set.ID != createdFull.Set.ID || len(previewFull.Documents) != 1 {
|
|
t.Fatalf("preview = %T %+v, want created set with one document", preview, preview)
|
|
}
|
|
|
|
if _, err := r.onMessagesInstallStickerSet(WithUserID(ctx, bob.ID), &tg.MessagesInstallStickerSetRequest{
|
|
Stickerset: &tg.InputStickerSetShortName{ShortName: "alice_fresh_pack"},
|
|
}); err != nil {
|
|
t.Fatalf("bob install sticker set: %v", err)
|
|
}
|
|
if got := installedStickerSetIDs(t, passwordStore, ctx, bob.ID, domain.StickerSetKindStickers, nil); len(got) != 1 || got[0] != createdFull.Set.ID {
|
|
t.Fatalf("bob installed sets = %v, want [%d]", got, createdFull.Set.ID)
|
|
}
|
|
|
|
if _, err := r.onMessagesSendMedia(WithUserID(ctx, bob.ID), &tg.MessagesSendMediaRequest{
|
|
Peer: &tg.InputPeerUser{UserID: alice.ID, AccessHash: alice.AccessHash},
|
|
Media: &tg.InputMediaDocument{ID: &tg.InputDocument{ID: 101, AccessHash: 1101}},
|
|
RandomID: 7001,
|
|
}); err != nil {
|
|
t.Fatalf("bob send sticker: %v", err)
|
|
}
|
|
|
|
historyReq := &tg.MessagesGetHistoryRequest{
|
|
Peer: &tg.InputPeerUser{UserID: bob.ID, AccessHash: bob.AccessHash},
|
|
Limit: 10,
|
|
}
|
|
var raw bin.Buffer
|
|
if err := historyReq.Encode(&raw); err != nil {
|
|
t.Fatalf("encode history request: %v", err)
|
|
}
|
|
enc, err := r.Dispatch(WithUserID(ctx, alice.ID), [8]byte{}, 0, &raw)
|
|
if err != nil {
|
|
t.Fatalf("alice get history: %v", err)
|
|
}
|
|
box, ok := enc.(*tg.MessagesMessagesBox)
|
|
if !ok {
|
|
t.Fatalf("history response = %T, want *tg.MessagesMessagesBox", enc)
|
|
}
|
|
messages, ok := box.Messages.(*tg.MessagesMessages)
|
|
if !ok {
|
|
t.Fatalf("history payload = %T, want *tg.MessagesMessages", box.Messages)
|
|
}
|
|
if len(messages.Messages) != 1 {
|
|
t.Fatalf("history messages = %d, want 1", len(messages.Messages))
|
|
}
|
|
msg, ok := messages.Messages[0].(*tg.Message)
|
|
if !ok {
|
|
t.Fatalf("history message = %T, want *tg.Message", messages.Messages[0])
|
|
}
|
|
media, ok := msg.Media.(*tg.MessageMediaDocument)
|
|
if !ok {
|
|
t.Fatalf("history media = %T, want *tg.MessageMediaDocument", msg.Media)
|
|
}
|
|
if got := tgDocumentID(media.Document); got != 101 {
|
|
t.Fatalf("history document id = %d, want 101", got)
|
|
}
|
|
}
|
|
|
|
func TestStickersBotCreatePackLinkInstallIsolationSmoke(t *testing.T) {
|
|
ctx := context.Background()
|
|
userStore := memory.NewUserStore()
|
|
alice, _ := userStore.Create(ctx, domain.User{AccessHash: 21, Phone: "15550009101", FirstName: "Alice"})
|
|
bob, _ := userStore.Create(ctx, domain.User{AccessHash: 22, Phone: "15550009102", FirstName: "Bob"})
|
|
dialogStore := memory.NewDialogStore()
|
|
messageStore := memory.NewMessageStore(dialogStore)
|
|
pollStore := memory.NewPollStore()
|
|
messageStore.AttachPollStore(pollStore)
|
|
passwordStore := memory.NewPasswordStore()
|
|
accountService := appaccount.NewService(passwordStore, appaccount.WithUserStickerSets(passwordStore))
|
|
botStore := memory.NewBotStore(userStore)
|
|
files := &fakeFiles{
|
|
docs: map[int64]domain.Document{
|
|
401: {
|
|
ID: 401,
|
|
AccessHash: 4401,
|
|
DCID: 2,
|
|
MimeType: "image/webp",
|
|
Size: 4096,
|
|
Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrFilename, FileName: "alice.webp"}},
|
|
},
|
|
},
|
|
photos: map[int64]domain.Photo{},
|
|
sets: map[domain.StickerSetKind][]domain.StickerSet{},
|
|
}
|
|
botsService := botsapp.NewService(userStore, botStore, messageStore,
|
|
botsapp.WithStickerSetCreator(files),
|
|
botsapp.WithUserStickerSets(accountService))
|
|
messagesService := appmessages.NewService(messageStore, dialogStore,
|
|
appmessages.WithBotResponder(botsService))
|
|
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{
|
|
Account: accountService,
|
|
Users: appusers.NewService(userStore),
|
|
Messages: messagesService,
|
|
Files: files,
|
|
Polls: apppolls.NewService(pollStore),
|
|
Sessions: &captureSessions{},
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
botsService.SetRouterHooks(r)
|
|
botsService.SetTextDraftPusher(r)
|
|
|
|
sendStickersBotText(t, r, alice, "/newpack", 9101)
|
|
waitForStickersReply(t, messageStore, alice.ID, "sticker pack")
|
|
sendStickersBotText(t, r, alice, "Alice Bot Pack", 9102)
|
|
waitForStickersReply(t, messageStore, alice.ID, "Lottie JSON")
|
|
sendStickersBotDocument(t, r, alice, 401, 4401, 9103)
|
|
waitForStickersReply(t, messageStore, alice.ID, "emoji")
|
|
sendStickersBotText(t, r, alice, "🙂", 9104)
|
|
waitForStickersReply(t, messageStore, alice.ID, "Added")
|
|
sendStickersBotText(t, r, alice, "/publish", 9105)
|
|
waitForStickersReply(t, messageStore, alice.ID, "short name")
|
|
sendStickersBotText(t, r, alice, "alice_bot_pack", 9106)
|
|
waitForStickersReply(t, messageStore, alice.ID, "https://telesrv.net/addstickers/alice_bot_pack")
|
|
|
|
created := files.sets[domain.StickerSetKindStickers]
|
|
if len(created) != 1 || created[0].ShortName != "alice_bot_pack" || created[0].CreatorUserID != alice.ID {
|
|
t.Fatalf("created sets = %+v, want Alice alice_bot_pack", created)
|
|
}
|
|
setID := created[0].ID
|
|
if got := installedStickerSetIDs(t, passwordStore, ctx, alice.ID, domain.StickerSetKindStickers, nil); len(got) != 1 || got[0] != setID {
|
|
t.Fatalf("alice installed sets = %v, want [%d]", got, setID)
|
|
}
|
|
if got := installedStickerSetIDs(t, passwordStore, ctx, bob.ID, domain.StickerSetKindStickers, nil); len(got) != 0 {
|
|
t.Fatalf("bob installed sets before link = %v, want empty", got)
|
|
}
|
|
if got := allStickerSetIDs(t, r, WithUserID(ctx, bob.ID), domain.StickerSetKindStickers); len(got) != 0 {
|
|
t.Fatalf("bob getAllStickers before install = %v, want empty", got)
|
|
}
|
|
|
|
web := newStickerLinkHandler(t, files)
|
|
rr := httptest.NewRecorder()
|
|
web.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/addstickers/alice_bot_pack", nil))
|
|
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "https://telesrv.net/addstickers/alice_bot_pack") {
|
|
t.Fatalf("sticker bot link response = %d %q", rr.Code, rr.Body.String())
|
|
}
|
|
preview, err := r.onMessagesGetStickerSet(WithUserID(ctx, bob.ID), &tg.MessagesGetStickerSetRequest{
|
|
Stickerset: &tg.InputStickerSetShortName{ShortName: "alice_bot_pack"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("bob preview bot-created sticker set: %v", err)
|
|
}
|
|
previewFull, ok := preview.(*tg.MessagesStickerSet)
|
|
if !ok || previewFull.Set.ID != setID || previewFull.Set.InstalledDate != 0 {
|
|
t.Fatalf("bob preview = %T %+v, want uninstalled created set", preview, preview)
|
|
}
|
|
if _, err := r.onMessagesInstallStickerSet(WithUserID(ctx, bob.ID), &tg.MessagesInstallStickerSetRequest{
|
|
Stickerset: &tg.InputStickerSetShortName{ShortName: "alice_bot_pack"},
|
|
}); err != nil {
|
|
t.Fatalf("bob install bot-created sticker set: %v", err)
|
|
}
|
|
if got := installedStickerSetIDs(t, passwordStore, ctx, bob.ID, domain.StickerSetKindStickers, nil); len(got) != 1 || got[0] != setID {
|
|
t.Fatalf("bob installed sets after link = %v, want [%d]", got, setID)
|
|
}
|
|
if got := allStickerSetIDs(t, r, WithUserID(ctx, alice.ID), domain.StickerSetKindStickers); len(got) != 1 || got[0] != setID {
|
|
t.Fatalf("alice getAllStickers = %v, want [%d]", got, setID)
|
|
}
|
|
if got := allStickerSetIDs(t, r, WithUserID(ctx, bob.ID), domain.StickerSetKindStickers); len(got) != 1 || got[0] != setID {
|
|
t.Fatalf("bob getAllStickers after install = %v, want [%d]", got, setID)
|
|
}
|
|
if got := allStickerSetIDs(t, r, WithUserID(ctx, bob.ID), domain.StickerSetKindEmoji); len(got) != 0 {
|
|
t.Fatalf("bob getEmojiStickers = %v, want empty", got)
|
|
}
|
|
}
|
|
|
|
func sendStickersBotText(t *testing.T, r *Router, user domain.User, text string, randomID int64) {
|
|
t.Helper()
|
|
if _, err := r.onMessagesSendMessage(WithUserID(context.Background(), user.ID), &tg.MessagesSendMessageRequest{
|
|
Peer: &tg.InputPeerUser{UserID: domain.StickersBotUserID, AccessHash: domain.StickersBotAccessHash},
|
|
Message: text,
|
|
RandomID: randomID,
|
|
}); err != nil {
|
|
t.Fatalf("send @Stickers text %q: %v", text, err)
|
|
}
|
|
}
|
|
|
|
func sendStickersBotDocument(t *testing.T, r *Router, user domain.User, docID, accessHash, randomID int64) {
|
|
t.Helper()
|
|
if _, err := r.onMessagesSendMedia(WithUserID(context.Background(), user.ID), &tg.MessagesSendMediaRequest{
|
|
Peer: &tg.InputPeerUser{UserID: domain.StickersBotUserID, AccessHash: domain.StickersBotAccessHash},
|
|
Media: &tg.InputMediaDocument{ID: &tg.InputDocument{ID: docID, AccessHash: accessHash}},
|
|
RandomID: randomID,
|
|
}); err != nil {
|
|
t.Fatalf("send @Stickers document %d: %v", docID, err)
|
|
}
|
|
}
|
|
|
|
func waitForStickersReply(t *testing.T, messages *memory.MessageStore, userID int64, want string) string {
|
|
t.Helper()
|
|
deadline := time.Now().Add(time.Second)
|
|
for {
|
|
list, err := messages.ListByUser(context.Background(), userID, domain.MessageFilter{
|
|
HasPeer: true,
|
|
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: domain.StickersBotUserID},
|
|
Limit: 100,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("list @Stickers history: %v", err)
|
|
}
|
|
for _, msg := range list.Messages {
|
|
if msg.From.ID == domain.StickersBotUserID && strings.Contains(msg.Body, want) {
|
|
return msg.Body
|
|
}
|
|
}
|
|
if time.Now().After(deadline) {
|
|
t.Fatalf("no @Stickers reply containing %q; history=%+v", want, list.Messages)
|
|
}
|
|
time.Sleep(5 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
func allStickerSetIDs(t *testing.T, r *Router, ctx context.Context, kind domain.StickerSetKind) []int64 {
|
|
t.Helper()
|
|
var (
|
|
out tg.MessagesAllStickersClass
|
|
err error
|
|
)
|
|
if kind == domain.StickerSetKindEmoji {
|
|
out, err = r.onMessagesGetEmojiStickers(ctx, 0)
|
|
} else {
|
|
out, err = r.onMessagesGetAllStickers(ctx, 0)
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("get sticker sets for kind %s: %v", kind, err)
|
|
}
|
|
full, ok := out.(*tg.MessagesAllStickers)
|
|
if !ok {
|
|
t.Fatalf("get sticker sets for kind %s = %T, want *tg.MessagesAllStickers", kind, out)
|
|
}
|
|
ids := make([]int64, 0, len(full.Sets))
|
|
for _, set := range full.Sets {
|
|
ids = append(ids, set.ID)
|
|
}
|
|
return ids
|
|
}
|