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,188 @@
package postgres
import (
"context"
"testing"
"time"
"telesrv/internal/domain"
)
// TestSendPrivateGroupedIDSurvivesReadPaths 回归迁移 0008相册 grouped_id 经私聊
// 全部读路径保真——发送结果双盒、history、GetByIDs、以及更新事件重建路径离线
// difference / 在线推送共用)。这是「漏一处读站点=分组静默丢失」最易踩的地方。
func TestSendPrivateGroupedIDSurvivesReadPaths(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: "+1995" + suffix + "01", FirstName: "AlbumSender"})
if err != nil {
t.Fatalf("create sender: %v", err)
}
recipient, err := users.Create(ctx, domain.User{AccessHash: 82, Phone: "+1995" + suffix + "02", FirstName: "AlbumRecipient"})
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{}))
const groupedID = int64(880000000123)
// 同一 album 的两条消息共享 grouped_id。
var senderMsgIDs []int
for i := 0; i < 2; i++ {
res, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
SenderUserID: sender.ID,
RecipientUserID: recipient.ID,
RandomID: time.Now().UnixNano() + int64(i),
Message: "album item",
GroupedID: groupedID,
Date: int(time.Now().Unix()),
})
if err != nil {
t.Fatalf("send album item %d: %v", i, err)
}
if res.SenderMessage.GroupedID != groupedID || res.RecipientMessage.GroupedID != groupedID {
t.Fatalf("send result grouped = sender %d recipient %d, want %d", res.SenderMessage.GroupedID, res.RecipientMessage.GroupedID, groupedID)
}
senderMsgIDs = append(senderMsgIDs, res.SenderMessage.ID)
}
// history两条都带同一 grouped_id客户端据此分组
hist, err := messages.ListByUser(ctx, recipient.ID, domain.MessageFilter{Limit: 10})
if err != nil {
t.Fatalf("recipient history: %v", err)
}
album := 0
for _, m := range hist.Messages {
if m.GroupedID == groupedID {
album++
}
}
if album != 2 {
t.Fatalf("recipient history grouped count = %d, want 2", album)
}
// GetByIDs 带 grouped_id。
byID, err := messages.GetByIDs(ctx, sender.ID, senderMsgIDs)
if err != nil {
t.Fatalf("sender get by ids: %v", err)
}
if len(byID.Messages) != 2 {
t.Fatalf("get by ids = %d, want 2", len(byID.Messages))
}
for _, m := range byID.Messages {
if m.GroupedID != groupedID {
t.Fatalf("get-by-id grouped = %d, want %d", m.GroupedID, groupedID)
}
}
// 更新事件重建路径关键getDifference/推送共用)带 grouped_id。
events := NewUpdateEventStore(pool)
got, err := events.ListAfter(ctx, recipient.ID, 0, 20)
if err != nil {
t.Fatalf("list recipient events: %v", err)
}
rebuilt := 0
for _, ev := range got {
if ev.Type == domain.UpdateEventNewMessage && ev.Message.ID != 0 {
if ev.Message.GroupedID != groupedID {
t.Fatalf("update-event grouped = %d, want %d重建路径丢失分组", ev.Message.GroupedID, groupedID)
}
rebuilt++
}
}
if rebuilt != 2 {
t.Fatalf("rebuilt new_message events with grouped = %d, want 2", rebuilt)
}
}
// TestSendChannelGroupedIDSurvivesReadPaths 回归迁移 0008频道侧手写 SQL相册
// grouped_id 经发送结果/事件/history/GetChannelMessages 保真。
func TestSendChannelGroupedIDSurvivesReadPaths(t *testing.T) {
pool := testPool(t)
ctx := context.Background()
suffix := randomSuffix(t)
users := NewUserStore(pool)
owner, err := users.Create(ctx, domain.User{AccessHash: 3201, Phone: "+1887" + suffix + "01", FirstName: "AlbumChannelOwner"})
if err != nil {
t.Fatalf("create owner: %v", err)
}
var channelID int64
t.Cleanup(func() {
if channelID != 0 {
_, _ = pool.Exec(ctx, "DELETE FROM channels WHERE id = $1", channelID)
}
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = $1", owner.ID)
})
channels := NewChannelStore(pool)
created, err := channels.CreateChannel(ctx, domain.CreateChannelRequest{
CreatorUserID: owner.ID,
Title: "Album Channel " + suffix,
Megagroup: true,
Date: 1700002100,
})
if err != nil {
t.Fatalf("create channel: %v", err)
}
channelID = created.Channel.ID
const groupedID = int64(990000000456)
var msgIDs []int
for i := 0; i < 2; i++ {
sent, err := channels.SendChannelMessage(ctx, domain.SendChannelMessageRequest{
UserID: owner.ID,
ChannelID: channelID,
RandomID: int64(990401 + i),
Message: "channel album item",
GroupedID: groupedID,
Date: 1700002101 + i,
})
if err != nil {
t.Fatalf("send channel album item %d: %v", i, err)
}
if sent.Message.GroupedID != groupedID || sent.Event.Message.GroupedID != groupedID {
t.Fatalf("sent grouped = msg %d event %d, want %d", sent.Message.GroupedID, sent.Event.Message.GroupedID, groupedID)
}
msgIDs = append(msgIDs, sent.Message.ID)
}
hist, err := channels.ListChannelHistory(ctx, owner.ID, domain.ChannelHistoryFilter{ChannelID: channelID, Limit: 10})
if err != nil {
t.Fatalf("channel history: %v", err)
}
album := 0
for _, m := range hist.Messages {
if m.GroupedID == groupedID {
album++
}
}
if album != 2 {
t.Fatalf("channel history grouped count = %d, want 2", album)
}
byID, err := channels.GetChannelMessages(ctx, owner.ID, channelID, msgIDs)
if err != nil {
t.Fatalf("get channel messages: %v", err)
}
if len(byID.Messages) != 2 {
t.Fatalf("get channel messages = %d, want 2", len(byID.Messages))
}
for _, m := range byID.Messages {
if m.GroupedID != groupedID {
t.Fatalf("channel get-by-id grouped = %d, want %d", m.GroupedID, groupedID)
}
}
}