chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
309
internal/rpc/music_rpc_test.go
Normal file
309
internal/rpc/music_rpc_test.go
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
appmessages "telesrv/internal/app/messages"
|
||||
appusers "telesrv/internal/app/users"
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/store/memory"
|
||||
)
|
||||
|
||||
func TestSavedMusicRPCLifecycle(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userStore := memory.NewUserStore()
|
||||
owner, err := userStore.Create(ctx, domain.User{AccessHash: 11, Phone: "15550000011", FirstName: "Owner"})
|
||||
if err != nil {
|
||||
t.Fatalf("create owner: %v", err)
|
||||
}
|
||||
passwords := memory.NewPasswordStore()
|
||||
music1 := testMusicDocument(101, 1001, "First")
|
||||
music2 := testMusicDocument(102, 1002, "Second")
|
||||
voice := domain.Document{ID: 103, AccessHash: 1003, MimeType: "audio/ogg", Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrAudio, Voice: true, AudioDuration: 3}}}
|
||||
files := &fakeFiles{docs: map[int64]domain.Document{
|
||||
music1.ID: music1,
|
||||
music2.ID: music2,
|
||||
voice.ID: voice,
|
||||
}}
|
||||
r := New(Config{}, Deps{
|
||||
Account: appaccount.NewService(passwords, appaccount.WithSavedMusic(passwords)),
|
||||
Files: files,
|
||||
Users: appusers.NewService(userStore),
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
reqCtx := WithUserID(ctx, owner.ID)
|
||||
|
||||
if ok, err := r.onAccountSaveMusic(reqCtx, &tg.AccountSaveMusicRequest{
|
||||
ID: &tg.InputDocument{ID: music1.ID, AccessHash: music1.AccessHash},
|
||||
}); err != nil || !ok {
|
||||
t.Fatalf("save first music = ok %v err %v, want true/nil", ok, err)
|
||||
}
|
||||
afterFirst := &tg.AccountSaveMusicRequest{
|
||||
ID: &tg.InputDocument{ID: music2.ID, AccessHash: music2.AccessHash},
|
||||
}
|
||||
afterFirst.SetAfterID(&tg.InputDocument{ID: music1.ID, AccessHash: music1.AccessHash})
|
||||
if ok, err := r.onAccountSaveMusic(reqCtx, afterFirst); err != nil || !ok {
|
||||
t.Fatalf("save second after first = ok %v err %v, want true/nil", ok, err)
|
||||
}
|
||||
idsBox, err := r.onAccountGetSavedMusicIDs(reqCtx, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("get saved music ids: %v", err)
|
||||
}
|
||||
ids, ok := idsBox.(*tg.AccountSavedMusicIDs)
|
||||
if !ok || !int64SliceEqual(ids.IDs, []int64{music1.ID, music2.ID}) {
|
||||
t.Fatalf("ids = %T %+v, want [first second]", idsBox, idsBox)
|
||||
}
|
||||
if notModified, err := r.onAccountGetSavedMusicIDs(reqCtx, int64(tdesktopCountHash(ids.IDs))); err != nil {
|
||||
t.Fatalf("get saved music ids hash: %v", err)
|
||||
} else if _, ok := notModified.(*tg.AccountSavedMusicIDsNotModified); !ok {
|
||||
t.Fatalf("hash result = %T, want notModified", notModified)
|
||||
}
|
||||
|
||||
page, err := r.onUsersGetSavedMusic(reqCtx, &tg.UsersGetSavedMusicRequest{ID: &tg.InputUserSelf{}, Limit: 2})
|
||||
if err != nil {
|
||||
t.Fatalf("users.getSavedMusic: %v", err)
|
||||
}
|
||||
saved, ok := page.(*tg.UsersSavedMusic)
|
||||
if !ok || saved.Count != 2 || len(saved.Documents) != 2 || documentClassID(saved.Documents[0]) != music1.ID {
|
||||
t.Fatalf("saved music page = %T %+v, want two docs with first first", page, page)
|
||||
}
|
||||
hashPage, err := r.onUsersGetSavedMusic(reqCtx, &tg.UsersGetSavedMusicRequest{
|
||||
ID: &tg.InputUserSelf{},
|
||||
Limit: 2,
|
||||
Hash: int64(tdesktopCountHash([]int64{music1.ID, music2.ID})),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("users.getSavedMusic hash: %v", err)
|
||||
}
|
||||
if nm, ok := hashPage.(*tg.UsersSavedMusicNotModified); !ok || nm.Count != 2 {
|
||||
t.Fatalf("hash page = %T %+v, want notModified count=2", hashPage, hashPage)
|
||||
}
|
||||
|
||||
full, err := r.onUsersGetFullUser(reqCtx, &tg.InputUserSelf{})
|
||||
if err != nil {
|
||||
t.Fatalf("users.getFullUser: %v", err)
|
||||
}
|
||||
if doc, ok := full.FullUser.GetSavedMusicAsNotEmpty(); !ok || doc.ID != music1.ID {
|
||||
t.Fatalf("full saved_music = %+v ok=%v, want first document", doc, ok)
|
||||
}
|
||||
byID, err := r.onUsersGetSavedMusicByID(reqCtx, &tg.UsersGetSavedMusicByIDRequest{
|
||||
ID: &tg.InputUserSelf{},
|
||||
Documents: []tg.InputDocumentClass{&tg.InputDocument{ID: music2.ID, AccessHash: music2.AccessHash}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("users.getSavedMusicByID: %v", err)
|
||||
}
|
||||
byIDMusic, ok := byID.(*tg.UsersSavedMusic)
|
||||
if !ok || len(byIDMusic.Documents) != 1 || documentClassID(byIDMusic.Documents[0]) != music2.ID {
|
||||
t.Fatalf("by id = %T %+v, want second document", byID, byID)
|
||||
}
|
||||
|
||||
if ok, err := r.onAccountSaveMusic(reqCtx, &tg.AccountSaveMusicRequest{
|
||||
ID: &tg.InputDocument{ID: music2.ID, AccessHash: music2.AccessHash},
|
||||
}); err != nil || !ok {
|
||||
t.Fatalf("move second to top = ok %v err %v, want true/nil", ok, err)
|
||||
}
|
||||
idsBox, err = r.onAccountGetSavedMusicIDs(reqCtx, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("get reordered ids: %v", err)
|
||||
}
|
||||
ids = idsBox.(*tg.AccountSavedMusicIDs)
|
||||
if !int64SliceEqual(ids.IDs, []int64{music2.ID, music1.ID}) {
|
||||
t.Fatalf("reordered ids = %v, want [second first]", ids.IDs)
|
||||
}
|
||||
unsave := &tg.AccountSaveMusicRequest{ID: &tg.InputDocument{ID: music2.ID, AccessHash: music2.AccessHash}}
|
||||
unsave.SetUnsave(true)
|
||||
if ok, err := r.onAccountSaveMusic(reqCtx, unsave); err != nil || !ok {
|
||||
t.Fatalf("unsave second = ok %v err %v, want true/nil", ok, err)
|
||||
}
|
||||
idsBox, err = r.onAccountGetSavedMusicIDs(reqCtx, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("get ids after unsave: %v", err)
|
||||
}
|
||||
ids = idsBox.(*tg.AccountSavedMusicIDs)
|
||||
if !int64SliceEqual(ids.IDs, []int64{music1.ID}) {
|
||||
t.Fatalf("ids after unsave = %v, want [first]", ids.IDs)
|
||||
}
|
||||
if _, err := r.onAccountSaveMusic(reqCtx, &tg.AccountSaveMusicRequest{
|
||||
ID: &tg.InputDocument{ID: voice.ID, AccessHash: voice.AccessHash},
|
||||
}); err == nil || !strings.Contains(err.Error(), "DOCUMENT_INVALID") {
|
||||
t.Fatalf("save voice err = %v, want DOCUMENT_INVALID", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessagesSearchMusicFiltersAudioDocuments(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userStore := memory.NewUserStore()
|
||||
alice, err := userStore.Create(ctx, domain.User{AccessHash: 21, Phone: "15550000021", FirstName: "Alice"})
|
||||
if err != nil {
|
||||
t.Fatalf("create alice: %v", err)
|
||||
}
|
||||
bob, err := userStore.Create(ctx, domain.User{AccessHash: 22, Phone: "15550000022", FirstName: "Bob"})
|
||||
if err != nil {
|
||||
t.Fatalf("create bob: %v", err)
|
||||
}
|
||||
dialogs := memory.NewDialogStore()
|
||||
messageStore := memory.NewMessageStore(dialogs)
|
||||
music := testMusicDocument(201, 2001, "Song")
|
||||
voice := domain.Document{ID: 202, AccessHash: 2002, Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrAudio, Voice: true, AudioDuration: 5}}}
|
||||
plain := domain.Document{ID: 203, AccessHash: 2003, Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrFilename, FileName: "file.bin"}}}
|
||||
for i, media := range []*domain.MessageMedia{
|
||||
{Kind: domain.MessageMediaKindDocument, Document: &plain},
|
||||
{Kind: domain.MessageMediaKindDocument, Document: &voice, Voice: true},
|
||||
{Kind: domain.MessageMediaKindDocument, Document: &music},
|
||||
} {
|
||||
if _, err := messageStore.SendPrivateText(ctx, domain.SendPrivateTextRequest{
|
||||
SenderUserID: alice.ID,
|
||||
RecipientUserID: bob.ID,
|
||||
RandomID: int64(300 + i),
|
||||
Message: "",
|
||||
Media: media,
|
||||
Date: 1700000000 + i,
|
||||
}); err != nil {
|
||||
t.Fatalf("send media %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
r := New(Config{}, Deps{
|
||||
Users: appusers.NewService(userStore),
|
||||
Messages: appmessages.NewService(messageStore, dialogs),
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
var payload bin.Buffer
|
||||
if err := (&tg.MessagesSearchRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: alice.ID, AccessHash: alice.AccessHash},
|
||||
Filter: &tg.InputMessagesFilterMusic{},
|
||||
Limit: 10,
|
||||
}).Encode(&payload); err != nil {
|
||||
t.Fatalf("encode messages.search: %v", err)
|
||||
}
|
||||
got, err := r.Dispatch(WithUserID(ctx, bob.ID), [8]byte{}, 0, &payload)
|
||||
if err != nil {
|
||||
t.Fatalf("messages.search music: %v", err)
|
||||
}
|
||||
messages, _, _ := searchMessagesPayload(t, got)
|
||||
if len(messages) != 1 {
|
||||
t.Fatalf("music search messages = %d, want 1", len(messages))
|
||||
}
|
||||
msg, ok := messages[0].(*tg.Message)
|
||||
if !ok {
|
||||
t.Fatalf("music search message = %T, want *tg.Message", messages[0])
|
||||
}
|
||||
docMedia, ok := msg.Media.(*tg.MessageMediaDocument)
|
||||
if !ok || documentClassID(docMedia.Document) != music.ID {
|
||||
t.Fatalf("music search media = %T %+v, want music document", msg.Media, msg.Media)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessagesSearchGlobalMusicAllowsEmptyQuery(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userStore := memory.NewUserStore()
|
||||
alice, err := userStore.Create(ctx, domain.User{AccessHash: 31, Phone: "15550000031", FirstName: "Alice"})
|
||||
if err != nil {
|
||||
t.Fatalf("create alice: %v", err)
|
||||
}
|
||||
bob, err := userStore.Create(ctx, domain.User{AccessHash: 32, Phone: "15550000032", FirstName: "Bob"})
|
||||
if err != nil {
|
||||
t.Fatalf("create bob: %v", err)
|
||||
}
|
||||
dialogs := memory.NewDialogStore()
|
||||
messageStore := memory.NewMessageStore(dialogs)
|
||||
music := testMusicDocument(301, 3001, "Global Song")
|
||||
voice := domain.Document{ID: 302, AccessHash: 3002, Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrAudio, Voice: true, AudioDuration: 5}}}
|
||||
for i, media := range []*domain.MessageMedia{
|
||||
{Kind: domain.MessageMediaKindDocument, Document: &voice, Voice: true},
|
||||
{Kind: domain.MessageMediaKindDocument, Document: &music},
|
||||
} {
|
||||
if _, err := messageStore.SendPrivateText(ctx, domain.SendPrivateTextRequest{
|
||||
SenderUserID: alice.ID,
|
||||
RecipientUserID: bob.ID,
|
||||
RandomID: int64(400 + i),
|
||||
Media: media,
|
||||
Date: 1700000100 + i,
|
||||
}); err != nil {
|
||||
t.Fatalf("send media %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
r := New(Config{}, Deps{
|
||||
Users: appusers.NewService(userStore),
|
||||
Messages: appmessages.NewService(messageStore, dialogs),
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
reqCtx := WithUserID(ctx, bob.ID)
|
||||
|
||||
if _, err := r.onMessagesSearchGlobal(reqCtx, &tg.MessagesSearchGlobalRequest{
|
||||
Filter: &tg.InputMessagesFilterEmpty{},
|
||||
OffsetPeer: &tg.InputPeerEmpty{},
|
||||
Limit: 10,
|
||||
}); err == nil || !strings.Contains(err.Error(), "SEARCH_QUERY_EMPTY") {
|
||||
t.Fatalf("empty non-music searchGlobal err = %v, want SEARCH_QUERY_EMPTY", err)
|
||||
}
|
||||
got, err := r.onMessagesSearchGlobal(reqCtx, &tg.MessagesSearchGlobalRequest{
|
||||
Filter: &tg.InputMessagesFilterMusic{},
|
||||
OffsetPeer: &tg.InputPeerEmpty{},
|
||||
Limit: 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("empty music searchGlobal: %v", err)
|
||||
}
|
||||
messages := messagesFromMessagesClass(t, got)
|
||||
if len(messages) != 1 {
|
||||
t.Fatalf("global music messages = %d, want 1", len(messages))
|
||||
}
|
||||
msg, ok := messages[0].(*tg.Message)
|
||||
if !ok {
|
||||
t.Fatalf("global music message = %T, want *tg.Message", messages[0])
|
||||
}
|
||||
docMedia, ok := msg.Media.(*tg.MessageMediaDocument)
|
||||
if !ok || documentClassID(docMedia.Document) != music.ID {
|
||||
t.Fatalf("global music media = %T %+v, want music document", msg.Media, msg.Media)
|
||||
}
|
||||
}
|
||||
|
||||
func testMusicDocument(id, accessHash int64, title string) domain.Document {
|
||||
return domain.Document{
|
||||
ID: id,
|
||||
AccessHash: accessHash,
|
||||
MimeType: "audio/mpeg",
|
||||
Attributes: []domain.DocumentAttribute{
|
||||
{Kind: domain.DocAttrAudio, AudioDuration: 180, Title: title, Performer: "Artist"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func documentClassID(doc tg.DocumentClass) int64 {
|
||||
if d, ok := doc.(*tg.Document); ok {
|
||||
return d.ID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func int64SliceEqual(a, b []int64) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func messagesFromMessagesClass(t *testing.T, got tg.MessagesMessagesClass) []tg.MessageClass {
|
||||
t.Helper()
|
||||
switch v := got.(type) {
|
||||
case *tg.MessagesMessages:
|
||||
return v.Messages
|
||||
case *tg.MessagesMessagesSlice:
|
||||
return v.Messages
|
||||
default:
|
||||
t.Fatalf("messages class = %T, want messages/messagesSlice", got)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue