owpengram-server/internal/store/postgres/channel_poll_fanout_integration_test.go

140 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package postgres
import (
"context"
"reflect"
"testing"
"telesrv/internal/domain"
)
// TestChannelPollFanoutViewsPostgres 验证 Phase 4 poll 模板化的 postgres 路径:新批量 SQL
// batchPollViewerOptions / batchChannelMemberAvailableMinID + pollViewerAggregates(viewer=0)
// 正确运行,且 ChannelPollFanoutViews 对每个 viewer 与逐 viewer GetChannelMessages旧 fan-out 路径)
// 的 enrich poll 字节等价、可见性一致。门控于 TELESRV_TEST_POSTGRES_DSN。
func TestChannelPollFanoutViewsPostgres(t *testing.T) {
pool := testPool(t)
ctx := context.Background()
suffix := randomSuffix(t)
users := NewUserStore(pool)
owner, err := users.Create(ctx, domain.User{AccessHash: 71, Phone: "+1893" + suffix + "01", FirstName: "PollOwner"})
if err != nil {
t.Fatalf("create owner: %v", err)
}
voted, err := users.Create(ctx, domain.User{AccessHash: 72, Phone: "+1893" + suffix + "02", FirstName: "PollVoted"})
if err != nil {
t.Fatalf("create voted: %v", err)
}
unvoted, err := users.Create(ctx, domain.User{AccessHash: 73, Phone: "+1893" + suffix + "03", FirstName: "PollUnvoted"})
if err != nil {
t.Fatalf("create unvoted: %v", err)
}
stranger, err := users.Create(ctx, domain.User{AccessHash: 74, Phone: "+1893" + suffix + "04", FirstName: "PollStranger"})
if err != nil {
t.Fatalf("create stranger: %v", err)
}
channels := NewChannelStore(pool)
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, voted.ID, unvoted.ID, stranger.ID})
})
created, err := channels.CreateChannel(ctx, domain.CreateChannelRequest{
CreatorUserID: owner.ID,
Title: "Poll Fanout " + suffix,
Megagroup: true,
MemberUserIDs: []int64{voted.ID, unvoted.ID},
Date: 1700002000,
})
if err != nil {
t.Fatalf("create channel: %v", err)
}
channelID = created.Channel.ID
pollID := int64(9300000000) + (channelID % 1000)
opt1, opt2 := []byte{1}, []byte{2}
polls := NewPollStore(pool)
if err := polls.CreatePoll(ctx, domain.PollDefinition{
ID: pollID,
CreatorUserID: owner.ID,
Options: [][]byte{opt1, opt2},
PublicVoters: true,
}); err != nil {
t.Fatalf("create poll: %v", err)
}
sent, err := channels.SendChannelMessage(ctx, domain.SendChannelMessageRequest{
UserID: owner.ID,
ChannelID: channelID,
RandomID: 93_001,
Date: 1700002001,
Media: &domain.MessageMedia{
Kind: domain.MessageMediaKindPoll,
Poll: &domain.MessagePoll{
ID: pollID,
Question: "Q?",
Answers: []domain.MessagePollAnswer{{Text: "A", Option: opt1}, {Text: "B", Option: opt2}},
PublicVoters: true,
},
},
})
if err != nil {
t.Fatalf("send poll message: %v", err)
}
msgID := sent.Message.ID
if _, err := channels.VoteChannelMessagePoll(ctx, domain.VoteChannelMessagePollRequest{
UserID: voted.ID, ChannelID: channelID, MessageID: msgID, Options: [][]byte{opt1}, Date: 1700002002,
}); err != nil {
t.Fatalf("vote: %v", err)
}
viewers := []int64{owner.ID, voted.ID, unvoted.ID, stranger.ID}
views, err := channels.ChannelPollFanoutViews(ctx, channelID, msgID, viewers, 1700002100)
if err != nil {
t.Fatalf("ChannelPollFanoutViews: %v", err)
}
if !views.Found {
t.Fatal("Found=false, want true")
}
for _, viewer := range viewers {
got, evaluated := views.Polls[viewer]
if !evaluated {
t.Fatalf("viewer %d not evaluated", viewer)
}
ref, err := channels.GetChannelMessages(ctx, viewer, channelID, []int{msgID})
var refPoll *domain.MessagePoll
if err == nil {
for _, m := range ref.Messages {
if m.ID == msgID && m.Media != nil {
refPoll = m.Media.Poll
}
}
}
if (got != nil) != (refPoll != nil) {
t.Fatalf("viewer %d visibility mismatch: batch nil=%v old nil=%v (err=%v)", viewer, got == nil, refPoll == nil, err)
}
if got != nil && !reflect.DeepEqual(got.Results, refPoll.Results) {
t.Fatalf("viewer %d Results mismatch:\n batch=%+v\n old=%+v", viewer, got.Results, refPoll.Results)
}
}
// members 可见、stranger非成员不可见。
if views.Polls[owner.ID] == nil || views.Polls[voted.ID] == nil || views.Polls[unvoted.ID] == nil {
t.Fatal("members should be visible")
}
if views.Polls[stranger.ID] != nil {
t.Fatal("non-member stranger must be invisible (nil)")
}
// voted 视角应见自己 chosen。
sawChosen := false
for _, v := range views.Polls[voted.ID].Results.Voters {
if v.Chosen {
sawChosen = true
}
}
if !sawChosen {
t.Fatal("voted viewer should see own chosen option")
}
}