chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
572
internal/rpc/updates_rpc_test.go
Normal file
572
internal/rpc/updates_rpc_test.go
Normal file
|
|
@ -0,0 +1,572 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gotd/td/clock"
|
||||
"github.com/gotd/td/proto"
|
||||
"github.com/gotd/td/tg"
|
||||
"go.uber.org/zap/zaptest"
|
||||
|
||||
appchannels "telesrv/internal/app/channels"
|
||||
appupdates "telesrv/internal/app/updates"
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/store/memory"
|
||||
)
|
||||
|
||||
func TestPushLoginMessageSendsUpdateNewMessage(t *testing.T) {
|
||||
sessions := &captureSessions{}
|
||||
r := New(Config{}, Deps{Sessions: sessions}, zaptest.NewLogger(t), clock.System)
|
||||
msg := domain.Message{
|
||||
ID: 99,
|
||||
OwnerUserID: 1000000001,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: domain.OfficialSystemUserID},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: domain.OfficialSystemUserID},
|
||||
Date: 1700000100,
|
||||
Body: "Login code: 12345",
|
||||
Entities: []domain.MessageEntity{{Type: domain.MessageEntityBold, Offset: 0, Length: 11}},
|
||||
}
|
||||
event := domain.UpdateEvent{Type: domain.UpdateEventNewMessage, Pts: 4, PtsCount: 1, Date: msg.Date, Message: msg}
|
||||
state := domain.UpdateState{Pts: 4, Date: 1700000100, Seq: 2}
|
||||
|
||||
r.pushLoginMessage(context.Background(), [8]byte{}, 55, event, state)
|
||||
|
||||
gotSession := sessions.snapshot()
|
||||
if gotSession.sessionID != 55 || gotSession.messageType != proto.MessageFromServer {
|
||||
t.Fatalf("push target = session %d type %v, want session 55 MessageFromServer", gotSession.sessionID, gotSession.messageType)
|
||||
}
|
||||
got, ok := gotSession.message.(*tg.Updates)
|
||||
if !ok {
|
||||
t.Fatalf("pushed message = %T, want *tg.Updates", gotSession.message)
|
||||
}
|
||||
if len(got.Updates) != 1 || len(got.Users) != 1 {
|
||||
t.Fatalf("updates payload = %+v, want one update and official user", got)
|
||||
}
|
||||
update, ok := got.Updates[0].(*tg.UpdateNewMessage)
|
||||
if !ok {
|
||||
t.Fatalf("update = %T, want *tg.UpdateNewMessage", got.Updates[0])
|
||||
}
|
||||
if update.Pts != event.Pts || update.PtsCount != event.PtsCount || got.Seq != state.Seq {
|
||||
t.Fatalf("update state = pts %d pts_count %d seq %d, want %d/%d/%d", update.Pts, update.PtsCount, got.Seq, event.Pts, event.PtsCount, state.Seq)
|
||||
}
|
||||
message, ok := update.Message.(*tg.Message)
|
||||
if !ok {
|
||||
t.Fatalf("update message = %T, want *tg.Message", update.Message)
|
||||
}
|
||||
if message.ID != msg.ID || message.Message != msg.Body {
|
||||
t.Fatalf("message = %+v, want login message %+v", message, msg)
|
||||
}
|
||||
user, ok := got.Users[0].(*tg.User)
|
||||
if !ok || user.ID != domain.OfficialSystemUserID || !user.Verified || !user.Support {
|
||||
t.Fatalf("user = %#v, want verified official system user", got.Users[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignInServiceNotificationMatchesEnterpriseShape(t *testing.T) {
|
||||
var authKeyID [8]byte
|
||||
authKeyID[0] = 9
|
||||
r := New(Config{}, Deps{}, zaptest.NewLogger(t), clock.System)
|
||||
ctx := WithClientInfo(context.Background(), ClientInfo{
|
||||
DeviceModel: "Telegram Desktop",
|
||||
SystemVersion: "Windows",
|
||||
AppVersion: "6.8.4",
|
||||
})
|
||||
|
||||
got := r.tgSignInServiceNotification(ctx, domain.User{
|
||||
ID: 1000000001,
|
||||
FirstName: "Test",
|
||||
LastName: "User",
|
||||
}, authKeyID)
|
||||
|
||||
if len(got.Updates) != 1 {
|
||||
t.Fatalf("updates = %+v, want one service notification", got.Updates)
|
||||
}
|
||||
update, ok := got.Updates[0].(*tg.UpdateServiceNotification)
|
||||
if !ok {
|
||||
t.Fatalf("update = %T, want *tg.UpdateServiceNotification", got.Updates[0])
|
||||
}
|
||||
if update.Popup || update.InboxDate == 0 || update.Media == nil {
|
||||
t.Fatalf("notification flags/media = popup %v inbox %d media %T", update.Popup, update.InboxDate, update.Media)
|
||||
}
|
||||
for _, want := range []string{"New login.", "Test User", "Telegram Desktop", "Settings > Devices"} {
|
||||
if !strings.Contains(update.Message, want) {
|
||||
t.Fatalf("notification message %q missing %q", update.Message, want)
|
||||
}
|
||||
}
|
||||
if len(update.Entities) < 3 {
|
||||
t.Fatalf("entities = %+v, want bold entities for title/settings links", update.Entities)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogOutClearsSessionAndUpdateState(t *testing.T) {
|
||||
var authKeyID [8]byte
|
||||
authKeyID[0] = 5
|
||||
auth := &captureAuthService{}
|
||||
updates := &captureUpdates{}
|
||||
sessions := &captureSessions{authKeyID: authKeyID, authKeyResolved: true, userID: 1000000001, userResolved: true}
|
||||
r := New(Config{}, Deps{
|
||||
Auth: auth,
|
||||
Updates: updates,
|
||||
Sessions: sessions,
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
_, err := r.onAuthLogOut(WithAuthKeyID(context.Background(), authKeyID))
|
||||
if err != nil {
|
||||
t.Fatalf("auth.logOut: %v", err)
|
||||
}
|
||||
if auth.loggedOutAuthKeyID != authKeyID {
|
||||
t.Fatalf("logged out auth key = %x, want %x", auth.loggedOutAuthKeyID, authKeyID)
|
||||
}
|
||||
if updates.clearedAuthKeyID != authKeyID || !updates.cleared {
|
||||
t.Fatalf("cleared auth key = %x cleared=%v, want %x", updates.clearedAuthKeyID, updates.cleared, authKeyID)
|
||||
}
|
||||
gotSession := sessions.snapshot()
|
||||
if gotSession.userID != 0 || !gotSession.userResolved {
|
||||
t.Fatalf("session user after logout = %d resolved=%v, want 0/true", gotSession.userID, gotSession.userResolved)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignInDifferentUserClearsAuthKeyUpdateState(t *testing.T) {
|
||||
var authKeyID [8]byte
|
||||
authKeyID[0] = 6
|
||||
auth := &captureAuthService{signInUser: domain.User{ID: 1000000002, FirstName: "Two"}}
|
||||
updates := &captureUpdates{}
|
||||
sessions := &captureSessions{authKeyID: authKeyID, authKeyResolved: true, userID: 1000000001, userResolved: true}
|
||||
r := New(Config{}, Deps{
|
||||
Auth: auth,
|
||||
Updates: updates,
|
||||
Sessions: sessions,
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
ctx := WithUserID(WithAuthKeyID(WithSessionID(context.Background(), 77), authKeyID), 1000000001)
|
||||
|
||||
_, err := r.onAuthSignIn(ctx, &tg.AuthSignInRequest{PhoneNumber: "15550000002", PhoneCodeHash: "hash", PhoneCode: "12345"})
|
||||
if err != nil {
|
||||
t.Fatalf("auth.signIn: %v", err)
|
||||
}
|
||||
if updates.clearedAuthKeyID != authKeyID || !updates.cleared {
|
||||
t.Fatalf("cleared auth key = %x cleared=%v, want %x", updates.clearedAuthKeyID, updates.cleared, authKeyID)
|
||||
}
|
||||
gotSession := sessions.snapshot()
|
||||
if gotSession.userID != 1000000002 {
|
||||
t.Fatalf("session user = %d, want new user 1000000002", gotSession.userID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatesGetStateMarksSessionReadyForPush(t *testing.T) {
|
||||
sessions := &captureSessions{}
|
||||
r := New(Config{}, Deps{
|
||||
Sessions: sessions,
|
||||
Updates: &captureUpdates{state: domain.UpdateState{Pts: 3, Date: 1700000000, Seq: 2}},
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
got, err := r.onUpdatesGetState(WithSessionID(context.Background(), 77))
|
||||
if err != nil {
|
||||
t.Fatalf("updates.getState: %v", err)
|
||||
}
|
||||
if got.Pts != 3 || got.Seq != 2 {
|
||||
t.Fatalf("state = %+v, want pts=3 seq=2", got)
|
||||
}
|
||||
gotSession := sessions.snapshot()
|
||||
if gotSession.sessionID != 77 || !gotSession.receives {
|
||||
t.Fatalf("session ready = id %d receives %v, want 77/true", gotSession.sessionID, gotSession.receives)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUpdatesGetStateReturnsAccountCurrentState 复现 TDesktop 冷启动未读重复:
|
||||
// getState 必须返回账号当前最新状态(而非设备旧确认水位),且不得再推
|
||||
// updatesTooLong 诱导差分——TDesktop 不持久化 pts,启动期离线数据由
|
||||
// getDialogs 快照承载,旧水位差分会把快照里已计入的消息重放一遍
|
||||
// (实测未读 2→3、dialog 预览被旧消息抢占)。
|
||||
func TestUpdatesGetStateReturnsAccountCurrentState(t *testing.T) {
|
||||
sessions := &captureSessions{}
|
||||
current := domain.UpdateState{Pts: 4, Date: 1700000001}
|
||||
updates := &captureUpdates{
|
||||
state: domain.UpdateState{Pts: 3, Date: 1700000000},
|
||||
currentState: ¤t,
|
||||
}
|
||||
r := New(Config{}, Deps{
|
||||
Sessions: sessions,
|
||||
Updates: updates,
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
got, err := r.onUpdatesGetState(WithSessionID(context.Background(), 77))
|
||||
if err != nil {
|
||||
t.Fatalf("updates.getState: %v", err)
|
||||
}
|
||||
if got.Pts != 4 {
|
||||
t.Fatalf("state pts = %d, want account current 4 (per-key stale=3 会触发客户端重放)", got.Pts)
|
||||
}
|
||||
if !updates.acknowledged {
|
||||
t.Fatal("getState 未推进设备确认水位")
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if msg := sessions.snapshot().message; msg != nil {
|
||||
if _, ok := msg.(*tg.UpdatesTooLong); ok {
|
||||
t.Fatal("getState 后不应再推 updatesTooLong(会诱导 TDesktop 重放快照前差分)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatesDifferenceIncludesLoginMessageAndOfficialUser(t *testing.T) {
|
||||
msg := domain.Message{
|
||||
ID: 88,
|
||||
OwnerUserID: 1000000001,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: domain.OfficialSystemUserID},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: domain.OfficialSystemUserID},
|
||||
Date: 1700000100,
|
||||
Body: "Login code: 12345",
|
||||
}
|
||||
got, ok := tgUpdatesDifference(0, domain.UpdateDifference{
|
||||
State: domain.UpdateState{Pts: 5, Date: msg.Date, Seq: 4},
|
||||
Events: []domain.UpdateEvent{{
|
||||
Type: domain.UpdateEventNewMessage,
|
||||
Pts: 5,
|
||||
PtsCount: 1,
|
||||
Date: msg.Date,
|
||||
Message: msg,
|
||||
}},
|
||||
}).(*tg.UpdatesDifference)
|
||||
if !ok {
|
||||
t.Fatalf("difference = %T, want *tg.UpdatesDifference", got)
|
||||
}
|
||||
if got.State.Pts != 5 || len(got.NewMessages) != 1 || len(got.Users) != 1 {
|
||||
t.Fatalf("difference = %+v, want one message, one official user, pts=5", got)
|
||||
}
|
||||
user, ok := got.Users[0].(*tg.User)
|
||||
if !ok || user.ID != domain.OfficialSystemUserID {
|
||||
t.Fatalf("user = %#v, want official system user", got.Users[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatesDifferenceMarksViewerUserAsSelf(t *testing.T) {
|
||||
viewerID := int64(1000000001)
|
||||
msg := domain.Message{
|
||||
ID: 90,
|
||||
OwnerUserID: viewerID,
|
||||
Out: true,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: 1000000002},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: viewerID},
|
||||
Date: 1700000102,
|
||||
Body: "self probe",
|
||||
}
|
||||
got, ok := tgUpdatesDifference(viewerID, domain.UpdateDifference{
|
||||
State: domain.UpdateState{Pts: 7, Date: msg.Date},
|
||||
Events: []domain.UpdateEvent{{
|
||||
UserID: viewerID,
|
||||
Type: domain.UpdateEventNewMessage,
|
||||
Pts: 7,
|
||||
PtsCount: 1,
|
||||
Date: msg.Date,
|
||||
Message: msg,
|
||||
Users: []domain.User{
|
||||
{ID: viewerID, FirstName: "Me"},
|
||||
{ID: 1000000002, FirstName: "Peer"},
|
||||
},
|
||||
}},
|
||||
}).(*tg.UpdatesDifference)
|
||||
if !ok {
|
||||
t.Fatalf("difference = %T, want *tg.UpdatesDifference", got)
|
||||
}
|
||||
var sawSelf, sawPeer bool
|
||||
for _, item := range got.Users {
|
||||
user, ok := item.(*tg.User)
|
||||
if !ok {
|
||||
t.Fatalf("user = %#v, want *tg.User", item)
|
||||
}
|
||||
switch user.ID {
|
||||
case viewerID:
|
||||
sawSelf = true
|
||||
if !user.Self {
|
||||
t.Fatalf("viewer user = %#v, want self flag set: clients persist this object as the current account", user)
|
||||
}
|
||||
case 1000000002:
|
||||
sawPeer = true
|
||||
if user.Self {
|
||||
t.Fatalf("peer user = %#v, must not carry self flag", user)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !sawSelf || !sawPeer {
|
||||
t.Fatalf("users = %+v, want viewer and peer entries", got.Users)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatesDifferenceIncludesForwardSourceChannelChat(t *testing.T) {
|
||||
source := domain.Channel{
|
||||
ID: 2000000001,
|
||||
AccessHash: 9001,
|
||||
Title: "Source Channel",
|
||||
Broadcast: true,
|
||||
Date: 1700000000,
|
||||
}
|
||||
msg := domain.Message{
|
||||
ID: 89,
|
||||
OwnerUserID: 1000000001,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: 1000000002},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: 1000000001},
|
||||
Date: 1700000101,
|
||||
Body: "forwarded",
|
||||
Forward: &domain.MessageForward{From: domain.Peer{Type: domain.PeerTypeChannel, ID: source.ID}, Date: 1700000000},
|
||||
}
|
||||
got, ok := tgUpdatesDifference(0, domain.UpdateDifference{
|
||||
State: domain.UpdateState{Pts: 6, Date: msg.Date},
|
||||
Events: []domain.UpdateEvent{{
|
||||
UserID: msg.OwnerUserID,
|
||||
Type: domain.UpdateEventNewMessage,
|
||||
Pts: 6,
|
||||
PtsCount: 1,
|
||||
Date: msg.Date,
|
||||
Message: msg,
|
||||
Channels: []domain.Channel{source},
|
||||
}},
|
||||
}).(*tg.UpdatesDifference)
|
||||
if !ok {
|
||||
t.Fatalf("difference = %T, want *tg.UpdatesDifference", got)
|
||||
}
|
||||
if len(got.Chats) != 1 {
|
||||
t.Fatalf("chats = %+v, want source channel", got.Chats)
|
||||
}
|
||||
ch, ok := got.Chats[0].(*tg.Channel)
|
||||
if !ok || ch.ID != source.ID || ch.Title != source.Title {
|
||||
t.Fatalf("chat = %#v, want source channel", got.Chats[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutboxEventIncludesForwardSourceChannelChat(t *testing.T) {
|
||||
source := domain.Channel{
|
||||
ID: 2000000002,
|
||||
AccessHash: 9002,
|
||||
Title: "Forward Source",
|
||||
Broadcast: true,
|
||||
Date: 1700000000,
|
||||
}
|
||||
update := tgUpdateForOutboxEvent(domain.UpdateEvent{
|
||||
UserID: 1000000001,
|
||||
Type: domain.UpdateEventNewMessage,
|
||||
Pts: 7,
|
||||
PtsCount: 1,
|
||||
Date: 1700000102,
|
||||
Message: domain.Message{
|
||||
ID: 90,
|
||||
OwnerUserID: 1000000001,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: 1000000002},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: 1000000002},
|
||||
Date: 1700000102,
|
||||
Body: "forwarded",
|
||||
Forward: &domain.MessageForward{From: domain.Peer{Type: domain.PeerTypeChannel, ID: source.ID}, Date: 1700000000},
|
||||
},
|
||||
Channels: []domain.Channel{source},
|
||||
})
|
||||
if update == nil || len(update.Chats) != 1 {
|
||||
t.Fatalf("update = %+v, want source channel chat", update)
|
||||
}
|
||||
ch, ok := update.Chats[0].(*tg.Channel)
|
||||
if !ok || ch.ID != source.ID {
|
||||
t.Fatalf("chat = %#v, want source channel", update.Chats[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutboxEventChannelViewForumAsMessages(t *testing.T) {
|
||||
update := tgUpdateForOutboxEvent(domain.UpdateEvent{
|
||||
UserID: 1000000001,
|
||||
Type: domain.UpdateEventChannelViewForum,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 2000000002},
|
||||
Bool: true,
|
||||
Date: 1700000200,
|
||||
})
|
||||
if update == nil || len(update.Updates) != 1 {
|
||||
t.Fatalf("update = %+v, want one updateChannelViewForumAsMessages", update)
|
||||
}
|
||||
got, ok := update.Updates[0].(*tg.UpdateChannelViewForumAsMessages)
|
||||
if !ok || got.ChannelID != 2000000002 || !got.Enabled {
|
||||
t.Fatalf("update = %#v, want channel view-forum-as-messages enabled", update.Updates[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatesDifferenceIncludesChannelTooLongNudge(t *testing.T) {
|
||||
const viewerID int64 = 1000000002
|
||||
got, ok := tgUpdatesDifference(viewerID, domain.UpdateDifference{
|
||||
State: domain.UpdateState{Pts: 8, Date: 1700000250, Seq: 0},
|
||||
ChannelNudges: []domain.ChannelDifferenceNudge{{
|
||||
ChannelID: 2000000001,
|
||||
Pts: 12,
|
||||
Channel: &domain.ChannelView{
|
||||
Channel: domain.Channel{
|
||||
ID: 2000000001,
|
||||
AccessHash: 9100000001,
|
||||
Title: "Dirty group",
|
||||
Megagroup: true,
|
||||
Date: 1700000200,
|
||||
},
|
||||
Self: domain.ChannelMember{
|
||||
ChannelID: 2000000001,
|
||||
UserID: viewerID,
|
||||
Role: domain.ChannelRoleMember,
|
||||
Status: domain.ChannelMemberActive,
|
||||
},
|
||||
},
|
||||
}},
|
||||
}).(*tg.UpdatesDifference)
|
||||
if !ok {
|
||||
t.Fatalf("difference = %T, want *tg.UpdatesDifference", got)
|
||||
}
|
||||
if got.State.Pts != 8 || len(got.OtherUpdates) != 1 || len(got.Chats) != 1 {
|
||||
t.Fatalf("difference = %+v, want one channel nudge, one chat and account pts unchanged", got)
|
||||
}
|
||||
update, ok := got.OtherUpdates[0].(*tg.UpdateChannelTooLong)
|
||||
if !ok || update.ChannelID != 2000000001 {
|
||||
t.Fatalf("update = %T %+v, want UpdateChannelTooLong", got.OtherUpdates[0], got.OtherUpdates[0])
|
||||
}
|
||||
if pts, ok := update.GetPts(); !ok || pts != 12 {
|
||||
t.Fatalf("channel nudge pts = %d set=%v, want 12", pts, ok)
|
||||
}
|
||||
chat, ok := got.Chats[0].(*tg.Channel)
|
||||
if !ok || chat.ID != 2000000001 || chat.Min {
|
||||
t.Fatalf("chat = %T %+v, want full channel chat for channel nudge", got.Chats[0], got.Chats[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatesDifferenceChannelNudgeUpgradesExistingMinChat(t *testing.T) {
|
||||
const viewerID int64 = 1000000001
|
||||
channel := domain.Channel{
|
||||
ID: 2000000003,
|
||||
AccessHash: 9100000003,
|
||||
CreatorUserID: viewerID,
|
||||
Title: "Offline created group",
|
||||
Megagroup: true,
|
||||
Date: 1700000300,
|
||||
}
|
||||
got, ok := tgUpdatesDifference(viewerID, domain.UpdateDifference{
|
||||
State: domain.UpdateState{Pts: 9, Date: 1700000310, Seq: 0},
|
||||
Events: []domain.UpdateEvent{{
|
||||
UserID: viewerID,
|
||||
Type: domain.UpdateEventChannelState,
|
||||
Pts: 8,
|
||||
PtsCount: 1,
|
||||
Date: 1700000300,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: channel.ID},
|
||||
Channels: []domain.Channel{channel},
|
||||
}},
|
||||
ChannelNudges: []domain.ChannelDifferenceNudge{{
|
||||
ChannelID: channel.ID,
|
||||
Pts: 3,
|
||||
Channel: &domain.ChannelView{
|
||||
Channel: channel,
|
||||
Self: domain.ChannelMember{
|
||||
ChannelID: channel.ID,
|
||||
UserID: viewerID,
|
||||
Role: domain.ChannelRoleCreator,
|
||||
Status: domain.ChannelMemberActive,
|
||||
},
|
||||
},
|
||||
}},
|
||||
}).(*tg.UpdatesDifference)
|
||||
if !ok {
|
||||
t.Fatalf("difference = %T, want *tg.UpdatesDifference", got)
|
||||
}
|
||||
if len(got.OtherUpdates) != 2 || len(got.Chats) != 1 {
|
||||
t.Fatalf("difference = %+v, want channel state, nudge and one deduped chat", got)
|
||||
}
|
||||
chat, ok := got.Chats[0].(*tg.Channel)
|
||||
if !ok || chat.ID != channel.ID || chat.Min || !chat.Creator {
|
||||
t.Fatalf("chat = %T %+v, want upgraded full creator channel for Android unknown-channel recovery", got.Chats[0], got.Chats[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatesGetDifferenceChannelNudgeIncludesFullChat(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
const (
|
||||
ownerID int64 = 1000000101
|
||||
memberID int64 = 1000000102
|
||||
)
|
||||
channelStore := memory.NewChannelStore()
|
||||
channelSvc := appchannels.NewService(channelStore)
|
||||
created, err := channelSvc.CreateMegagroupFromCreateChat(ctx, ownerID, domain.CreateChannelRequest{
|
||||
Title: "Android visible group",
|
||||
Date: 1700000400,
|
||||
MemberUserIDs: []int64{memberID},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMegagroupFromCreateChat: %v", err)
|
||||
}
|
||||
updateSvc := appupdates.NewService(memory.NewUpdateStateStore(), memory.NewUpdateEventStore())
|
||||
r := New(Config{}, Deps{
|
||||
Updates: updateSvc,
|
||||
Channels: channelSvc,
|
||||
}, zaptest.NewLogger(t), fixedClock{now: time.Unix(1700000410, 0)})
|
||||
|
||||
diff, err := r.onUpdatesGetDifference(WithUserID(ctx, memberID), &tg.UpdatesGetDifferenceRequest{
|
||||
Date: 1700000399,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("updates.getDifference: %v", err)
|
||||
}
|
||||
got, ok := diff.(*tg.UpdatesDifference)
|
||||
if !ok {
|
||||
t.Fatalf("difference = %T, want *tg.UpdatesDifference", diff)
|
||||
}
|
||||
if len(got.OtherUpdates) != 1 || len(got.Chats) != 1 {
|
||||
t.Fatalf("difference = %+v, want channel nudge with chat", got)
|
||||
}
|
||||
update, ok := got.OtherUpdates[0].(*tg.UpdateChannelTooLong)
|
||||
if !ok || update.ChannelID != created.Channel.ID {
|
||||
t.Fatalf("update = %T %+v, want updateChannelTooLong for channel %d", got.OtherUpdates[0], got.OtherUpdates[0], created.Channel.ID)
|
||||
}
|
||||
chat, ok := got.Chats[0].(*tg.Channel)
|
||||
if !ok || chat.ID != created.Channel.ID || chat.Min {
|
||||
t.Fatalf("chat = %T %+v, want full channel chat for Android unknown-channel recovery", got.Chats[0], got.Chats[0])
|
||||
}
|
||||
if chat.AccessHash == 0 {
|
||||
t.Fatalf("chat access_hash = 0, want full channel access hash")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatesDifferenceIncludesSettingsUpdates(t *testing.T) {
|
||||
peer := domain.Peer{Type: domain.PeerTypeUser, ID: 1000000002}
|
||||
got, ok := tgUpdatesDifference(0, domain.UpdateDifference{
|
||||
State: domain.UpdateState{Pts: 6, Date: 1700000300, Seq: 0},
|
||||
Events: []domain.UpdateEvent{
|
||||
{Type: domain.UpdateEventContactsReset, Pts: 1, PtsCount: 1, Date: 1700000300},
|
||||
{Type: domain.UpdateEventDialogPinned, Pts: 2, PtsCount: 1, Date: 1700000300, Peer: peer, Bool: true},
|
||||
{Type: domain.UpdateEventPinnedDialogs, Pts: 3, PtsCount: 1, Date: 1700000300, Peers: []domain.Peer{peer}},
|
||||
{Type: domain.UpdateEventDialogUnreadMark, Pts: 4, PtsCount: 1, Date: 1700000300, Peer: peer, Bool: false},
|
||||
{Type: domain.UpdateEventPeerSettings, Pts: 5, PtsCount: 1, Date: 1700000300, Peer: peer, Settings: domain.PeerSettings{ShareContact: true}},
|
||||
{Type: domain.UpdateEventPeerStoryBlocked, Pts: 6, PtsCount: 1, Date: 1700000300, Peer: peer, Bool: true},
|
||||
},
|
||||
}).(*tg.UpdatesDifference)
|
||||
if !ok {
|
||||
t.Fatalf("difference = %T, want *tg.UpdatesDifference", got)
|
||||
}
|
||||
if got.State.Pts != 6 || len(got.OtherUpdates) != 6 {
|
||||
t.Fatalf("difference = %+v, want six settings updates and pts=6", got)
|
||||
}
|
||||
if _, ok := got.OtherUpdates[0].(*tg.UpdateContactsReset); !ok {
|
||||
t.Fatalf("update[0] = %T, want contacts reset", got.OtherUpdates[0])
|
||||
}
|
||||
pinned, ok := got.OtherUpdates[1].(*tg.UpdateDialogPinned)
|
||||
if !ok || !pinned.Pinned {
|
||||
t.Fatalf("update[1] = %+v (%T), want pinned dialog", got.OtherUpdates[1], got.OtherUpdates[1])
|
||||
}
|
||||
pinnedDialogs, ok := got.OtherUpdates[2].(*tg.UpdatePinnedDialogs)
|
||||
if !ok || len(pinnedDialogs.Order) != 1 {
|
||||
t.Fatalf("update[2] = %T, want pinned dialogs", got.OtherUpdates[2])
|
||||
}
|
||||
unread, ok := got.OtherUpdates[3].(*tg.UpdateDialogUnreadMark)
|
||||
if !ok || unread.Unread {
|
||||
t.Fatalf("update[3] = %+v (%T), want unread=false mark", got.OtherUpdates[3], got.OtherUpdates[3])
|
||||
}
|
||||
peerSettings, ok := got.OtherUpdates[4].(*tg.UpdatePeerSettings)
|
||||
if !ok || !peerSettings.Settings.ShareContact {
|
||||
t.Fatalf("update[4] = %T, want peer settings", got.OtherUpdates[4])
|
||||
}
|
||||
peerBlocked, ok := got.OtherUpdates[5].(*tg.UpdatePeerBlocked)
|
||||
if !ok || !peerBlocked.Blocked || !peerBlocked.BlockedMyStoriesFrom {
|
||||
t.Fatalf("update[5] = %+v (%T), want story peer blocked", got.OtherUpdates[5], got.OtherUpdates[5])
|
||||
}
|
||||
if peerUser, ok := peerBlocked.PeerID.(*tg.PeerUser); !ok || peerUser.UserID != peer.ID {
|
||||
t.Fatalf("update[5] peer = %#v, want user %d", peerBlocked.PeerID, peer.ID)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue