owpengram-server/internal/rpc/messages_suggested_post_rpc_test.go

158 lines
6.4 KiB
Go

package rpc
import (
"context"
"testing"
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/clock"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"go.uber.org/zap/zaptest"
appchannels "telesrv/internal/app/channels"
appusers "telesrv/internal/app/users"
"telesrv/internal/domain"
"telesrv/internal/store/memory"
)
func TestMessagesToggleSuggestedPostApprovalRegisteredAndProjectsLifecycle(t *testing.T) {
ctx := context.Background()
users := memory.NewUserStore()
owner, err := users.Create(ctx, domain.User{AccessHash: 101, Phone: "15551110001", FirstName: "Owner"})
if err != nil {
t.Fatal(err)
}
subscriber, err := users.Create(ctx, domain.User{AccessHash: 102, Phone: "15551110002", FirstName: "Subscriber"})
if err != nil {
t.Fatal(err)
}
channelsStore := memory.NewChannelStore()
channels := appchannels.NewService(channelsStore)
created, err := channels.CreateChannel(ctx, owner.ID, domain.CreateChannelRequest{Title: "Suggested", Broadcast: true, Date: 1_700_000_000})
if err != nil {
t.Fatal(err)
}
enabled, err := channelsStore.SetPaidMessagesPrice(ctx, owner.ID, created.Channel.ID, 0, true)
if err != nil {
t.Fatal(err)
}
mono, err := channelsStore.GetChannelByID(ctx, enabled.Channel.LinkedMonoforumID)
if err != nil {
t.Fatal(err)
}
saved := domain.Peer{Type: domain.PeerTypeUser, ID: subscriber.ID}
suggestion, err := channels.SendMonoforumMessage(ctx, domain.SendMonoforumMessageRequest{MonoforumID: mono.ID, SenderUserID: subscriber.ID, SavedPeer: saved, RandomID: 91, Message: "RPC suggestion", SuggestedPost: &domain.SuggestedPost{}, Date: 1_700_000_100})
if err != nil {
t.Fatal(err)
}
router := New(Config{}, Deps{Users: appusers.NewService(users), Channels: channels}, zaptest.NewLogger(t), clock.System)
req := &tg.MessagesToggleSuggestedPostApprovalRequest{Peer: &tg.InputPeerChannel{ChannelID: mono.ID, AccessHash: mono.AccessHash}, MsgID: suggestion.Message.ID}
var raw bin.Buffer
if err := req.Encode(&raw); err != nil {
t.Fatal(err)
}
response, err := router.Dispatch(WithLayer(WithUserID(ctx, owner.ID), 228), [8]byte{}, 0, &raw)
if err != nil {
t.Fatalf("dispatch toggleSuggestedPostApproval: %v", err)
}
updates, ok := response.(*tg.Updates)
if !ok {
t.Fatalf("response=%T, want *tg.Updates", response)
}
var edited, approval, published bool
for _, update := range updates.Updates {
switch item := update.(type) {
case *tg.UpdateEditChannelMessage:
message, ok := item.Message.(*tg.Message)
if ok && message.ID == suggestion.Message.ID {
post, present := message.GetSuggestedPost()
edited = present && post.GetAccepted()
}
case *tg.UpdateNewChannelMessage:
switch message := item.Message.(type) {
case *tg.MessageService:
action, ok := message.Action.(*tg.MessageActionSuggestedPostApproval)
if ok {
scheduleDate, hasScheduleDate := action.GetScheduleDate()
approval = !action.Rejected && !action.BalanceTooLow && hasScheduleDate && scheduleDate > 0
}
if savedPeer, present := message.GetSavedPeerID(); !present {
t.Fatalf("approval service missing saved_peer_id")
} else if peer, ok := savedPeer.(*tg.PeerUser); !ok || peer.UserID != subscriber.ID {
t.Fatalf("approval saved_peer=%#v", savedPeer)
}
case *tg.Message:
published = message.PeerID.(*tg.PeerChannel).ChannelID == created.Channel.ID && message.Post && message.Message == "RPC suggestion"
}
}
}
if !edited || !approval || !published {
t.Fatalf("updates missing edit/approval/publish: %#v", updates.Updates)
}
var retryRaw bin.Buffer
if err := req.Encode(&retryRaw); err != nil {
t.Fatal(err)
}
retry, err := router.Dispatch(WithLayer(WithUserID(ctx, owner.ID), 227), [8]byte{}, 0, &retryRaw)
if err != nil {
t.Fatalf("layer 227 retry: %v", err)
}
got, ok := retry.(*tg.Updates)
if !ok {
t.Fatalf("layer 227 response=%T", retry)
}
// Duplicate replay returns the persisted approval + published update to
// the caller but is never fanned out again.
if len(got.Updates) != 3 {
t.Fatalf("layer 227 duplicate updates=%d, want 3", len(got.Updates))
}
}
func TestSuggestedPostTLProjectionSeparatesSuggestionAndPublishedPaymentFlags(t *testing.T) {
original := domain.ChannelMessage{ChannelID: 10, ID: 1, SenderUserID: 20, From: domain.Peer{Type: domain.PeerTypeUser, ID: 20}, SavedPeer: domain.Peer{Type: domain.PeerTypeUser, ID: 20}, Date: 100, Body: "proposal", SuggestedPost: &domain.SuggestedPost{Accepted: true, Price: &domain.SuggestedPostPrice{Kind: domain.SuggestedPostPriceStars, Amount: 10}}}
proposal := tgChannelMessage(20, original).(*tg.Message)
if _, present := proposal.GetSuggestedPost(); !present || proposal.GetPaidSuggestedPostStars() {
t.Fatalf("proposal flags=%+v", proposal)
}
published := original
published.ChannelID, published.ID, published.Post, published.SavedPeer = 11, 2, true, domain.Peer{}
post := tgChannelMessage(20, published).(*tg.Message)
if !post.GetPaidSuggestedPostStars() {
t.Fatalf("published Stars post missing paid flag")
}
if _, present := post.GetSuggestedPost(); present {
t.Fatalf("published post leaked suggested_post")
}
published.SuggestedPost.Price = &domain.SuggestedPostPrice{Kind: domain.SuggestedPostPriceTON, Amount: 10_000_000}
ton := tgChannelMessage(20, published).(*tg.Message)
if !ton.GetPaidSuggestedPostTon() || ton.GetPaidSuggestedPostStars() {
t.Fatalf("published TON flags=%+v", ton)
}
}
func TestSuggestedPostApprovalScheduleDateSurvivesExactProfiles(t *testing.T) {
action := tgChannelMessageAction(domain.ChannelMessageAction{
Type: domain.ChannelActionSuggestedPostApproval,
SuggestedPostScheduleDate: 1_700_000_200,
})
for _, profile := range []tlprofile.Profile{tlprofile.Profile227, tlprofile.Profile228} {
wire := &bin.Buffer{}
if err := tlprofile.EncodeObject(profile, action, wire); err != nil {
t.Fatalf("encode Layer %d approval action: %v", profile, err)
}
decodedObject, err := tlprofile.DecodeObject(profile, &bin.Buffer{Buf: wire.Copy()}, tlprofile.Limits{})
if err != nil {
t.Fatalf("decode Layer %d approval action: %v", profile, err)
}
decoded, ok := decodedObject.(*tg.MessageActionSuggestedPostApproval)
if !ok {
t.Fatalf("decode Layer %d approval action = %T", profile, decodedObject)
}
date, present := decoded.GetScheduleDate()
if !present || date != 1_700_000_200 {
t.Fatalf("Layer %d approval date=%d/%v, want 1700000200/true", profile, date, present)
}
}
}