package vip import ( "encoding/json" "testing" "time" "chatapp3-golang/internal/model" ) 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","url":"https://example.com/a.svga"}`), &payload); err != nil { t.Fatalf("unmarshal resource payload: %v", err) } if payload.ResourceID.Int64() != 2045029471274201089 { t.Fatalf("resource id = %d, want 2045029471274201089", payload.ResourceID.Int64()) } 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"}` { t.Fatalf("json = %s", got) } } 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 }