Ctrl K
ComunidadGuías…

internal/domain/star_gift_lifecycle_normalize_test.go

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

Archivo original ↗Preguntar a KiBot
package domain

import (
	"errors"
	"testing"
)

// TestNormalizeLifecycleAuthoringSatisfiesCatalogConstraints locks the derived
// fields that star_gift_catalog_revisions' CHECK constraints require. An operator
// states supply, slug, per-round and (optionally) a start time; "limited", the
// seeded availability_remains and a positive auction_start_date are consequences.
// Without them the revision INSERT is rejected by the database, which is exactly
// the failure this test exists to prevent regressing.
func TestNormalizeLifecycleAuthoringSatisfiesCatalogConstraints(t *testing.T) {
	const now = 1_000_000

	t.Run("auction with immediate start", func(t *testing.T) {
		w := StarGiftCatalogWrite{
			Auction: true, AuctionSlug: "spring", GiftsPerRound: 2, AvailabilityTotal: 10,
		}
		if err := w.ValidateLifecycleAuthoring(now); err != nil {
			t.Fatalf("validate: %v", err)
		}
		w.NormalizeLifecycleAuthoring(now)
		if !w.Limited {
			t.Fatalf("auction must be limited so the supply and auction checks hold")
		}
		if w.AuctionStartDate != now {
			t.Fatalf("start date = %d, want %d (a zero start means start now)", w.AuctionStartDate, now)
		}
		if w.AvailabilityRemains != 10 {
			t.Fatalf("availability remains = %d, want the full supply 10", w.AvailabilityRemains)
		}
	})

	t.Run("auction with explicit future start is preserved", func(t *testing.T) {
		w := StarGiftCatalogWrite{
			Auction: true, AuctionSlug: "summer", GiftsPerRound: 5, AvailabilityTotal: 20,
			AuctionStartDate: now + 3600,
		}
		w.NormalizeLifecycleAuthoring(now)
		if w.AuctionStartDate != now+3600 {
			t.Fatalf("start date = %d, want the operator's %d", w.AuctionStartDate, now+3600)
		}
		if w.AvailabilityRemains != 20 {
			t.Fatalf("availability remains = %d, want 20", w.AvailabilityRemains)
		}
	})

	// A scheduled drop is an ordinary unlimited gift behind a countdown. Marking it
	// limited would violate "NOT limited AND availability_total = 0".
	t.Run("scheduled drop stays unlimited", func(t *testing.T) {
		w := StarGiftCatalogWrite{LockedUntilDate: now + 120}
		if err := w.ValidateLifecycleAuthoring(now); err != nil {
			t.Fatalf("validate: %v", err)
		}
		w.NormalizeLifecycleAuthoring(now)
		if w.Limited || w.AvailabilityTotal != 0 || w.AvailabilityRemains != 0 {
			t.Fatalf("drop was altered: limited=%v total=%d remains=%d",
				w.Limited, w.AvailabilityTotal, w.AvailabilityRemains)
		}
		if w.AuctionStartDate != 0 {
			t.Fatalf("start date = %d, want 0 on a non-auction gift", w.AuctionStartDate)
		}
	})

	t.Run("regular gift is untouched", func(t *testing.T) {
		w := StarGiftCatalogWrite{}
		w.NormalizeLifecycleAuthoring(now)
		if w.Limited || w.AuctionStartDate != 0 || w.AvailabilityRemains != 0 {
			t.Fatalf("regular gift was altered: limited=%v start=%d remains=%d",
				w.Limited, w.AuctionStartDate, w.AvailabilityRemains)
		}
	})
}

// TestValidateLifecycleAuthoringBoundsOpeningBid guards the ceiling shared with
// MaxStarGiftAuctionBidStars. ensureStarGiftAuction seeds min_bid_amount from the
// gift price, so an opening bid above the cap would produce an auction in which
// every bid is rejected by starGiftAuctionBidTarget — and, once a top bid climbs
// into that range, one that crashes official clients outright.
func TestValidateLifecycleAuthoringBoundsOpeningBid(t *testing.T) {
	const now = 1_000_000

	base := func(stars int64) StarGiftCatalogWrite {
		return StarGiftCatalogWrite{
			Auction: true, AuctionSlug: "capped", GiftsPerRound: 1, AvailabilityTotal: 3,
			Stars: stars,
		}
	}

	w := base(MaxStarGiftAuctionBidStars)
	if err := w.ValidateLifecycleAuthoring(now); err != nil {
		t.Fatalf("opening bid exactly at the cap must be allowed: %v", err)
	}

	w = base(MaxStarGiftAuctionBidStars + 1)
	if err := w.ValidateLifecycleAuthoring(now); err == nil {
		t.Fatalf("opening bid of %d stars was accepted, want rejection above %d",
			MaxStarGiftAuctionBidStars+1, MaxStarGiftAuctionBidStars)
	} else if !errors.Is(err, ErrStarGiftLifecycleInvalid) {
		t.Fatalf("error = %v, want it to wrap ErrStarGiftLifecycleInvalid", err)
	}

	// The cap is an auction rule. A plain gift priced above it is a market matter,
	// governed by the resale limits, and must not be rejected here.
	plain := StarGiftCatalogWrite{Stars: MaxStarGiftAuctionBidStars + 1}
	if err := plain.ValidateLifecycleAuthoring(now); err != nil {
		t.Fatalf("non-auction gift priced above the bid cap: %v", err)
	}
}