chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
124
internal/store/postgres/message_rich_message_integration_test.go
Normal file
124
internal/store/postgres/message_rich_message_integration_test.go
Normal 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)存活;
|
||||
// - history(ListByUser)/ 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)
|
||||
|
||||
// GetByIDs(getMessages / 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")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue