275 lines
10 KiB
Go
275 lines
10 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gotd/td/clock"
|
|
"github.com/gotd/td/tg"
|
|
"go.uber.org/zap/zaptest"
|
|
|
|
appcontacts "telesrv/internal/app/contacts"
|
|
appmessages "telesrv/internal/app/messages"
|
|
appusers "telesrv/internal/app/users"
|
|
"telesrv/internal/domain"
|
|
"telesrv/internal/store/memory"
|
|
)
|
|
|
|
func newContactProfilePhotoTestRouter(t *testing.T) (*Router, *memory.ContactStore, *captureSessions, domain.User, domain.User) {
|
|
t.Helper()
|
|
ctx := context.Background()
|
|
userStore := memory.NewUserStore()
|
|
owner, err := userStore.Create(ctx, domain.User{AccessHash: 11, Phone: "15550002001", FirstName: "Owner"})
|
|
if err != nil {
|
|
t.Fatalf("create owner: %v", err)
|
|
}
|
|
friend, err := userStore.Create(ctx, domain.User{AccessHash: 12, Phone: "15550002002", FirstName: "Friend"})
|
|
if err != nil {
|
|
t.Fatalf("create friend: %v", err)
|
|
}
|
|
contactStore := memory.NewContactStore()
|
|
contacts := appcontacts.NewService(contactStore, userStore)
|
|
if _, err := contacts.AddContact(ctx, owner.ID, domain.ContactInput{ContactUserID: friend.ID, FirstName: "Friend"}); err != nil {
|
|
t.Fatalf("add contact: %v", err)
|
|
}
|
|
dialogStore := memory.NewDialogStore()
|
|
messageStore := memory.NewMessageStore(dialogStore)
|
|
users := appusers.NewService(userStore, appusers.WithContactStore(contactStore))
|
|
files := &fakeFiles{photos: map[int64]domain.Photo{}}
|
|
sessions := &captureSessions{}
|
|
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{
|
|
Users: users,
|
|
Contacts: contacts,
|
|
Files: files,
|
|
Messages: appmessages.NewService(messageStore, dialogStore, appmessages.WithContactStore(contactStore)),
|
|
Sessions: sessions,
|
|
}, zaptest.NewLogger(t), clock.System)
|
|
return r, contactStore, sessions, owner, friend
|
|
}
|
|
|
|
func contactProfilePhotoReq(friend domain.User) *tg.PhotosUploadContactProfilePhotoRequest {
|
|
return &tg.PhotosUploadContactProfilePhotoRequest{
|
|
UserID: &tg.InputUser{UserID: friend.ID, AccessHash: friend.AccessHash},
|
|
}
|
|
}
|
|
|
|
func uploadedAvatarFile() *tg.InputFile {
|
|
return &tg.InputFile{ID: 42, Parts: 1, Name: "avatar.jpg", MD5Checksum: "d41d8cd98f00b204e9800998ecf8427e"}
|
|
}
|
|
|
|
func personalPhotoID(t *testing.T, store *memory.ContactStore, ownerID, friendID int64) int64 {
|
|
t.Helper()
|
|
refs, err := store.PersonalPhotos(context.Background(), ownerID, []int64{friendID})
|
|
if err != nil {
|
|
t.Fatalf("personal photos: %v", err)
|
|
}
|
|
return refs[friendID].PhotoID
|
|
}
|
|
|
|
func firstPhotosUser(t *testing.T, got *tg.PhotosPhoto) *tg.User {
|
|
t.Helper()
|
|
if got == nil || len(got.Users) == 0 {
|
|
t.Fatalf("photos.photo users missing: %+v", got)
|
|
}
|
|
user, ok := got.Users[0].(*tg.User)
|
|
if !ok {
|
|
t.Fatalf("photos.photo user = %T, want *tg.User", got.Users[0])
|
|
}
|
|
return user
|
|
}
|
|
|
|
func TestUploadContactProfilePhotoSaveAndClearPersonalPhoto(t *testing.T) {
|
|
ctx := context.Background()
|
|
r, contacts, _, owner, friend := newContactProfilePhotoTestRouter(t)
|
|
|
|
saveReq := contactProfilePhotoReq(friend)
|
|
saveReq.SetSave(true)
|
|
saveReq.SetFile(uploadedAvatarFile())
|
|
got, err := r.onPhotosUploadContactProfilePhoto(WithUserID(ctx, owner.ID), saveReq)
|
|
if err != nil {
|
|
t.Fatalf("save contact profile photo: %v", err)
|
|
}
|
|
if photo, ok := got.Photo.(*tg.Photo); !ok || photo.ID != 778 || photo.DCID != 2 {
|
|
t.Fatalf("returned photo = %+v, want photo 778/dc2", got.Photo)
|
|
}
|
|
if id := personalPhotoID(t, contacts, owner.ID, friend.ID); id != 778 {
|
|
t.Fatalf("personal photo id = %d, want 778", id)
|
|
}
|
|
userPhoto, ok := firstPhotosUser(t, got).Photo.(*tg.UserProfilePhoto)
|
|
if !ok || userPhoto.PhotoID != 778 || userPhoto.DCID != 2 || !userPhoto.Personal {
|
|
t.Fatalf("returned user photo = %+v, want personal 778/dc2", firstPhotosUser(t, got).Photo)
|
|
}
|
|
|
|
clearReq := contactProfilePhotoReq(friend)
|
|
clearReq.SetSave(true)
|
|
cleared, err := r.onPhotosUploadContactProfilePhoto(WithUserID(ctx, owner.ID), clearReq)
|
|
if err != nil {
|
|
t.Fatalf("clear contact profile photo: %v", err)
|
|
}
|
|
if _, ok := cleared.Photo.(*tg.PhotoEmpty); !ok {
|
|
t.Fatalf("cleared photo = %T, want *tg.PhotoEmpty", cleared.Photo)
|
|
}
|
|
if id := personalPhotoID(t, contacts, owner.ID, friend.ID); id != 0 {
|
|
t.Fatalf("personal photo after clear = %d, want 0", id)
|
|
}
|
|
}
|
|
|
|
func TestUploadContactProfilePhotoSuggestCreatesServiceMessage(t *testing.T) {
|
|
ctx := context.Background()
|
|
r, contacts, sessions, owner, friend := newContactProfilePhotoTestRouter(t)
|
|
|
|
req := contactProfilePhotoReq(friend)
|
|
req.SetSuggest(true)
|
|
req.SetFile(uploadedAvatarFile())
|
|
got, err := r.onPhotosUploadContactProfilePhoto(WithSessionID(WithUserID(ctx, owner.ID), 91), req)
|
|
if err != nil {
|
|
t.Fatalf("suggest contact profile photo: %v", err)
|
|
}
|
|
if photo, ok := got.Photo.(*tg.Photo); !ok || photo.ID != 778 {
|
|
t.Fatalf("suggest returned photo = %+v, want photo 778", got.Photo)
|
|
}
|
|
if id := personalPhotoID(t, contacts, owner.ID, friend.ID); id != 0 {
|
|
t.Fatalf("suggest set personal photo id = %d, want 0", id)
|
|
}
|
|
if firstPhotosUser(t, got).Photo != nil {
|
|
t.Fatalf("suggest returned user photo = %+v, want unchanged user", firstPhotosUser(t, got).Photo)
|
|
}
|
|
|
|
assertSuggestProfilePhotoHistory(t, r, owner.ID, friend.ID, true)
|
|
assertSuggestProfilePhotoHistory(t, r, friend.ID, owner.ID, false)
|
|
snap := sessions.snapshot()
|
|
pushed, ok := snap.message.(*tg.Updates)
|
|
if !ok {
|
|
t.Fatalf("current-session push = %T, want *tg.Updates", snap.message)
|
|
}
|
|
if len(pushed.Updates) != 1 {
|
|
t.Fatalf("current-session updates = %+v, want UpdateNewMessage only", pushed.Updates)
|
|
}
|
|
update, ok := pushed.Updates[0].(*tg.UpdateNewMessage)
|
|
if !ok {
|
|
t.Fatalf("current-session update = %T, want *tg.UpdateNewMessage", pushed.Updates[0])
|
|
}
|
|
if service, ok := update.Message.(*tg.MessageService); !ok || !service.Out {
|
|
t.Fatalf("current-session message = %T %+v, want outgoing MessageService", update.Message, update.Message)
|
|
}
|
|
}
|
|
|
|
func TestUploadContactProfilePhotoAnimatedInputsSetPersonalPhoto(t *testing.T) {
|
|
ctx := context.Background()
|
|
r, contacts, _, owner, friend := newContactProfilePhotoTestRouter(t)
|
|
|
|
videoReq := contactProfilePhotoReq(friend)
|
|
videoReq.SetSave(true)
|
|
videoReq.SetVideo(&tg.InputFile{ID: 43, Parts: 1, Name: "avatar.mp4"})
|
|
videoReq.SetVideoStartTs(0.5)
|
|
videoGot, err := r.onPhotosUploadContactProfilePhoto(WithUserID(ctx, owner.ID), videoReq)
|
|
if err != nil {
|
|
t.Fatalf("save animated video profile photo: %v", err)
|
|
}
|
|
videoPhoto, ok := videoGot.Photo.(*tg.Photo)
|
|
if !ok || videoPhoto.ID != 779 || len(videoPhoto.VideoSizes) != 1 {
|
|
t.Fatalf("video photo = %+v, want photo 779 with video_sizes", videoGot.Photo)
|
|
}
|
|
videoSize, ok := videoPhoto.VideoSizes[0].(*tg.VideoSize)
|
|
if !ok || videoSize.Type != "u" || videoSize.VideoStartTs != 0.5 {
|
|
t.Fatalf("video size = %+v, want videoSize u start 0.5", videoPhoto.VideoSizes[0])
|
|
}
|
|
if id := personalPhotoID(t, contacts, owner.ID, friend.ID); id != 779 {
|
|
t.Fatalf("personal photo after video = %d, want 779", id)
|
|
}
|
|
|
|
markupReq := contactProfilePhotoReq(friend)
|
|
markupReq.SetSave(true)
|
|
markupReq.SetVideoEmojiMarkup(&tg.VideoSizeEmojiMarkup{EmojiID: 99, BackgroundColors: []int{0xffffff}})
|
|
markupGot, err := r.onPhotosUploadContactProfilePhoto(WithUserID(ctx, owner.ID), markupReq)
|
|
if err != nil {
|
|
t.Fatalf("save emoji markup profile photo: %v", err)
|
|
}
|
|
markupPhoto, ok := markupGot.Photo.(*tg.Photo)
|
|
if !ok || markupPhoto.ID != 780 || len(markupPhoto.VideoSizes) != 1 {
|
|
t.Fatalf("markup photo = %+v, want photo 780 with video_sizes", markupGot.Photo)
|
|
}
|
|
if _, ok := markupPhoto.VideoSizes[0].(*tg.VideoSizeEmojiMarkup); !ok {
|
|
t.Fatalf("markup video size = %T, want *tg.VideoSizeEmojiMarkup", markupPhoto.VideoSizes[0])
|
|
}
|
|
if id := personalPhotoID(t, contacts, owner.ID, friend.ID); id != 780 {
|
|
t.Fatalf("personal photo after markup = %d, want 780", id)
|
|
}
|
|
}
|
|
|
|
func TestUploadContactProfilePhotoInvalidAnimatedFlagsDoNotClear(t *testing.T) {
|
|
ctx := context.Background()
|
|
r, contacts, _, owner, friend := newContactProfilePhotoTestRouter(t)
|
|
saveReq := contactProfilePhotoReq(friend)
|
|
saveReq.SetSave(true)
|
|
saveReq.SetFile(uploadedAvatarFile())
|
|
if _, err := r.onPhotosUploadContactProfilePhoto(WithUserID(ctx, owner.ID), saveReq); err != nil {
|
|
t.Fatalf("seed personal photo: %v", err)
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
setup func(*tg.PhotosUploadContactProfilePhotoRequest)
|
|
}{
|
|
{
|
|
name: "file-and-video",
|
|
setup: func(req *tg.PhotosUploadContactProfilePhotoRequest) {
|
|
req.SetSave(true)
|
|
req.SetFile(uploadedAvatarFile())
|
|
req.SetVideo(&tg.InputFile{ID: 44, Parts: 1, Name: "avatar.mp4"})
|
|
},
|
|
},
|
|
{
|
|
name: "video-start-without-video",
|
|
setup: func(req *tg.PhotosUploadContactProfilePhotoRequest) {
|
|
req.SetSave(true)
|
|
req.SetVideoStartTs(0.5)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
req := contactProfilePhotoReq(friend)
|
|
tc.setup(req)
|
|
if _, err := r.onPhotosUploadContactProfilePhoto(WithUserID(ctx, owner.ID), req); err == nil || !strings.Contains(err.Error(), "PHOTO_INVALID") {
|
|
t.Fatalf("invalid animated request error = %v, want PHOTO_INVALID", err)
|
|
}
|
|
if id := personalPhotoID(t, contacts, owner.ID, friend.ID); id != 778 {
|
|
t.Fatalf("personal photo after invalid request = %d, want preserved 778", id)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func assertSuggestProfilePhotoHistory(t *testing.T, r *Router, ownerID, peerID int64, out bool) {
|
|
t.Helper()
|
|
list, err := r.deps.Messages.GetHistory(context.Background(), ownerID, domain.MessageFilter{
|
|
HasPeer: true,
|
|
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: peerID},
|
|
Limit: 10,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("get history owner %d peer %d: %v", ownerID, peerID, err)
|
|
}
|
|
if len(list.Messages) != 1 {
|
|
t.Fatalf("history owner %d peer %d len = %d, want 1", ownerID, peerID, len(list.Messages))
|
|
}
|
|
msg := tgMessage(list.Messages[0])
|
|
service, ok := msg.(*tg.MessageService)
|
|
if !ok {
|
|
t.Fatalf("history message = %T %+v, want *tg.MessageService", msg, msg)
|
|
}
|
|
if service.Out != out {
|
|
t.Fatalf("history service out = %v, want %v", service.Out, out)
|
|
}
|
|
action, ok := service.Action.(*tg.MessageActionSuggestProfilePhoto)
|
|
if !ok {
|
|
t.Fatalf("service action = %T, want suggest profile photo", service.Action)
|
|
}
|
|
photo, ok := action.Photo.(*tg.Photo)
|
|
if !ok || photo.ID != 778 {
|
|
t.Fatalf("suggest action photo = %+v, want photo 778", action.Photo)
|
|
}
|
|
}
|