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,124 @@
package postgres
import (
"bytes"
"context"
"testing"
"time"
"telesrv/internal/domain"
)
// TestSendPrivateRichMessageSurvivesReadPaths 验证 Layer 227 富文本rich_message JSONB
// 经全部读路径字节级保真且双盒一致:
// - 发送结果双端sender/recipient携带 rich_message
// - Blocks 是不透明 TL 字节,含 0x00/0xFF/非 UTF-8 经 JSONB base64 逐字节相等;
// - 内嵌 photo 快照id/access_hash/sizes存活
// - historyListByUser/ GetByIDs / 更新事件重建路径均带 rich_message。
func TestSendPrivateRichMessageSurvivesReadPaths(t *testing.T) {
pool := testPool(t)
ctx := context.Background()
suffix := randomSuffix(t)
users := NewUserStore(pool)
sender, err := users.Create(ctx, domain.User{AccessHash: 81, Phone: "+1996" + suffix + "01", FirstName: "RichSender"})
if err != nil {
t.Fatalf("create sender: %v", err)
}
recipient, err := users.Create(ctx, domain.User{AccessHash: 82, Phone: "+1996" + suffix + "02", FirstName: "RichRecipient"})
if err != nil {
t.Fatalf("create recipient: %v", err)
}
ids := []int64{sender.ID, recipient.ID}
t.Cleanup(func() {
_, _ = pool.Exec(ctx, "DELETE FROM dispatch_outbox WHERE target_user_id = ANY($1::bigint[])", ids)
_, _ = pool.Exec(ctx, "DELETE FROM user_update_events WHERE user_id = ANY($1::bigint[])", ids)
_, _ = pool.Exec(ctx, "DELETE FROM message_boxes WHERE owner_user_id = ANY($1::bigint[])", ids)
_, _ = pool.Exec(ctx, "DELETE FROM private_messages WHERE sender_user_id = ANY($1::bigint[])", ids)
_, _ = pool.Exec(ctx, "DELETE FROM dialogs WHERE user_id = ANY($1::bigint[])", ids)
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", ids)
})
messages := NewMessageStore(pool, WithMessageAllocators(&perUserCounterAllocator{}))
// Blocks 在 store 层是不透明字节rpc 层 TL 编解码),含 0x00/0xFF/非 UTF-8 以验证
// JSONB base64 字节级 round-trip。
rawBlocks := []byte{0x00, 0x01, 0xFF, 0x80, 'b', 'l', 'k'}
rich := &domain.MessageRichMessage{
Rtl: true,
Blocks: rawBlocks,
Photos: []domain.Photo{{ID: 9911, AccessHash: 7, DCID: 2, Sizes: []domain.PhotoSize{{Kind: domain.PhotoSizeKindDefault, Type: "x", W: 800, H: 600}}}},
}
res, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
SenderUserID: sender.ID,
RecipientUserID: recipient.ID,
RandomID: time.Now().UnixNano(),
Message: "rich",
RichMessage: rich,
Date: int(time.Now().Unix()),
})
if err != nil {
t.Fatalf("send private rich: %v", err)
}
assertRich := func(name string, m *domain.MessageRichMessage) {
t.Helper()
if m == nil {
t.Fatalf("%s: rich message lost", name)
}
if !m.Rtl {
t.Errorf("%s: rtl = false, want true", name)
}
if !bytes.Equal(m.Blocks, rawBlocks) {
t.Fatalf("%s: blocks byte mismatch: %v want %v", name, m.Blocks, rawBlocks)
}
if len(m.Photos) != 1 || m.Photos[0].ID != 9911 || m.Photos[0].AccessHash != 7 {
t.Fatalf("%s: embedded photo lost: %+v", name, m.Photos)
}
if len(m.Photos[0].Sizes) != 1 || m.Photos[0].Sizes[0].Type != "x" {
t.Fatalf("%s: embedded photo sizes lost: %+v", name, m.Photos[0].Sizes)
}
}
// 双端发送结果带 rich_message。
assertRich("sender result", res.SenderMessage.RichMessage)
assertRich("recipient result", res.RecipientMessage.RichMessage)
// history 读回getHistory 主渲染路径backward 扁平查询)。
list, err := messages.ListByUser(ctx, recipient.ID, domain.MessageFilter{Limit: 10})
if err != nil {
t.Fatalf("list by user: %v", err)
}
if len(list.Messages) == 0 {
t.Fatal("recipient history empty")
}
assertRich("recipient history", list.Messages[0].RichMessage)
// GetByIDsgetMessages / getRichMessage 共用取数路径)。
byID, err := messages.GetByIDs(ctx, recipient.ID, []int{res.RecipientMessage.ID})
if err != nil {
t.Fatalf("recipient get by ids: %v", err)
}
if len(byID.Messages) != 1 {
t.Fatalf("get by ids = %d, want 1", len(byID.Messages))
}
assertRich("recipient get by ids", byID.Messages[0].RichMessage)
// 接收方经更新事件重建(离线 difference / 在线推送共用路径)。
events := NewUpdateEventStore(pool)
got, err := events.ListAfter(ctx, recipient.ID, 0, 10)
if err != nil {
t.Fatalf("list recipient events: %v", err)
}
var sawEvent bool
for _, ev := range got {
if ev.Type == domain.UpdateEventNewMessage && ev.Message.ID != 0 {
sawEvent = true
assertRich("recipient update-event", ev.Message.RichMessage)
}
}
if !sawEvent {
t.Fatal("no new_message event for recipient")
}
}