chore: refresh gramsrv public release

This commit is contained in:
A 2026-06-30 14:37:43 +08:00
parent 75cebe8dbf
commit 70b6820474
1274 changed files with 378751 additions and 59919 deletions

View file

@ -0,0 +1,129 @@
package postgres
import (
"context"
"errors"
"testing"
"telesrv/internal/domain"
)
// TestChannelPaidReactionPostgres 回归迁移 0010:广播频道付费 reaction 对真实 PG 的累计 +
// 聚合(总星数 / viewer 自身 / top reactors 降序 / 同 reactor 多次累加)。
func TestChannelPaidReactionPostgres(t *testing.T) {
pool := testPool(t)
ctx := context.Background()
suffix := randomSuffix(t)
users := NewUserStore(pool)
owner, err := users.Create(ctx, domain.User{AccessHash: 521, Phone: "+1778" + suffix + "41", FirstName: "PaidRxOwner"})
if err != nil {
t.Fatalf("create owner: %v", err)
}
member, err := users.Create(ctx, domain.User{AccessHash: 522, Phone: "+1778" + suffix + "42", FirstName: "PaidRxMember"})
if err != nil {
t.Fatalf("create member: %v", err)
}
var channelID int64
t.Cleanup(func() {
if channelID != 0 {
_, _ = pool.Exec(ctx, "DELETE FROM channel_message_paid_reactions WHERE channel_id = $1", channelID)
_, _ = 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: "Paid Reaction " + suffix,
Broadcast: true,
MemberUserIDs: []int64{member.ID},
Date: 1700000400,
})
if err != nil {
t.Fatalf("create broadcast channel: %v", err)
}
channelID = created.Channel.ID
sent, err := channels.SendChannelMessage(ctx, domain.SendChannelMessageRequest{
UserID: owner.ID,
ChannelID: channelID,
RandomID: 9401,
Message: "paid reaction target",
Date: 1700000401,
})
if err != nil {
t.Fatalf("send message: %v", err)
}
msgID := sent.Message.ID
// owner 投 100。
res, err := channels.AddChannelMessagePaidReaction(ctx, domain.SendChannelPaidReactionRequest{
UserID: owner.ID, ChannelID: channelID, MessageID: msgID, Stars: 100, Date: 1700000402,
})
if err != nil {
t.Fatalf("owner paid reaction: %v", err)
}
if res.Paid.TotalStars != 100 || res.Paid.MyStars != 100 {
t.Fatalf("after owner 100 = total %d my %d, want 100/100", res.Paid.TotalStars, res.Paid.MyStars)
}
// member 投 250 → 总 350,member 视角 my=250,top reactors 降序 member(250)/owner(100)。
res, err = channels.AddChannelMessagePaidReaction(ctx, domain.SendChannelPaidReactionRequest{
UserID: member.ID, ChannelID: channelID, MessageID: msgID, Stars: 250, Date: 1700000403,
})
if err != nil {
t.Fatalf("member paid reaction: %v", err)
}
if res.Paid.TotalStars != 350 || res.Paid.MyStars != 250 {
t.Fatalf("after member 250 = total %d my %d, want 350/250", res.Paid.TotalStars, res.Paid.MyStars)
}
if len(res.Paid.TopReactors) != 2 || res.Paid.TopReactors[0].Stars != 250 || !res.Paid.TopReactors[0].My || res.Paid.TopReactors[1].Stars != 100 {
t.Fatalf("top reactors = %+v, want member(250,My)/owner(100)", res.Paid.TopReactors)
}
// owner 再投 50 → 累加到 150,总 400。
res, err = channels.AddChannelMessagePaidReaction(ctx, domain.SendChannelPaidReactionRequest{
UserID: owner.ID, ChannelID: channelID, MessageID: msgID, Stars: 50, Date: 1700000404,
})
if err != nil {
t.Fatalf("owner re-invest: %v", err)
}
if res.Paid.TotalStars != 400 || res.Paid.MyStars != 150 {
t.Fatalf("after owner +50 = total %d my %d, want 400/150 (accumulated)", res.Paid.TotalStars, res.Paid.MyStars)
}
// 关键回归(读路径修复):fresh 读回该消息必须携带付费 reaction(不止实时推送)。
// owner 视角:Paid.TotalStars=400, MyStars=150。
read, err := channels.GetChannelMessageReactions(ctx, domain.ChannelMessageReactionsRequest{
UserID: owner.ID, ChannelID: channelID, IDs: []int{msgID},
})
if err != nil {
t.Fatalf("get message reactions: %v", err)
}
if len(read.Messages) != 1 || read.Messages[0].Reactions == nil || read.Messages[0].Reactions.Paid == nil {
t.Fatalf("fresh read messages=%d reactions/paid missing: %+v", len(read.Messages), read.Messages)
}
paid := read.Messages[0].Reactions.Paid
if paid.TotalStars != 400 || paid.MyStars != 150 {
t.Fatalf("fresh read paid = total %d my %d, want 400/150 (读路径须回显付费 reaction)", paid.TotalStars, paid.MyStars)
}
// member 视角读同一条:MyStars=250、TopReactors 含 member 自己带 My。
readMember, err := channels.GetChannelMessageReactions(ctx, domain.ChannelMessageReactionsRequest{
UserID: member.ID, ChannelID: channelID, IDs: []int{msgID},
})
if err != nil {
t.Fatalf("get message reactions (member): %v", err)
}
mp := readMember.Messages[0].Reactions.Paid
if mp == nil || mp.TotalStars != 400 || mp.MyStars != 250 {
t.Fatalf("member fresh read paid = %+v, want total 400 my 250", mp)
}
// 非法星数被拒。
if _, err := channels.AddChannelMessagePaidReaction(ctx, domain.SendChannelPaidReactionRequest{
UserID: owner.ID, ChannelID: channelID, MessageID: msgID, Stars: 0, Date: 1700000405,
}); !errors.Is(err, domain.ErrChannelInvalid) {
t.Fatalf("zero-stars err = %v, want ErrChannelInvalid", err)
}
}