internal/store/postgres/channel_dialog_mention_integration_test.go
Rama main · réplica de lectura del repositorio original.
package postgres
import (
"context"
"testing"
"telesrv/internal/domain"
)
// TestChannelDialogTopMessageCarriesMentionFlags reproduces the @ badge returning after restart:
// getDialogs' channel top message must carry mentioned/media_unread per viewer,
// otherwise the incomplete version TDesktop caches first never triggers contents-read, the server's unread
// mention stays stuck, and every cold start brings the @ badge back via unread_mentions_count.
func TestChannelDialogTopMessageCarriesMentionFlags(t *testing.T) {
pool := testPool(t)
ctx := context.Background()
suffix := randomSuffix(t)
users := NewUserStore(pool)
owner, err := users.Create(ctx, domain.User{AccessHash: 97, Phone: "+1675" + suffix + "01", FirstName: "MentionOwner"})
if err != nil {
t.Fatalf("create owner: %v", err)
}
member, err := users.Create(ctx, domain.User{AccessHash: 98, Phone: "+1675" + suffix + "02", FirstName: "MentionMember"})
if err != nil {
t.Fatalf("create member: %v", err)
}
t.Cleanup(func() {
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", []int64{owner.ID, member.ID})
})
channels := NewChannelStore(pool, WithChannelTopMessageCache(NewChannelTopMessageCache(32)))
created, err := channels.CreateChannel(ctx, domain.CreateChannelRequest{
CreatorUserID: owner.ID,
Title: "MentionDialog " + suffix,
Megagroup: true,
MemberUserIDs: []int64{member.ID},
Date: 1700000910,
})
if err != nil {
t.Fatalf("create channel: %v", err)
}
sent, err := channels.SendChannelMessage(ctx, domain.SendChannelMessageRequest{
UserID: owner.ID,
ChannelID: created.Channel.ID,
RandomID: 851001,
Message: "ping member",
MentionUserIDs: []int64{member.ID},
Date: 1700000911,
})
if err != nil {
t.Fatalf("send mention: %v", err)
}
assertFlags := func(name string, list domain.ChannelDialogList) {
t.Helper()
found := false
for _, msg := range list.Messages {
if msg.ChannelID != created.Channel.ID || msg.ID != sent.Message.ID {
continue
}
found = true
if !msg.Mentioned || !msg.MediaUnread {
t.Fatalf("%s top message flags = mentioned %v media_unread %v, want both true (missing them makes the @ badge return after restart)", name, msg.Mentioned, msg.MediaUnread)
}
}
if !found {
t.Fatalf("%s lacks top message %d: %+v", name, sent.Message.ID, list.Messages)
}
}
list, err := channels.ListChannelDialogs(ctx, member.ID, domain.DialogFilter{Limit: 50})
if err != nil {
t.Fatalf("list channel dialogs: %v", err)
}
assertFlags("ListChannelDialogs", list)
got, err := channels.GetChannelDialogs(ctx, member.ID, []int64{created.Channel.ID})
if err != nil {
t.Fatalf("get channel dialogs: %v", err)
}
assertFlags("GetChannelDialogs", got)
// After the mention is read: mentioned is permanently kept, media_unread flips to false.
if _, err := channels.ReadChannelMentions(ctx, domain.ReadChannelMentionsRequest{
UserID: member.ID,
ChannelID: created.Channel.ID,
}); err != nil {
t.Fatalf("read mentions: %v", err)
}
after, err := channels.GetChannelDialogs(ctx, member.ID, []int64{created.Channel.ID})
if err != nil {
t.Fatalf("get channel dialogs after read: %v", err)
}
for _, msg := range after.Messages {
if msg.ID == sent.Message.ID && (!msg.Mentioned || msg.MediaUnread) {
t.Fatalf("after read flags = mentioned %v media_unread %v, want mentioned kept with media_unread cleared", msg.Mentioned, msg.MediaUnread)
}
}
}