152 lines
5.8 KiB
Go
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)
|
|
}
|
|
}
|