package bots import ( "context" "strings" "testing" "telesrv/internal/domain" "telesrv/internal/store/memory" ) type fakeBotAvatar struct { ownerType domain.PeerType ownerID int64 sourcePhotoID int64 calls int err error } func (f *fakeBotAvatar) SetAvatarFromExistingPhoto(_ context.Context, ownerType domain.PeerType, ownerID, sourcePhotoID int64, _ int) (domain.Photo, error) { f.calls++ f.ownerType, f.ownerID, f.sourcePhotoID = ownerType, ownerID, sourcePhotoID if f.err != nil { return domain.Photo{}, f.err } return domain.Photo{ID: 90210}, nil } func botFatherPhotoMsg(userID, photoID int64) domain.Message { return domain.Message{ From: domain.Peer{Type: domain.PeerTypeUser, ID: userID}, Peer: domain.Peer{Type: domain.PeerTypeUser, ID: domain.BotFatherUserID}, Media: &domain.MessageMedia{ Kind: domain.MessageMediaKindPhoto, Photo: &domain.Photo{ID: photoID}, }, } } func openBotpicPrompt(t *testing.T, svc *Service, messages *memory.MessageStore, owner domain.User) { t.Helper() sendToBotFather(t, svc, messages, owner, "/mybots") pressBotFather(t, svc, messages, owner.ID, "@pic_mb_bot") pressBotFather(t, svc, messages, owner.ID, "Edit Bot") _, prompt := pressBotFather(t, svc, messages, owner.ID, "Edit Botpic") if !strings.Contains(prompt.Body, "profile picture") { t.Fatalf("botpic prompt = %q", prompt.Body) } } func TestMyBotsEditBotpicSetsPhoto(t *testing.T) { users := memory.NewUserStore() bots := memory.NewBotStore(users) messages := memory.NewMessageStore(memory.NewDialogStore()) avatar := &fakeBotAvatar{} svc := NewService(users, bots, messages, WithBotAvatarStore(avatar)) owner := newOwner(t, users, "+2100") created, _, err := svc.CreateBot(context.Background(), owner.ID, "Pic Bot", "pic_mb_bot") if err != nil { t.Fatalf("create bot: %v", err) } openBotpicPrompt(t, svc, messages, owner) // A non-photo message keeps the step and asks again. if reply := sendToBotFather(t, svc, messages, owner, "here you go"); !strings.Contains(reply, "send a photo") { t.Fatalf("text-instead-of-photo reply = %q", reply) } if avatar.calls != 0 { t.Fatalf("avatar setter called for a non-photo message") } svc.respondAsBotFather(owner.ID, botFatherPhotoMsg(owner.ID, 7777)) reply := botFatherUserReply(t, messages, owner.ID) if !strings.Contains(reply.Body, "Profile picture updated") { t.Fatalf("botpic success reply = %q", reply.Body) } if avatar.calls != 1 || avatar.ownerID != created.ID || avatar.sourcePhotoID != 7777 || avatar.ownerType != domain.PeerTypeUser { t.Fatalf("avatar setter got (calls=%d owner=%d photo=%d type=%s)", avatar.calls, avatar.ownerID, avatar.sourcePhotoID, avatar.ownerType) } // The dialog is done: a stray message no longer lands on the botpic step. if reply := sendToBotFather(t, svc, messages, owner, "anything"); strings.Contains(reply, "profile picture") { t.Fatalf("botpic step still active after success: %q", reply) } } func TestMyBotsEditBotpicRejectsBadImage(t *testing.T) { users := memory.NewUserStore() bots := memory.NewBotStore(users) messages := memory.NewMessageStore(memory.NewDialogStore()) avatar := &fakeBotAvatar{err: domain.ErrPhotoInvalid} svc := NewService(users, bots, messages, WithBotAvatarStore(avatar)) owner := newOwner(t, users, "+2101") if _, _, err := svc.CreateBot(context.Background(), owner.ID, "Pic Bot", "pic_mb_bot"); err != nil { t.Fatalf("create bot: %v", err) } openBotpicPrompt(t, svc, messages, owner) svc.respondAsBotFather(owner.ID, botFatherPhotoMsg(owner.ID, 7777)) reply := botFatherUserReply(t, messages, owner.ID) if !strings.Contains(reply.Body, "couldn't use that image") { t.Fatalf("bad image reply = %q", reply.Body) } // Step is kept so the user can send another photo. if reply := sendToBotFather(t, svc, messages, owner, "x"); !strings.Contains(reply, "send a photo") { t.Fatalf("after bad image, step not kept: %q", reply) } } func TestMyBotsEditBotpicUnsupported(t *testing.T) { svc, users, _, messages := newTestService(t) // no WithBotAvatarStore owner := newOwner(t, users, "+2102") if _, _, err := svc.CreateBot(context.Background(), owner.ID, "Pic Bot", "pic_mb_bot"); err != nil { t.Fatalf("create bot: %v", err) } openBotpicPrompt(t, svc, messages, owner) svc.respondAsBotFather(owner.ID, botFatherPhotoMsg(owner.ID, 7777)) reply := botFatherUserReply(t, messages, owner.ID) if !strings.Contains(reply.Body, "isn't available on this server") { t.Fatalf("unsupported reply = %q", reply.Body) } }