fix: sync WebK compatibility paths
This commit is contained in:
parent
766c5db992
commit
76bfc5100f
12 changed files with 387 additions and 8 deletions
|
|
@ -14,7 +14,7 @@ import (
|
|||
// (记录于 docs/compatibility-matrix.md)。
|
||||
func BuildConfig(dc int, ip string, port int, now time.Time, publicBaseURL string) *tg.Config {
|
||||
meURLPrefix := links.NormalizeBaseURL(publicBaseURL) + "/"
|
||||
return &tg.Config{
|
||||
config := &tg.Config{
|
||||
Date: int(now.Unix()),
|
||||
Expires: int(now.Add(time.Hour).Unix()),
|
||||
TestMode: false,
|
||||
|
|
@ -55,6 +55,8 @@ func BuildConfig(dc int, ip string, port int, now time.Time, publicBaseURL strin
|
|||
MessageLengthMax: 4096,
|
||||
WebfileDCID: dc,
|
||||
}
|
||||
config.SetReactionsDefault(&tg.ReactionEmoji{Emoticon: DefaultReactionEmoticon})
|
||||
return config
|
||||
}
|
||||
|
||||
// NearestDC 构造 help.getNearestDc 返回值。
|
||||
|
|
|
|||
20
internal/compat/tdesktop/config_test.go
Normal file
20
internal/compat/tdesktop/config_test.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package tdesktop
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/iamxvbaba/td/tg"
|
||||
)
|
||||
|
||||
func TestBuildConfigIncludesDefaultReaction(t *testing.T) {
|
||||
config := BuildConfig(2, "127.0.0.1", 2398, time.Unix(1, 0), "https://telesrv.net")
|
||||
reaction, ok := config.GetReactionsDefault()
|
||||
if !ok {
|
||||
t.Fatal("reactions_default is absent")
|
||||
}
|
||||
emoji, ok := reaction.(*tg.ReactionEmoji)
|
||||
if !ok || emoji.Emoticon != DefaultReactionEmoticon {
|
||||
t.Fatalf("reactions_default = %#v, want %q emoji", reaction, DefaultReactionEmoticon)
|
||||
}
|
||||
}
|
||||
|
|
@ -194,7 +194,7 @@ func UniqueGiftChatThemes(hash int64) tg.AccountChatThemesClass {
|
|||
}
|
||||
}
|
||||
|
||||
// WallPapers returns the read-only Default wallpaper catalog. User wallpaper
|
||||
// WallPapers returns the read-only default wallpaper catalog. User wallpaper
|
||||
// upload/save/install remains outside the current TDesktop compatibility scope.
|
||||
func WallPapers(hash int64) tg.AccountWallPapersClass {
|
||||
if hash == wallPapersHash {
|
||||
|
|
@ -256,13 +256,17 @@ func DefaultGroupPhotoEmojis() tg.EmojiListClass {
|
|||
const availableReactionsHash = 20260602
|
||||
const emptyStickerSetHash = 20260602
|
||||
|
||||
// DefaultReactionEmoticon is the account fallback used by config and the
|
||||
// built-in available-reactions catalog.
|
||||
const DefaultReactionEmoticon = "\U0001f44d"
|
||||
|
||||
type defaultReaction struct {
|
||||
emoticon string
|
||||
title string
|
||||
}
|
||||
|
||||
var defaultAvailableReactions = []defaultReaction{
|
||||
{emoticon: "\U0001f44d", title: "Thumbs Up"},
|
||||
{emoticon: DefaultReactionEmoticon, title: "Thumbs Up"},
|
||||
{emoticon: "\u2764\ufe0f", title: "Red Heart"},
|
||||
{emoticon: "\U0001f602", title: "Face With Tears of Joy"},
|
||||
{emoticon: "\U0001f62e", title: "Face With Open Mouth"},
|
||||
|
|
|
|||
|
|
@ -1100,7 +1100,7 @@ func privacyErr(err error) error {
|
|||
}
|
||||
|
||||
type accountReactionSettingsService interface {
|
||||
GetReactionSettings(ctx context.Context, userID int64) (domain.AccountReactionSettings, error)
|
||||
accountReactionSettingsReader
|
||||
SetReactionsNotifySettings(ctx context.Context, userID int64, settings domain.ReactionsNotifySettings) (domain.AccountReactionSettings, error)
|
||||
}
|
||||
|
||||
|
|
|
|||
106
internal/rpc/client_info_test.go
Normal file
106
internal/rpc/client_info_test.go
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func TestNormalizeClientInfoBoundsMetadataAfterClassification(t *testing.T) {
|
||||
raw := ClientInfo{
|
||||
APIID: 2040,
|
||||
DeviceModel: "Mozilla/5.0 (iPhone) AppleWebKit/605.1.15 Safari/604.1 " + strings.Repeat("x", 160),
|
||||
SystemVersion: strings.Repeat("iOS", 30),
|
||||
AppVersion: strings.Repeat("2.2", 30),
|
||||
SystemLangCode: strings.Repeat("zh-", 30),
|
||||
LangPack: "webk" + strings.Repeat("x", 80),
|
||||
LangCode: strings.Repeat("zh-", 30),
|
||||
}
|
||||
|
||||
got := normalizeClientInfo(raw)
|
||||
if got.ClientType() != ClientTypeTWeb {
|
||||
t.Fatalf("client type = %s, want %s", got.ClientType(), ClientTypeTWeb)
|
||||
}
|
||||
assertClientMetadataRunes(t, "device model", got.DeviceModel, maxClientDeviceModelRunes)
|
||||
assertClientMetadataRunes(t, "system version", got.SystemVersion, maxClientMetadataRunes)
|
||||
assertClientMetadataRunes(t, "app version", got.AppVersion, maxClientMetadataRunes)
|
||||
assertClientMetadataRunes(t, "system language", got.SystemLangCode, maxClientMetadataRunes)
|
||||
assertClientMetadataRunes(t, "language pack", got.LangPack, maxClientMetadataRunes)
|
||||
assertClientMetadataRunes(t, "language code", got.LangCode, maxClientMetadataRunes)
|
||||
}
|
||||
|
||||
func TestNormalizeClientInfoUsesRuneLimitsAndProducesValidUTF8(t *testing.T) {
|
||||
got := normalizeClientInfo(ClientInfo{
|
||||
DeviceModel: strings.Repeat("\u754c", maxClientDeviceModelRunes+1),
|
||||
SystemVersion: strings.Repeat("\U0001f4f1", maxClientMetadataRunes+1),
|
||||
AppVersion: "2.2" + string([]byte{0xff}),
|
||||
LangPack: "webk",
|
||||
})
|
||||
|
||||
assertClientMetadataRunes(t, "device model", got.DeviceModel, maxClientDeviceModelRunes)
|
||||
assertClientMetadataRunes(t, "system version", got.SystemVersion, maxClientMetadataRunes)
|
||||
if !utf8.ValidString(got.AppVersion) {
|
||||
t.Fatalf("app version is not valid UTF-8: %q", got.AppVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizedClientInfoIsSharedByAuthPersistencePaths(t *testing.T) {
|
||||
authKeyID := [8]byte{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
ctx := WithLayer(context.Background(), 227)
|
||||
ctx = WithAuthKeyID(ctx, authKeyID)
|
||||
ctx = WithClientInfo(ctx, ClientInfo{
|
||||
APIID: 2040,
|
||||
DeviceModel: "Mozilla/5.0 AppleWebKit/605.1.15 " + strings.Repeat("x", 160),
|
||||
SystemVersion: strings.Repeat("iOS", 30),
|
||||
AppVersion: strings.Repeat("2.2", 30),
|
||||
LangPack: "webk",
|
||||
})
|
||||
info, ok := ClientInfoFrom(ctx)
|
||||
if !ok {
|
||||
t.Fatal("normalized client info missing from context")
|
||||
}
|
||||
|
||||
authz := (&Router{}).authzFromCtx(ctx)
|
||||
authKeyInfo := domainAuthKeyClientInfo(clientSessionInfo{
|
||||
layer: LayerFrom(ctx),
|
||||
hasClientInfo: true,
|
||||
clientInfo: info,
|
||||
})
|
||||
if authz.DeviceModel != authKeyInfo.DeviceModel ||
|
||||
authz.SystemVersion != authKeyInfo.SystemVersion ||
|
||||
authz.AppVersion != authKeyInfo.AppVersion ||
|
||||
authz.Platform != authKeyInfo.Platform ||
|
||||
authz.APIID != authKeyInfo.APIID ||
|
||||
authz.Layer != authKeyInfo.Layer {
|
||||
t.Fatalf("authorization metadata %+v differs from auth-key metadata %+v", authz, authKeyInfo)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeClientInfoPreservesMetadataWithinLimits(t *testing.T) {
|
||||
raw := ClientInfo{
|
||||
APIID: 4,
|
||||
DeviceModel: "Google Pixel 9",
|
||||
SystemVersion: "SDK 36",
|
||||
AppVersion: "12.7.3",
|
||||
SystemLangCode: "en-US",
|
||||
LangPack: "android",
|
||||
LangCode: "en",
|
||||
}
|
||||
got := normalizeClientInfo(raw)
|
||||
if got.DeviceModel != raw.DeviceModel || got.SystemVersion != raw.SystemVersion ||
|
||||
got.AppVersion != raw.AppVersion || got.SystemLangCode != raw.SystemLangCode ||
|
||||
got.LangPack != raw.LangPack || got.LangCode != raw.LangCode {
|
||||
t.Fatalf("metadata within limits changed: got %+v, want %+v", got, raw)
|
||||
}
|
||||
}
|
||||
|
||||
func assertClientMetadataRunes(t *testing.T, field, value string, want int) {
|
||||
t.Helper()
|
||||
if got := utf8.RuneCountInString(value); got != want {
|
||||
t.Fatalf("%s rune count = %d, want %d", field, got, want)
|
||||
}
|
||||
if !utf8.ValidString(value) {
|
||||
t.Fatalf("%s is not valid UTF-8", field)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/iamxvbaba/td/tg"
|
||||
)
|
||||
|
|
@ -35,6 +36,11 @@ func inboundRPCBytesFrom(ctx context.Context) int {
|
|||
|
||||
const currentClientLayer = tg.Layer
|
||||
|
||||
const (
|
||||
maxClientDeviceModelRunes = 128
|
||||
maxClientMetadataRunes = 64
|
||||
)
|
||||
|
||||
var androidSDKVersionRE = regexp.MustCompile(`\bsdk\s+\d+\b`)
|
||||
|
||||
type ClientType string
|
||||
|
|
@ -98,11 +104,33 @@ func ClientTypeFrom(ctx context.Context) ClientType {
|
|||
}
|
||||
|
||||
func normalizeClientInfo(info ClientInfo) ClientInfo {
|
||||
// Classify against the complete wire metadata before bounding the values
|
||||
// shared by auth-key and authorization persistence.
|
||||
info.Type = detectClientType(info)
|
||||
info.DeviceModel = truncateClientMetadata(info.DeviceModel, maxClientDeviceModelRunes)
|
||||
info.SystemVersion = truncateClientMetadata(info.SystemVersion, maxClientMetadataRunes)
|
||||
info.AppVersion = truncateClientMetadata(info.AppVersion, maxClientMetadataRunes)
|
||||
info.SystemLangCode = truncateClientMetadata(info.SystemLangCode, maxClientMetadataRunes)
|
||||
info.LangPack = truncateClientMetadata(info.LangPack, maxClientMetadataRunes)
|
||||
info.LangCode = truncateClientMetadata(info.LangCode, maxClientMetadataRunes)
|
||||
info.typeResolved = true
|
||||
return info
|
||||
}
|
||||
|
||||
func truncateClientMetadata(value string, maxRunes int) string {
|
||||
if maxRunes <= 0 || value == "" {
|
||||
return ""
|
||||
}
|
||||
if utf8.ValidString(value) && utf8.RuneCountInString(value) <= maxRunes {
|
||||
return value
|
||||
}
|
||||
runes := []rune(value)
|
||||
if len(runes) > maxRunes {
|
||||
runes = runes[:maxRunes]
|
||||
}
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
func (info ClientInfo) ClientType() ClientType {
|
||||
if info.typeResolved {
|
||||
if knownClientType(info.Type) {
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ import (
|
|||
|
||||
// registerHelp 注册 help.* RPC handler(DC 配置、最近 DC)。
|
||||
func (r *Router) registerHelp(d *tg.ServerDispatcher) {
|
||||
d.OnHelpGetConfig(func(ctx context.Context) (*tg.Config, error) {
|
||||
return tdesktop.BuildConfig(r.cfg.DC, r.cfg.IP, r.cfg.Port, r.clock.Now(), r.cfg.PublicBaseURL), nil
|
||||
})
|
||||
d.OnHelpGetConfig(r.onHelpGetConfig)
|
||||
d.OnHelpGetNearestDC(func(ctx context.Context) (*tg.NearestDC, error) {
|
||||
return tdesktop.NearestDC(r.cfg.DC), nil
|
||||
})
|
||||
|
|
@ -81,6 +79,29 @@ func (r *Router) registerHelp(d *tg.ServerDispatcher) {
|
|||
d.OnHelpGetPremiumPromo(r.onHelpGetPremiumPromo)
|
||||
}
|
||||
|
||||
func (r *Router) onHelpGetConfig(ctx context.Context) (*tg.Config, error) {
|
||||
config := tdesktop.BuildConfig(r.cfg.DC, r.cfg.IP, r.cfg.Port, r.clock.Now(), r.cfg.PublicBaseURL)
|
||||
userID, authorized, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
if !authorized || userID == 0 {
|
||||
return config, nil
|
||||
}
|
||||
if svc, ok := r.deps.Account.(accountReactionSettingsReader); ok {
|
||||
settings, err := svc.GetReactionSettings(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
reaction := tgMessageReaction(settings.DefaultReaction)
|
||||
if reaction == nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
config.SetReactionsDefault(reaction)
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// onHelpDismissSuggestion 为 DrKLO 改号成功后的 suggestion 清理提供有界兼容。
|
||||
// Android 会先把 suggestion 从本地状态删除,再发送该 RPC,且 generic 500 会被
|
||||
// 连接层持续重试。当前 server 不发布 pending suggestions,故非空 dismissal
|
||||
|
|
|
|||
|
|
@ -10,8 +10,12 @@ type accountDefaultReactionService interface {
|
|||
SetDefaultReaction(ctx context.Context, userID int64, reaction domain.MessageReaction) (domain.AccountReactionSettings, error)
|
||||
}
|
||||
|
||||
type accountPaidReactionPrivacyService interface {
|
||||
type accountReactionSettingsReader interface {
|
||||
GetReactionSettings(ctx context.Context, userID int64) (domain.AccountReactionSettings, error)
|
||||
}
|
||||
|
||||
type accountPaidReactionPrivacyService interface {
|
||||
accountReactionSettingsReader
|
||||
SetPaidReactionPrivacy(ctx context.Context, userID int64, privacy domain.PaidReactionPrivacy) (domain.AccountReactionSettings, error)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package rpc
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/iamxvbaba/td/tg"
|
||||
"go.uber.org/zap"
|
||||
|
|
@ -18,6 +19,9 @@ func (r *Router) onMessagesSetDefaultReaction(ctx context.Context, reaction tg.R
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := r.validateDefaultReaction(ctx, parsed); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if svc, ok := r.deps.Account.(accountDefaultReactionService); ok {
|
||||
if _, err := svc.SetDefaultReaction(ctx, userID, parsed); err != nil {
|
||||
return false, internalErr()
|
||||
|
|
@ -26,6 +30,37 @@ func (r *Router) onMessagesSetDefaultReaction(ctx context.Context, reaction tg.R
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (r *Router) validateDefaultReaction(ctx context.Context, reaction domain.MessageReaction) error {
|
||||
if reaction.Type == domain.MessageReactionCustomEmoji {
|
||||
return nil
|
||||
}
|
||||
if reaction.Type != domain.MessageReactionEmoji {
|
||||
return reactionInvalidErr()
|
||||
}
|
||||
|
||||
if r.deps.Files != nil {
|
||||
catalog, err := r.deps.Files.ListAvailableReactions(ctx)
|
||||
if err != nil {
|
||||
return internalErr()
|
||||
}
|
||||
if len(catalog) > 0 {
|
||||
for _, item := range catalog {
|
||||
if !item.Inactive && strings.TrimSpace(item.Reaction) == reaction.Emoticon {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return reactionInvalidErr()
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range staticReactionCatalog() {
|
||||
if item.Key() == reaction.Key() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return reactionInvalidErr()
|
||||
}
|
||||
|
||||
func (r *Router) onMessagesGetPaidReactionPrivacy(ctx context.Context) (tg.UpdatesClass, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
|
|
|
|||
25
internal/rpc/messages_received.go
Normal file
25
internal/rpc/messages_received.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/iamxvbaba/td/tg"
|
||||
)
|
||||
|
||||
// onMessagesReceivedMessages acknowledges the client's highest observed
|
||||
// message id. telesrv does not enqueue mobile PUSH notifications, so there are
|
||||
// no pending notifications to cancel and the exact cancellation set is empty.
|
||||
// This acknowledgement must never advance read boundaries or update state.
|
||||
func (r *Router) onMessagesReceivedMessages(ctx context.Context, _ int) ([]tg.ReceivedNotifyMessage, error) {
|
||||
userID, authorized, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
if !authorized || userID == 0 {
|
||||
return nil, authKeyUnregisteredErr()
|
||||
}
|
||||
if r.userIsBot(ctx, userID) {
|
||||
return nil, botMethodInvalidErr()
|
||||
}
|
||||
return []tg.ReceivedNotifyMessage{}, nil
|
||||
}
|
||||
133
internal/rpc/messages_received_config_test.go
Normal file
133
internal/rpc/messages_received_config_test.go
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/iamxvbaba/td/bin"
|
||||
"github.com/iamxvbaba/td/clock"
|
||||
"github.com/iamxvbaba/td/tg"
|
||||
"github.com/iamxvbaba/td/tgerr"
|
||||
"go.uber.org/zap/zaptest"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
type reactionSettingsAccountStub struct {
|
||||
AccountService
|
||||
settings domain.AccountReactionSettings
|
||||
getErr error
|
||||
setCalls int
|
||||
}
|
||||
|
||||
func (s *reactionSettingsAccountStub) GetReactionSettings(context.Context, int64) (domain.AccountReactionSettings, error) {
|
||||
return s.settings, s.getErr
|
||||
}
|
||||
|
||||
func (s *reactionSettingsAccountStub) SetDefaultReaction(_ context.Context, _ int64, reaction domain.MessageReaction) (domain.AccountReactionSettings, error) {
|
||||
s.setCalls++
|
||||
s.settings.DefaultReaction = reaction
|
||||
return s.settings, nil
|
||||
}
|
||||
|
||||
type reactionCatalogErrorFiles struct {
|
||||
FilesService
|
||||
}
|
||||
|
||||
func (reactionCatalogErrorFiles) ListAvailableReactions(context.Context) ([]domain.AvailableReaction, error) {
|
||||
return nil, errors.New("catalog unavailable")
|
||||
}
|
||||
|
||||
func TestHelpGetConfigReturnsDefaultAndAccountReaction(t *testing.T) {
|
||||
account := &reactionSettingsAccountStub{settings: domain.DefaultAccountReactionSettings()}
|
||||
r := New(Config{DC: 2, PublicBaseURL: "https://telesrv.net"}, Deps{Account: account}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
preAuth, err := r.onHelpGetConfig(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("pre-auth help.getConfig: %v", err)
|
||||
}
|
||||
if emoji, ok := preAuth.ReactionsDefault.(*tg.ReactionEmoji); !ok || emoji.Emoticon != "\U0001f44d" {
|
||||
t.Fatalf("pre-auth reactions_default = %#v, want thumbs up", preAuth.ReactionsDefault)
|
||||
}
|
||||
|
||||
account.settings.DefaultReaction = domain.MessageReaction{Type: domain.MessageReactionCustomEmoji, DocumentID: 7001}
|
||||
authorized, err := r.onHelpGetConfig(WithUserID(context.Background(), 42))
|
||||
if err != nil {
|
||||
t.Fatalf("authorized help.getConfig: %v", err)
|
||||
}
|
||||
if custom, ok := authorized.ReactionsDefault.(*tg.ReactionCustomEmoji); !ok || custom.DocumentID != 7001 {
|
||||
t.Fatalf("authorized reactions_default = %#v, want custom emoji 7001", authorized.ReactionsDefault)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaultReactionRequiresActiveCatalogEmoji(t *testing.T) {
|
||||
account := &reactionSettingsAccountStub{settings: domain.DefaultAccountReactionSettings()}
|
||||
files := &fakeFiles{reactions: []domain.AvailableReaction{
|
||||
{Reaction: "\U0001f525"},
|
||||
{Reaction: "\U0001f602", Inactive: true},
|
||||
}}
|
||||
r := New(Config{}, Deps{Account: account, Files: files}, zaptest.NewLogger(t), clock.System)
|
||||
ctx := WithUserID(context.Background(), 42)
|
||||
|
||||
if ok, err := r.onMessagesSetDefaultReaction(ctx, &tg.ReactionEmoji{Emoticon: "\U0001f525"}); err != nil || !ok {
|
||||
t.Fatalf("set active reaction = %v, %v", ok, err)
|
||||
}
|
||||
if account.setCalls != 1 {
|
||||
t.Fatalf("set calls = %d, want 1", account.setCalls)
|
||||
}
|
||||
for _, emoticon := range []string{"\U0001f602", "\U0001f680"} {
|
||||
if _, err := r.onMessagesSetDefaultReaction(ctx, &tg.ReactionEmoji{Emoticon: emoticon}); !tgerr.Is(err, "REACTION_INVALID") {
|
||||
t.Fatalf("set reaction %q err = %v, want REACTION_INVALID", emoticon, err)
|
||||
}
|
||||
}
|
||||
if account.setCalls != 1 {
|
||||
t.Fatalf("invalid reactions reached persistence: set calls = %d", account.setCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaultReactionFailsClosedWhenCatalogReadFails(t *testing.T) {
|
||||
account := &reactionSettingsAccountStub{settings: domain.DefaultAccountReactionSettings()}
|
||||
r := New(Config{}, Deps{Account: account, Files: reactionCatalogErrorFiles{}}, zaptest.NewLogger(t), clock.System)
|
||||
if _, err := r.onMessagesSetDefaultReaction(WithUserID(context.Background(), 42), &tg.ReactionEmoji{Emoticon: "\U0001f44d"}); !tgerr.Is(err, "INTERNAL_SERVER_ERROR") {
|
||||
t.Fatalf("set reaction err = %v, want INTERNAL_SERVER_ERROR", err)
|
||||
}
|
||||
if account.setCalls != 0 {
|
||||
t.Fatalf("failed catalog read reached persistence: set calls = %d", account.setCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessagesReceivedMessagesIsAuthorizedNoop(t *testing.T) {
|
||||
r := New(Config{}, Deps{}, zaptest.NewLogger(t), clock.System)
|
||||
for _, maxID := range []int{-1, 0, 1, int(^uint32(0) >> 1)} {
|
||||
got, err := r.onMessagesReceivedMessages(WithUserID(context.Background(), 42), maxID)
|
||||
if err != nil {
|
||||
t.Fatalf("max_id %d: %v", maxID, err)
|
||||
}
|
||||
if got == nil || len(got) != 0 {
|
||||
t.Fatalf("max_id %d result = %#v, want non-nil empty cancellation set", maxID, got)
|
||||
}
|
||||
}
|
||||
if _, err := r.onMessagesReceivedMessages(context.Background(), 1); !tgerr.Is(err, "AUTH_KEY_UNREGISTERED") {
|
||||
t.Fatalf("unauthorized err = %v, want AUTH_KEY_UNREGISTERED", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessagesReceivedMessagesIsRegistered(t *testing.T) {
|
||||
r := New(Config{}, Deps{}, zaptest.NewLogger(t), clock.System)
|
||||
var request bin.Buffer
|
||||
if err := (&tg.MessagesReceivedMessagesRequest{MaxID: 99}).Encode(&request); err != nil {
|
||||
t.Fatalf("encode messages.receivedMessages: %v", err)
|
||||
}
|
||||
result, method, err := r.DispatchWithMethod(WithUserID(context.Background(), 42), [8]byte{1}, 7, &request)
|
||||
if err != nil {
|
||||
t.Fatalf("dispatch messages.receivedMessages: %v", err)
|
||||
}
|
||||
if method != "messages.receivedMessages" {
|
||||
t.Fatalf("method = %q, want messages.receivedMessages", method)
|
||||
}
|
||||
vector, ok := result.(*tg.ReceivedNotifyMessageVector)
|
||||
if !ok || vector == nil || vector.Elems == nil || len(vector.Elems) != 0 {
|
||||
t.Fatalf("result = %#v (%T), want non-nil empty ReceivedNotifyMessageVector", result, result)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
// registerMessages 注册 messages.* RPC handler。
|
||||
func (r *Router) registerMessages(d *tg.ServerDispatcher) {
|
||||
d.OnMessagesReceivedMessages(r.onMessagesReceivedMessages)
|
||||
d.OnMessagesSetTyping(r.onMessagesSetTyping)
|
||||
d.OnMessagesSaveDraft(r.onMessagesSaveDraft)
|
||||
d.OnMessagesSaveDefaultSendAs(r.onMessagesSaveDefaultSendAs)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue