package subscribe import ( "strings" "testing" "time" "github.com/perfect-panel/server/internal/model/user" "gorm.io/driver/mysql" "gorm.io/gorm" ) func TestPromoEligibilityEvaluatorMatch(t *testing.T) { now := time.Unix(1710000000, 0) campaignEnd := now.Add(2 * time.Hour) campaign := subscribePromoCandidate{ RuleName: "限时活动", RuleType: promoRuleTypeCampaign, PromoPrice: 99, EndTime: &campaignEnd, } ok, expiresAt, err := (&promoEligibilityEvaluator{}).match(campaign, now) if err != nil { t.Fatalf("campaign match error: %v", err) } if !ok { t.Fatal("campaign promo should match without login") } if got, want := unixSeconds(expiresAt), campaignEnd.Unix(); got != want { t.Fatalf("campaign expires_at = %d, want %d", got, want) } newUser := subscribePromoCandidate{ RuleName: "新客7天优惠", RuleType: promoRuleTypeNewUser, PromoPrice: 279, Params: `{"window_hours":168}`, } ok, _, err = (&promoEligibilityEvaluator{}).match(newUser, now) if err != nil { t.Fatalf("anonymous new_user match error: %v", err) } if ok { t.Fatal("new_user promo should not match without login") } userInfo := &user.User{Id: 1, CreatedAt: now.Add(-24 * time.Hour)} ok, expiresAt, err = (&promoEligibilityEvaluator{userInfo: userInfo}).match(newUser, now) if err != nil { t.Fatalf("logged-in new_user match error: %v", err) } if !ok { t.Fatal("new_user promo should match inside window") } if got, want := unixSeconds(expiresAt), userInfo.CreatedAt.Add(168*time.Hour).Unix(); got != want { t.Fatalf("new_user expires_at = %d, want %d", got, want) } } func TestSubscribePromoCandidateActiveWindow(t *testing.T) { now := time.Unix(1710000000, 0) start := now.Add(-time.Hour) end := now.Add(time.Hour) if !(subscribePromoCandidate{PromoPrice: 1, StartTime: &start, EndTime: &end}).isActive(now) { t.Fatal("candidate inside active window should be active") } if (subscribePromoCandidate{PromoPrice: 0, StartTime: &start, EndTime: &end}).isActive(now) { t.Fatal("candidate with zero promo price should not be active") } if (subscribePromoCandidate{PromoPrice: 1, StartTime: &end}).isActive(now) { t.Fatal("candidate before start time should not be active") } if (subscribePromoCandidate{PromoPrice: 1, EndTime: &start}).isActive(now) { t.Fatal("candidate after end time should not be active") } } func TestLastSubscribeExpireAtPrioritizesPermanentSubscription(t *testing.T) { db, err := gorm.Open(mysql.New(mysql.Config{ DSN: "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local", SkipInitializeWithVersion: true, }), &gorm.Config{DryRun: true, DisableAutomaticPing: true}) if err != nil { t.Fatalf("open dry-run db: %v", err) } evaluator := &promoEligibilityEvaluator{ db: db, userInfo: &user.User{Id: 7}, } var item user.Subscribe tx := evaluator.lastSubscribeExpireQuery().Limit(1).Take(&item) sql := tx.Statement.SQL.String() if !strings.Contains(sql, "CASE WHEN expire_time = ? THEN 0 ELSE 1 END") { t.Fatalf("SQL missing permanent subscription priority order: %s", sql) } if strings.Contains(sql, "expire_time !=") { t.Fatalf("SQL should not filter out permanent subscriptions: %s", sql) } if len(tx.Statement.Vars) < 2 { t.Fatalf("SQL vars length = %d, want at least 2; vars=%v", len(tx.Statement.Vars), tx.Statement.Vars) } if got, want := tx.Statement.Vars[1], time.UnixMilli(0); got != want { t.Fatalf("permanent subscription order var = %v, want %v; vars=%v", got, want, tx.Statement.Vars) } }