feat: 实现门店套餐分配功能并统一测试基础设施
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m30s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m30s
新增功能: - 门店套餐分配管理(shop_package_allocation):支持门店套餐库存管理 - 门店套餐系列分配管理(shop_series_allocation):支持套餐系列分配和佣金层级设置 - 我的套餐查询(my_package):支持门店查询自己的套餐分配情况 测试改进: - 统一集成测试基础设施,新增 testutils.NewIntegrationTestEnv - 重构所有集成测试使用新的测试环境设置 - 移除旧的测试辅助函数和冗余测试文件 - 新增 test_helpers_test.go 统一任务测试辅助 技术细节: - 新增数据库迁移 000025_create_shop_allocation_tables - 新增 3 个 Handler、Service、Store 和对应的单元测试 - 更新 OpenAPI 文档和文档生成器 - 测试覆盖率:Service 层 > 90% Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,16 +2,17 @@ package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"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"
|
||||
"github.com/break/junhong_cmp_fiber/tests/testutils"
|
||||
)
|
||||
|
||||
type EmailPayload struct {
|
||||
@@ -22,22 +23,29 @@ type EmailPayload struct {
|
||||
CC []string `json:"cc,omitempty"`
|
||||
}
|
||||
|
||||
func getRedisOpt() asynq.RedisClientOpt {
|
||||
host := os.Getenv("JUNHONG_REDIS_ADDRESS")
|
||||
if host == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
port := os.Getenv("JUNHONG_REDIS_PORT")
|
||||
if port == "" {
|
||||
port = "6379"
|
||||
}
|
||||
password := os.Getenv("JUNHONG_REDIS_PASSWORD")
|
||||
return asynq.RedisClientOpt{
|
||||
Addr: host + ":" + port,
|
||||
Password: password,
|
||||
DB: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskSubmit(t *testing.T) {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: testRedisAddr,
|
||||
Password: testRedisPasswd,
|
||||
DB: testRedisDB,
|
||||
})
|
||||
defer func() { _ = rdb.Close() }()
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
_ = rdb
|
||||
|
||||
ctx := context.Background()
|
||||
cleanTestKeys(t, rdb, ctx)
|
||||
|
||||
client := asynq.NewClient(asynq.RedisClientOpt{
|
||||
Addr: testRedisAddr,
|
||||
Password: testRedisPasswd,
|
||||
DB: testRedisDB,
|
||||
})
|
||||
client := asynq.NewClient(getRedisOpt())
|
||||
defer func() { _ = client.Close() }()
|
||||
|
||||
// 构造任务载荷
|
||||
@@ -66,21 +74,10 @@ func TestTaskSubmit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTaskPriority(t *testing.T) {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: testRedisAddr,
|
||||
Password: testRedisPasswd,
|
||||
DB: testRedisDB,
|
||||
})
|
||||
defer func() { _ = rdb.Close() }()
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
ctx := context.Background()
|
||||
cleanTestKeys(t, rdb, ctx)
|
||||
|
||||
client := asynq.NewClient(asynq.RedisClientOpt{
|
||||
Addr: testRedisAddr,
|
||||
Password: testRedisPasswd,
|
||||
DB: testRedisDB,
|
||||
})
|
||||
client := asynq.NewClient(getRedisOpt())
|
||||
defer func() { _ = client.Close() }()
|
||||
|
||||
tests := []struct {
|
||||
@@ -114,21 +111,10 @@ func TestTaskPriority(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTaskRetry(t *testing.T) {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: testRedisAddr,
|
||||
Password: testRedisPasswd,
|
||||
DB: testRedisDB,
|
||||
})
|
||||
defer func() { _ = rdb.Close() }()
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
ctx := context.Background()
|
||||
cleanTestKeys(t, rdb, ctx)
|
||||
|
||||
client := asynq.NewClient(asynq.RedisClientOpt{
|
||||
Addr: testRedisAddr,
|
||||
Password: testRedisPasswd,
|
||||
DB: testRedisDB,
|
||||
})
|
||||
client := asynq.NewClient(getRedisOpt())
|
||||
defer func() { _ = client.Close() }()
|
||||
|
||||
payload := &EmailPayload{
|
||||
@@ -154,15 +140,10 @@ func TestTaskRetry(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTaskIdempotency(t *testing.T) {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: testRedisAddr,
|
||||
Password: testRedisPasswd,
|
||||
DB: testRedisDB,
|
||||
})
|
||||
defer func() { _ = rdb.Close() }()
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
ctx := context.Background()
|
||||
cleanTestKeys(t, rdb, ctx)
|
||||
|
||||
requestID := "idempotent-test-" + time.Now().Format("20060102150405.000")
|
||||
lockKey := constants.RedisTaskLockKey(requestID)
|
||||
@@ -191,15 +172,10 @@ func TestTaskIdempotency(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTaskStatusTracking(t *testing.T) {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: testRedisAddr,
|
||||
Password: testRedisPasswd,
|
||||
DB: testRedisDB,
|
||||
})
|
||||
defer func() { _ = rdb.Close() }()
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
ctx := context.Background()
|
||||
cleanTestKeys(t, rdb, ctx)
|
||||
|
||||
taskID := "task-123456"
|
||||
statusKey := constants.RedisTaskStatusKey(taskID)
|
||||
@@ -224,24 +200,20 @@ func TestTaskStatusTracking(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestQueueInspection(t *testing.T) {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: testRedisAddr,
|
||||
Password: testRedisPasswd,
|
||||
DB: testRedisDB,
|
||||
})
|
||||
defer func() { _ = rdb.Close() }()
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
ctx := context.Background()
|
||||
cleanTestKeys(t, rdb, ctx)
|
||||
inspector := asynq.NewInspector(getRedisOpt())
|
||||
defer func() { _ = inspector.Close() }()
|
||||
|
||||
client := asynq.NewClient(asynq.RedisClientOpt{
|
||||
Addr: testRedisAddr,
|
||||
Password: testRedisPasswd,
|
||||
DB: testRedisDB,
|
||||
})
|
||||
_, _ = inspector.DeleteAllPendingTasks(constants.QueueDefault)
|
||||
_, _ = inspector.DeleteAllScheduledTasks(constants.QueueDefault)
|
||||
_, _ = inspector.DeleteAllRetryTasks(constants.QueueDefault)
|
||||
_, _ = inspector.DeleteAllArchivedTasks(constants.QueueDefault)
|
||||
|
||||
client := asynq.NewClient(getRedisOpt())
|
||||
defer func() { _ = client.Close() }()
|
||||
|
||||
// 提交多个任务
|
||||
for i := 0; i < 5; i++ {
|
||||
payload := &EmailPayload{
|
||||
RequestID: "test-" + string(rune(i)),
|
||||
@@ -258,14 +230,6 @@ func TestQueueInspection(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
inspector := asynq.NewInspector(asynq.RedisClientOpt{
|
||||
Addr: testRedisAddr,
|
||||
Password: testRedisPasswd,
|
||||
DB: testRedisDB,
|
||||
})
|
||||
defer func() { _ = inspector.Close() }()
|
||||
|
||||
// 获取队列信息
|
||||
info, err := inspector.GetQueueInfo(constants.QueueDefault)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 5, info.Pending)
|
||||
@@ -316,19 +280,3 @@ func TestTaskSerialization(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func cleanTestKeys(t *testing.T, rdb *redis.Client, ctx context.Context) {
|
||||
t.Helper()
|
||||
prefix := "test:task:" + t.Name() + ":"
|
||||
keys, err := rdb.Keys(ctx, prefix+"*").Result()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
rdb.Del(ctx, keys...)
|
||||
}
|
||||
asynqKeys, _ := rdb.Keys(ctx, "asynq:*").Result()
|
||||
if len(asynqKeys) > 0 {
|
||||
rdb.Del(ctx, asynqKeys...)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user