package integration import ( "context" "testing" "time" "github.com/bytedance/sonic" "github.com/hibiken/asynq" "github.com/redis/go-redis/v9" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/break/junhong_cmp_fiber/pkg/constants" ) // EmailPayload 邮件任务载荷(测试用) type EmailPayload struct { RequestID string `json:"request_id"` To string `json:"to"` Subject string `json:"subject"` Body string `json:"body"` CC []string `json:"cc,omitempty"` } // TestTaskSubmit 测试任务提交 func TestTaskSubmit(t *testing.T) { // 创建 Redis 客户端 redisClient := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) defer redisClient.Close() // 清理测试数据 ctx := context.Background() redisClient.FlushDB(ctx) // 创建 Asynq 客户端 client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", }) defer client.Close() // 构造任务载荷 payload := &EmailPayload{ RequestID: "test-request-001", To: "test@example.com", Subject: "Test Email", Body: "This is a test email", } payloadBytes, err := sonic.Marshal(payload) require.NoError(t, err) // 提交任务 task := asynq.NewTask(constants.TaskTypeEmailSend, payloadBytes) info, err := client.Enqueue(task, asynq.Queue(constants.QueueDefault), asynq.MaxRetry(constants.DefaultRetryMax), ) // 验证 require.NoError(t, err) assert.NotEmpty(t, info.ID) assert.Equal(t, constants.QueueDefault, info.Queue) assert.Equal(t, constants.DefaultRetryMax, info.MaxRetry) } // TestTaskPriority 测试任务优先级 func TestTaskPriority(t *testing.T) { // 创建 Redis 客户端 redisClient := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) defer redisClient.Close() ctx := context.Background() redisClient.FlushDB(ctx) // 创建 Asynq 客户端 client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", }) defer client.Close() tests := []struct { name string queue string }{ {"Critical Priority", constants.QueueCritical}, {"Default Priority", constants.QueueDefault}, {"Low Priority", constants.QueueLow}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { payload := &EmailPayload{ RequestID: "test-request-" + tt.queue, To: "test@example.com", Subject: "Test", Body: "Test", } payloadBytes, err := sonic.Marshal(payload) require.NoError(t, err) task := asynq.NewTask(constants.TaskTypeEmailSend, payloadBytes) info, err := client.Enqueue(task, asynq.Queue(tt.queue)) require.NoError(t, err) assert.Equal(t, tt.queue, info.Queue) }) } } // TestTaskRetry 测试任务重试机制 func TestTaskRetry(t *testing.T) { redisClient := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) defer redisClient.Close() ctx := context.Background() redisClient.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", }) defer client.Close() payload := &EmailPayload{ RequestID: "retry-test-001", To: "test@example.com", Subject: "Retry Test", Body: "Test retry mechanism", } payloadBytes, err := sonic.Marshal(payload) require.NoError(t, err) // 提交任务并设置重试次数 task := asynq.NewTask(constants.TaskTypeEmailSend, payloadBytes) info, err := client.Enqueue(task, asynq.MaxRetry(3), asynq.Timeout(30*time.Second), ) require.NoError(t, err) assert.Equal(t, 3, info.MaxRetry) assert.Equal(t, 30*time.Second, info.Timeout) } // TestTaskIdempotency 测试任务幂等性键 func TestTaskIdempotency(t *testing.T) { redisClient := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) defer redisClient.Close() ctx := context.Background() redisClient.FlushDB(ctx) requestID := "idempotent-test-001" lockKey := constants.RedisTaskLockKey(requestID) // 第一次设置锁(模拟任务开始执行) result, err := redisClient.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() require.NoError(t, err) assert.True(t, result, "第一次设置锁应该成功") // 第二次设置锁(模拟重复任务) result, err = redisClient.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() require.NoError(t, err) assert.False(t, result, "第二次设置锁应该失败(幂等性)") // 验证锁存在 exists, err := redisClient.Exists(ctx, lockKey).Result() require.NoError(t, err) assert.Equal(t, int64(1), exists) // 验证 TTL ttl, err := redisClient.TTL(ctx, lockKey).Result() require.NoError(t, err) assert.Greater(t, ttl.Hours(), 23.0) assert.LessOrEqual(t, ttl.Hours(), 24.0) } // TestTaskStatusTracking 测试任务状态跟踪 func TestTaskStatusTracking(t *testing.T) { redisClient := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) defer redisClient.Close() ctx := context.Background() redisClient.FlushDB(ctx) taskID := "task-123456" statusKey := constants.RedisTaskStatusKey(taskID) // 设置任务状态 statuses := []string{"pending", "processing", "completed"} for _, status := range statuses { err := redisClient.Set(ctx, statusKey, status, 7*24*time.Hour).Err() require.NoError(t, err) // 读取状态 result, err := redisClient.Get(ctx, statusKey).Result() require.NoError(t, err) assert.Equal(t, status, result) } // 验证 TTL ttl, err := redisClient.TTL(ctx, statusKey).Result() require.NoError(t, err) assert.Greater(t, ttl.Hours(), 24.0*6) } // TestQueueInspection 测试队列检查 func TestQueueInspection(t *testing.T) { redisClient := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) defer redisClient.Close() ctx := context.Background() redisClient.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", }) defer client.Close() // 提交多个任务 for i := 0; i < 5; i++ { payload := &EmailPayload{ RequestID: "test-" + string(rune(i)), To: "test@example.com", Subject: "Test", Body: "Test", } payloadBytes, err := sonic.Marshal(payload) require.NoError(t, err) task := asynq.NewTask(constants.TaskTypeEmailSend, payloadBytes) _, err = client.Enqueue(task, asynq.Queue(constants.QueueDefault)) require.NoError(t, err) } // 创建 Inspector 检查队列 inspector := asynq.NewInspector(asynq.RedisClientOpt{ Addr: "localhost:6379", }) defer inspector.Close() // 获取队列信息 info, err := inspector.GetQueueInfo(constants.QueueDefault) require.NoError(t, err) assert.Equal(t, 5, info.Pending) assert.Equal(t, 0, info.Active) } // TestTaskSerialization 测试任务序列化 func TestTaskSerialization(t *testing.T) { tests := []struct { name string payload EmailPayload }{ { name: "Simple Email", payload: EmailPayload{ RequestID: "req-001", To: "user@example.com", Subject: "Hello", Body: "Hello World", }, }, { name: "Email with CC", payload: EmailPayload{ RequestID: "req-002", To: "user@example.com", Subject: "Hello", Body: "Hello World", CC: []string{"cc1@example.com", "cc2@example.com"}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // 序列化 payloadBytes, err := sonic.Marshal(tt.payload) require.NoError(t, err) assert.NotEmpty(t, payloadBytes) // 反序列化 var decoded EmailPayload err = sonic.Unmarshal(payloadBytes, &decoded) require.NoError(t, err) // 验证 assert.Equal(t, tt.payload.RequestID, decoded.RequestID) assert.Equal(t, tt.payload.To, decoded.To) assert.Equal(t, tt.payload.Subject, decoded.Subject) assert.Equal(t, tt.payload.Body, decoded.Body) assert.Equal(t, tt.payload.CC, decoded.CC) }) } }