2026-06-18 18:48:47 +08:00

115 lines
4.1 KiB
Go

package wheel
import (
"strings"
"testing"
activityv1 "hyapp.local/api/proto/activity/v1"
)
func TestConfigToProtoSupportsPoolTierCountAndForcesPropRTPZero(t *testing.T) {
tiers := validClassicTiers()
tiers[0] = tierDTO{DisplayName: "金币", RewardType: " coin ", RewardCount: 1, RewardCoins: 1000, ProbabilityPercent: defaultProbabilityPercent(0, 8), Enabled: true}
tiers[1] = tierDTO{DisplayName: "礼物", RewardType: "gift", RewardID: "gift_1", RewardCount: 1, RTPValueCoins: 5000, ProbabilityPercent: defaultProbabilityPercent(1, 8), Enabled: true}
tiers[2] = tierDTO{DisplayName: "装扮", RewardType: "dress_up", RewardID: "dress_1", RewardCount: 1, RTPValueCoins: 999999, ProbabilityPercent: defaultProbabilityPercent(2, 8), Enabled: true}
req := configRequest{
WheelID: " classic ",
Enabled: true,
DrawPriceCoins: 10000,
TargetRTPPercent: 90,
PoolRatePercent: 92,
SettlementWindowDraws: 1000,
Tiers: tiers,
}
if err := validateConfigRequest(req); err != nil {
t.Fatalf("validate config failed: %v", err)
}
config := configToProto(req)
if config.GetWheelId() != "classic" {
t.Fatalf("wheel id = %q, want classic", config.GetWheelId())
}
if config.GetTargetRtpPpm() != 900000 || config.GetPoolRatePpm() != 920000 {
t.Fatalf("ppm mismatch: target=%d pool=%d", config.GetTargetRtpPpm(), config.GetPoolRatePpm())
}
protoTiers := config.GetTiers()
if len(protoTiers) != 8 {
t.Fatalf("tier count = %d, want 8", len(protoTiers))
}
if protoTiers[0].GetTierId() != "coin-01" || protoTiers[0].GetRtpValueCoins() != 1000 {
t.Fatalf("coin tier mismatch: %+v", protoTiers[0])
}
if protoTiers[2].GetRewardType() != "dress" || protoTiers[2].GetRtpValueCoins() != 0 {
t.Fatalf("dress tier must force rtp to zero: %+v", protoTiers[2])
}
}
func TestValidateConfigRequiresPoolTierCountAndProbabilitySum(t *testing.T) {
err := validateConfigRequest(configRequest{
WheelID: "classic",
Tiers: []tierDTO{
{RewardType: "coin", RewardCoins: 100, ProbabilityPercent: 50},
{RewardType: "gift", RewardID: "gift_1", ProbabilityPercent: 30},
{RewardType: "gift", RewardID: "gift_2", ProbabilityPercent: 20},
},
})
if err == nil || !strings.Contains(err.Error(), "8 个奖品档位") {
t.Fatalf("classic tier count should be rejected, got %v", err)
}
tiers := validClassicTiers()
tiers[7].ProbabilityPercent = 1
err = validateConfigRequest(configRequest{
WheelID: "classic",
Tiers: tiers,
})
if err == nil || !strings.Contains(err.Error(), "概率合计") {
t.Fatalf("probability sum should be rejected, got %v", err)
}
}
func TestDefaultConfigDoesNotCreateMockPrizeTiers(t *testing.T) {
config := defaultConfigDTO("classic")
if len(config.Tiers) != 0 {
t.Fatalf("default config must not create mock prize tiers: %+v", config.Tiers)
}
if config.WheelID != "classic" || config.DrawPriceCoins <= 0 {
t.Fatalf("default base config mismatch: %+v", config)
}
}
func TestConfigFromProtoConvertsPPMToPercent(t *testing.T) {
config := configFromProto(&activityv1.WheelRuleConfig{
AppCode: "lalu",
WheelId: "default",
RuleVersion: 3,
Enabled: true,
DrawPriceCoins: 10000,
TargetRtpPpm: 900000,
PoolRatePpm: 920000,
SettlementWindowDraws: 1000,
Tiers: []*activityv1.WheelPrizeTier{
{TierId: "coin", DisplayName: "金币", RewardType: "coin", RewardCoins: 1000, RtpValueCoins: 1000, WeightPpm: 600000, Enabled: true},
},
})
if config.TargetRTPPercent != 90 || config.PoolRatePercent != 92 {
t.Fatalf("percent mismatch: target=%v pool=%v", config.TargetRTPPercent, config.PoolRatePercent)
}
if len(config.Tiers) != 1 || config.Tiers[0].ProbabilityPercent != 60 {
t.Fatalf("tier probability mismatch: %+v", config.Tiers)
}
}
func validClassicTiers() []tierDTO {
tiers := make([]tierDTO, 0, 8)
for index := 0; index < 8; index++ {
tiers = append(tiers, tierDTO{
DisplayName: "金币",
RewardType: "coin",
RewardCount: 1,
RewardCoins: 100,
ProbabilityPercent: defaultProbabilityPercent(index, 8),
Enabled: true,
})
}
return tiers
}