Ctrl K
ComunidadGuías…

internal/store/postgres/channel_dialog_mention_integration_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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)
		}
	}
}