479 lines
18 KiB
Go
479 lines
18 KiB
Go
package vip
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"chatapp3-golang/internal/model"
|
|
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func TestBuildPurchasePlanNewPurchase(t *testing.T) {
|
|
now := time.Date(2026, 4, 29, 10, 0, 0, 0, time.UTC)
|
|
configs := vipTestConfigs()
|
|
|
|
plan, err := buildPurchasePlan(now, nil, configs[2], configs)
|
|
if err != nil {
|
|
t.Fatalf("buildPurchasePlan returned error: %v", err)
|
|
}
|
|
|
|
if plan.OrderType != orderTypePurchase {
|
|
t.Fatalf("order type = %s, want %s", plan.OrderType, orderTypePurchase)
|
|
}
|
|
if plan.CurrentLevel != 0 || plan.TargetLevel != 2 {
|
|
t.Fatalf("levels = %d -> %d, want 0 -> 2", plan.CurrentLevel, plan.TargetLevel)
|
|
}
|
|
if plan.PayableGold != 2000 {
|
|
t.Fatalf("payable = %d, want 2000", plan.PayableGold)
|
|
}
|
|
if !plan.StartAt.Equal(now) || !plan.ExpireAt.Equal(now.AddDate(0, 0, vipDurationDay)) {
|
|
t.Fatalf("period = %s -> %s, want %s -> %s", plan.StartAt, plan.ExpireAt, now, now.AddDate(0, 0, vipDurationDay))
|
|
}
|
|
}
|
|
|
|
func TestBuildPurchasePlanUpgradePaysDifferenceAndKeepsExpireAt(t *testing.T) {
|
|
now := time.Date(2026, 4, 29, 10, 0, 0, 0, time.UTC)
|
|
startAt := now.AddDate(0, 0, -5)
|
|
expireAt := now.AddDate(0, 0, 25)
|
|
configs := vipTestConfigs()
|
|
state := &model.UserVipState{
|
|
Level: 2,
|
|
Status: statusActive,
|
|
StartAt: startAt,
|
|
ExpireAt: expireAt,
|
|
}
|
|
|
|
plan, err := buildPurchasePlan(now, state, configs[4], configs)
|
|
if err != nil {
|
|
t.Fatalf("buildPurchasePlan returned error: %v", err)
|
|
}
|
|
|
|
if plan.OrderType != orderTypeUpgrade {
|
|
t.Fatalf("order type = %s, want %s", plan.OrderType, orderTypeUpgrade)
|
|
}
|
|
if plan.PayableGold != 2000 {
|
|
t.Fatalf("payable = %d, want 2000", plan.PayableGold)
|
|
}
|
|
if !plan.StartAt.Equal(startAt) || !plan.ExpireAt.Equal(expireAt) {
|
|
t.Fatalf("upgrade period changed to %s -> %s, want %s -> %s", plan.StartAt, plan.ExpireAt, startAt, expireAt)
|
|
}
|
|
}
|
|
|
|
func TestBuildPurchasePlanRenewExtendsThirtyDays(t *testing.T) {
|
|
now := time.Date(2026, 4, 29, 10, 0, 0, 0, time.UTC)
|
|
startAt := now.AddDate(0, 0, -5)
|
|
expireAt := now.AddDate(0, 0, 25)
|
|
configs := vipTestConfigs()
|
|
state := &model.UserVipState{
|
|
Level: 3,
|
|
Status: statusActive,
|
|
StartAt: startAt,
|
|
ExpireAt: expireAt,
|
|
}
|
|
|
|
plan, err := buildPurchasePlan(now, state, configs[3], configs)
|
|
if err != nil {
|
|
t.Fatalf("buildPurchasePlan returned error: %v", err)
|
|
}
|
|
|
|
if plan.OrderType != orderTypeRenew {
|
|
t.Fatalf("order type = %s, want %s", plan.OrderType, orderTypeRenew)
|
|
}
|
|
if plan.PayableGold != 3000 {
|
|
t.Fatalf("payable = %d, want 3000", plan.PayableGold)
|
|
}
|
|
if !plan.ExpireAt.Equal(expireAt.AddDate(0, 0, vipDurationDay)) {
|
|
t.Fatalf("renew expireAt = %s, want %s", plan.ExpireAt, expireAt.AddDate(0, 0, vipDurationDay))
|
|
}
|
|
}
|
|
|
|
func TestBuildPurchasePlanRejectsDowngrade(t *testing.T) {
|
|
now := time.Date(2026, 4, 29, 10, 0, 0, 0, time.UTC)
|
|
configs := vipTestConfigs()
|
|
state := &model.UserVipState{
|
|
Level: 4,
|
|
Status: statusActive,
|
|
StartAt: now.AddDate(0, 0, -5),
|
|
ExpireAt: now.AddDate(0, 0, 25),
|
|
}
|
|
|
|
if _, err := buildPurchasePlan(now, state, configs[2], configs); err == nil {
|
|
t.Fatal("buildPurchasePlan returned nil error for downgrade")
|
|
}
|
|
}
|
|
|
|
func TestVipResourcePayloadAcceptsStringResourceID(t *testing.T) {
|
|
var payload VipResourcePayload
|
|
if err := json.Unmarshal([]byte(`{"resourceId":"2045029471274201089","name":"vip","sourceUrl":"https://example.com/a.svga","coverUrl":"https://example.com/a.png"}`), &payload); err != nil {
|
|
t.Fatalf("unmarshal resource payload: %v", err)
|
|
}
|
|
payload = normalizeResource(payload)
|
|
if payload.ResourceID.Int64() != 2045029471274201089 {
|
|
t.Fatalf("resource id = %d, want 2045029471274201089", payload.ResourceID.Int64())
|
|
}
|
|
if payload.URL != "https://example.com/a.svga" || payload.SourceURL != payload.URL || payload.ResourceURL != payload.URL {
|
|
t.Fatalf("resource urls = url:%q sourceUrl:%q resourceUrl:%q", payload.URL, payload.SourceURL, payload.ResourceURL)
|
|
}
|
|
if payload.Cover != "https://example.com/a.png" || payload.CoverURL != payload.Cover {
|
|
t.Fatalf("resource cover = cover:%q coverUrl:%q", payload.Cover, payload.CoverURL)
|
|
}
|
|
|
|
body, err := json.Marshal(payload)
|
|
if err != nil {
|
|
t.Fatalf("marshal resource payload: %v", err)
|
|
}
|
|
if got := string(body); got != `{"resourceId":"2045029471274201089","name":"vip","url":"https://example.com/a.svga","sourceUrl":"https://example.com/a.svga","resourceUrl":"https://example.com/a.svga","cover":"https://example.com/a.png","coverUrl":"https://example.com/a.png"}` {
|
|
t.Fatalf("json = %s", got)
|
|
}
|
|
}
|
|
|
|
func TestVipLevelPayloadMarshalsIDAsString(t *testing.T) {
|
|
payload := payloadFromConfig(model.VipLevelConfig{
|
|
ID: 2049402425177018368,
|
|
SysOrigin: "LIKEI",
|
|
Level: 1,
|
|
LevelCode: "VIP1",
|
|
DisplayName: "VIP 1",
|
|
Enabled: true,
|
|
})
|
|
|
|
body, err := json.Marshal(payload)
|
|
if err != nil {
|
|
t.Fatalf("marshal vip level payload: %v", err)
|
|
}
|
|
if !strings.Contains(string(body), `"id":"2049402425177018368"`) {
|
|
t.Fatalf("vip level id was not emitted as string: %s", body)
|
|
}
|
|
|
|
var decoded VipLevelPayload
|
|
if err := json.Unmarshal(body, &decoded); err != nil {
|
|
t.Fatalf("unmarshal vip level payload: %v", err)
|
|
}
|
|
if decoded.ID.Int64() != 2049402425177018368 {
|
|
t.Fatalf("decoded id = %d, want 2049402425177018368", decoded.ID.Int64())
|
|
}
|
|
}
|
|
|
|
func TestResourcePayloadUsesResourceGroupDetails(t *testing.T) {
|
|
row := model.VipLevelConfig{
|
|
Level: 2,
|
|
LevelCode: "VIP2",
|
|
DisplayName: "VIP 2",
|
|
Enabled: true,
|
|
DurationDays: vipDurationDay,
|
|
PriceGold: 2000,
|
|
AvatarFrameResourceID: 11,
|
|
AvatarFrameName: "stale frame",
|
|
AvatarFrameURL: "https://example.com/stale.svga",
|
|
}
|
|
|
|
payload := payloadFromConfigWithResources(row, vipResourceLookup{
|
|
11: {
|
|
ID: 11,
|
|
Name: "resource group frame",
|
|
Cover: "https://example.com/live.png",
|
|
SourceURL: "https://example.com/live.svga",
|
|
},
|
|
})
|
|
|
|
if payload.AvatarFrame.Name != "resource group frame" ||
|
|
payload.AvatarFrame.URL != "https://example.com/live.svga" ||
|
|
payload.AvatarFrame.Cover != "https://example.com/live.png" {
|
|
t.Fatalf("avatar frame was not loaded from resource group: %+v", payload.AvatarFrame)
|
|
}
|
|
}
|
|
|
|
func TestApplyPlanToStateKeepsOnlyResourceIDsAndPayloadUsesLookup(t *testing.T) {
|
|
now := time.Date(2026, 4, 29, 10, 0, 0, 0, time.UTC)
|
|
plan := purchasePlan{
|
|
TargetLevel: 3,
|
|
StartAt: now,
|
|
ExpireAt: now.AddDate(0, 0, vipDurationDay),
|
|
DurationDays: vipDurationDay,
|
|
TargetConfig: model.VipLevelConfig{
|
|
Level: 3,
|
|
DisplayName: "VIP 3",
|
|
BadgeResourceID: 10,
|
|
BadgeName: "long badge",
|
|
BadgeURL: "https://example.com/long.png",
|
|
ShortBadgeResourceID: 15,
|
|
ShortBadgeName: "short badge",
|
|
ShortBadgeURL: "https://example.com/short.png",
|
|
AvatarFrameResourceID: 11,
|
|
AvatarFrameName: "frame",
|
|
AvatarFrameURL: "https://example.com/frame.svga",
|
|
EntryEffectResourceID: 12,
|
|
EntryEffectName: "ride",
|
|
EntryEffectURL: "https://example.com/ride.svga",
|
|
ChatBubbleResourceID: 13,
|
|
ChatBubbleName: "bubble",
|
|
ChatBubbleURL: "https://example.com/bubble.svga",
|
|
FloatPictureResourceID: 14,
|
|
FloatPictureName: "float",
|
|
FloatPictureURL: "https://example.com/float.svga",
|
|
BackgroundCardResourceID: 16,
|
|
BackgroundCardName: "profile card",
|
|
BackgroundCardURL: "https://example.com/card.pag",
|
|
EffectImageResourceID: 17,
|
|
EffectImageName: "effect image",
|
|
EffectImageURL: "https://example.com/effect.svga",
|
|
},
|
|
}
|
|
|
|
state := &model.UserVipState{UserID: 1001, SysOrigin: "LIKEI"}
|
|
applyPlanToState(state, plan, now)
|
|
|
|
if state.BadgeResourceID != plan.TargetConfig.BadgeResourceID ||
|
|
state.ShortBadgeResourceID != plan.TargetConfig.ShortBadgeResourceID ||
|
|
state.AvatarFrameResourceID != plan.TargetConfig.AvatarFrameResourceID ||
|
|
state.EntryEffectResourceID != plan.TargetConfig.EntryEffectResourceID ||
|
|
state.ChatBubbleResourceID != plan.TargetConfig.ChatBubbleResourceID ||
|
|
state.FloatPictureResourceID != plan.TargetConfig.FloatPictureResourceID ||
|
|
state.BackgroundCardResourceID != plan.TargetConfig.BackgroundCardResourceID ||
|
|
state.EffectImageResourceID != plan.TargetConfig.EffectImageResourceID {
|
|
t.Fatalf("state resource ids were not copied from target config")
|
|
}
|
|
if state.AvatarFrameName != "" || state.AvatarFrameURL != "" {
|
|
t.Fatalf("state stored resource details: name=%q url=%q", state.AvatarFrameName, state.AvatarFrameURL)
|
|
}
|
|
|
|
payload := statePayloadFromModelWithResources(state, now, vipResourceLookup{
|
|
10: {ID: 10, Name: "long badge", Cover: "https://example.com/long.png", SourceURL: "https://example.com/long.svga"},
|
|
11: {ID: 11, Name: "frame", Cover: "https://example.com/frame.png", SourceURL: "https://example.com/frame.svga"},
|
|
12: {ID: 12, Name: "ride", Cover: "https://example.com/ride.png", SourceURL: "https://example.com/ride.svga"},
|
|
13: {ID: 13, Name: "bubble", Cover: "https://example.com/bubble.png", SourceURL: "https://example.com/bubble.svga"},
|
|
14: {ID: 14, Name: "float", Cover: "https://example.com/float.png", SourceURL: "https://example.com/float.svga"},
|
|
15: {ID: 15, Name: "short badge", Cover: "https://example.com/short.png", SourceURL: "https://example.com/short.svga"},
|
|
16: {ID: 16, Name: "profile card", Cover: "https://example.com/card.png", SourceURL: "https://example.com/card.pag"},
|
|
17: {ID: 17, Name: "effect image", Cover: "https://example.com/effect.png", SourceURL: "https://example.com/effect.svga"},
|
|
})
|
|
if payload.LongBadge.Cover != "https://example.com/long.png" ||
|
|
payload.Badge.Cover != "https://example.com/long.png" ||
|
|
payload.ShortBadge.Cover != "https://example.com/short.png" ||
|
|
payload.AvatarFrame.Cover != "https://example.com/frame.png" ||
|
|
payload.EntryEffect.Cover != "https://example.com/ride.png" ||
|
|
payload.ChatBubble.Cover != "https://example.com/bubble.png" ||
|
|
payload.FloatPicture.Cover != "https://example.com/float.png" ||
|
|
payload.BackgroundCard.Cover != "https://example.com/card.png" ||
|
|
payload.EffectImage.Cover != "https://example.com/effect.png" {
|
|
t.Fatalf("state payload covers were not loaded from resource lookup")
|
|
}
|
|
}
|
|
|
|
func TestMapWalletPurchaseErrorInsufficientBalance(t *testing.T) {
|
|
err := errors.New(`java api failed: status=406 body={"errorCode":5000,"errorCodeName":"INSUFFICIENT_BALANCE","errorMsg":"balance not made"}`)
|
|
|
|
appErr := mapWalletPurchaseError(err)
|
|
if appErr.Status != http.StatusNotAcceptable {
|
|
t.Fatalf("status = %d, want %d", appErr.Status, http.StatusNotAcceptable)
|
|
}
|
|
if appErr.Code != "vip_insufficient_balance" {
|
|
t.Fatalf("code = %s, want vip_insufficient_balance", appErr.Code)
|
|
}
|
|
if appErr.Message != "insufficient balance" {
|
|
t.Fatalf("message = %s, want insufficient balance", appErr.Message)
|
|
}
|
|
}
|
|
|
|
func TestMapWalletPurchaseErrorGatewayFailure(t *testing.T) {
|
|
err := errors.New("java api failed: status=500 body=boom")
|
|
|
|
appErr := mapWalletPurchaseError(err)
|
|
if appErr.Status != http.StatusBadGateway {
|
|
t.Fatalf("status = %d, want %d", appErr.Status, http.StatusBadGateway)
|
|
}
|
|
if appErr.Code != "vip_wallet_deduct_failed" {
|
|
t.Fatalf("code = %s, want vip_wallet_deduct_failed", appErr.Code)
|
|
}
|
|
}
|
|
|
|
func TestVIPGrantResourcesFromConfigMapsBackpackTypes(t *testing.T) {
|
|
config := model.VipLevelConfig{
|
|
BadgeResourceID: 10,
|
|
ShortBadgeResourceID: 15,
|
|
AvatarFrameResourceID: 11,
|
|
EntryEffectResourceID: 12,
|
|
ChatBubbleResourceID: 13,
|
|
FloatPictureResourceID: 14,
|
|
BackgroundCardResourceID: 16,
|
|
EffectImageResourceID: 17,
|
|
}
|
|
|
|
resources := vipGrantResourcesFromConfig(config)
|
|
if len(resources) != 7 {
|
|
t.Fatalf("resources len = %d, want 7", len(resources))
|
|
}
|
|
assertGrant := func(index int, resourceID int64, propsType string, badge bool) {
|
|
t.Helper()
|
|
got := resources[index]
|
|
if got.ResourceID != resourceID || got.PropsType != propsType || got.Badge != badge {
|
|
t.Fatalf("resource[%d] = %+v, want id=%d type=%s badge=%v", index, got, resourceID, propsType, badge)
|
|
}
|
|
}
|
|
assertGrant(0, 10, "", true)
|
|
assertGrant(1, 15, "", true)
|
|
assertGrant(2, 11, vipPropsTypeAvatarFrame, false)
|
|
assertGrant(3, 12, vipPropsTypeRide, false)
|
|
assertGrant(4, 13, vipPropsTypeChatBubble, false)
|
|
assertGrant(5, 14, vipPropsTypeFloatPicture, false)
|
|
assertGrant(6, 16, vipPropsTypeDataCard, false)
|
|
}
|
|
|
|
func TestCleanupSupersededVIPResourcesExpiresOnlyOldVIPResources(t *testing.T) {
|
|
db := openVIPCleanupTestDB(t)
|
|
service := &Service{db: db}
|
|
now := time.Date(2026, 5, 30, 10, 0, 0, 0, time.UTC)
|
|
future := now.AddDate(0, 0, 20)
|
|
oldTime := now.AddDate(0, 0, -1)
|
|
userID := int64(1001)
|
|
|
|
seedVIPCleanupRows(t, db,
|
|
vipCleanupPropsRow{ID: 1, UserID: userID, PropsID: 101, Type: vipPropsTypeAvatarFrame, ExpireTime: future, UseProps: true, AllowGive: true, UpdateTime: oldTime},
|
|
vipCleanupPropsRow{ID: 2, UserID: userID, PropsID: 201, Type: vipPropsTypeAvatarFrame, ExpireTime: future, UseProps: true, AllowGive: true, UpdateTime: oldTime},
|
|
vipCleanupPropsRow{ID: 3, UserID: userID, PropsID: 999, Type: vipPropsTypeAvatarFrame, ExpireTime: future, UseProps: true, AllowGive: true, UpdateTime: oldTime},
|
|
vipCleanupPropsRow{ID: 4, UserID: userID, PropsID: 102, Type: vipPropsTypeRide, ExpireTime: future, UseProps: true, AllowGive: true, UpdateTime: oldTime},
|
|
)
|
|
seedVIPCleanupBadges(t, db,
|
|
vipCleanupBadgeRow{ID: 1, UserID: userID, BadgeID: 100, ExpireTime: future, UseProps: true, UpdateTime: oldTime},
|
|
vipCleanupBadgeRow{ID: 2, UserID: userID, BadgeID: 200, ExpireTime: future, UseProps: true, UpdateTime: oldTime},
|
|
)
|
|
|
|
target := vipGrantResourcesFromConfig(model.VipLevelConfig{
|
|
BadgeResourceID: 200,
|
|
AvatarFrameResourceID: 201,
|
|
})
|
|
all := append(vipGrantResourcesFromConfig(model.VipLevelConfig{
|
|
BadgeResourceID: 100,
|
|
AvatarFrameResourceID: 101,
|
|
EntryEffectResourceID: 102,
|
|
}), target...)
|
|
|
|
if err := service.cleanupSupersededVIPResources(context.Background(), userID, target, all, now); err != nil {
|
|
t.Fatalf("cleanupSupersededVIPResources() error = %v", err)
|
|
}
|
|
|
|
assertVIPCleanupProps(t, db, 101, now, false, false)
|
|
assertVIPCleanupProps(t, db, 102, now, false, false)
|
|
assertVIPCleanupProps(t, db, 201, future, true, true)
|
|
assertVIPCleanupProps(t, db, 999, future, true, true)
|
|
assertVIPCleanupBadge(t, db, 100, now, false)
|
|
assertVIPCleanupBadge(t, db, 200, future, true)
|
|
}
|
|
|
|
func TestVIPGrantDaysUntilCeilsPartialDay(t *testing.T) {
|
|
now := time.Date(2026, 4, 29, 10, 0, 0, 0, time.UTC)
|
|
expireAt := now.Add((24 * time.Hour) + time.Second)
|
|
|
|
if got := vipGrantDaysUntil(now, expireAt); got != 2 {
|
|
t.Fatalf("days = %d, want 2", got)
|
|
}
|
|
if got := vipGrantDaysUntil(now, now); got != 1 {
|
|
t.Fatalf("expired days = %d, want 1", got)
|
|
}
|
|
}
|
|
|
|
type vipCleanupPropsRow struct {
|
|
ID int64 `gorm:"column:id;primaryKey"`
|
|
UserID int64 `gorm:"column:user_id"`
|
|
PropsID int64 `gorm:"column:props_id"`
|
|
Type string `gorm:"column:type"`
|
|
ExpireTime time.Time `gorm:"column:expire_time"`
|
|
UseProps bool `gorm:"column:is_use_props"`
|
|
AllowGive bool `gorm:"column:allow_give"`
|
|
UpdateTime time.Time `gorm:"column:update_time"`
|
|
}
|
|
|
|
func (vipCleanupPropsRow) TableName() string {
|
|
return "user_props_backpack"
|
|
}
|
|
|
|
type vipCleanupBadgeRow struct {
|
|
ID int64 `gorm:"column:id;primaryKey"`
|
|
UserID int64 `gorm:"column:user_id"`
|
|
BadgeID int64 `gorm:"column:badge_id"`
|
|
ExpireType string `gorm:"column:expire_type"`
|
|
ExpireTime time.Time `gorm:"column:expire_time"`
|
|
UseProps bool `gorm:"column:is_use_props"`
|
|
UpdateTime time.Time `gorm:"column:update_time"`
|
|
}
|
|
|
|
func (vipCleanupBadgeRow) TableName() string {
|
|
return "user_badge_backpack"
|
|
}
|
|
|
|
func openVIPCleanupTestDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatalf("open sqlite: %v", err)
|
|
}
|
|
if err := db.AutoMigrate(&vipCleanupPropsRow{}, &vipCleanupBadgeRow{}); err != nil {
|
|
t.Fatalf("auto migrate cleanup tables: %v", err)
|
|
}
|
|
return db
|
|
}
|
|
|
|
func seedVIPCleanupRows(t *testing.T, db *gorm.DB, rows ...vipCleanupPropsRow) {
|
|
t.Helper()
|
|
for _, row := range rows {
|
|
if err := db.Create(&row).Error; err != nil {
|
|
t.Fatalf("seed props row: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func seedVIPCleanupBadges(t *testing.T, db *gorm.DB, rows ...vipCleanupBadgeRow) {
|
|
t.Helper()
|
|
for _, row := range rows {
|
|
if err := db.Create(&row).Error; err != nil {
|
|
t.Fatalf("seed badge row: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertVIPCleanupProps(t *testing.T, db *gorm.DB, propsID int64, expireTime time.Time, useProps bool, allowGive bool) {
|
|
t.Helper()
|
|
var row vipCleanupPropsRow
|
|
if err := db.Where("props_id = ?", propsID).First(&row).Error; err != nil {
|
|
t.Fatalf("load props %d: %v", propsID, err)
|
|
}
|
|
if !row.ExpireTime.Equal(expireTime) || row.UseProps != useProps || row.AllowGive != allowGive {
|
|
t.Fatalf("props %d = expire:%s use:%v allow:%v, want expire:%s use:%v allow:%v",
|
|
propsID, row.ExpireTime, row.UseProps, row.AllowGive, expireTime, useProps, allowGive)
|
|
}
|
|
}
|
|
|
|
func assertVIPCleanupBadge(t *testing.T, db *gorm.DB, badgeID int64, expireTime time.Time, useProps bool) {
|
|
t.Helper()
|
|
var row vipCleanupBadgeRow
|
|
if err := db.Where("badge_id = ?", badgeID).First(&row).Error; err != nil {
|
|
t.Fatalf("load badge %d: %v", badgeID, err)
|
|
}
|
|
if !row.ExpireTime.Equal(expireTime) || row.UseProps != useProps {
|
|
t.Fatalf("badge %d = expire:%s use:%v, want expire:%s use:%v",
|
|
badgeID, row.ExpireTime, row.UseProps, expireTime, useProps)
|
|
}
|
|
}
|
|
|
|
func vipTestConfigs() map[int]model.VipLevelConfig {
|
|
configs := make(map[int]model.VipLevelConfig, levelCount)
|
|
for level := 1; level <= levelCount; level++ {
|
|
configs[level] = model.VipLevelConfig{
|
|
Level: level,
|
|
LevelCode: levelCode(level),
|
|
DisplayName: levelDisplayName(level),
|
|
Enabled: true,
|
|
DurationDays: vipDurationDay,
|
|
PriceGold: int64(level * 1000),
|
|
}
|
|
}
|
|
return configs
|
|
}
|