152 lines
5.8 KiB
Go

package threshold_activity
import (
"context"
"testing"
"time"
)
func TestDraw_AbortWhenParticipantsBelowMin(t *testing.T) {
svc, _, db := newThresholdTestService(t)
ctx := context.Background()
now := time.Now()
activity := Activity{
ID: 11,
Title: "开奖流产",
Type: TypeDaily,
QualificationMode: QualificationModeSpendOnly,
MinParticipants: 2,
StartTime: now.Add(-2 * time.Hour),
EndTime: now.Add(2 * time.Hour),
DrawTime: now.Add(-time.Minute),
Status: StatusActive,
}
mustInsertActivity(t, db, activity)
mustExec(t, db, `INSERT INTO threshold_activity_participants (activity_id, user_id, period_key, qualification_source, paid_amount_snapshot, effective_invite_count_snapshot, created_at) VALUES (11, 101, '2026-06-09', 'spend', 1000, 0, ?)`, now)
if err := svc.Draw(ctx, activity.ID); err != nil {
t.Fatalf("Draw failed: %v", err)
}
var updated Activity
if err := db.Where("id = ?", activity.ID).First(&updated).Error; err != nil {
t.Fatalf("query activity failed: %v", err)
}
if updated.Status != StatusAborted {
t.Fatalf("expected status aborted, got %s", updated.Status)
}
if updated.AbortReason != "min_participants_not_met" {
t.Fatalf("expected abort reason min_participants_not_met, got %s", updated.AbortReason)
}
var winnerCount int64
if err := db.Table("threshold_activity_winners").Where("activity_id = ?", activity.ID).Count(&winnerCount).Error; err != nil {
t.Fatalf("count winners failed: %v", err)
}
if winnerCount != 0 {
t.Fatalf("expected no winners when aborted, got %d", winnerCount)
}
}
func TestDraw_FinishWhenParticipantsReachMinCreatesWinner(t *testing.T) {
svc, _, db := newThresholdTestService(t)
ctx := context.Background()
now := time.Now()
activity := Activity{
ID: 12,
Title: "正常开奖",
Type: TypeDaily,
QualificationMode: QualificationModeSpendOnly,
MinParticipants: 1,
StartTime: now.Add(-2 * time.Hour),
EndTime: now.Add(2 * time.Hour),
DrawTime: now.Add(-time.Minute),
Status: StatusActive,
}
mustInsertActivity(t, db, activity)
mustExec(t, db, `INSERT INTO threshold_activity_participants (activity_id, user_id, period_key, qualification_source, paid_amount_snapshot, effective_invite_count_snapshot, created_at) VALUES (12, 201, '2026-06-09', 'spend', 2000, 0, ?)`, now)
mustExec(t, db, `INSERT INTO products (id, name, price, cost_price, stock, images_json) VALUES (501, '测试商品', 1999, 888, 5, '[]')`)
mustExec(t, db, `INSERT INTO threshold_activity_prizes (id, activity_id, reward_type, reward_ref_id, reward_name_snapshot, reward_image_snapshot, reward_value_snapshot_cents, cost_snapshot_cents, quantity, remaining_quantity, sort, created_at, updated_at) VALUES (601, 12, 'product', 501, '测试商品', '', 1999, 888, 1, 1, 1, ?, ?)`, now, now)
if err := svc.Draw(ctx, activity.ID); err != nil {
t.Fatalf("Draw failed: %v", err)
}
var updated Activity
if err := db.Where("id = ?", activity.ID).First(&updated).Error; err != nil {
t.Fatalf("query activity failed: %v", err)
}
if updated.Status != StatusFinished {
t.Fatalf("expected status finished, got %s", updated.Status)
}
if updated.DrawBatch == "" {
t.Fatalf("expected draw batch to be set")
}
var winnerCount int64
if err := db.Table("threshold_activity_winners").Where("activity_id = ?", activity.ID).Count(&winnerCount).Error; err != nil {
t.Fatalf("count winners failed: %v", err)
}
if winnerCount != 1 {
t.Fatalf("expected 1 winner, got %d", winnerCount)
}
var prize Prize
if err := db.Where("id = ?", 601).First(&prize).Error; err != nil {
t.Fatalf("query prize failed: %v", err)
}
if prize.RemainingQuantity != 0 {
t.Fatalf("expected remaining quantity 0, got %d", prize.RemainingQuantity)
}
var stock int64
if err := db.Table("products").Select("stock").Where("id = ?", 501).Scan(&stock).Error; err != nil {
t.Fatalf("query stock failed: %v", err)
}
if stock != 4 {
t.Fatalf("expected product stock 4, got %d", stock)
}
}
func TestDrawDueActivities_ProcessesOnlyDueActivities(t *testing.T) {
svc, _, db := newThresholdTestService(t)
ctx := context.Background()
now := time.Now()
due := Activity{
ID: 21,
Title: "到期活动",
Type: TypeDaily,
QualificationMode: QualificationModeSpendOnly,
MinParticipants: 2,
StartTime: now.Add(-2 * time.Hour),
EndTime: now.Add(2 * time.Hour),
DrawTime: now.Add(-time.Minute),
Status: StatusActive,
}
future := Activity{
ID: 22,
Title: "未到期活动",
Type: TypeDaily,
QualificationMode: QualificationModeSpendOnly,
MinParticipants: 1,
StartTime: now.Add(-2 * time.Hour),
EndTime: now.Add(2 * time.Hour),
DrawTime: now.Add(time.Hour),
Status: StatusActive,
}
mustInsertActivity(t, db, due)
mustInsertActivity(t, db, future)
mustExec(t, db, `INSERT INTO threshold_activity_participants (activity_id, user_id, period_key, qualification_source, paid_amount_snapshot, effective_invite_count_snapshot, created_at) VALUES (21, 301, '2026-06-09', 'spend', 1000, 0, ?)`, now)
if err := svc.DrawDueActivities(ctx); err != nil {
t.Fatalf("DrawDueActivities failed: %v", err)
}
var dueUpdated, futureUpdated Activity
if err := db.Where("id = ?", 21).First(&dueUpdated).Error; err != nil {
t.Fatalf("query due activity failed: %v", err)
}
if err := db.Where("id = ?", 22).First(&futureUpdated).Error; err != nil {
t.Fatalf("query future activity failed: %v", err)
}
if dueUpdated.Status != StatusAborted {
t.Fatalf("expected due activity aborted, got %s", dueUpdated.Status)
}
if futureUpdated.Status != StatusActive {
t.Fatalf("expected future activity remain active, got %s", futureUpdated.Status)
}
}