Ctrl K
ComunidadGuías…

internal/store/postgres/channel_reply_top_msg_id_integration_test.go

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

Archivo original ↗Preguntar a KiBot
package postgres

import (
	"context"
	"errors"
	"testing"

	"telesrv/internal/domain"
)

// Regression (postgres semantics): top_msg_id must not be a reason for rejection.
// In messages.sendMedia TDesktop sets inputReplyToMessage.top_msg_id (observed 1), while
// messages.sendMessage does not. Previously the mismatch with the server's thread root yielded
// ErrReplyMessageIDInvalid, i.e. "replying with a photo in a group" was impossible.
func TestChannelReplyTopMsgIDMismatchIsAccepted(t *testing.T) {
	pool := testPool(t)
	ctx := context.Background()
	suffix := randomSuffix(t)

	users := NewUserStore(pool)
	owner, err := users.Create(ctx, domain.User{AccessHash: 11, Phone: "+1555" + suffix + "01", FirstName: "Owner"})
	if err != nil {
		t.Fatalf("create owner: %v", err)
	}
	member, err := users.Create(ctx, domain.User{AccessHash: 12, Phone: "+1555" + suffix + "02", FirstName: "Member"})
	if err != nil {
		t.Fatalf("create member: %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 = ANY($1::bigint[])", []int64{owner.ID, member.ID})
	})

	channels := NewChannelStore(pool)
	created, err := channels.CreateChannel(ctx, domain.CreateChannelRequest{
		CreatorUserID: owner.ID, Title: "TopMsg " + suffix, Megagroup: true,
		MemberUserIDs: []int64{member.ID}, Date: 1700000300,
	})
	if err != nil {
		t.Fatalf("create channel: %v", err)
	}
	channelID = created.Channel.ID
	channelPeer := domain.Peer{Type: domain.PeerTypeChannel, ID: channelID}

	root, err := channels.SendChannelMessage(ctx, domain.SendChannelMessageRequest{
		UserID: member.ID, ChannelID: channelID, RandomID: 7001, Message: "root", Date: 1700000301,
	})
	if err != nil {
		t.Fatalf("send root: %v", err)
	}
	photoTarget, err := channels.SendChannelMessage(ctx, domain.SendChannelMessageRequest{
		UserID: owner.ID, ChannelID: channelID, RandomID: 7002, Date: 1700000302,
		Media: &domain.MessageMedia{Kind: domain.MessageMediaKindPhoto, Photo: &domain.Photo{ID: 77, AccessHash: 7}},
	})
	if err != nil {
		t.Fatalf("send photo: %v", err)
	}

	// top_msg_id=1 — exactly what TDesktop sends for a media reply.
	res, err := channels.SendChannelMessage(ctx, domain.SendChannelMessageRequest{
		UserID: member.ID, ChannelID: channelID, RandomID: 7003, Date: 1700000303, Message: "reply",
		ReplyTo: &domain.MessageReply{MessageID: photoTarget.Message.ID, TopMessageID: 1, Peer: channelPeer},
	})
	if err != nil {
		t.Fatalf("reply with foreign top_msg_id rejected: %v", err)
	}
	if res.Message.ReplyTo == nil || res.Message.ReplyTo.MessageID != photoTarget.Message.ID {
		t.Fatalf("reply projection = %+v, want message id %d", res.Message.ReplyTo, photoTarget.Message.ID)
	}
	if want := photoTarget.Message.ID; res.Message.ReplyTo.TopMessageID != want {
		t.Fatalf("computed top_msg_id = %d, want server-derived %d", res.Message.ReplyTo.TopMessageID, want)
	}
	_ = root

	// Guardrail: a nonexistent target is still rejected.
	if _, err := channels.SendChannelMessage(ctx, domain.SendChannelMessageRequest{
		UserID: member.ID, ChannelID: channelID, RandomID: 7004, Date: 1700000304, Message: "bad",
		ReplyTo: &domain.MessageReply{MessageID: 999999, Peer: channelPeer},
	}); !errors.Is(err, domain.ErrReplyMessageIDInvalid) {
		t.Fatalf("missing reply target err = %v, want ErrReplyMessageIDInvalid", err)
	}
}