owpengram-server/internal/rpc/messages_polls_tdesktop_shape_test.go

143 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package rpc
import (
"context"
"testing"
"github.com/iamxvbaba/td/tg"
)
// 回归TDesktop 创建 poll 的真实请求形状kDefaultPollCreateFlags 默认带
// public_voters/multiple_choice/open_answers/shuffle_answers答案为无 option 的
// inputPollAnswer——option 键由服务端分配)。修复前 open_answers 被一刀切
// MEDIA_INVALIDinputPollAnswer 被 POLL_ANSWER_INVALID。
func TestSendMediaPollTDesktopShape(t *testing.T) {
ctx := context.Background()
r, owner, friend := newMediaTestRouter(t)
// 第一个答案带配图(实测 TDesktop 创建面板可给答案附 photo/document
// 引用 newMediaTestRouter 预置的 document 555。
withMedia := &tg.InputPollAnswer{Text: tg.TextWithEntities{Text: "r", Entities: []tg.MessageEntityClass{}}}
withMedia.SetMedia(&tg.InputMediaDocument{ID: &tg.InputDocument{ID: 555, AccessHash: 5}})
media := &tg.InputMediaPoll{
Poll: tg.Poll{
ID: 0,
PublicVoters: true,
MultipleChoice: true,
OpenAnswers: true,
ShuffleAnswers: true,
Question: tg.TextWithEntities{Text: "color?", Entities: []tg.MessageEntityClass{}},
Answers: []tg.PollAnswerClass{
withMedia,
&tg.InputPollAnswer{Text: tg.TextWithEntities{Text: "blue", Entities: []tg.MessageEntityClass{}}},
&tg.InputPollAnswer{Text: tg.TextWithEntities{Text: "green", Entities: []tg.MessageEntityClass{}}},
},
},
}
updates, err := r.onMessagesSendMedia(WithUserID(ctx, owner.ID), &tg.MessagesSendMediaRequest{
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
Media: media,
RandomID: 9001,
})
if err != nil {
t.Fatalf("sendMedia TDesktop poll shape: %v", err)
}
msg := newMessageFromUpdates(t, updates)
mediaPoll, ok := msg.Media.(*tg.MessageMediaPoll)
if !ok {
t.Fatalf("expected MessageMediaPoll, got %T", msg.Media)
}
poll := mediaPoll.Poll
if !poll.PublicVoters || !poll.MultipleChoice || !poll.ShuffleAnswers {
t.Fatalf("create flags lost: %+v", poll)
}
if poll.OpenAnswers {
t.Fatal("open_answers must be stripped (addPollAnswer not wired, no add-answer UI entry)")
}
if len(poll.Answers) != 3 {
t.Fatalf("answers = %d, want 3", len(poll.Answers))
}
seen := map[string]struct{}{}
for i, answerClass := range poll.Answers {
answer, ok := answerClass.(*tg.PollAnswer)
if !ok {
t.Fatalf("echoed answer %d type = %T, want pollAnswer", i, answerClass)
}
if len(answer.Option) == 0 {
t.Fatalf("answer %d option not assigned server-side", i)
}
if _, dup := seen[string(answer.Option)]; dup {
t.Fatalf("answer %d option duplicated", i)
}
seen[string(answer.Option)] = struct{}{}
}
// 答案配图必须随快照回 echo。
firstAnswer := poll.Answers[0].(*tg.PollAnswer)
answerMedia, hasAnswerMedia := firstAnswer.GetMedia()
if !hasAnswerMedia {
t.Fatal("first answer media lost")
}
docMedia, ok := answerMedia.(*tg.MessageMediaDocument)
if !ok {
t.Fatalf("answer media = %T, want MessageMediaDocument", answerMedia)
}
if doc, ok := docMedia.Document.(*tg.Document); !ok || doc.ID != 555 {
t.Fatalf("answer media document = %#v, want id 555", docMedia.Document)
}
// 服务端分配的 option 必须能直接用于投票。
firstOption := poll.Answers[0].(*tg.PollAnswer).Option
voteUpdates, err := r.onMessagesSendVote(WithUserID(ctx, friend.ID), &tg.MessagesSendVoteRequest{
Peer: &tg.InputPeerUser{UserID: owner.ID, AccessHash: owner.AccessHash},
MsgID: msg.ID,
Options: [][]byte{firstOption},
})
if err != nil {
t.Fatalf("vote with server-assigned option: %v", err)
}
_, results := pollFromUpdates(t, voteUpdates)
if total, _ := results.GetTotalVoters(); total != 1 {
t.Fatalf("total voters = %d, want 1", total)
}
}
// 回归quiz 同样走 inputPollAnswer + 下标 correct_answers服务端分配 option 后下标语义必须成立。
func TestSendMediaQuizTDesktopShape(t *testing.T) {
ctx := context.Background()
r, owner, friend := newMediaTestRouter(t)
media := &tg.InputMediaPoll{
Poll: tg.Poll{
Quiz: true,
Question: tg.TextWithEntities{Text: "2+2?", Entities: []tg.MessageEntityClass{}},
Answers: []tg.PollAnswerClass{
&tg.InputPollAnswer{Text: tg.TextWithEntities{Text: "3", Entities: []tg.MessageEntityClass{}}},
&tg.InputPollAnswer{Text: tg.TextWithEntities{Text: "4", Entities: []tg.MessageEntityClass{}}},
},
},
}
media.SetCorrectAnswers([]int{1})
updates, err := r.onMessagesSendMedia(WithUserID(ctx, owner.ID), &tg.MessagesSendMediaRequest{
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
Media: media,
RandomID: 9002,
})
if err != nil {
t.Fatalf("sendMedia quiz shape: %v", err)
}
msg := newMessageFromUpdates(t, updates)
poll := msg.Media.(*tg.MessageMediaPoll).Poll
correctOption := poll.Answers[1].(*tg.PollAnswer).Option
voteUpdates, err := r.onMessagesSendVote(WithUserID(ctx, friend.ID), &tg.MessagesSendVoteRequest{
Peer: &tg.InputPeerUser{UserID: owner.ID, AccessHash: owner.AccessHash},
MsgID: msg.ID,
Options: [][]byte{correctOption},
})
if err != nil {
t.Fatalf("quiz vote: %v", err)
}
_, results := pollFromUpdates(t, voteUpdates)
byOption := votersByOption(t, results)
if v := byOption[correctOption[0]]; !v.Chosen || !v.Correct {
t.Fatalf("correct answer entry = %+v, want chosen+correct", v)
}
}