Ctrl K
ComunidadGuías…

internal/store/postgres/dialog_top_metadata_integration_test.go

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

Archivo original ↗Preguntar a KiBot
package postgres

import (
	"context"
	"testing"
	"time"

	"telesrv/internal/domain"
)

// TestDialogTopMessageCarriesFullMetadata regresses the top-message projection completeness of
// getDialogs/getPeerDialogs: the dialog list's top message is a separate query (dialog.sql, not
// reusing message.sql) and must carry grouped_id / effect / reply_markup / rich_message —
// TDesktop caches getDialogs messages and does not overwrite them with later getHistory, so any
// missing field makes the latest message render permanently in its missing state.
func TestDialogTopMessageCarriesFullMetadata(t *testing.T) {
	pool := testPool(t)
	ctx := context.Background()
	suffix := randomSuffix(t)

	users := NewUserStore(pool)
	sender, err := users.Create(ctx, domain.User{AccessHash: 71, Phone: "+1997" + suffix + "01", FirstName: "MetaSender"})
	if err != nil {
		t.Fatalf("create sender: %v", err)
	}
	recipient, err := users.Create(ctx, domain.User{AccessHash: 72, Phone: "+1997" + suffix + "02", FirstName: "MetaRecipient"})
	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(990000000777)
	const effect = int64(5104841169784993603)
	markup := &domain.MessageReplyMarkup{Inline: [][]domain.MarkupButton{{
		{Type: domain.MarkupButtonCallback, Text: "Press", Data: []byte{0x00, 0x01, 0xFF, 'x'}},
	}}}
	rich := &domain.MessageRichMessage{Rtl: true, Blocks: []byte{0x10, 0x20, 0x30}}

	res, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
		SenderUserID:    sender.ID,
		RecipientUserID: recipient.ID,
		RandomID:        time.Now().UnixNano(),
		Message:         "full metadata top",
		GroupedID:       groupedID,
		Effect:          effect,
		ReplyMarkup:     markup,
		RichMessage:     rich,
		Date:            int(time.Now().Unix()),
	})
	if err != nil {
		t.Fatalf("send message: %v", err)
	}

	dialogs := NewDialogStore(pool)
	check := func(label string, ownerID int64, boxID int) {
		t.Helper()
		list, err := dialogs.ListByUser(ctx, ownerID, domain.DialogFilter{Limit: 20})
		if err != nil {
			t.Fatalf("%s getDialogs: %v", label, err)
		}
		var top *domain.Message
		for i := range list.Messages {
			if list.Messages[i].ID == boxID {
				top = &list.Messages[i]
			}
		}
		if top == nil {
			t.Fatalf("%s getDialogs missing top message box %d", label, boxID)
		}
		if top.GroupedID != groupedID {
			t.Fatalf("%s dialog top grouped_id = %d, want %d", label, top.GroupedID, groupedID)
		}
		if top.Effect != effect {
			t.Fatalf("%s dialog top effect = %d, want %d", label, top.Effect, effect)
		}
		if top.ReplyMarkup == nil || len(top.ReplyMarkup.Inline) != 1 || len(top.ReplyMarkup.Inline[0]) != 1 ||
			top.ReplyMarkup.Inline[0][0].Text != "Press" {
			t.Fatalf("%s dialog top reply_markup not preserved: %+v", label, top.ReplyMarkup)
		}
		if top.RichMessage == nil || top.RichMessage.IsZero() || !top.RichMessage.Rtl {
			t.Fatalf("%s dialog top rich_message not preserved: %+v", label, top.RichMessage)
		}
	}
	check("sender", sender.ID, res.SenderMessage.ID)
	check("recipient", recipient.ID, res.RecipientMessage.ID)
}

// TestSavedDialogTopMessageCarriesFullMetadata regresses the top-message projection of
// getSavedDialogs: the Saved Messages sub-conversation top message comes from another set of
// separate queries (saved_dialog.sql, 3 of them), also not reusing message.sql, and must carry
// grouped_id/effect/reply_markup/rich_message (a forwarded album / bot keyboard / rich text
// entering Saved Messages would be missing from the sub-conversation's latest entry).
func TestSavedDialogTopMessageCarriesFullMetadata(t *testing.T) {
	pool := testPool(t)
	ctx := context.Background()
	suffix := randomSuffix(t)

	users := NewUserStore(pool)
	alice, err := users.Create(ctx, domain.User{AccessHash: 73, Phone: "+1998" + suffix + "01", FirstName: "SavedMeta"})
	if err != nil {
		t.Fatalf("create alice: %v", err)
	}
	t.Cleanup(func() {
		_, _ = pool.Exec(ctx, "DELETE FROM message_boxes WHERE owner_user_id = $1", alice.ID)
		_, _ = pool.Exec(ctx, "DELETE FROM private_messages WHERE sender_user_id = $1", alice.ID)
		_, _ = pool.Exec(ctx, "DELETE FROM dialogs WHERE user_id = $1", alice.ID)
		_, _ = pool.Exec(ctx, "DELETE FROM saved_dialog_pins WHERE user_id = $1", alice.ID)
		_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = $1", alice.ID)
	})

	messages := NewMessageStore(pool, WithMessageAllocators(&perUserCounterAllocator{}))
	const groupedID = int64(990000000888)
	const effect = int64(5104841169784993603)
	markup := &domain.MessageReplyMarkup{Inline: [][]domain.MarkupButton{{
		{Type: domain.MarkupButtonCallback, Text: "Saved", Data: []byte{0x09}},
	}}}
	rich := &domain.MessageRichMessage{Part: true, Blocks: []byte{0x44, 0x55}}

	// A directly sent note → saved_peer = self; the sub-conversation top is this message.
	note, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
		SenderUserID:    alice.ID,
		RecipientUserID: alice.ID,
		RandomID:        time.Now().UnixNano(),
		Message:         "self note full meta",
		GroupedID:       groupedID,
		Effect:          effect,
		ReplyMarkup:     markup,
		RichMessage:     rich,
		Date:            int(time.Now().Unix()),
	})
	if err != nil {
		t.Fatalf("send self note: %v", err)
	}

	list, err := messages.ListSavedDialogs(ctx, alice.ID, domain.SavedDialogsFilter{Limit: 20})
	if err != nil {
		t.Fatalf("ListSavedDialogs: %v", err)
	}
	var top *domain.Message
	for i := range list.Messages {
		if list.Messages[i].ID == note.SenderMessage.ID {
			top = &list.Messages[i]
		}
	}
	if top == nil {
		t.Fatalf("getSavedDialogs missing top message box %d (got %d messages)", note.SenderMessage.ID, len(list.Messages))
	}
	if top.GroupedID != groupedID {
		t.Fatalf("saved dialog top grouped_id = %d, want %d", top.GroupedID, groupedID)
	}
	if top.Effect != effect {
		t.Fatalf("saved dialog top effect = %d, want %d", top.Effect, effect)
	}
	if top.ReplyMarkup == nil || len(top.ReplyMarkup.Inline) != 1 || top.ReplyMarkup.Inline[0][0].Text != "Saved" {
		t.Fatalf("saved dialog top reply_markup not preserved: %+v", top.ReplyMarkup)
	}
	if top.RichMessage == nil || top.RichMessage.IsZero() || !top.RichMessage.Part {
		t.Fatalf("saved dialog top rich_message not preserved: %+v", top.RichMessage)
	}
}