188 lines
6.3 KiB
Go
188 lines
6.3 KiB
Go
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)
|
||
}
|
||
}
|
||
}
|