Ctrl K
ComunidadGuías…

internal/domain/star_gift_collectible_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package domain

import (
	"crypto/sha256"
	"errors"
	"strings"
	"testing"
)

func TestSavedStarGiftRefRequiresOneOfficialIdentity(t *testing.T) {
	user := Peer{Type: PeerTypeUser, ID: 42}
	channel := Peer{Type: PeerTypeChannel, ID: 84}
	tests := []struct {
		name string
		ref  SavedStarGiftRef
		want bool
	}{
		{name: "user message", ref: SavedStarGiftRef{Owner: user, MsgID: 10}, want: true},
		{name: "channel saved id", ref: SavedStarGiftRef{Owner: channel, SavedID: 20}, want: true},
		{name: "user collectible slug", ref: SavedStarGiftRef{Owner: user, Slug: "official-42-1"}, want: true},
		{name: "channel collectible slug", ref: SavedStarGiftRef{Owner: channel, Slug: "official-84-1"}, want: true},
		{name: "message and slug", ref: SavedStarGiftRef{Owner: user, MsgID: 10, Slug: "official-42-1"}},
		{name: "saved id and slug", ref: SavedStarGiftRef{Owner: channel, SavedID: 20, Slug: "official-84-1"}},
		{name: "whitespace slug", ref: SavedStarGiftRef{Owner: user, Slug: " official-42-1"}},
		{name: "oversized slug", ref: SavedStarGiftRef{Owner: user, Slug: strings.Repeat("x", MaxStarGiftSlugBytes+1)}},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.ref.Valid(); got != tt.want {
				t.Fatalf("Valid() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestStarGiftLifecycleStatusRequiresExplicitActive(t *testing.T) {
	if StarGiftLifecycleStatus("").Live() {
		t.Fatal("empty lifecycle status must not be treated as active")
	}
	if !StarGiftLifecycleActive.Live() {
		t.Fatal("active lifecycle status must be live")
	}
}

func validCollectibleDraft() StarGiftCollectibleWrite {
	animation := &StarGiftAnimation{JSON: []byte(`{}`), TGS: []byte{1}, SHA256: make([]byte, sha256.Size)}
	return StarGiftCollectibleWrite{
		GiftID: 1, UpgradeStars: 25, SupplyTotal: 100, SlugPrefix: "official-1", CommandID: "test",
		Models: []StarGiftCollectibleAttribute{
			{Kind: StarGiftCollectibleModel, Name: "Regular", RarityKind: StarGiftRarityPermille, RarityPermille: 922, Animation: animation},
			{Kind: StarGiftCollectibleModel, Name: "Regular Two", RarityKind: StarGiftRarityPermille, RarityPermille: 78, Animation: animation},
			{Kind: StarGiftCollectibleModel, Name: "Crafted", RarityKind: StarGiftRarityLegendary, Crafted: true, Animation: animation},
		},
		Patterns: []StarGiftCollectibleAttribute{
			{Kind: StarGiftCollectiblePattern, Name: "Pattern", RarityKind: StarGiftRarityPermille, RarityPermille: 989, Animation: animation},
			{Kind: StarGiftCollectiblePattern, Name: "Pattern Two", RarityKind: StarGiftRarityPermille, RarityPermille: 11, Animation: animation},
		},
		Backdrops: []StarGiftCollectibleAttribute{
			{Kind: StarGiftCollectibleBackdrop, Name: "Backdrop", BackdropID: 0, RarityKind: StarGiftRarityPermille, RarityPermille: 999},
			{Kind: StarGiftCollectibleBackdrop, Name: "Backdrop Two", BackdropID: 1, RarityKind: StarGiftRarityPermille, RarityPermille: 1},
		},
	}
}

func TestValidateStarGiftCollectibleDraftOfficialProvenance(t *testing.T) {
	write := validCollectibleDraft()
	write.OfficialGiftID = 10
	write.SourceManifestSHA256 = make([]byte, sha256.Size)
	if err := ValidateStarGiftCollectibleDraft(write); err != nil {
		t.Fatalf("valid official draft: %v", err)
	}

	tests := map[string]StarGiftCollectibleWrite{}
	withoutHash := write
	withoutHash.SourceManifestSHA256 = nil
	tests["official ID without hash"] = withoutHash
	withoutID := write
	withoutID.OfficialGiftID = 0
	tests["hash without official ID"] = withoutID
	negativeID := write
	negativeID.OfficialGiftID = -1
	tests["negative official ID"] = negativeID
	for name, invalid := range tests {
		t.Run(name, func(t *testing.T) {
			if err := ValidateStarGiftCollectibleDraft(invalid); !errors.Is(err, ErrStarGiftCollectibleInvalid) {
				t.Fatalf("err=%v, want ErrStarGiftCollectibleInvalid", err)
			}
		})
	}
}

// Telegram sends attributes with permille=0 for sold-out limited gifts (in the pool
// but never drawn): they must pass validation, but a whole pool of zero weights is
// still invalid because there is nothing left to draw.
func TestValidateStarGiftCollectibleDraftAcceptsZeroPermille(t *testing.T) {
	write := validCollectibleDraft()
	write.Models[0].RarityPermille = 0
	write.Patterns[0].RarityPermille = 0
	write.Backdrops = append(write.Backdrops, StarGiftCollectibleAttribute{Kind: StarGiftCollectibleBackdrop,
		Name: "Retired Backdrop", BackdropID: 2, RarityKind: StarGiftRarityPermille, RarityPermille: 0})
	if err := ValidateStarGiftCollectibleDraft(write); err != nil {
		t.Fatalf("zero-weight attributes: %v", err)
	}

	zeroOnly := validCollectibleDraft()
	for i := range zeroOnly.Models {
		if zeroOnly.Models[i].RarityKind == StarGiftRarityPermille {
			zeroOnly.Models[i].RarityPermille = 0
		}
	}
	if err := ValidateStarGiftCollectibleDraft(zeroOnly); !errors.Is(err, ErrStarGiftCollectibleInvalid) {
		t.Fatalf("all-zero model pool err=%v, want ErrStarGiftCollectibleInvalid", err)
	}

	negative := validCollectibleDraft()
	negative.Patterns[0].RarityPermille = -1
	if err := ValidateStarGiftCollectibleDraft(negative); !errors.Is(err, ErrStarGiftCollectibleInvalid) {
		t.Fatalf("negative permille err=%v, want ErrStarGiftCollectibleInvalid", err)
	}
}

// The upgrade preview samples only attributes with weight > 0: zero-weight
// backdrops cannot supply the two backdrops the preview needs, otherwise the
// preview shows only one backdrop and TDesktop's upgrade animation never stops;
// but zero-weight backdrops still participate in backdrop_id deduplication.
func TestValidateStarGiftCollectibleDraftCountsOnlyDrawableBackdrops(t *testing.T) {
	retired := StarGiftCollectibleAttribute{Kind: StarGiftCollectibleBackdrop, Name: "Retired Backdrop",
		BackdropID: 2, RarityKind: StarGiftRarityPermille, RarityPermille: 0}

	oneDrawable := validCollectibleDraft()
	oneDrawable.Backdrops[1].RarityPermille = 0
	oneDrawable.Backdrops = append(oneDrawable.Backdrops, retired)
	if err := ValidateStarGiftCollectibleDraft(oneDrawable); !errors.Is(err, ErrStarGiftCollectibleInvalid) {
		t.Fatalf("one drawable backdrop err=%v, want ErrStarGiftCollectibleInvalid", err)
	}

	duplicate := validCollectibleDraft()
	duplicateRetired := retired
	duplicateRetired.BackdropID = duplicate.Backdrops[0].BackdropID
	duplicate.Backdrops = append(duplicate.Backdrops, duplicateRetired)
	if err := ValidateStarGiftCollectibleDraft(duplicate); !errors.Is(err, ErrStarGiftCollectibleInvalid) {
		t.Fatalf("zero-weight backdrop reusing a backdrop_id err=%v, want ErrStarGiftCollectibleInvalid", err)
	}
}

func TestValidateStarGiftCollectibleDraftRejectsImplicitRarity(t *testing.T) {
	write := validCollectibleDraft()
	write.Models[0].RarityKind = ""
	if err := ValidateStarGiftCollectibleDraft(write); !errors.Is(err, ErrStarGiftCollectibleInvalid) {
		t.Fatalf("err=%v, want ErrStarGiftCollectibleInvalid", err)
	}
}

func storedCollectibleWrite() StarGiftCollectibleWrite {
	write := validCollectibleDraft()
	for i := range write.Models {
		write.Models[i].Document = &Document{
			ID: int64(100 + i), MimeType: "application/x-tgsticker",
			Attributes: []DocumentAttribute{{Kind: DocAttrSticker, Alt: "🎁"}},
		}
		write.Models[i].Blob = &FileBlob{LocationKey: "model"}
	}
	for i := range write.Patterns {
		write.Patterns[i].Document = &Document{
			ID: int64(200 + i), MimeType: "application/x-tgsticker",
			Attributes: []DocumentAttribute{{Kind: DocAttrCustomEmoji, Alt: "🎁", TextColor: true}},
			Thumbs:     []PhotoSize{{Kind: PhotoSizeKindPath, Type: "j", Bytes: []byte{1}}},
		}
		write.Patterns[i].Blob = &FileBlob{LocationKey: "pattern"}
	}
	return write
}

func TestValidateStarGiftCollectibleDraftAllowsDeterministicAnimatedAttributes(t *testing.T) {
	for _, kind := range []StarGiftCollectibleAttributeKind{StarGiftCollectibleModel, StarGiftCollectiblePattern} {
		t.Run(string(kind), func(t *testing.T) {
			write := validCollectibleDraft()
			if kind == StarGiftCollectibleModel {
				write.Models = write.Models[:1]
			} else {
				write.Patterns = write.Patterns[:1]
			}
			if err := ValidateStarGiftCollectibleDraft(write); err != nil {
				t.Fatalf("singleton %s: %v", kind, err)
			}
		})
	}
}

func TestValidateStarGiftCollectibleDraftRequiresClientSafePreviewPool(t *testing.T) {
	tests := map[string]func(*StarGiftCollectibleWrite){
		"no selectable model":   func(write *StarGiftCollectibleWrite) { write.Models = nil },
		"no selectable pattern": func(write *StarGiftCollectibleWrite) { write.Patterns = nil },
		"one selectable backdrop": func(write *StarGiftCollectibleWrite) {
			write.Backdrops = write.Backdrops[:1]
		},
		"duplicate backdrop id": func(write *StarGiftCollectibleWrite) {
			write.Backdrops[1].BackdropID = write.Backdrops[0].BackdropID
		},
	}
	for name, mutate := range tests {
		t.Run(name, func(t *testing.T) {
			write := validCollectibleDraft()
			mutate(&write)
			if err := ValidateStarGiftCollectibleDraft(write); !errors.Is(err, ErrStarGiftCollectibleInvalid) {
				t.Fatalf("err=%v, want ErrStarGiftCollectibleInvalid", err)
			}
		})
	}
}

func TestValidateStarGiftCollectibleWriteRequiresDistinctPreviewDocuments(t *testing.T) {
	for _, kind := range []StarGiftCollectibleAttributeKind{StarGiftCollectibleModel, StarGiftCollectiblePattern} {
		t.Run(string(kind), func(t *testing.T) {
			write := storedCollectibleWrite()
			if kind == StarGiftCollectibleModel {
				write.Models[1].Document = write.Models[0].Document
			} else {
				write.Patterns[1].Document = write.Patterns[0].Document
			}
			if err := ValidateStarGiftCollectibleWrite(write); !errors.Is(err, ErrStarGiftCollectibleInvalid) {
				t.Fatalf("err=%v, want ErrStarGiftCollectibleInvalid", err)
			}
		})
	}
}

func TestValidateStarGiftCollectibleWriteRequiresExactDocumentRoles(t *testing.T) {
	if err := ValidateStarGiftCollectibleWrite(storedCollectibleWrite()); err != nil {
		t.Fatalf("valid stored collectible: %v", err)
	}

	tests := map[string]func(*StarGiftCollectibleWrite){
		"pattern stored as sticker": func(write *StarGiftCollectibleWrite) {
			write.Patterns[0].Document.Attributes = []DocumentAttribute{{Kind: DocAttrSticker, Alt: "🎁"}}
		},
		"pattern custom emoji without text color": func(write *StarGiftCollectibleWrite) {
			write.Patterns[0].Document.Attributes[0].TextColor = false
		},
		"pattern without inline path thumb": func(write *StarGiftCollectibleWrite) {
			write.Patterns[0].Document.Thumbs = nil
		},
		"model stored as custom emoji": func(write *StarGiftCollectibleWrite) {
			write.Models[0].Document.Attributes = []DocumentAttribute{{Kind: DocAttrCustomEmoji, Alt: "🎁", TextColor: true}}
		},
		"ambiguous model render attributes": func(write *StarGiftCollectibleWrite) {
			write.Models[0].Document.Attributes = append(write.Models[0].Document.Attributes,
				DocumentAttribute{Kind: DocAttrCustomEmoji, Alt: "🎁", TextColor: true})
		},
	}
	for name, mutate := range tests {
		t.Run(name, func(t *testing.T) {
			write := storedCollectibleWrite()
			mutate(&write)
			if err := ValidateStarGiftCollectibleWrite(write); !errors.Is(err, ErrStarGiftCollectibleInvalid) {
				t.Fatalf("err=%v, want ErrStarGiftCollectibleInvalid", err)
			}
		})
	}
}