package common import ( "context" "testing" "time" "github.com/perfect-panel/server/internal/model/promo" "github.com/perfect-panel/server/internal/svc" "gorm.io/gorm" ) func TestEvaluateInactiveUserExpire(t *testing.T) { now := time.Date(2026, 5, 27, 8, 0, 0, 0, time.UTC) params := promoRuleParams{InactiveMonths: 3} tests := []struct { name string lastExpire time.Time want bool }{ { name: "permanent subscription is not inactive", lastExpire: time.UnixMilli(0), want: false, }, { name: "active subscription is not inactive", lastExpire: now.Add(time.Hour), want: false, }, { name: "expire at threshold is inactive", lastExpire: now.AddDate(0, -3, 0), want: true, }, { name: "expire before threshold is inactive", lastExpire: now.AddDate(0, -3, -1), want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := evaluateInactiveUserExpire(tt.lastExpire, params, now); got != tt.want { t.Fatalf("evaluateInactiveUserExpire() = %v, want %v", got, tt.want) } }) } } func TestEvaluatePromoAllowsAnonymousCampaign(t *testing.T) { end := time.Now().Add(time.Hour) model := &fakePromoModel{rules: []*promo.RuleWithPrice{ { Rule: promo.Rule{ Id: 3, Name: "campaign", Type: promo.RuleTypeCampaign, Enabled: true, EndTime: &end, }, PromoPrice: 199, }, }} got, err := EvaluatePromo(context.Background(), &svc.ServiceContext{DB: &gorm.DB{}, PromoModel: model}, 0, 7, 12) if err != nil { t.Fatalf("EvaluatePromo returned error: %v", err) } if !got.Eligible { t.Fatal("anonymous campaign promo should be eligible") } if got.RuleID != 3 { t.Fatalf("RuleID = %d, want 3", got.RuleID) } if got.PromoPrice != 199 { t.Fatalf("PromoPrice = %d, want 199", got.PromoPrice) } if model.lastSubscribeID != 7 { t.Fatalf("lastSubscribeID = %d, want 7", model.lastSubscribeID) } if model.lastQuantity != 12 { t.Fatalf("lastQuantity = %d, want 12", model.lastQuantity) } } type fakePromoModel struct { rules []*promo.RuleWithPrice lastSubscribeID int64 lastQuantity int64 } func (m *fakePromoModel) QueryEligibleRules(_ context.Context, subscribeID int64, quantity int64) ([]*promo.RuleWithPrice, error) { m.lastSubscribeID = subscribeID m.lastQuantity = quantity return m.rules, nil } func (m *fakePromoModel) InsertUsage(context.Context, *promo.Usage, ...*gorm.DB) error { return nil } func (m *fakePromoModel) InsertRule(context.Context, *promo.Rule) error { return nil } func (m *fakePromoModel) FindRule(context.Context, int64) (*promo.Rule, error) { return nil, gorm.ErrRecordNotFound } func (m *fakePromoModel) UpdateRule(context.Context, *promo.Rule) error { return nil } func (m *fakePromoModel) DeleteRule(context.Context, int64) error { return nil } func (m *fakePromoModel) QueryRuleList(context.Context, int, int, string, *bool, string) (int64, []*promo.Rule, error) { return 0, nil, nil } func (m *fakePromoModel) UpsertPrices(context.Context, int64, []*promo.SubscribePromo) error { return nil } func (m *fakePromoModel) FindPrice(context.Context, int64) (*promo.SubscribePromo, error) { return nil, gorm.ErrRecordNotFound } func (m *fakePromoModel) DeletePrice(context.Context, int64) error { return nil } func (m *fakePromoModel) QueryPriceList(context.Context, promo.PriceFilter) (int64, []*promo.SubscribePromo, error) { return 0, nil, nil } func (m *fakePromoModel) QueryUsageList(context.Context, promo.UsageFilter) (int64, []*promo.Usage, error) { return 0, nil, nil } func (m *fakePromoModel) Transaction(context.Context, func(*gorm.DB) error) error { return nil }