From 77377ed27b868fa83eed6bf9e8516a13a75f5909 Mon Sep 17 00:00:00 2001 From: shanshanzhong Date: Fri, 29 May 2026 23:25:22 -0700 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D(#129):=20=E6=96=B0=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E7=94=A8=E6=88=B7=E4=B8=8D=E5=91=BD=E4=B8=AD=E6=B2=89?= =?UTF-8?q?=E9=BB=98=E4=BF=83=E9=94=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: multica-agent --- internal/logic/common/promoEligibility.go | 2 +- .../logic/common/promoEligibility_test.go | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/internal/logic/common/promoEligibility.go b/internal/logic/common/promoEligibility.go index 83da6e5..ac76760 100644 --- a/internal/logic/common/promoEligibility.go +++ b/internal/logic/common/promoEligibility.go @@ -175,7 +175,7 @@ func evaluateInactiveUserPromo( Take(&lastSub).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return true, ruleExpiresAt, nil + return false, time.Time{}, nil } return false, time.Time{}, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query promo inactive user subscription failed") } diff --git a/internal/logic/common/promoEligibility_test.go b/internal/logic/common/promoEligibility_test.go index 8476376..7dbc277 100644 --- a/internal/logic/common/promoEligibility_test.go +++ b/internal/logic/common/promoEligibility_test.go @@ -2,9 +2,13 @@ package common import ( "context" + "regexp" "testing" "time" + "github.com/DATA-DOG/go-sqlmock" + "gorm.io/driver/mysql" + "github.com/perfect-panel/server/internal/model/promo" "github.com/perfect-panel/server/internal/svc" "gorm.io/gorm" @@ -134,6 +138,42 @@ func TestEvaluatePromoNewUserRequiresFirstPurchase(t *testing.T) { } } +func TestEvaluatePromoRejectsInactiveRuleWhenUserHasNoSubscription(t *testing.T) { + db, mock, cleanup := newCommonPromoTestDB(t) + defer cleanup() + + model := &fakePromoModel{rules: []*promo.RuleWithPrice{ + { + Rule: promo.Rule{ + Id: 9, + Name: "inactive", + Type: promo.RuleTypeInactiveUser, + Params: `{"inactive_months":3}`, + Enabled: true, + }, + PromoPrice: 100, + }, + }} + + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `user_subscribe` WHERE user_id = ? ORDER BY CASE WHEN expire_time = ? THEN 0 ELSE 1 END, expire_time DESC LIMIT ?")). + WithArgs(int64(51640), time.UnixMilli(0), 1). + WillReturnError(gorm.ErrRecordNotFound) + + got, err := EvaluatePromo(context.Background(), &svc.ServiceContext{DB: db, PromoModel: model}, 51640, 1, 30, true) + if err != nil { + t.Fatalf("EvaluatePromo returned error: %v", err) + } + if got.Eligible { + t.Fatal("new user without subscription history should not be eligible for inactive promo") + } + if got.RuleID != 0 || got.PromoPrice != 0 { + t.Fatalf("promo fields = (%d, %d), want zero values", got.RuleID, got.PromoPrice) + } + if err := mock.ExpectationsWereMet(); err != nil { + t.Fatalf("unmet db expectations: %v", err) + } +} + type fakePromoModel struct { rules []*promo.RuleWithPrice lastSubscribeID int64 @@ -193,3 +233,22 @@ func (m *fakePromoModel) QueryUsageList(context.Context, promo.UsageFilter) (int func (m *fakePromoModel) Transaction(context.Context, func(*gorm.DB) error) error { return nil } + +func newCommonPromoTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock, func()) { + t.Helper() + + sqlDB, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("create sqlmock: %v", err) + } + + db, err := gorm.Open(mysql.New(mysql.Config{Conn: sqlDB, SkipInitializeWithVersion: true}), &gorm.Config{}) + if err != nil { + _ = sqlDB.Close() + t.Fatalf("open gorm db: %v", err) + } + + return db, mock, func() { + _ = sqlDB.Close() + } +}