fix: sync WebA labels and forward fixes
This commit is contained in:
parent
7bb7e11603
commit
4d3bbeabd8
11 changed files with 422 additions and 10 deletions
|
|
@ -72,7 +72,7 @@ func (r *Router) langpackLanguage(ctx context.Context, langPack, langCode string
|
|||
langCode = "en"
|
||||
}
|
||||
}
|
||||
langCode = strings.ToLower(langCode)
|
||||
langCode = normalizeLangpackCode(langCode)
|
||||
languages := r.langpackLanguages(ctx, langPack)
|
||||
for _, lang := range languages {
|
||||
if strings.ToLower(lang.LangCode) == langCode {
|
||||
|
|
@ -150,3 +150,11 @@ func langPackFromClient(ctx context.Context) string {
|
|||
}
|
||||
return "tdesktop"
|
||||
}
|
||||
|
||||
func normalizeLangpackCode(langCode string) string {
|
||||
code := strings.ToLower(strings.TrimSpace(langCode))
|
||||
if code == "" {
|
||||
return "en"
|
||||
}
|
||||
return strings.TrimSuffix(code, "-raw")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,11 @@ func TestLangpackGetLanguage(t *testing.T) {
|
|||
if lang.LangCode != "zh-hans" || lang.PluralCode != "zh" {
|
||||
t.Fatalf("language = %+v, want zh-hans", lang)
|
||||
}
|
||||
|
||||
raw := r.langpackLanguage(context.Background(), "tdesktop", "en-raw")
|
||||
if raw.LangCode != "en" {
|
||||
t.Fatalf("language(en-raw) = %+v, want en", raw)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLangpackAndroidPersianLanguage(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ const (
|
|||
maxPollVotesOffsetLength = 128
|
||||
maxTodoItems = 30
|
||||
maxTodoTitleLength = 200
|
||||
// maxTodoItemID 是清单项 id 的防御上限:协议只要求列表内唯一正整数(客户端通常
|
||||
// 顺序分配),不能用条目数上限当 id 边界,否则非顺序分配的合法 id 被误拒。
|
||||
maxTodoItemID = 1 << 16
|
||||
// maxTodoItemID 是清单项 id 的防御上限:协议字段是 int32,WebA 会用 8 位左右
|
||||
// 的稀疏本地 id,不能用条目数上限或顺序分配假设当 id 边界。
|
||||
maxTodoItemID = 1<<31 - 1
|
||||
maxVenueTitleLength = 256
|
||||
maxVenueAddressLength = 512
|
||||
maxVenueProviderLength = 64
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ func (r *Router) onMessagesForwardMessages(ctx context.Context, req *tg.Messages
|
|||
if !topMsgIDSet && req.TopMsgID != 0 {
|
||||
topMsgID, topMsgIDSet = req.TopMsgID, true
|
||||
}
|
||||
if topMsgIDSet && topMsgID == -1 {
|
||||
topMsgID, topMsgIDSet = 0, false
|
||||
}
|
||||
if topMsgIDSet && (topMsgID < 0 || topMsgID > domain.MaxMessageBoxID) {
|
||||
return nil, replyMessageIDInvalidErr()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,52 @@ func TestMessagesForwardMessagesRecordsRequestAndReturnsUpdates(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestMessagesForwardMessagesTreatsMainThreadTopMsgSentinelAsAbsent(t *testing.T) {
|
||||
const (
|
||||
ownerID = int64(1000000101)
|
||||
fromID = int64(1000000102)
|
||||
toID = int64(1000000103)
|
||||
)
|
||||
ctx := context.Background()
|
||||
messages := &captureMessages{list: domain.MessageList{Messages: []domain.Message{
|
||||
{
|
||||
ID: 8,
|
||||
OwnerUserID: ownerID,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: fromID},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: fromID},
|
||||
Date: 1700000108,
|
||||
Body: "main thread source",
|
||||
},
|
||||
}}}
|
||||
r := New(Config{}, Deps{
|
||||
Messages: messages,
|
||||
Users: mapUsersService{users: map[int64]domain.User{
|
||||
ownerID: {ID: ownerID, FirstName: "Owner"},
|
||||
fromID: {ID: fromID, FirstName: "From"},
|
||||
toID: {ID: toID, FirstName: "To"},
|
||||
}},
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
req := &tg.MessagesForwardMessagesRequest{
|
||||
FromPeer: &tg.InputPeerUser{UserID: fromID},
|
||||
ToPeer: &tg.InputPeerUser{UserID: toID},
|
||||
ID: []int{8},
|
||||
RandomID: []int64{8001},
|
||||
}
|
||||
req.SetTopMsgID(-1)
|
||||
|
||||
updatesClass, err := r.onMessagesForwardMessages(WithUserID(ctx, ownerID), req)
|
||||
if err != nil {
|
||||
t.Fatalf("forward with main thread top_msg_id sentinel: %v", err)
|
||||
}
|
||||
if messages.sendReq.ReplyTo != nil {
|
||||
t.Fatalf("reply = %+v, want nil for main thread sentinel", messages.sendReq.ReplyTo)
|
||||
}
|
||||
updates, ok := updatesClass.(*tg.Updates)
|
||||
if !ok || len(updates.Updates) != 2 {
|
||||
t.Fatalf("updates = %T %+v, want updateMessageID + updateNewMessage", updatesClass, updatesClass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessagesForwardMessagesLoadsPrivateSourcesInSingleBatch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userStore := memory.NewUserStore()
|
||||
|
|
@ -312,6 +358,30 @@ func TestMessagesForwardMessagesInputPeerEmptyRejectsBadIDsBeforeLookup(t *testi
|
|||
}
|
||||
}
|
||||
|
||||
func TestMessagesForwardMessagesRejectsOtherNegativeTopMsgID(t *testing.T) {
|
||||
const ownerID = int64(1780243210)
|
||||
ctx := context.Background()
|
||||
messages := &captureMessages{}
|
||||
r := New(Config{}, Deps{
|
||||
Messages: messages,
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
req := &tg.MessagesForwardMessagesRequest{
|
||||
FromPeer: &tg.InputPeerUser{UserID: 1780243211},
|
||||
ToPeer: &tg.InputPeerUser{UserID: 1780243212},
|
||||
ID: []int{1},
|
||||
RandomID: []int64{10001},
|
||||
}
|
||||
req.SetTopMsgID(-2)
|
||||
|
||||
_, err := r.onMessagesForwardMessages(WithUserID(ctx, ownerID), req)
|
||||
if err == nil || !strings.Contains(err.Error(), "REPLY_MESSAGE_ID_INVALID") {
|
||||
t.Fatalf("forward negative top_msg_id err = %v, want REPLY_MESSAGE_ID_INVALID", err)
|
||||
}
|
||||
if messages.getMessagesCalls != 0 {
|
||||
t.Fatalf("GetMessages calls = %d, want no source lookup for invalid top_msg_id", messages.getMessagesCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessagesForwardMessagesNormalizesAndroidDuplicateIDRetry(t *testing.T) {
|
||||
const (
|
||||
ownerID = int64(1780243210)
|
||||
|
|
|
|||
|
|
@ -201,6 +201,9 @@ func (r *Router) messageReplyFromInput(ctx context.Context, userID int64, peer d
|
|||
return nil, inputConstructorInvalidErr()
|
||||
}
|
||||
}
|
||||
if reply.Zero() {
|
||||
return nil, nil
|
||||
}
|
||||
if _, ok := reply.GetMonoforumPeerID(); ok {
|
||||
return nil, replyToMonoforumPeerInvalidErr()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -244,6 +244,37 @@ func TestMessageReplyFromInputStorySucceedsAndProjectsStoryHeader(t *testing.T)
|
|||
}
|
||||
}
|
||||
|
||||
func TestMessageReplyFromInputEmptyMessageIsAbsent(t *testing.T) {
|
||||
const userID = int64(1000000001)
|
||||
ctx := WithUserID(context.Background(), userID)
|
||||
r := New(Config{}, Deps{}, zaptest.NewLogger(t), clock.System)
|
||||
peer := domain.Peer{Type: domain.PeerTypeUser, ID: 1000000002}
|
||||
|
||||
reply, err := r.messageReplyFromInput(ctx, userID, peer, &tg.InputReplyToMessage{})
|
||||
if err != nil {
|
||||
t.Fatalf("empty reply err = %v, want nil", err)
|
||||
}
|
||||
if reply != nil {
|
||||
t.Fatalf("empty reply = %+v, want nil", reply)
|
||||
}
|
||||
|
||||
topicReply := &tg.InputReplyToMessage{}
|
||||
topicReply.SetTopMsgID(123)
|
||||
reply, err = r.messageReplyFromInput(ctx, userID, peer, topicReply)
|
||||
if err != nil {
|
||||
t.Fatalf("topic-only reply err = %v, want nil", err)
|
||||
}
|
||||
if reply == nil || reply.MessageID != 0 || reply.TopMessageID != 123 {
|
||||
t.Fatalf("topic-only reply = %+v, want top_msg_id=123", reply)
|
||||
}
|
||||
|
||||
quoteOnly := &tg.InputReplyToMessage{}
|
||||
quoteOnly.SetQuoteText("orphan quote")
|
||||
if _, err := r.messageReplyFromInput(ctx, userID, peer, quoteOnly); err == nil || !strings.Contains(err.Error(), "REPLY_MESSAGE_ID_INVALID") {
|
||||
t.Fatalf("quote-only reply err = %v, want REPLY_MESSAGE_ID_INVALID", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageReplyFromInputUnsupportedShapesReturnExplicitErrors(t *testing.T) {
|
||||
const userID = int64(1000000001)
|
||||
ctx := WithUserID(context.Background(), userID)
|
||||
|
|
|
|||
|
|
@ -53,6 +53,59 @@ func TestSendMediaTodoEcho(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSendMediaTodoWithEmptyReplyToTreatsReplyAsAbsent(t *testing.T) {
|
||||
r, owner, friend := newMediaTestRouter(t)
|
||||
req := &tg.MessagesSendMediaRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
|
||||
Media: &tg.InputMediaTodo{Todo: tg.TodoList{
|
||||
Title: tg.TextWithEntities{Text: "web checklist", Entities: []tg.MessageEntityClass{}},
|
||||
List: []tg.TodoItem{
|
||||
{ID: 1, Title: tg.TextWithEntities{Text: "send item", Entities: []tg.MessageEntityClass{}}},
|
||||
},
|
||||
}},
|
||||
RandomID: 7005,
|
||||
}
|
||||
req.SetReplyTo(&tg.InputReplyToMessage{})
|
||||
|
||||
updates, err := r.onMessagesSendMedia(WithUserID(context.Background(), owner.ID), req)
|
||||
if err != nil {
|
||||
t.Fatalf("sendMedia todo with empty reply: %v", err)
|
||||
}
|
||||
msg := newMessageFromUpdates(t, updates)
|
||||
if msg.ReplyTo != nil {
|
||||
t.Fatalf("reply_to = %T, want nil for empty input reply", msg.ReplyTo)
|
||||
}
|
||||
if media, ok := msg.Media.(*tg.MessageMediaToDo); !ok || media.Todo.Title.Text != "web checklist" {
|
||||
t.Fatalf("media = %#v, want todo checklist", msg.Media)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendMediaTodoAcceptsSparseLargeItemIDs(t *testing.T) {
|
||||
r, owner, friend := newMediaTestRouter(t)
|
||||
updates, err := r.onMessagesSendMedia(WithUserID(context.Background(), owner.ID), &tg.MessagesSendMediaRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
|
||||
Media: &tg.InputMediaTodo{Todo: tg.TodoList{
|
||||
Title: tg.TextWithEntities{Text: "web sparse ids", Entities: []tg.MessageEntityClass{}},
|
||||
List: []tg.TodoItem{
|
||||
{ID: 56151290, Title: tg.TextWithEntities{Text: "first", Entities: []tg.MessageEntityClass{}}},
|
||||
{ID: 56151305, Title: tg.TextWithEntities{Text: "second", Entities: []tg.MessageEntityClass{}}},
|
||||
},
|
||||
}},
|
||||
RandomID: 7006,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("sendMedia todo sparse ids: %v", err)
|
||||
}
|
||||
msg := newMessageFromUpdates(t, updates)
|
||||
media, ok := msg.Media.(*tg.MessageMediaToDo)
|
||||
if !ok {
|
||||
t.Fatalf("media = %T, want MessageMediaToDo", msg.Media)
|
||||
}
|
||||
if got := media.Todo.List[0].ID; got != 56151290 {
|
||||
t.Fatalf("first todo id = %d, want 56151290", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToggleTodoCompletedAndAppend(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
r, owner, friend := newMediaTestRouter(t)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue