Sync telesrv bd15657 (feat(account): implement collectible emoji status). Skipped telesrv docs changes per public sync rules.
134 lines
5.1 KiB
Go
134 lines
5.1 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/iamxvbaba/td/clock"
|
|
"github.com/iamxvbaba/td/tg"
|
|
"github.com/iamxvbaba/td/tgerr"
|
|
"go.uber.org/zap/zaptest"
|
|
|
|
appusers "telesrv/internal/app/users"
|
|
"telesrv/internal/domain"
|
|
"telesrv/internal/store/memory"
|
|
)
|
|
|
|
type collectibleEmojiGiftService struct {
|
|
GiftsService
|
|
gifts map[int64]domain.UniqueStarGift
|
|
}
|
|
|
|
func (s *collectibleEmojiGiftService) UniqueByID(_ context.Context, id int64) (domain.UniqueStarGift, bool, error) {
|
|
gift, ok := s.gifts[id]
|
|
return gift, ok, nil
|
|
}
|
|
|
|
func (s *collectibleEmojiGiftService) ListUniqueByOwner(_ context.Context, owner domain.Peer, limit int) ([]domain.UniqueStarGift, error) {
|
|
out := make([]domain.UniqueStarGift, 0, len(s.gifts))
|
|
for _, gift := range s.gifts {
|
|
if gift.Owner == owner && !gift.Burned && gift.OwnerAddress == "" {
|
|
out = append(out, gift)
|
|
}
|
|
}
|
|
if len(out) > limit {
|
|
out = out[:limit]
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func collectibleEmojiTestGift(ownerID int64) domain.UniqueStarGift {
|
|
return domain.UniqueStarGift{
|
|
ID: 9001, Title: "Plush Pepe", Slug: "PlushPepe-1",
|
|
Owner: domain.Peer{Type: domain.PeerTypeUser, ID: ownerID},
|
|
Model: domain.StarGiftCollectibleAttribute{Document: &domain.Document{ID: 7101}},
|
|
Pattern: domain.StarGiftCollectibleAttribute{Document: &domain.Document{ID: 7201}},
|
|
Backdrop: domain.StarGiftCollectibleAttribute{
|
|
CenterColor: 0x102030, EdgeColor: 0x405060,
|
|
PatternColor: 0x708090, TextColor: 0xa0b0c0,
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestAccountCollectibleEmojiStatusListSetAndRejectNonOwner(t *testing.T) {
|
|
ctx := context.Background()
|
|
userStore := memory.NewUserStore()
|
|
owner, err := userStore.Create(ctx, domain.User{AccessHash: 1, Phone: "15550009101", FirstName: "Owner"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
other, err := userStore.Create(ctx, domain.User{AccessHash: 2, Phone: "15550009102", FirstName: "Other"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
users := appusers.NewService(userStore)
|
|
if _, err := users.GrantPremium(ctx, owner.ID, 1); err != nil {
|
|
t.Fatalf("grant premium: %v", err)
|
|
}
|
|
gift := collectibleEmojiTestGift(owner.ID)
|
|
gifts := &collectibleEmojiGiftService{gifts: map[int64]domain.UniqueStarGift{gift.ID: gift}}
|
|
r := New(Config{}, Deps{Users: users, Gifts: gifts}, zaptest.NewLogger(t), clock.System)
|
|
ownerCtx := WithUserID(ctx, owner.ID)
|
|
|
|
listed, err := r.onAccountGetCollectibleEmojiStatuses(ownerCtx, 0)
|
|
if err != nil {
|
|
t.Fatalf("get collectible statuses: %v", err)
|
|
}
|
|
statuses, ok := listed.(*tg.AccountEmojiStatuses)
|
|
if !ok || len(statuses.Statuses) != 1 || statuses.Hash == 0 {
|
|
t.Fatalf("collectible list = %T %#v", listed, listed)
|
|
}
|
|
collectible, ok := statuses.Statuses[0].(*tg.EmojiStatusCollectible)
|
|
if !ok || collectible.CollectibleID != gift.ID || collectible.DocumentID != gift.Model.Document.ID ||
|
|
collectible.PatternDocumentID != gift.Pattern.Document.ID || collectible.PatternColor != gift.Backdrop.PatternColor {
|
|
t.Fatalf("collectible status = %T %#v", statuses.Statuses[0], statuses.Statuses[0])
|
|
}
|
|
if cached, err := r.onAccountGetCollectibleEmojiStatuses(ownerCtx, statuses.Hash); err != nil {
|
|
t.Fatalf("get cached collectible statuses: %v", err)
|
|
} else if _, ok := cached.(*tg.AccountEmojiStatusesNotModified); !ok {
|
|
t.Fatalf("cached collectible statuses = %T, want notModified", cached)
|
|
}
|
|
|
|
input := &tg.InputEmojiStatusCollectible{CollectibleID: gift.ID}
|
|
input.SetUntil(2_000_000_000)
|
|
if ok, err := r.onAccountUpdateEmojiStatus(ownerCtx, input); err != nil || !ok {
|
|
t.Fatalf("set collectible status: ok=%v err=%v", ok, err)
|
|
}
|
|
self, err := users.Self(ctx, owner.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !self.EmojiStatusCollectible.Valid() || self.EmojiStatusCollectible.CollectibleID != gift.ID ||
|
|
self.EmojiStatusUntil != 2_000_000_000 {
|
|
t.Fatalf("persisted collectible status = %+v", self.EmojiStatus())
|
|
}
|
|
wire, ok := tgUserEmojiStatus(self, time.Now().Unix()).(*tg.EmojiStatusCollectible)
|
|
if !ok || wire.Slug != gift.Slug || wire.TextColor != gift.Backdrop.TextColor {
|
|
t.Fatalf("wire collectible = %T %#v", tgUserEmojiStatus(self, time.Now().Unix()), wire)
|
|
}
|
|
|
|
stolen := gift
|
|
stolen.Owner = domain.Peer{Type: domain.PeerTypeUser, ID: other.ID}
|
|
gifts.gifts[gift.ID] = stolen
|
|
if ok, err := r.onAccountUpdateEmojiStatus(ownerCtx, &tg.InputEmojiStatusCollectible{CollectibleID: gift.ID}); ok || !tgerr.Is(err, "COLLECTIBLE_INVALID") {
|
|
t.Fatalf("set non-owned collectible: ok=%v err=%v", ok, err)
|
|
}
|
|
}
|
|
|
|
func TestCollectibleEmojiStatusDurableUpdateProjection(t *testing.T) {
|
|
collectible, ok := domain.CollectibleEmojiStatus(collectibleEmojiTestGift(1))
|
|
if !ok {
|
|
t.Fatal("test gift should project")
|
|
}
|
|
value := domain.UserEmojiStatus{DocumentID: collectible.DocumentID, Collectible: collectible}
|
|
update, ok := tgOtherUpdateFromEvent(domain.UpdateEvent{
|
|
UserID: 1, Type: domain.UpdateEventUserEmojiStatus, EmojiStatus: value,
|
|
}).(*tg.UpdateUserEmojiStatus)
|
|
if !ok {
|
|
t.Fatal("durable event did not produce updateUserEmojiStatus")
|
|
}
|
|
if status, ok := update.EmojiStatus.(*tg.EmojiStatusCollectible); !ok || status.PatternDocumentID != collectible.PatternDocumentID {
|
|
t.Fatalf("durable wire status = %T %#v", update.EmojiStatus, update.EmojiStatus)
|
|
}
|
|
}
|