owpengram-server/internal/rpc/privacy_paid_messages_test.go
2026-07-24 11:56:59 +08:00

166 lines
5.9 KiB
Go

package rpc
import (
"context"
"strings"
"testing"
"time"
"github.com/iamxvbaba/td/clock"
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap/zaptest"
appaccount "telesrv/internal/app/account"
appprivacy "telesrv/internal/app/privacy"
appusers "telesrv/internal/app/users"
"telesrv/internal/domain"
"telesrv/internal/store/memory"
)
func newPrivateRequirementRouter(t *testing.T, senderPremium bool) (*Router, *appaccount.Service, *appprivacy.Service, *memory.ContactStore, domain.User, domain.User) {
t.Helper()
ctx := context.Background()
usersStore := memory.NewUserStore()
aliceInput := domain.User{AccessHash: 11, Phone: "15550008101", FirstName: "Alice"}
if senderPremium {
aliceInput.PremiumUntil = int(time.Now().Add(time.Hour).Unix())
}
alice, err := usersStore.Create(ctx, aliceInput)
if err != nil {
t.Fatalf("create Alice: %v", err)
}
bob, err := usersStore.Create(ctx, domain.User{AccessHash: 22, Phone: "15550008102", FirstName: "Bob"})
if err != nil {
t.Fatalf("create Bob: %v", err)
}
users := appusers.NewService(usersStore)
contacts := memory.NewContactStore()
privacy := appprivacy.NewService(memory.NewPrivacyStore(), contacts).ConfigureReadModels(users, nil)
settingsStore := memory.NewPasswordStore()
account := appaccount.NewService(settingsStore, appaccount.WithAccountSettings(settingsStore))
router := New(Config{}, Deps{
Account: account,
Privacy: privacy,
Users: users,
Messages: &captureMessages{},
}, zaptest.NewLogger(t), clock.System)
return router, account, privacy, contacts, alice, bob
}
func TestPrivateContactRequirementUsesNoPaidMessagesReadModel(t *testing.T) {
ctx := context.Background()
r, account, privacy, contacts, alice, bob := newPrivateRequirementRouter(t, false)
if _, err := account.SetGlobalPrivacy(ctx, bob.ID, domain.GlobalPrivacy{NoncontactPeersPaidStars: 7}); err != nil {
t.Fatalf("set Bob paid requirement: %v", err)
}
full, err := r.onUsersGetFullUser(WithUserID(ctx, alice.ID), &tg.InputUser{
UserID: bob.ID, AccessHash: bob.AccessHash,
})
if err != nil {
t.Fatalf("get Bob full user: %v", err)
}
if stars, ok := full.FullUser.GetSendPaidMessagesStars(); !ok || stars != 7 {
t.Fatalf("full user paid stars = %d, %v; want 7, true", stars, ok)
}
projected, ok := full.Users[0].(*tg.User)
if !ok {
t.Fatalf("projected user = %T, want *tg.User", full.Users[0])
}
if stars, ok := projected.GetSendPaidMessagesStars(); !ok || stars != 7 {
t.Fatalf("user paid stars = %d, %v; want 7, true", stars, ok)
}
requirements, err := r.onUsersGetRequirementsToContact(WithUserID(ctx, alice.ID), []tg.InputUserClass{
&tg.InputUser{UserID: bob.ID, AccessHash: bob.AccessHash},
})
if err != nil {
t.Fatalf("get requirements: %v", err)
}
if paid, ok := requirements[0].(*tg.RequirementToContactPaidMessages); !ok || paid.StarsAmount != 7 {
t.Fatalf("requirement = %#v, want paid 7", requirements[0])
}
send := func(randomID int64, allow int64) error {
req := &tg.MessagesSendMessageRequest{
Peer: &tg.InputPeerUser{UserID: bob.ID, AccessHash: bob.AccessHash},
Message: "hello",
RandomID: randomID,
}
if allow != 0 {
req.SetAllowPaidStars(allow)
}
_, err := r.onMessagesSendMessage(WithUserID(ctx, alice.ID), req)
return err
}
if err := send(81001, 0); err == nil || !strings.Contains(err.Error(), "ALLOW_PAYMENT_REQUIRED") || !strings.Contains(err.Error(), "(7)") {
t.Fatalf("non-exempt send err = %v, want ALLOW_PAYMENT_REQUIRED 7", err)
}
if err := send(81002, 7); err == nil || !strings.Contains(err.Error(), "PAYMENT_UNSUPPORTED") {
t.Fatalf("authorized paid send err = %v, want explicit unsupported ledger", err)
}
if _, err := privacy.SetRules(ctx, bob.ID, domain.PrivacyKeyNoPaidMessages, []domain.PrivacyRule{{
Kind: domain.PrivacyRuleAllowUsers,
UserIDs: []int64{alice.ID},
}, {
Kind: domain.PrivacyRuleDisallowAll,
}}); err != nil {
t.Fatalf("allow Alice in NoPaidMessages: %v", err)
}
if err := send(81003, 0); err != nil {
t.Fatalf("explicit no-paid exception send: %v", err)
}
full, err = r.onUsersGetFullUser(WithUserID(ctx, alice.ID), &tg.InputUser{
UserID: bob.ID, AccessHash: bob.AccessHash,
})
if err != nil {
t.Fatalf("get exempt Bob full user: %v", err)
}
if _, ok := full.FullUser.GetSendPaidMessagesStars(); ok || full.FullUser.ContactRequirePremium {
t.Fatalf("exempt full user still carries contact restriction: %+v", full.FullUser)
}
if _, err := privacy.SetRules(ctx, bob.ID, domain.PrivacyKeyNoPaidMessages, []domain.PrivacyRule{{Kind: domain.PrivacyRuleDisallowAll}}); err != nil {
t.Fatalf("clear explicit exception: %v", err)
}
if _, err := contacts.Upsert(ctx, bob.ID, domain.ContactInput{
ContactUserID: alice.ID,
FirstName: "Alice",
}); err != nil {
t.Fatalf("Bob add Alice contact: %v", err)
}
if err := send(81004, 0); err != nil {
t.Fatalf("recipient contact must be free: %v", err)
}
}
func TestPrivateContactRequirementPremiumGateUsesViewerFactsReadModel(t *testing.T) {
ctx := context.Background()
for _, tc := range []struct {
name string
senderPremium bool
wantErr bool
}{
{name: "non-premium blocked", wantErr: true},
{name: "premium allowed", senderPremium: true},
} {
t.Run(tc.name, func(t *testing.T) {
r, account, _, _, alice, bob := newPrivateRequirementRouter(t, tc.senderPremium)
if _, err := account.SetGlobalPrivacy(ctx, bob.ID, domain.GlobalPrivacy{NewNoncontactPeersRequirePremium: true}); err != nil {
t.Fatalf("set premium requirement: %v", err)
}
_, err := r.onMessagesSendMessage(WithUserID(ctx, alice.ID), &tg.MessagesSendMessageRequest{
Peer: &tg.InputPeerUser{UserID: bob.ID, AccessHash: bob.AccessHash},
Message: "hello",
RandomID: 82001,
})
if tc.wantErr {
if err == nil || !strings.Contains(err.Error(), "PREMIUM_ACCOUNT_REQUIRED") {
t.Fatalf("send err = %v, want PREMIUM_ACCOUNT_REQUIRED", err)
}
} else if err != nil {
t.Fatalf("premium sender should pass: %v", err)
}
})
}
}