chore: refresh gramsrv public release

This commit is contained in:
A 2026-06-30 14:37:43 +08:00
parent 75cebe8dbf
commit 70b6820474
1274 changed files with 378751 additions and 59919 deletions

View file

@ -0,0 +1,234 @@
package rpc
import (
"bytes"
"context"
"testing"
"github.com/gotd/td/tg"
"github.com/gotd/td/tgerr"
)
func TestSendMediaPrivateGeoPoint(t *testing.T) {
ctx := context.Background()
r, owner, friend := newMediaTestRouter(t)
point := &tg.InputGeoPoint{Lat: 39.9042, Long: 116.4074}
point.SetAccuracyRadius(30) // gotd flag 字段必须 Set*() 置 flag 位,直接赋字段 getter 读不到
updates, err := r.onMessagesSendMedia(WithUserID(ctx, owner.ID), &tg.MessagesSendMediaRequest{
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
Media: &tg.InputMediaGeoPoint{GeoPoint: point},
RandomID: 2001,
})
if err != nil {
t.Fatalf("sendMedia geo: %v", err)
}
msg := newMessageFromUpdates(t, updates)
media, ok := msg.Media.(*tg.MessageMediaGeo)
if !ok {
t.Fatalf("expected MessageMediaGeo, got %T", msg.Media)
}
geo, ok := media.Geo.(*tg.GeoPoint)
if !ok {
t.Fatalf("expected tg.GeoPoint, got %T", media.Geo)
}
if geo.Lat != 39.9042 || geo.Long != 116.4074 {
t.Errorf("geo = (%v, %v), want (39.9042, 116.4074)", geo.Lat, geo.Long)
}
if geo.AccessHash == 0 {
t.Error("geo access_hash should be non-zero (clients echo it to upload.getWebFile)")
}
if radius, ok := geo.GetAccuracyRadius(); !ok || radius != 30 {
t.Errorf("accuracy_radius = %d (%v), want 30", radius, ok)
}
}
func TestSendMediaGeoPointOutOfRange(t *testing.T) {
ctx := context.Background()
r, owner, friend := newMediaTestRouter(t)
for _, point := range []*tg.InputGeoPoint{
{Lat: 90.5, Long: 0},
{Lat: -91, Long: 0},
{Lat: 0, Long: 180.01},
{Lat: 0, Long: -181},
} {
_, err := r.onMessagesSendMedia(WithUserID(ctx, owner.ID), &tg.MessagesSendMediaRequest{
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
Media: &tg.InputMediaGeoPoint{GeoPoint: point},
RandomID: 2002,
})
if err == nil || !tgerr.Is(err, "MEDIA_INVALID") {
t.Errorf("geo (%v,%v): err = %v, want MEDIA_INVALID", point.Lat, point.Long, err)
}
}
}
func TestSendMediaGeoPointEmpty(t *testing.T) {
ctx := context.Background()
r, owner, friend := newMediaTestRouter(t)
_, err := r.onMessagesSendMedia(WithUserID(ctx, owner.ID), &tg.MessagesSendMediaRequest{
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
Media: &tg.InputMediaGeoPoint{GeoPoint: &tg.InputGeoPointEmpty{}},
RandomID: 2003,
})
if err == nil || !tgerr.Is(err, "MEDIA_EMPTY") {
t.Fatalf("err = %v, want MEDIA_EMPTY", err)
}
}
func TestSendMediaPrivateVenue(t *testing.T) {
ctx := context.Background()
r, owner, friend := newMediaTestRouter(t)
updates, err := r.onMessagesSendMedia(WithUserID(ctx, owner.ID), &tg.MessagesSendMediaRequest{
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
Media: &tg.InputMediaVenue{
GeoPoint: &tg.InputGeoPoint{Lat: 31.2304, Long: 121.4737},
Title: "People's Square",
Address: "Huangpu, Shanghai",
Provider: "foursquare",
VenueID: "4b5b3c4ff964a520fd0029e3",
VenueType: "arts_entertainment/default",
},
RandomID: 2004,
})
if err != nil {
t.Fatalf("sendMedia venue: %v", err)
}
msg := newMessageFromUpdates(t, updates)
media, ok := msg.Media.(*tg.MessageMediaVenue)
if !ok {
t.Fatalf("expected MessageMediaVenue, got %T", msg.Media)
}
if media.Title != "People's Square" || media.Address != "Huangpu, Shanghai" ||
media.Provider != "foursquare" || media.VenueID != "4b5b3c4ff964a520fd0029e3" ||
media.VenueType != "arts_entertainment/default" {
t.Fatalf("venue media = %+v, want preserved venue payload", media)
}
geo, ok := media.Geo.(*tg.GeoPoint)
if !ok || geo.Lat != 31.2304 || geo.Long != 121.4737 {
t.Fatalf("venue geo = %#v, want (31.2304, 121.4737)", media.Geo)
}
}
func TestSendMediaVenueWithoutTitle(t *testing.T) {
ctx := context.Background()
r, owner, friend := newMediaTestRouter(t)
_, err := r.onMessagesSendMedia(WithUserID(ctx, owner.ID), &tg.MessagesSendMediaRequest{
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
Media: &tg.InputMediaVenue{
GeoPoint: &tg.InputGeoPoint{Lat: 1, Long: 1},
Title: " ",
},
RandomID: 2005,
})
if err == nil || !tgerr.Is(err, "MEDIA_EMPTY") {
t.Fatalf("err = %v, want MEDIA_EMPTY", err)
}
}
func TestSendMediaPrivateDiceValueRanges(t *testing.T) {
ctx := context.Background()
r, owner, friend := newMediaTestRouter(t)
cases := []struct {
emoticon string
want string
max int
}{
{"\U0001F3B2", "\U0001F3B2", 6}, // 🎲
{"\U0001F3AF", "\U0001F3AF", 6}, // 🎯
{"\U0001F3B3", "\U0001F3B3", 6}, // 🎳
{"\U0001F3C0", "\U0001F3C0", 5}, // 🏀
{"⚽", "⚽", 5}, // 裸足球
{"⚽️", "⚽", 5}, // 带 U+FE0F 变体,须归一
{"\U0001F3B0", "\U0001F3B0", 64}, // 🎰
}
randomID := int64(3000)
for _, tc := range cases {
// 多抽几次覆盖取值边界;value 必须始终落在 [1, max]。
for i := 0; i < 8; i++ {
randomID++
updates, err := r.onMessagesSendMedia(WithUserID(ctx, owner.ID), &tg.MessagesSendMediaRequest{
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
Media: &tg.InputMediaDice{Emoticon: tc.emoticon},
RandomID: randomID,
})
if err != nil {
t.Fatalf("sendMedia dice %q: %v", tc.emoticon, err)
}
msg := newMessageFromUpdates(t, updates)
media, ok := msg.Media.(*tg.MessageMediaDice)
if !ok {
t.Fatalf("expected MessageMediaDice, got %T", msg.Media)
}
if media.Emoticon != tc.want {
t.Fatalf("dice emoticon = %q, want %q", media.Emoticon, tc.want)
}
if media.Value < 1 || media.Value > tc.max {
t.Fatalf("dice %q value = %d, want in [1, %d]", tc.emoticon, media.Value, tc.max)
}
}
}
}
func TestSendMediaDiceUnknownEmoticon(t *testing.T) {
ctx := context.Background()
r, owner, friend := newMediaTestRouter(t)
_, err := r.onMessagesSendMedia(WithUserID(ctx, owner.ID), &tg.MessagesSendMediaRequest{
Peer: &tg.InputPeerUser{UserID: friend.ID, AccessHash: friend.AccessHash},
Media: &tg.InputMediaDice{Emoticon: "\U0001F600"},
RandomID: 3100,
})
if err == nil || !tgerr.Is(err, "EMOTICON_INVALID") {
t.Fatalf("err = %v, want EMOTICON_INVALID", err)
}
}
func TestUploadGetWebFileGeoTile(t *testing.T) {
ctx := context.Background()
r, owner, _ := newMediaTestRouter(t)
req := &tg.UploadGetWebFileRequest{
Location: &tg.InputWebFileGeoPointLocation{
GeoPoint: &tg.InputGeoPoint{Lat: 39.9, Long: 116.4},
AccessHash: 12345,
W: 100, H: 100, Zoom: 15, Scale: 1,
},
Offset: 0,
Limit: 1 << 17,
}
file, err := r.onUploadGetWebFile(WithUserID(ctx, owner.ID), req)
if err != nil {
t.Fatalf("getWebFile: %v", err)
}
if file.Size != 12 || len(file.Bytes) != 12 {
t.Fatalf("size = %d bytes = %d, want 12/12 (fake tile)", file.Size, len(file.Bytes))
}
if file.MimeType != "image/png" {
t.Errorf("mime = %q, want image/png", file.MimeType)
}
// offset 续传按字节切片,size 始终是全量大小。
req.Offset = 4
req.Limit = 4
chunk, err := r.onUploadGetWebFile(WithUserID(ctx, owner.ID), req)
if err != nil {
t.Fatalf("getWebFile offset: %v", err)
}
if chunk.Size != 12 || !bytes.Equal(chunk.Bytes, []byte{0x0D, 0x0A, 0x1A, 0x0A}) {
t.Fatalf("offset chunk = %v (size %d), want bytes 4..8 of tile", chunk.Bytes, chunk.Size)
}
// URL 形态当前无来源,必须显式拒绝而非静默。
if _, err := r.onUploadGetWebFile(WithUserID(ctx, owner.ID), &tg.UploadGetWebFileRequest{
Location: &tg.InputWebFileLocation{URL: "https://example.com/a.png", AccessHash: 1},
Limit: 4096,
}); err == nil || !tgerr.Is(err, "LOCATION_INVALID") {
t.Fatalf("url webfile err = %v, want LOCATION_INVALID", err)
}
}