package bots import ( "context" "strings" "testing" "telesrv/internal/domain" "telesrv/internal/store/memory" ) func sendTextToStickers(t *testing.T, svc *Service, messages *memory.MessageStore, owner domain.User, text string) string { t.Helper() return sendMessageToStickers(t, svc, messages, owner, domain.Message{Body: text}) } func sendDocumentToStickers(t *testing.T, svc *Service, messages *memory.MessageStore, owner domain.User, doc domain.Document) string { t.Helper() return sendMessageToStickers(t, svc, messages, owner, domain.Message{ Media: &domain.MessageMedia{ Kind: domain.MessageMediaKindDocument, Document: &doc, }, }) } func sendMessageToStickers(t *testing.T, svc *Service, messages *memory.MessageStore, owner domain.User, msg domain.Message) string { t.Helper() msg.From = domain.Peer{Type: domain.PeerTypeUser, ID: owner.ID} msg.Peer = domain.Peer{Type: domain.PeerTypeUser, ID: domain.StickersBotUserID} svc.respondAsStickers(owner.ID, msg) return latestStickersReply(t, messages, owner.ID).Body } func latestStickersReply(t *testing.T, messages *memory.MessageStore, userID int64) domain.Message { t.Helper() 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) } var latest domain.Message for _, msg := range list.Messages { if msg.From.ID == domain.StickersBotUserID && msg.ID > latest.ID { latest = msg } } if latest.ID == 0 { t.Fatal("no Stickers reply") } return latest } func newStickersBotTestService(t *testing.T) (*Service, *memory.UserStore, *memory.BotStore, *memory.MessageStore, *stickersBotFakeCreator, *stickersBotFakeInstaller) { t.Helper() users := memory.NewUserStore() bots := memory.NewBotStore(users) dialogs := memory.NewDialogStore() messages := memory.NewMessageStore(dialogs) creator := &stickersBotFakeCreator{} installer := &stickersBotFakeInstaller{} svc := NewService(users, bots, messages, WithStickerSetCreator(creator), WithUserStickerSets(installer)) return svc, users, bots, messages, creator, installer } func stickerBotTestDocument(id, accessHash int64, attr domain.DocumentAttributeKind) domain.Document { return domain.Document{ ID: id, AccessHash: accessHash, DCID: 2, MimeType: "image/webp", Attributes: []domain.DocumentAttribute{{Kind: attr}}, } } func stickerBotUploadDocument(id, accessHash int64, mimeType, fileName string) domain.Document { return domain.Document{ ID: id, AccessHash: accessHash, DCID: 2, MimeType: mimeType, Size: 4096, Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrFilename, FileName: fileName}}, } } func stickerBotSetDocument(id, accessHash, setID, setAccessHash int64) domain.Document { return domain.Document{ ID: id, AccessHash: accessHash, DCID: 2, MimeType: "image/webp", Attributes: []domain.DocumentAttribute{{ Kind: domain.DocAttrSticker, StickerSetID: setID, StickerSetAccessHash: setAccessHash, }}, } } func stickerBotSetCustomEmojiDocument(id, accessHash, setID, setAccessHash int64) domain.Document { return domain.Document{ ID: id, AccessHash: accessHash, DCID: 2, MimeType: "application/x-tgsticker", Attributes: []domain.DocumentAttribute{{ Kind: domain.DocAttrCustomEmoji, StickerSetID: setID, StickerSetAccessHash: setAccessHash, }}, } } func TestStickersBotSystemSeedStartAndCancel(t *testing.T) { svc, users, bots, messages, _, _ := newStickersBotTestService(t) owner := newOwner(t, users, "+3000") ctx := context.Background() if !svc.HandlesBot(domain.BotFatherUserID) || !svc.HandlesBot(domain.StickersBotUserID) { t.Fatal("service should handle BotFather and Stickers") } u, found, err := users.ByUsername(ctx, "Stickers") if err != nil || !found { t.Fatalf("@Stickers user not seeded: found=%v err=%v", found, err) } if u.ID != domain.StickersBotUserID || !u.Bot || u.BotInfoVersion < 1 { t.Fatalf("@Stickers user = %+v, want seeded bot", u) } profile, found, err := bots.GetBot(ctx, domain.StickersBotUserID) if err != nil || !found { t.Fatalf("@Stickers profile not seeded: found=%v err=%v", found, err) } if !botCommandExists(profile.Commands, "newpack") || !botCommandExists(profile.Commands, "newemoji") || !botCommandExists(profile.Commands, "publish") || !botCommandExists(profile.Commands, "addsticker") || !botCommandExists(profile.Commands, "delsticker") { t.Fatalf("@Stickers commands = %+v, want newpack/newemoji/publish/addsticker/delsticker", profile.Commands) } if reply := sendTextToStickers(t, svc, messages, owner, "/start"); !strings.Contains(reply, "/newpack") || !strings.Contains(reply, "/newemoji") || !strings.Contains(reply, "/addsticker") { t.Fatalf("/start reply = %q, want help text", reply) } startReply := latestStickersReply(t, messages, owner.ID) assertReplyEntityText(t, startReply, domain.MessageEntityBotCommand, "/newpack") assertReplyEntityText(t, startReply, domain.MessageEntityBotCommand, "/newemoji") assertReplyEntityText(t, startReply, domain.MessageEntityBotCommand, "/addsticker") sendTextToStickers(t, svc, messages, owner, "/newpack") if reply := sendTextToStickers(t, svc, messages, owner, "/cancel"); !strings.Contains(reply, "Cancelled") { t.Fatalf("/cancel reply = %q, want cancelled", reply) } if _, found, _ := bots.GetBotChatState(ctx, domain.StickersBotUserID, owner.ID); found { t.Fatal("stickers bot state still present after /cancel") } } func TestStickersBotNewPackNoMaterialAndInvalidEmoji(t *testing.T) { svc, users, _, messages, creator, _ := newStickersBotTestService(t) owner := newOwner(t, users, "+3001") if reply := sendTextToStickers(t, svc, messages, owner, "/newpack"); !strings.Contains(reply, "sticker pack") { t.Fatalf("/newpack reply = %q, want sticker pack prompt", reply) } if reply := sendTextToStickers(t, svc, messages, owner, "My Pack"); !strings.Contains(reply, "Lottie JSON") { t.Fatalf("title reply = %q, want document prompt", reply) } if reply := sendTextToStickers(t, svc, messages, owner, "/publish"); !strings.Contains(reply, "Add at least one") { t.Fatalf("empty publish reply = %q, want no material notice", reply) } if reply := sendTextToStickers(t, svc, messages, owner, "not a document"); !strings.Contains(reply, "WebM/MP4 must include video metadata") { t.Fatalf("text in document step reply = %q, want material prompt", reply) } if reply := sendDocumentToStickers(t, svc, messages, owner, stickerBotTestDocument(101, 1101, domain.DocAttrSticker)); !strings.Contains(reply, "emoji") { t.Fatalf("document reply = %q, want emoji prompt", reply) } if reply := sendTextToStickers(t, svc, messages, owner, "notemoji"); !strings.Contains(reply, "valid emoji") { t.Fatalf("invalid emoji reply = %q, want emoji validation", reply) } if len(creator.created) != 0 { t.Fatalf("creator called before valid publish: %+v", creator.created) } } func TestStickersBotPublishStickerPack(t *testing.T) { svc, users, bots, messages, creator, installer := newStickersBotTestService(t) hooks := &stickersBotHookRecorder{} svc.SetRouterHooks(hooks) owner := newOwner(t, users, "+3002") sendTextToStickers(t, svc, messages, owner, "/newpack") sendTextToStickers(t, svc, messages, owner, "Fresh Pack") sendDocumentToStickers(t, svc, messages, owner, stickerBotTestDocument(201, 2201, domain.DocAttrSticker)) sendTextToStickers(t, svc, messages, owner, "🙂") sendTextToStickers(t, svc, messages, owner, "/publish") reply := sendTextToStickers(t, svc, messages, owner, "fresh_pack") if !strings.Contains(reply, "https://telesrv.net/addstickers/fresh_pack") { t.Fatalf("publish reply = %q, want addstickers link", reply) } publishReply := latestStickersReply(t, messages, owner.ID) assertReplyEntityText(t, publishReply, domain.MessageEntityURL, "https://telesrv.net/addstickers/fresh_pack") if len(creator.created) != 1 { t.Fatalf("created requests = %d, want 1", len(creator.created)) } req := creator.created[0] if req.CreatorUserID != owner.ID || req.Title != "Fresh Pack" || req.ShortName != "fresh_pack" || req.Kind != domain.StickerSetKindStickers { t.Fatalf("create request = %+v, want owner/title/short/kind", req) } if len(req.Items) != 1 || req.Items[0].DocumentID != 201 || req.Items[0].DocumentAccessHash != 2201 || req.Items[0].Emoji != "🙂" { t.Fatalf("create items = %+v, want doc+emoji", req.Items) } if len(installer.installs) != 1 || installer.installs[0].userID != owner.ID || installer.installs[0].setID != creator.sets[0].ID { t.Fatalf("installs = %+v, want creator install", installer.installs) } if hooks.userID != owner.ID || hooks.kind != domain.StickerSetKindStickers { t.Fatalf("sticker update hook = user %d kind %q, want creator stickers", hooks.userID, hooks.kind) } if _, found, _ := bots.GetBotChatState(context.Background(), domain.StickersBotUserID, owner.ID); found { t.Fatal("stickers bot state still present after publish") } } func TestStickersBotPublishUploadedTGS(t *testing.T) { svc, users, _, messages, creator, installer := newStickersBotTestService(t) owner := newOwner(t, users, "+3004") sendTextToStickers(t, svc, messages, owner, "/newemoji") sendTextToStickers(t, svc, messages, owner, "Local Emoji") reply := sendDocumentToStickers(t, svc, messages, owner, stickerBotUploadDocument(401, 4401, "application/octet-stream", "wave.tgs")) if !strings.Contains(reply, "emoji") { t.Fatalf("uploaded tgs reply = %q, want emoji prompt", reply) } sendTextToStickers(t, svc, messages, owner, "👋") sendTextToStickers(t, svc, messages, owner, "/publish") reply = sendTextToStickers(t, svc, messages, owner, "local_emoji") if !strings.Contains(reply, "https://telesrv.net/addemoji/local_emoji") { t.Fatalf("publish uploaded tgs reply = %q, want addemoji link", reply) } if len(creator.created) != 1 { t.Fatalf("created requests = %d, want 1", len(creator.created)) } req := creator.created[0] if req.Kind != domain.StickerSetKindEmoji || len(req.Items) != 1 || req.Items[0].DocumentID != 401 || req.Items[0].DocumentAccessHash != 4401 { t.Fatalf("create request = %+v, want uploaded tgs item in emoji pack", req) } if len(installer.installs) != 1 || installer.installs[0].kind != domain.StickerSetKindEmoji { t.Fatalf("installs = %+v, want emoji install", installer.installs) } } func TestStickersBotPublishUploadedLottieJSON(t *testing.T) { svc, users, _, messages, creator, installer := newStickersBotTestService(t) owner := newOwner(t, users, "+3005") sendTextToStickers(t, svc, messages, owner, "/newpack") sendTextToStickers(t, svc, messages, owner, "Lottie Pack") reply := sendDocumentToStickers(t, svc, messages, owner, stickerBotUploadDocument(402, 4402, "application/json", "wave.json")) if !strings.Contains(reply, "emoji") { t.Fatalf("uploaded lottie reply = %q, want emoji prompt", reply) } sendTextToStickers(t, svc, messages, owner, "👋") sendTextToStickers(t, svc, messages, owner, "/publish") reply = sendTextToStickers(t, svc, messages, owner, "lottie_pack") if !strings.Contains(reply, "https://telesrv.net/addstickers/lottie_pack") { t.Fatalf("publish uploaded lottie reply = %q, want addstickers link", reply) } if len(creator.created) != 1 { t.Fatalf("created requests = %d, want 1", len(creator.created)) } req := creator.created[0] if req.Kind != domain.StickerSetKindStickers || len(req.Items) != 1 || req.Items[0].DocumentID != 402 || req.Items[0].DocumentAccessHash != 4402 { t.Fatalf("create request = %+v, want uploaded lottie item in sticker pack", req) } if len(installer.installs) != 1 || installer.installs[0].kind != domain.StickerSetKindStickers { t.Fatalf("installs = %+v, want sticker install", installer.installs) } } func TestStickersBotPublishCustomEmojiPack(t *testing.T) { svc, users, _, messages, creator, installer := newStickersBotTestService(t) owner := newOwner(t, users, "+3003") sendTextToStickers(t, svc, messages, owner, "/newemoji") sendTextToStickers(t, svc, messages, owner, "Emoji Pack") sendDocumentToStickers(t, svc, messages, owner, stickerBotTestDocument(301, 3301, domain.DocAttrCustomEmoji)) sendTextToStickers(t, svc, messages, owner, "🔥") sendTextToStickers(t, svc, messages, owner, "/publish") reply := sendTextToStickers(t, svc, messages, owner, "emoji_pack") if !strings.Contains(reply, "https://telesrv.net/addemoji/emoji_pack") { t.Fatalf("publish emoji reply = %q, want addemoji link", reply) } if len(creator.created) != 1 || creator.created[0].Kind != domain.StickerSetKindEmoji { t.Fatalf("created emoji requests = %+v, want kind emoji", creator.created) } if len(installer.installs) != 1 || installer.installs[0].kind != domain.StickerSetKindEmoji { t.Fatalf("emoji installs = %+v, want emoji kind install", installer.installs) } } func TestStickersBotAddStickerToExistingPack(t *testing.T) { svc, users, bots, messages, manager, _ := newStickersBotTestService(t) hooks := &stickersBotHookRecorder{} svc.SetRouterHooks(hooks) owner := newOwner(t, users, "+3006") manager.sets = append(manager.sets, domain.StickerSet{ ID: 7100, AccessHash: 8100, ShortName: "fresh_pack", Title: "Fresh Pack", Kind: domain.StickerSetKindStickers, Creator: true, CreatorUserID: owner.ID, Count: 1, DocumentIDs: []int64{501}, }) if reply := sendTextToStickers(t, svc, messages, owner, "/addsticker"); !strings.Contains(reply, "short name") { t.Fatalf("/addsticker reply = %q, want short name prompt", reply) } if reply := sendTextToStickers(t, svc, messages, owner, "https://telesrv.net/addstickers/fresh_pack"); !strings.Contains(reply, "Selected Fresh Pack") { t.Fatalf("select pack reply = %q, want selected pack", reply) } if reply := sendDocumentToStickers(t, svc, messages, owner, stickerBotUploadDocument(502, 5502, "application/json", "new.json")); !strings.Contains(reply, "emoji") { t.Fatalf("add document reply = %q, want emoji prompt", reply) } reply := sendTextToStickers(t, svc, messages, owner, "😄") if !strings.Contains(reply, "Done. Added to Fresh Pack") || !strings.Contains(reply, "https://telesrv.net/addstickers/fresh_pack") { t.Fatalf("add final reply = %q, want done link", reply) } if len(manager.adds) != 1 { t.Fatalf("adds = %+v, want one add", manager.adds) } add := manager.adds[0] if add.userID != owner.ID || add.ref.ID != 7100 || add.item.DocumentID != 502 || add.item.DocumentAccessHash != 5502 || add.item.Emoji != "😄" { t.Fatalf("add call = %+v, want owner/set/doc/emoji", add) } if hooks.userID != owner.ID || hooks.kind != domain.StickerSetKindStickers { t.Fatalf("hook = user %d kind %q, want owner stickers", hooks.userID, hooks.kind) } if _, found, _ := bots.GetBotChatState(context.Background(), domain.StickersBotUserID, owner.ID); found { t.Fatal("stickers bot state still present after add") } } func TestStickersBotAddStickerDuplicateStaysInFlow(t *testing.T) { svc, users, bots, messages, manager, _ := newStickersBotTestService(t) owner := newOwner(t, users, "+3008") manager.sets = append(manager.sets, domain.StickerSet{ ID: 7300, AccessHash: 8300, ShortName: "fresh_pack", Title: "Fresh Pack", Kind: domain.StickerSetKindStickers, CreatorUserID: owner.ID, Count: 1, DocumentIDs: []int64{501}, }) sendTextToStickers(t, svc, messages, owner, "/addsticker") sendTextToStickers(t, svc, messages, owner, "fresh_pack") sendDocumentToStickers(t, svc, messages, owner, stickerBotSetDocument(501, 5501, 7300, 8300)) reply := sendTextToStickers(t, svc, messages, owner, "😄") if !strings.Contains(reply, "already in this pack") { t.Fatalf("duplicate add reply = %q, want already-in-pack notice", reply) } if len(manager.adds) != 0 { t.Fatalf("adds = %+v, want no manager add for duplicate", manager.adds) } state, found, _ := bots.GetBotChatState(context.Background(), domain.StickersBotUserID, owner.ID) if !found || state.Step != stickersBotStepDocument { t.Fatalf("state after duplicate = %+v found=%v, want document step", state, found) } } func TestStickersBotDeleteStickerFromExistingPack(t *testing.T) { svc, users, bots, messages, manager, _ := newStickersBotTestService(t) hooks := &stickersBotHookRecorder{} svc.SetRouterHooks(hooks) owner := newOwner(t, users, "+3007") manager.sets = append(manager.sets, domain.StickerSet{ ID: 7200, AccessHash: 8200, ShortName: "old_pack", Title: "Old Pack", Kind: domain.StickerSetKindStickers, Creator: true, CreatorUserID: owner.ID, Count: 2, DocumentIDs: []int64{601, 602}, }) sendTextToStickers(t, svc, messages, owner, "/delsticker") if reply := sendTextToStickers(t, svc, messages, owner, "old_pack"); !strings.Contains(reply, "Selected Old Pack") { t.Fatalf("select delete pack reply = %q, want selected pack", reply) } doc := stickerBotSetDocument(601, 6601, 7200, 8200) reply := sendDocumentToStickers(t, svc, messages, owner, doc) if !strings.Contains(reply, "Done. Removed from Old Pack") || !strings.Contains(reply, "https://telesrv.net/addstickers/old_pack") { t.Fatalf("delete final reply = %q, want done link", reply) } if len(manager.removes) != 1 || manager.removes[0].documentID != 601 || manager.removes[0].accessHash != 6601 { t.Fatalf("removes = %+v, want document 601", manager.removes) } if hooks.userID != owner.ID || hooks.kind != domain.StickerSetKindStickers { t.Fatalf("hook = user %d kind %q, want owner stickers", hooks.userID, hooks.kind) } if _, found, _ := bots.GetBotChatState(context.Background(), domain.StickersBotUserID, owner.ID); found { t.Fatal("stickers bot state still present after delete") } } func TestStickersBotDeleteCustomEmojiEntityFromExistingPack(t *testing.T) { svc, users, bots, messages, manager, _ := newStickersBotTestService(t) owner := newOwner(t, users, "+3009") manager.sets = append(manager.sets, domain.StickerSet{ ID: 7400, AccessHash: 8400, ShortName: "emoji_pack", Title: "Emoji Pack", Kind: domain.StickerSetKindEmoji, Emojis: true, CreatorUserID: owner.ID, Count: 2, DocumentIDs: []int64{701, 702}, }) manager.docs = map[int64]domain.Document{ 701: stickerBotSetCustomEmojiDocument(701, 7701, 7400, 8400), } sendTextToStickers(t, svc, messages, owner, "/delsticker") sendTextToStickers(t, svc, messages, owner, "emoji_pack") reply := sendMessageToStickers(t, svc, messages, owner, domain.Message{ Body: "🔥", Entities: []domain.MessageEntity{{ Type: domain.MessageEntityCustomEmoji, Offset: 0, Length: 2, DocumentID: 701, }}, }) if !strings.Contains(reply, "Done. Removed from Emoji Pack") || !strings.Contains(reply, "https://telesrv.net/addemoji/emoji_pack") { t.Fatalf("delete custom emoji reply = %q, want done emoji link", reply) } if len(manager.removes) != 1 || manager.removes[0].documentID != 701 || manager.removes[0].accessHash != 7701 { t.Fatalf("removes = %+v, want custom emoji document 701", manager.removes) } if _, found, _ := bots.GetBotChatState(context.Background(), domain.StickersBotUserID, owner.ID); found { t.Fatal("stickers bot state still present after custom emoji delete") } } func botCommandExists(commands []domain.BotCommand, want string) bool { for _, c := range commands { if c.Command == want { return true } } return false } func assertReplyEntityText(t *testing.T, msg domain.Message, typ domain.MessageEntityType, want string) { t.Helper() for _, entity := range msg.Entities { if entity.Type != typ { continue } if entity.Offset < 0 || entity.Length < 0 || entity.Offset+entity.Length > len(msg.Body) { t.Fatalf("entity %+v out of ASCII bounds for %q", entity, msg.Body) } if got := msg.Body[entity.Offset : entity.Offset+entity.Length]; got == want { return } } t.Fatalf("message %q entities %+v missing %s entity for %q", msg.Body, msg.Entities, typ, want) } type stickersBotFakeCreator struct { created []domain.CreateStickerSetRequest sets []domain.StickerSet docs map[int64]domain.Document adds []stickersBotAdd removes []stickersBotRemove } type stickersBotAdd struct { userID int64 ref domain.StickerSetRef item domain.StickerSetItemInput } type stickersBotRemove struct { userID int64 documentID int64 accessHash int64 } func (f *stickersBotFakeCreator) CreateStickerSet(_ context.Context, req domain.CreateStickerSetRequest) (domain.StickerSet, []domain.Document, error) { f.created = append(f.created, req) docIDs := make([]int64, 0, len(req.Items)) for _, item := range req.Items { docIDs = append(docIDs, item.DocumentID) } kind := req.Kind if kind == "" { kind = domain.StickerSetKindStickers } set := domain.StickerSet{ ID: 7000 + int64(len(f.sets)), AccessHash: 8000 + int64(len(f.sets)), ShortName: strings.ToLower(strings.TrimSpace(req.ShortName)), Title: req.Title, Kind: kind, Emojis: kind == domain.StickerSetKindEmoji, Creator: true, CreatorUserID: req.CreatorUserID, Count: len(docIDs), DocumentIDs: docIDs, } f.sets = append(f.sets, set) return set, nil, nil } func (f *stickersBotFakeCreator) ResolveStickerSet(_ context.Context, ref domain.StickerSetRef) (domain.StickerSet, []domain.Document, bool, error) { idx := f.indexSet(ref) if idx < 0 { return domain.StickerSet{}, nil, false, nil } return f.sets[idx], nil, true, nil } func (f *stickersBotFakeCreator) ListCreatedStickerSets(_ context.Context, userID int64, _ int64, limit int) ([]domain.StickerSet, int, error) { var out []domain.StickerSet for _, set := range f.sets { if set.CreatorUserID == userID { out = append(out, set) } } total := len(out) if limit > 0 && len(out) > limit { out = out[:limit] } return out, total, nil } func (f *stickersBotFakeCreator) GetDocuments(_ context.Context, ids []int64) ([]domain.Document, error) { out := make([]domain.Document, 0, len(ids)) for _, id := range ids { if doc, ok := f.docs[id]; ok { out = append(out, doc) } } return out, nil } func (f *stickersBotFakeCreator) AddStickerToSet(_ context.Context, actorUserID int64, ref domain.StickerSetRef, item domain.StickerSetItemInput) (domain.StickerSet, []domain.Document, error) { f.adds = append(f.adds, stickersBotAdd{userID: actorUserID, ref: ref, item: item}) idx := f.indexSet(ref) if idx < 0 || f.sets[idx].CreatorUserID != actorUserID { return domain.StickerSet{}, nil, domain.ErrStickerSetInvalid } set := f.sets[idx] if !stickersBotTestContainsInt64(set.DocumentIDs, item.DocumentID) { set.DocumentIDs = append(set.DocumentIDs, item.DocumentID) set.Count = len(set.DocumentIDs) } f.sets[idx] = set return set, nil, nil } func (f *stickersBotFakeCreator) RemoveStickerFromSet(_ context.Context, actorUserID int64, documentID int64, accessHash int64) (domain.StickerSet, []domain.Document, error) { f.removes = append(f.removes, stickersBotRemove{userID: actorUserID, documentID: documentID, accessHash: accessHash}) for i, set := range f.sets { if set.CreatorUserID != actorUserID { continue } for idx, id := range set.DocumentIDs { if id != documentID { continue } set.DocumentIDs = append(append([]int64(nil), set.DocumentIDs[:idx]...), set.DocumentIDs[idx+1:]...) set.Count = len(set.DocumentIDs) f.sets[i] = set return set, nil, nil } } return domain.StickerSet{}, nil, domain.ErrStickerSetFileInvalid } func (f *stickersBotFakeCreator) indexSet(ref domain.StickerSetRef) int { for i, set := range f.sets { switch ref.Kind { case domain.StickerSetRefByID: if set.ID == ref.ID && (ref.AccessHash == 0 || set.AccessHash == ref.AccessHash) { return i } case domain.StickerSetRefByShortName: if strings.EqualFold(set.ShortName, ref.ShortName) { return i } } } return -1 } func stickersBotTestContainsInt64(values []int64, want int64) bool { for _, value := range values { if value == want { return true } } return false } type stickersBotFakeInstaller struct { installs []stickersBotInstall } type stickersBotInstall struct { userID int64 setID int64 kind domain.StickerSetKind } func (f *stickersBotFakeInstaller) InstallUserStickerSet(_ context.Context, userID int64, setID int64, kind domain.StickerSetKind, _ bool, _ int) error { f.installs = append(f.installs, stickersBotInstall{userID: userID, setID: setID, kind: kind}) return nil } type stickersBotHookRecorder struct { userID int64 kind domain.StickerSetKind } func (h *stickersBotHookRecorder) RevokeBotSessions(context.Context, int64) error { return nil } func (h *stickersBotHookRecorder) PushBotCommandsChanged(context.Context, int64, []domain.BotCommand) { } func (h *stickersBotHookRecorder) PushStickerSetsChanged(_ context.Context, userID int64, kind domain.StickerSetKind) { h.userID = userID h.kind = kind } func TestNormalizeStickersBotShortNameAcceptsHostBasedAppLinks(t *testing.T) { for _, tc := range []struct { raw string want string }{ {raw: "telesrv://addstickers?set=Legacy_Pack", want: "legacy_pack"}, {raw: "owpg://tenant.example.test/addstickers?set=Hosted_Pack", want: "hosted_pack"}, {raw: "owpg://tenant.example.test/addemoji?set=Emoji_Pack", want: "emoji_pack"}, {raw: "https://telesrv.net/addstickers/Web_Pack", want: "web_pack"}, } { if got := normalizeStickersBotShortName(tc.raw); got != tc.want { t.Fatalf("normalizeStickersBotShortName(%q) = %q, want %q", tc.raw, got, tc.want) } } }