chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
140
internal/store/postgres/channel_poll_fanout_integration_test.go
Normal file
140
internal/store/postgres/channel_poll_fanout_integration_test.go
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
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")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue