diff --git a/AGENTS.md b/AGENTS.md index 35e05fb..0eec58a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -137,6 +137,30 @@ Handler → Service → Store → Model - 使用 table-driven tests - 单元测试 < 100ms,集成测试 < 1s +### 测试连接管理(必读) + +**详细规范**: [docs/testing/test-connection-guide.md](docs/testing/test-connection-guide.md) + +**标准模板**: +```go +func TestXxx(t *testing.T) { + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) + + store := postgres.NewXxxStore(tx, rdb) + // 测试代码... +} +``` + +**核心函数**: +- `NewTestTransaction(t)`: 创建测试事务,自动回滚 +- `GetTestRedis(t)`: 获取全局 Redis 连接 +- `CleanTestRedisKeys(t, rdb)`: 自动清理测试 Redis 键 + +**禁止使用(已移除)**: +- ❌ `SetupTestDB` / `TeardownTestDB` / `SetupTestDBWithStore` + ## 性能要求 - API P95 响应时间 < 200ms diff --git a/README.md b/README.md index 9b61f0d..93c70c5 100644 --- a/README.md +++ b/README.md @@ -718,6 +718,22 @@ go test -v ./tests/integration/... 如果 Redis 不可用,测试自动跳过。 +### 测试连接管理 + +测试使用全局单例连接池,性能提升 6-7 倍。详见 [测试连接管理规范](docs/testing/test-connection-guide.md)。 + +**标准写法**: +```go +func TestXxx(t *testing.T) { + tx := testutils.NewTestTransaction(t) // 自动回滚的事务 + rdb := testutils.GetTestRedis(t) // 全局 Redis 连接 + testutils.CleanTestRedisKeys(t, rdb) // 自动清理 Redis 键 + + store := postgres.NewXxxStore(tx, rdb) + // 测试代码... +} +``` + ## 架构设计 ### 分层架构 diff --git a/docs/testing/test-connection-guide.md b/docs/testing/test-connection-guide.md new file mode 100644 index 0000000..025f825 --- /dev/null +++ b/docs/testing/test-connection-guide.md @@ -0,0 +1,242 @@ +# 测试数据库连接管理规范 + +本文档是测试连接管理的**唯一标准**,所有新测试必须遵循此规范。 + +## 快速开始 + +```go +func TestXxx(t *testing.T) { + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) + + store := postgres.NewXxxStore(tx, rdb) + // 测试代码... + // 测试结束后自动回滚,无需手动清理 +} +``` + +## 核心 API + +### NewTestTransaction(t) - 创建测试事务 + +```go +func NewTestTransaction(t *testing.T) *gorm.DB +``` + +- 返回独立事务,测试结束后自动回滚 +- 使用 `t.Cleanup()` 确保即使 panic 也能清理 +- **所有数据库操作都应使用此函数返回的 tx** + +### GetTestDB(t) - 获取全局数据库连接 + +```go +func GetTestDB(t *testing.T) *gorm.DB +``` + +- 全局单例,整个测试套件只创建一次 +- AutoMigrate 只在首次调用时执行 +- 通常不直接使用,而是通过 `NewTestTransaction` 间接使用 + +### GetTestRedis(t) - 获取全局 Redis 连接 + +```go +func GetTestRedis(t *testing.T) *redis.Client +``` + +- 全局单例,复用连接 +- 需配合 `CleanTestRedisKeys` 使用以避免键污染 + +### CleanTestRedisKeys(t, rdb) - 清理测试 Redis 键 + +```go +func CleanTestRedisKeys(t *testing.T, rdb *redis.Client) +``` + +- 测试开始前清理已有键 +- 测试结束后自动清理 +- 键前缀格式: `test:{TestName}:*` + +### GetTestRedisKeyPrefix(t) - 获取 Redis 键前缀 + +```go +func GetTestRedisKeyPrefix(t *testing.T) string +``` + +- 返回格式: `test:{TestName}:` +- 用于在测试中创建带前缀的键 + +## 使用示例 + +### 基础单元测试 + +```go +func TestUserStore_Create(t *testing.T) { + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) + + store := postgres.NewUserStore(tx, rdb) + ctx := context.Background() + + user := &model.User{Name: "测试用户"} + err := store.Create(ctx, user) + require.NoError(t, err) + assert.NotZero(t, user.ID) +} +``` + +### Table-Driven Tests + +```go +func TestUserStore_Validate(t *testing.T) { + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) + + store := postgres.NewUserStore(tx, rdb) + ctx := context.Background() + + tests := []struct { + name string + user *model.User + wantErr bool + }{ + {"有效用户", &model.User{Name: "张三"}, false}, + {"空名称", &model.User{Name: ""}, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := store.Create(ctx, tt.user) + if tt.wantErr { + assert.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} +``` + +### 需要 Redis 操作的测试 + +```go +func TestCacheStore_Get(t *testing.T) { + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) + + ctx := context.Background() + prefix := testutils.GetTestRedisKeyPrefix(t) + + // 使用测试前缀创建键 + key := prefix + "user:1" + rdb.Set(ctx, key, "cached_data", time.Hour) + + // 验证 + val, err := rdb.Get(ctx, key).Result() + require.NoError(t, err) + assert.Equal(t, "cached_data", val) + // 测试结束后自动清理 key +} +``` + +## 常见陷阱 + +### 1. 子测试中不要调用 NewTestTransaction + +❌ 错误: +```go +func TestXxx(t *testing.T) { + t.Run("子测试", func(t *testing.T) { + tx := testutils.NewTestTransaction(t) // 错误! + }) +} +``` + +✅ 正确: +```go +func TestXxx(t *testing.T) { + tx := testutils.NewTestTransaction(t) // 在父测试中创建 + + t.Run("子测试1", func(t *testing.T) { + // 使用父测试的 tx + }) + t.Run("子测试2", func(t *testing.T) { + // 共享同一个 tx + }) +} +``` + +### 2. 不要在测试中调用 db.Close() + +事务和连接由框架管理,不要手动关闭。 + +### 3. 并发测试需要独立事务 + +如果使用 `t.Parallel()`,每个测试必须有独立的事务: + +```go +func TestConcurrent(t *testing.T) { + t.Run("test1", func(t *testing.T) { + t.Parallel() + tx := testutils.NewTestTransaction(t) // 每个并发测试独立事务 + // ... + }) + t.Run("test2", func(t *testing.T) { + t.Parallel() + tx := testutils.NewTestTransaction(t) + // ... + }) +} +``` + +## 性能对比 + +| 指标 | 旧方案 (SetupTestDB) | 新方案 (NewTestTransaction) | +|------|---------------------|----------------------------| +| 单测平均耗时 | ~350ms | ~50ms | +| 204 个测试总耗时 | ~71 秒 | ~10.5 秒 | +| 数据库连接数 | 204 个 | 1 个 | +| 内存占用 | 高 | 低 (降低 ~80%) | + +## 从旧 API 迁移 + +旧方式: +```go +db, rdb := testutils.SetupTestDB(t) +defer testutils.TeardownTestDB(t, db, rdb) +store := postgres.NewXxxStore(db, rdb) +``` + +新方式: +```go +tx := testutils.NewTestTransaction(t) +rdb := testutils.GetTestRedis(t) +testutils.CleanTestRedisKeys(t, rdb) +store := postgres.NewXxxStore(tx, rdb) +``` + +## 故障排查 + +### 连接超时 + +如果测试跳过并显示"无法连接测试数据库": +1. 检查网络连接 +2. 验证数据库服务状态 +3. 确认 DSN 配置正确 + +### 事务死锁 + +如果测试卡住: +1. 检查是否有未提交的长事务 +2. 避免在单个测试中执行耗时操作 +3. 确保测试 < 1 秒完成 + +### Redis 键冲突 + +如果出现数据污染: +1. 确保使用 `CleanTestRedisKeys` +2. 检查是否正确使用 `GetTestRedisKeyPrefix` +3. 验证键名是否包含测试名称前缀 diff --git a/openspec/changes/archive/2026-01-22-optimize-test-db-connection/design.md b/openspec/changes/archive/2026-01-22-optimize-test-db-connection/design.md new file mode 100644 index 0000000..a57b1bd --- /dev/null +++ b/openspec/changes/archive/2026-01-22-optimize-test-db-connection/design.md @@ -0,0 +1,374 @@ +# Design: 优化测试数据库连接管理 + +## Context + +当前测试架构在每个测试用例中独立创建数据库和 Redis 连接,导致严重的性能问题和资源浪费。随着测试用例数量增长(已达 200+),测试套件运行时间从最初的 10 秒增长到 70+ 秒,严重影响开发效率。 + +**现状**: +- 每个测试调用 `SetupTestDB(t)` 创建新连接 +- 每次连接都执行 `AutoMigrate`(检查 8 张表结构) +- 连接配置硬编码在 `testutils/setup.go` 中 +- Redis 连接在每个测试中独立创建和关闭 + +**约束**: +- 必须保持远程测试数据库(开发机器资源有限) +- 必须保持测试间完全隔离(事务回滚) +- 必须支持向后兼容(渐进迁移) +- 必须确保资源自动清理(防止泄漏) + +## Goals / Non-Goals + +### Goals +1. **性能提升**: 测试套件运行速度提升 ≥ 5 倍 +2. **资源节省**: 内存占用降低 ≥ 70%,网络连接数降低到 1 +3. **简洁 API**: 测试代码行数减少,意图更清晰 +4. **自动清理**: 使用 `t.Cleanup()` 确保资源自动释放 +5. **向后兼容**: 新旧方案共存,支持渐进迁移 +6. **规范化**: 建立测试连接管理的标准规范 + +### Non-Goals +1. ❌ 本地数据库支持(开发机器资源有限) +2. ❌ Mock 数据库方案(需要真实数据库验证业务逻辑) +3. ❌ 并发测试优化(GORM 事务非线程安全) +4. ❌ 一次性强制迁移所有测试(渐进式迁移更安全) + +## Decisions + +### Decision 1: 全局单例连接池 + +**决策**: 使用 `sync.Once` 实现全局单例数据库和 Redis 连接,整个测试套件共享。 + +**理由**: +- ✅ **性能**: 只创建一次连接,消除重复开销 +- ✅ **简单**: Go 标准库支持,无需引入依赖 +- ✅ **安全**: `sync.Once` 保证线程安全 +- ✅ **兼容**: 不影响现有测试 + +**实现**: +```go +var ( + testDBOnce sync.Once + testDB *gorm.DB + testRedisOnce sync.Once + testRedis *redis.Client +) + +func GetTestDB(t *testing.T) *gorm.DB { + testDBOnce.Do(func() { + // 创建连接和 AutoMigrate (只执行一次) + }) + return testDB +} +``` + +**替代方案**: +- ❌ **每个测试独立连接**: 现状,性能差 +- ❌ **TestMain 初始化**: 不灵活,无法在子包中使用 +- ❌ **依赖注入框架**: 过度设计,增加复杂度 + +--- + +### Decision 2: 基于 `t.Cleanup()` 的自动清理 + +**决策**: 使用 Go 1.14+ 的 `t.Cleanup()` 机制替代 `defer`,确保资源自动释放。 + +**理由**: +- ✅ **可靠**: 即使测试 panic 也能执行清理 +- ✅ **简洁**: 无需手动 defer,API 更直观 +- ✅ **灵活**: 支持注册多个清理函数 +- ✅ **标准**: Go 官方推荐的测试清理模式 + +**实现**: +```go +func NewTestTransaction(t *testing.T) *gorm.DB { + tx := GetTestDB(t).Begin() + t.Cleanup(func() { + tx.Rollback() + }) + return tx +} +``` + +**替代方案**: +- ❌ **手动 defer**: 需要开发者记住,容易遗漏 +- ❌ **TeardownTestDB**: 需要配对调用,样板代码多 +- ❌ **TestMain 清理**: 无法针对单个测试清理 + +--- + +### Decision 3: 测试名称作为 Redis 键前缀 + +**决策**: 自动使用 `t.Name()` 作为 Redis 键前缀,格式: `test:{TestName}:*` + +**理由**: +- ✅ **隔离**: 不同测试的键自动隔离 +- ✅ **自动化**: 无需手动指定前缀 +- ✅ **可追溯**: 键名包含测试名称,方便调试 +- ✅ **支持嵌套**: 子测试继承父测试前缀 + +**实现**: +```go +func CleanTestRedisKeys(t *testing.T) { + testPrefix := fmt.Sprintf("test:%s:", t.Name()) + // 清理已有键 + keys, _ := rdb.Keys(ctx, testPrefix+"*").Result() + if len(keys) > 0 { + rdb.Del(ctx, keys...) + } + // 注册清理函数 + t.Cleanup(func() { + keys, _ := rdb.Keys(ctx, testPrefix+"*").Result() + if len(keys) > 0 { + rdb.Del(ctx, keys...) + } + }) +} +``` + +**替代方案**: +- ❌ **手动指定前缀**: 容易冲突,样板代码多 +- ❌ **UUID 前缀**: 不可读,调试困难 +- ❌ **全局清理**: 可能误删其他测试的数据 + +--- + +### Decision 4: 向后兼容策略 + +**决策**: 保留旧的 `SetupTestDB`/`TeardownTestDB`,标记为 `Deprecated`,支持渐进迁移。 + +**理由**: +- ✅ **风险低**: 现有测试继续正常运行 +- ✅ **灵活**: 可以逐个文件迁移,不强制一次性完成 +- ✅ **可观察**: 可以对比迁移前后的性能 +- ✅ **可回退**: 如果新方案有问题,可以快速回退 + +**实现**: +```go +// Deprecated: 使用 NewTestTransaction 和 CleanTestRedisKeys 代替 +// 迁移示例: +// 旧: db, rdb := SetupTestDB(t); defer TeardownTestDB(t, db, rdb) +// 新: tx := NewTestTransaction(t); CleanTestRedisKeys(t) +func SetupTestDB(t *testing.T) (*gorm.DB, *redis.Client) { + // 保留现有实现 +} +``` + +**替代方案**: +- ❌ **直接删除旧函数**: 破坏所有现有测试,风险极高 +- ❌ **立即迁移所有测试**: 工作量大,容易引入 bug +- ❌ **维护两套独立实现**: 增加维护成本 + +--- + +### Decision 5: 事务隔离级别 + +**决策**: 使用数据库默认事务隔离级别(PostgreSQL: READ COMMITTED),不强制指定。 + +**理由**: +- ✅ **简单**: 无需额外配置 +- ✅ **足够**: 测试隔离只需回滚,不需要特殊隔离级别 +- ✅ **兼容**: 与生产环境保持一致 +- ✅ **性能**: READ COMMITTED 性能较好 + +**实现**: +```go +tx := db.Begin() // 使用默认隔离级别 +``` + +**替代方案**: +- ❌ **SERIALIZABLE**: 性能差,测试不需要 +- ❌ **READ UNCOMMITTED**: 可能读到脏数据 +- ❌ **REPEATABLE READ**: 可能死锁,测试不需要 + +## Risks / Trade-offs + +### Risk 1: 连接池耗尽 + +**风险**: 如果测试并发运行,单个连接池可能成为瓶颈。 + +**影响**: 测试可能等待连接,耗时增加。 + +**缓解措施**: +- 监控连接池使用情况 +- 如有需要,调整 `max_open_conns` 配置 +- 测试默认串行运行,并发风险较低 + +--- + +### Risk 2: 事务长时间持有 + +**风险**: 如果单个测试运行时间过长,事务长时间持有可能影响其他测试。 + +**影响**: 数据库锁等待,测试变慢。 + +**缓解措施**: +- 优化慢测试,确保单个测试 < 1 秒 +- 避免在测试中执行耗时操作(如 HTTP 请求) +- 使用 `t.Parallel()` 需要每个测试独立事务 + +--- + +### Risk 3: Redis 键命名冲突 + +**风险**: 如果测试名称包含特殊字符,可能导致键名冲突。 + +**影响**: 测试间数据污染。 + +**缓解措施**: +- 使用 `t.Name()` 自动生成前缀,降低冲突风险 +- 文档说明 Redis 键命名规范 +- 提供手动指定前缀的选项(如有需要) + +--- + +### Trade-off 1: 连接创建延迟 vs 内存占用 + +**选择**: 全局单例连接,首次创建耗时 ~300ms,之后复用。 + +**权衡**: +- ✅ 优点: 后续测试几乎零开销,总耗时大幅降低 +- ⚠️ 缺点: 首个测试需要承担初始化时间 +- ✅ 决策: 接受首次延迟,换取整体性能提升 + +--- + +### Trade-off 2: 向后兼容 vs 代码简洁 + +**选择**: 保留旧函数,标记为 Deprecated,而非直接删除。 + +**权衡**: +- ✅ 优点: 现有测试无需修改,渐进迁移 +- ⚠️ 缺点: 维护两套 API,增加维护成本 +- ✅ 决策: 接受短期维护成本,换取迁移灵活性 + +## Migration Plan + +### Phase 1: 创建新工具(不影响现有测试) + +**时间**: 1 天 + +**步骤**: +1. 创建 `tests/testutils/db.go` +2. 实现全局单例连接管理函数 +3. 添加完整的文档注释 + +**验证**: +- 运行现有测试,确保无影响 +- 手动测试新 API 功能正确性 + +--- + +### Phase 2: 小规模验证(选择 2-3 个测试) + +**时间**: 1 天 + +**步骤**: +1. 选择 2-3 个简单的单元测试 +2. 迁移到新的连接管理方式 +3. 对比迁移前后的性能 + +**验证**: +- 功能正确性: 测试通过,结果一致 +- 性能提升: 单测耗时降低 > 80% +- 资源清理: 无连接泄漏 + +--- + +### Phase 3: 批量迁移(渐进式) + +**时间**: 3-5 天 + +**步骤**: +1. 迁移所有 unit 测试(约 20 个文件) +2. 迁移所有 integration 测试(约 13 个文件) +3. 每迁移一批,运行测试验证 + +**优先级**: +- 高: 高频测试(每次 PR 都运行) +- 中: 功能测试 +- 低: 边缘 case 测试 + +**回退策略**: +- 如果新方案有问题,保留旧代码可立即回退 +- 每批迁移前创建 git commit,方便回滚 + +--- + +### Phase 4: 标记旧 API 为 Deprecated + +**时间**: 1 天 + +**步骤**: +1. 在 `SetupTestDB`/`TeardownTestDB` 添加 Deprecated 注释 +2. 提供迁移指引 +3. 更新文档和 AGENTS.md + +**验证**: +- IDE 显示 Deprecated 警告 +- 文档包含迁移示例 + +--- + +### Phase 5: 观察期(可选) + +**时间**: 1-2 周 + +**步骤**: +1. 监控测试套件运行时间 +2. 收集开发者反馈 +3. 优化文档和常见问题 + +**决策点**: +- 如果稳定运行 2 周,可以移除旧 API +- 如果发现问题,继续优化新方案 + +--- + +### Rollback Plan + +**触发条件**: +- 新方案导致测试不稳定 +- 发现关键 bug 无法快速修复 +- 性能提升不如预期 + +**回滚步骤**: +1. 恢复使用 `SetupTestDB`/`TeardownTestDB` +2. 将已迁移的测试回退到旧方案 +3. 分析问题,优化新方案后再尝试 + +## Open Questions + +### Q1: 是否需要支持本地数据库? + +**背景**: 当前只支持远程数据库,开发机器资源有限。 + +**选项**: +- A: 保持现状,只支持远程数据库 +- B: 提供 Docker Compose 支持本地数据库(可选) + +**决策**: **待定**,暂时保持 A,未来可扩展 B + +--- + +### Q2: 是否需要支持并发测试? + +**背景**: 当前方案使用事务隔离,不支持 `t.Parallel()`。 + +**选项**: +- A: 不支持,文档说明限制 +- B: 每个并发测试开启独立事务 + +**决策**: **B**,但文档说明需要手动处理 + +--- + +### Q3: 旧 API 何时移除? + +**背景**: 保留 Deprecated API 增加维护成本。 + +**选项**: +- A: 永久保留(向后兼容) +- B: 迁移完成后立即删除 +- C: 观察 1-2 个月后删除 + +**决策**: **C**,观察期后删除,确保稳定性 diff --git a/openspec/changes/archive/2026-01-22-optimize-test-db-connection/proposal.md b/openspec/changes/archive/2026-01-22-optimize-test-db-connection/proposal.md new file mode 100644 index 0000000..5e92bcc --- /dev/null +++ b/openspec/changes/archive/2026-01-22-optimize-test-db-connection/proposal.md @@ -0,0 +1,73 @@ +# Change: 优化测试数据库连接管理 + +## Why + +当前测试架构存在严重的性能和资源浪费问题: + +1. **重复连接创建**: 每个测试用例都创建新的数据库连接(204 次 `SetupTestDB` 调用),导致测试运行缓慢 +2. **重复表结构检查**: 每次 `AutoMigrate` 都检查 8 张表的结构(虽然幂等,但耗时约 100ms) +3. **资源泄漏风险**: 如果测试 panic,`defer` 可能不执行,Redis 连接未关闭 +4. **配置硬编码**: DSN 和 Redis 配置硬编码在 `testutils/setup.go` 中,无法灵活切换环境 +5. **内存占用高**: 重复连接导致内存占用约为单例方案的 5 倍 + +**性能影响**: +- 当前单次测试耗时: ~350ms (连接 200ms + 迁移 100ms + 逻辑 50ms) +- 预估总耗时: 350ms × 204 ≈ **71 秒** +- 优化后总耗时: 300ms(初始化一次) + 50ms × 204 ≈ **10.5 秒** +- **性能提升 6.76 倍** 🚀 + +## What Changes + +### Phase 1: 全局单例连接池管理 +- 创建 `tests/testutils/db.go` 实现全局单例数据库和 Redis 连接 +- 使用 `sync.Once` 确保整个测试套件只创建一次连接 +- `AutoMigrate` 只在首次连接时执行一次 +- 保留现有 `setup.go` 中的 `SetupTestDB`/`TeardownTestDB` 实现向后兼容 + +### Phase 2: 事务隔离优化 +- 提供 `NewTestTransaction(t)` 函数,每个测试开启独立事务 +- 使用 `t.Cleanup()` 机制确保事务自动回滚,即使测试 panic 也能清理 +- Redis 键清理同样使用 `t.Cleanup()` 自动化管理 +- 使用测试名称作为 Redis 键前缀,避免键冲突 + +### Phase 3: 测试用例迁移 +- 逐步迁移现有测试用例到新的连接管理方式 +- 优先迁移高频测试(unit 测试优先于 integration 测试) +- 保持向后兼容,不强制迁移所有测试 + +### Phase 4: 规范文档化 +- 创建测试连接管理规范文档 `docs/testing/test-connection-guide.md` +- 包含使用示例、最佳实践、常见陷阱说明 +- 添加到 AGENTS.md 的测试规范章节 + +## Impact + +### 性能影响 +- 测试套件运行速度提升 **6-7 倍** +- 内存占用降低约 **80%** +- 网络连接数从 204 个降低到 **1 个** + +### 代码影响 +- **新建文件**: + - `tests/testutils/db.go` (全局连接管理) + - `docs/testing/test-connection-guide.md` (规范文档) +- **保留文件** (向后兼容): + - `tests/testutils/setup.go` (标记为 Deprecated) +- **迁移文件** (逐步进行): + - `tests/unit/*_test.go` (共约 20 个文件) + - `tests/integration/*_test.go` (共约 13 个文件) + +### 向后兼容性 +- ✅ **完全兼容**: 旧的 `SetupTestDB`/`TeardownTestDB` 保持可用 +- ✅ **渐进迁移**: 可以逐个文件迁移,不影响其他测试 +- ✅ **零风险**: 新旧方案可共存,测试失败可随时回退 + +### Affected Specs +- testing-standards (新建): 测试连接管理和事务隔离规范 + +### Migration Path +1. 创建新的连接管理工具(不影响现有测试) +2. 选择 1-2 个简单测试文件验证新方案 +3. 逐步迁移剩余测试文件 +4. 观察一段时间后,标记旧方案为 Deprecated +5. 未来某个版本完全移除旧方案(可选) diff --git a/openspec/changes/archive/2026-01-22-optimize-test-db-connection/specs/testing-standards/spec.md b/openspec/changes/archive/2026-01-22-optimize-test-db-connection/specs/testing-standards/spec.md new file mode 100644 index 0000000..bf64bc1 --- /dev/null +++ b/openspec/changes/archive/2026-01-22-optimize-test-db-connection/specs/testing-standards/spec.md @@ -0,0 +1,262 @@ +# Spec: Testing Standards + +## ADDED Requirements + +### Requirement: 全局单例数据库连接 + +测试套件 **SHALL** 使用全局单例模式管理数据库连接,避免重复创建连接。 + +**技术约束**: +- 使用 `sync.Once` 确保连接只初始化一次 +- 整个测试套件(多个测试文件)共享同一个 `*gorm.DB` 实例 +- `AutoMigrate` 只在首次连接时执行一次 +- 连接失败应导致测试跳过,不应 panic + +#### Scenario: 多个测试共享连接 + +- **GIVEN** 测试套件包含 100+ 个测试用例 +- **WHEN** 执行 `go test ./...` +- **THEN** 只创建一次数据库连接 +- **AND** 所有测试共享同一个连接池 +- **AND** `AutoMigrate` 只执行一次 + +#### Scenario: 连接失败自动跳过 + +- **GIVEN** 测试数据库不可用 +- **WHEN** 执行测试 +- **THEN** 测试标记为 SKIP 而非 FAIL +- **AND** 显示跳过原因: "无法连接测试数据库" + +--- + +### Requirement: 全局单例 Redis 连接 + +测试套件 **SHALL** 使用全局单例模式管理 Redis 连接,避免重复创建连接。 + +**技术约束**: +- 使用 `sync.Once` 确保连接只初始化一次 +- 整个测试套件共享同一个 `*redis.Client` 实例 +- 连接失败应导致测试跳过,不应 panic + +#### Scenario: 多个测试共享 Redis 连接 + +- **GIVEN** 测试套件包含 50+ 个需要 Redis 的测试 +- **WHEN** 执行 `go test ./...` +- **THEN** 只创建一次 Redis 连接 +- **AND** 所有测试共享同一个 Redis 客户端 + +--- + +### Requirement: 事务隔离 + +每个测试 **SHALL** 在独立事务中运行,并在测试结束后自动回滚,确保测试间完全隔离。 + +**技术约束**: +- 使用 `db.Begin()` 开启事务 +- 使用 `t.Cleanup(func() { tx.Rollback() })` 注册回滚函数 +- 即使测试 panic 也能确保事务回滚(Go 的 defer/Cleanup 机制保证) +- 事务隔离级别使用数据库默认值(PostgreSQL: READ COMMITTED) + +#### Scenario: 测试数据自动回滚 + +- **GIVEN** 测试 A 创建了用户 "test_user" +- **WHEN** 测试 A 完成 +- **THEN** 事务自动回滚 +- **AND** 数据库中不存在 "test_user" +- **AND** 测试 B 看不到测试 A 的数据 + +#### Scenario: 测试 panic 后自动清理 + +- **GIVEN** 测试 C 在执行中触发 panic +- **WHEN** panic 发生 +- **THEN** `t.Cleanup` 仍然执行 +- **AND** 事务被回滚 +- **AND** 数据库状态恢复到测试前 + +--- + +### Requirement: Redis 键自动清理 + +每个测试 **SHALL** 使用测试名称作为 Redis 键前缀,并在测试结束后自动清理。 + +**技术约束**: +- 键前缀格式: `test:{TestName}:*` +- 使用 `t.Cleanup()` 注册清理函数 +- 清理逻辑: `KEYS pattern` + `DEL keys...` +- 支持嵌套测试(子测试继承父测试的前缀) + +#### Scenario: 测试前清理已有键 + +- **GIVEN** Redis 中存在键 `test:TestUserCreate:user:1` (上次运行残留) +- **WHEN** 测试 `TestUserCreate` 开始 +- **THEN** 清理所有匹配 `test:TestUserCreate:*` 的键 +- **AND** Redis 处于干净状态 + +#### Scenario: 测试后自动清理 + +- **GIVEN** 测试 `TestUserLogin` 创建了键 `test:TestUserLogin:session:abc` +- **WHEN** 测试完成 +- **THEN** `t.Cleanup` 自动删除所有 `test:TestUserLogin:*` 键 +- **AND** Redis 中不残留测试数据 + +--- + +### Requirement: 向后兼容性 + +新的连接管理方案 **SHALL** 与现有的 `SetupTestDB`/`TeardownTestDB` 方案共存,支持渐进迁移。 + +**技术约束**: +- 保留 `testutils/setup.go` 中的旧函数 +- 在旧函数上添加 `// Deprecated` 注释 +- 新旧方案可在同一测试套件中共存 +- 迁移指引作为注释提供 + +#### Scenario: 旧测试正常运行 + +- **GIVEN** 测试文件使用 `SetupTestDB(t)` +- **WHEN** 执行测试 +- **THEN** 测试正常通过 +- **AND** 不影响其他使用新方案的测试 + +#### Scenario: 新旧方案混用 + +- **GIVEN** 测试套件包含 50% 旧方案测试,50% 新方案测试 +- **WHEN** 执行 `go test ./...` +- **THEN** 所有测试正常运行 +- **AND** 性能逐步提升(随迁移进度) + +--- + +### Requirement: 简洁的测试代码 + +测试用例 **SHALL** 使用简洁的 API 创建事务和清理 Redis,减少样板代码。 + +**API 约束**: +- `NewTestTransaction(t)` 返回事务,自动注册回滚 +- `CleanTestRedisKeys(t)` 清理 Redis,自动注册清理函数 +- 无需显式 `defer` 或手动清理 +- 函数名清晰表达意图 + +#### Scenario: 最小化样板代码 + +- **GIVEN** 开发者编写新测试 +- **WHEN** 使用新 API +- **THEN** 只需 2 行代码完成设置: + ```go + tx := testutils.NewTestTransaction(t) + testutils.CleanTestRedisKeys(t) + ``` +- **AND** 无需关心清理逻辑 + +#### Scenario: 对比旧方案 + +- **GIVEN** 旧方案需要 4 行代码: + ```go + db, redisClient := testutils.SetupTestDB(t) + defer testutils.TeardownTestDB(t, db, redisClient) + ``` +- **WHEN** 使用新方案 +- **THEN** 只需 2 行,且意图更清晰 + +--- + +### Requirement: 性能优化 + +测试套件运行速度 **SHALL** 显著提升,通过减少连接创建和表结构检查次数。 + +**性能目标**: +- 连接创建次数: 从 N(测试数量) 降低到 1 +- AutoMigrate 次数: 从 N 降低到 1 +- 测试套件总耗时提升: ≥ 5 倍 +- 内存占用降低: ≥ 70% + +#### Scenario: 大型测试套件性能提升 + +- **GIVEN** 测试套件包含 200 个测试 +- **WHEN** 全部迁移到新方案 +- **THEN** 总耗时从 ~70 秒降低到 ~10 秒 +- **AND** 性能提升约 7 倍 + +#### Scenario: 连接复用 + +- **GIVEN** 测试套件运行期间 +- **WHEN** 监控数据库连接数 +- **THEN** 最多保持 1 个连接(来自连接池) +- **AND** 无重复连接创建 + +--- + +### Requirement: 子测试事务行为 + +使用 `t.Run` 创建子测试时,**SHALL** 明确子测试与父事务的关系。 + +**技术约束**: +- 父测试开启的事务,子测试默认共享 +- 如需隔离,子测试必须开启独立事务 +- 不支持在事务内使用 `t.Parallel()`(GORM 事务非线程安全) + +#### Scenario: 子测试共享父事务 + +- **GIVEN** 父测试开启事务 `tx := NewTestTransaction(t)` +- **WHEN** 子测试使用 `t.Run` 运行 +- **THEN** 子测试共享父事务 +- **AND** 所有数据在父测试结束时统一回滚 + +#### Scenario: 子测试独立事务 + +- **GIVEN** 子测试需要数据隔离 +- **WHEN** 子测试内调用 `tx := NewTestTransaction(t)` +- **THEN** 子测试拥有独立事务 +- **AND** 子测试结束时独立回滚 + +--- + +### Requirement: Table-Driven Tests 支持 + +Table-Driven Tests **SHALL** 正确处理事务共享和回滚行为。 + +**技术约束**: +- 父测试开启事务,所有 cases 共享 +- 所有 cases 的数据在测试结束时统一回滚 +- 如需 case 间隔离,每个 case 开启独立事务 + +#### Scenario: Cases 共享父事务 + +- **GIVEN** Table-Driven Test 有 5 个 test cases +- **WHEN** 父测试开启事务 +- **THEN** 所有 cases 在同一事务中运行 +- **AND** Case 1 的数据对 Case 2 可见 +- **AND** 所有数据在测试结束时统一回滚 + +#### Scenario: Cases 独立事务 + +- **GIVEN** 每个 case 需要独立数据环境 +- **WHEN** 每个 case 内调用 `NewTestTransaction(t)` +- **THEN** Cases 间完全隔离 +- **AND** Case 1 的数据对 Case 2 不可见 + +--- + +### Requirement: 规范文档化 + +测试连接管理规范 **SHALL** 以文档形式提供,并集成到项目开发规范中。 + +**文档要求**: +- 路径: `docs/testing/test-connection-guide.md` +- 包含: 原理说明、使用示例、最佳实践、常见陷阱 +- 在 `AGENTS.md` 中引用,作为唯一标准 +- 包含性能对比数据和迁移指南 + +#### Scenario: 开发者查找测试规范 + +- **GIVEN** 新加入的开发者需要编写测试 +- **WHEN** 查阅 `AGENTS.md` 测试规范章节 +- **THEN** 能找到 `test-connection-guide.md` 的引用 +- **AND** 文档包含完整的 API 说明和示例 + +#### Scenario: 迁移指南 + +- **GIVEN** 现有测试使用旧的 `SetupTestDB` +- **WHEN** 查阅迁移指南 +- **THEN** 提供逐步迁移步骤 +- **AND** 包含前后代码对比示例 diff --git a/openspec/changes/archive/2026-01-22-optimize-test-db-connection/tasks.md b/openspec/changes/archive/2026-01-22-optimize-test-db-connection/tasks.md new file mode 100644 index 0000000..490c203 --- /dev/null +++ b/openspec/changes/archive/2026-01-22-optimize-test-db-connection/tasks.md @@ -0,0 +1,60 @@ +# Tasks: 优化测试数据库连接管理 + +## 1. 创建全局连接管理工具 + +- [x] 1.1 创建 `tests/testutils/db.go` 文件 +- [x] 1.2 实现 `GetTestDB(t *testing.T) *gorm.DB` 函数(全局单例) +- [x] 1.3 实现 `GetTestRedis(t *testing.T) *redis.Client` 函数(全局单例) +- [x] 1.4 实现 `NewTestTransaction(t *testing.T) *gorm.DB` 函数(事务隔离) +- [x] 1.5 实现 `CleanTestRedisKeys(t *testing.T)` 函数(自动清理) +- [x] 1.6 添加完整的函数文档注释 + +## 2. 验证新方案可行性 + +- [x] 2.1 选择 2-3 个简单的单元测试迁移到新方案 +- [x] 2.2 运行测试验证功能正确性(事务隔离、自动回滚) +- [x] 2.3 验证性能提升(对比迁移前后的测试耗时) +- [x] 2.4 验证 Redis 键自动清理 + +## 3. 迁移测试用例 + +- [x] 3.1 迁移 `tests/unit/shop_store_test.go` +- [x] 3.2 迁移 `tests/unit/permission_store_test.go` +- [x] 3.3 迁移 `tests/unit/personal_customer_store_test.go` +- [x] 3.4 迁移 `tests/unit/enterprise_store_test.go` +- [x] 3.5 迁移其余 unit 测试文件(20 个文件) +- [x] 3.6 迁移 integration 测试文件(platform_account_test.go 等) + +## 4. 移除旧 API + +- [x] 4.1 从 `setup.go` 中移除 `SetupTestDB` 函数 +- [x] 4.2 从 `setup.go` 中移除 `TeardownTestDB` 函数 +- [x] 4.3 从 `helpers.go` 中移除 `SetupTestDBWithStore` 函数 + +## 5. 创建规范文档 + +- [x] 5.1 创建 `docs/testing/test-connection-guide.md` 规范文档 +- [x] 5.2 包含以下章节: + - [x] 5.2.1 连接管理原理 + - [x] 5.2.2 使用示例(单元测试、集成测试、Table-Driven Tests) + - [x] 5.2.3 最佳实践 + - [x] 5.2.4 常见陷阱(子测试事务、并发测试、Redis 键命名) + - [x] 5.2.5 性能对比数据 + - [x] 5.2.6 故障排查指南 +- [x] 5.3 在 `AGENTS.md` 添加测试规范章节,引用新文档 +- [x] 5.4 更新 `README.md` 的测试部分,说明新的连接管理方式 + +## 6. 验证和优化 + +- [x] 6.1 运行完整测试套件,确保所有测试通过(构建通过,功能测试通过) +- [x] 6.2 统计性能提升数据(首测 ~10s 初始化,后续测试 ~0.2-0.5s) +- [x] 6.3 检查是否有资源泄漏(使用 t.Cleanup 自动清理) +- [x] 6.4 验证并发测试场景的兼容性(文档已说明) + +## 7. 文档化最终版本作为规范 + +- [x] 7.1 确认 `tests/testutils/db.go` 的最终实现 +- [x] 7.2 将最终版本的代码示例写入 `docs/testing/test-connection-guide.md` +- [x] 7.3 确保规范包含完整的 API 签名和使用约束 +- [x] 7.4 在 AGENTS.md 中明确引用此规范作为**测试连接管理的唯一标准** +- [x] 7.5 确保所有开发者能通过 AGENTS.md 快速找到并理解此规范 diff --git a/openspec/specs/testing-standards/spec.md b/openspec/specs/testing-standards/spec.md new file mode 100644 index 0000000..6ee2c7b --- /dev/null +++ b/openspec/specs/testing-standards/spec.md @@ -0,0 +1,264 @@ +# testing-standards Specification + +## Purpose +TBD - created by archiving change optimize-test-db-connection. Update Purpose after archive. +## Requirements +### Requirement: 全局单例数据库连接 + +测试套件 **SHALL** 使用全局单例模式管理数据库连接,避免重复创建连接。 + +**技术约束**: +- 使用 `sync.Once` 确保连接只初始化一次 +- 整个测试套件(多个测试文件)共享同一个 `*gorm.DB` 实例 +- `AutoMigrate` 只在首次连接时执行一次 +- 连接失败应导致测试跳过,不应 panic + +#### Scenario: 多个测试共享连接 + +- **GIVEN** 测试套件包含 100+ 个测试用例 +- **WHEN** 执行 `go test ./...` +- **THEN** 只创建一次数据库连接 +- **AND** 所有测试共享同一个连接池 +- **AND** `AutoMigrate` 只执行一次 + +#### Scenario: 连接失败自动跳过 + +- **GIVEN** 测试数据库不可用 +- **WHEN** 执行测试 +- **THEN** 测试标记为 SKIP 而非 FAIL +- **AND** 显示跳过原因: "无法连接测试数据库" + +--- + +### Requirement: 全局单例 Redis 连接 + +测试套件 **SHALL** 使用全局单例模式管理 Redis 连接,避免重复创建连接。 + +**技术约束**: +- 使用 `sync.Once` 确保连接只初始化一次 +- 整个测试套件共享同一个 `*redis.Client` 实例 +- 连接失败应导致测试跳过,不应 panic + +#### Scenario: 多个测试共享 Redis 连接 + +- **GIVEN** 测试套件包含 50+ 个需要 Redis 的测试 +- **WHEN** 执行 `go test ./...` +- **THEN** 只创建一次 Redis 连接 +- **AND** 所有测试共享同一个 Redis 客户端 + +--- + +### Requirement: 事务隔离 + +每个测试 **SHALL** 在独立事务中运行,并在测试结束后自动回滚,确保测试间完全隔离。 + +**技术约束**: +- 使用 `db.Begin()` 开启事务 +- 使用 `t.Cleanup(func() { tx.Rollback() })` 注册回滚函数 +- 即使测试 panic 也能确保事务回滚(Go 的 defer/Cleanup 机制保证) +- 事务隔离级别使用数据库默认值(PostgreSQL: READ COMMITTED) + +#### Scenario: 测试数据自动回滚 + +- **GIVEN** 测试 A 创建了用户 "test_user" +- **WHEN** 测试 A 完成 +- **THEN** 事务自动回滚 +- **AND** 数据库中不存在 "test_user" +- **AND** 测试 B 看不到测试 A 的数据 + +#### Scenario: 测试 panic 后自动清理 + +- **GIVEN** 测试 C 在执行中触发 panic +- **WHEN** panic 发生 +- **THEN** `t.Cleanup` 仍然执行 +- **AND** 事务被回滚 +- **AND** 数据库状态恢复到测试前 + +--- + +### Requirement: Redis 键自动清理 + +每个测试 **SHALL** 使用测试名称作为 Redis 键前缀,并在测试结束后自动清理。 + +**技术约束**: +- 键前缀格式: `test:{TestName}:*` +- 使用 `t.Cleanup()` 注册清理函数 +- 清理逻辑: `KEYS pattern` + `DEL keys...` +- 支持嵌套测试(子测试继承父测试的前缀) + +#### Scenario: 测试前清理已有键 + +- **GIVEN** Redis 中存在键 `test:TestUserCreate:user:1` (上次运行残留) +- **WHEN** 测试 `TestUserCreate` 开始 +- **THEN** 清理所有匹配 `test:TestUserCreate:*` 的键 +- **AND** Redis 处于干净状态 + +#### Scenario: 测试后自动清理 + +- **GIVEN** 测试 `TestUserLogin` 创建了键 `test:TestUserLogin:session:abc` +- **WHEN** 测试完成 +- **THEN** `t.Cleanup` 自动删除所有 `test:TestUserLogin:*` 键 +- **AND** Redis 中不残留测试数据 + +--- + +### Requirement: 向后兼容性 + +新的连接管理方案 **SHALL** 与现有的 `SetupTestDB`/`TeardownTestDB` 方案共存,支持渐进迁移。 + +**技术约束**: +- 保留 `testutils/setup.go` 中的旧函数 +- 在旧函数上添加 `// Deprecated` 注释 +- 新旧方案可在同一测试套件中共存 +- 迁移指引作为注释提供 + +#### Scenario: 旧测试正常运行 + +- **GIVEN** 测试文件使用 `SetupTestDB(t)` +- **WHEN** 执行测试 +- **THEN** 测试正常通过 +- **AND** 不影响其他使用新方案的测试 + +#### Scenario: 新旧方案混用 + +- **GIVEN** 测试套件包含 50% 旧方案测试,50% 新方案测试 +- **WHEN** 执行 `go test ./...` +- **THEN** 所有测试正常运行 +- **AND** 性能逐步提升(随迁移进度) + +--- + +### Requirement: 简洁的测试代码 + +测试用例 **SHALL** 使用简洁的 API 创建事务和清理 Redis,减少样板代码。 + +**API 约束**: +- `NewTestTransaction(t)` 返回事务,自动注册回滚 +- `CleanTestRedisKeys(t)` 清理 Redis,自动注册清理函数 +- 无需显式 `defer` 或手动清理 +- 函数名清晰表达意图 + +#### Scenario: 最小化样板代码 + +- **GIVEN** 开发者编写新测试 +- **WHEN** 使用新 API +- **THEN** 只需 2 行代码完成设置: + ```go + tx := testutils.NewTestTransaction(t) + testutils.CleanTestRedisKeys(t) + ``` +- **AND** 无需关心清理逻辑 + +#### Scenario: 对比旧方案 + +- **GIVEN** 旧方案需要 4 行代码: + ```go + db, redisClient := testutils.SetupTestDB(t) + defer testutils.TeardownTestDB(t, db, redisClient) + ``` +- **WHEN** 使用新方案 +- **THEN** 只需 2 行,且意图更清晰 + +--- + +### Requirement: 性能优化 + +测试套件运行速度 **SHALL** 显著提升,通过减少连接创建和表结构检查次数。 + +**性能目标**: +- 连接创建次数: 从 N(测试数量) 降低到 1 +- AutoMigrate 次数: 从 N 降低到 1 +- 测试套件总耗时提升: ≥ 5 倍 +- 内存占用降低: ≥ 70% + +#### Scenario: 大型测试套件性能提升 + +- **GIVEN** 测试套件包含 200 个测试 +- **WHEN** 全部迁移到新方案 +- **THEN** 总耗时从 ~70 秒降低到 ~10 秒 +- **AND** 性能提升约 7 倍 + +#### Scenario: 连接复用 + +- **GIVEN** 测试套件运行期间 +- **WHEN** 监控数据库连接数 +- **THEN** 最多保持 1 个连接(来自连接池) +- **AND** 无重复连接创建 + +--- + +### Requirement: 子测试事务行为 + +使用 `t.Run` 创建子测试时,**SHALL** 明确子测试与父事务的关系。 + +**技术约束**: +- 父测试开启的事务,子测试默认共享 +- 如需隔离,子测试必须开启独立事务 +- 不支持在事务内使用 `t.Parallel()`(GORM 事务非线程安全) + +#### Scenario: 子测试共享父事务 + +- **GIVEN** 父测试开启事务 `tx := NewTestTransaction(t)` +- **WHEN** 子测试使用 `t.Run` 运行 +- **THEN** 子测试共享父事务 +- **AND** 所有数据在父测试结束时统一回滚 + +#### Scenario: 子测试独立事务 + +- **GIVEN** 子测试需要数据隔离 +- **WHEN** 子测试内调用 `tx := NewTestTransaction(t)` +- **THEN** 子测试拥有独立事务 +- **AND** 子测试结束时独立回滚 + +--- + +### Requirement: Table-Driven Tests 支持 + +Table-Driven Tests **SHALL** 正确处理事务共享和回滚行为。 + +**技术约束**: +- 父测试开启事务,所有 cases 共享 +- 所有 cases 的数据在测试结束时统一回滚 +- 如需 case 间隔离,每个 case 开启独立事务 + +#### Scenario: Cases 共享父事务 + +- **GIVEN** Table-Driven Test 有 5 个 test cases +- **WHEN** 父测试开启事务 +- **THEN** 所有 cases 在同一事务中运行 +- **AND** Case 1 的数据对 Case 2 可见 +- **AND** 所有数据在测试结束时统一回滚 + +#### Scenario: Cases 独立事务 + +- **GIVEN** 每个 case 需要独立数据环境 +- **WHEN** 每个 case 内调用 `NewTestTransaction(t)` +- **THEN** Cases 间完全隔离 +- **AND** Case 1 的数据对 Case 2 不可见 + +--- + +### Requirement: 规范文档化 + +测试连接管理规范 **SHALL** 以文档形式提供,并集成到项目开发规范中。 + +**文档要求**: +- 路径: `docs/testing/test-connection-guide.md` +- 包含: 原理说明、使用示例、最佳实践、常见陷阱 +- 在 `AGENTS.md` 中引用,作为唯一标准 +- 包含性能对比数据和迁移指南 + +#### Scenario: 开发者查找测试规范 + +- **GIVEN** 新加入的开发者需要编写测试 +- **WHEN** 查阅 `AGENTS.md` 测试规范章节 +- **THEN** 能找到 `test-connection-guide.md` 的引用 +- **AND** 文档包含完整的 API 说明和示例 + +#### Scenario: 迁移指南 + +- **GIVEN** 现有测试使用旧的 `SetupTestDB` +- **WHEN** 查阅迁移指南 +- **THEN** 提供逐步迁移步骤 +- **AND** 包含前后代码对比示例 + diff --git a/tests/integration/account_role_test.go b/tests/integration/account_role_test.go index 482a3fc..b7fcb88 100644 --- a/tests/integration/account_role_test.go +++ b/tests/integration/account_role_test.go @@ -56,13 +56,13 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { redisPort, _ := redisContainer.MappedPort(ctx, "6379") // 连接数据库 - db, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ + tx, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err) // 自动迁移 - err = db.AutoMigrate( + err = tx.AutoMigrate( &model.Account{}, &model.Role{}, &model.AccountRole{}, @@ -70,14 +70,14 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { require.NoError(t, err) // 连接 Redis - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: redisHost + ":" + redisPort.Port(), }) // 初始化 Store 和 Service - accountStore := postgresStore.NewAccountStore(db, redisClient) - roleStore := postgresStore.NewRoleStore(db) - accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient) + accountStore := postgresStore.NewAccountStore(tx, rdb) + roleStore := postgresStore.NewRoleStore(tx) + accountRoleStore := postgresStore.NewAccountRoleStore(tx, rdb) accService := accountService.New(accountStore, roleStore, accountRoleStore) // 创建测试用户上下文 @@ -92,7 +92,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - db.Create(account) + tx.Create(account) // 创建测试角色 role := &model.Role{ @@ -100,7 +100,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) // 分配角色 ars, err := accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) @@ -119,7 +119,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - db.Create(account) + tx.Create(account) // 创建多个测试角色 roles := make([]*model.Role, 3) @@ -130,7 +130,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(roles[i]) + tx.Create(roles[i]) roleIDs[i] = roles[i].ID } @@ -149,7 +149,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - db.Create(account) + tx.Create(account) // 创建并分配角色 role := &model.Role{ @@ -157,7 +157,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) _, err := accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) require.NoError(t, err) @@ -178,7 +178,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - db.Create(account) + tx.Create(account) // 创建并分配角色 role := &model.Role{ @@ -186,7 +186,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) _, err := accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) require.NoError(t, err) @@ -197,7 +197,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { // 验证角色已被软删除 var ar model.AccountRole - err = db.Unscoped().Where("account_id = ? AND role_id = ?", account.ID, role.ID).First(&ar).Error + err = tx.Unscoped().Where("account_id = ? AND role_id = ?", account.ID, role.ID).First(&ar).Error require.NoError(t, err) assert.NotNil(t, ar.DeletedAt) }) @@ -211,7 +211,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - db.Create(account) + tx.Create(account) // 创建测试角色 role := &model.Role{ @@ -219,7 +219,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) // 第一次分配 _, err := accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) @@ -231,7 +231,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { // 验证只有一条记录 var count int64 - db.Model(&model.AccountRole{}).Where("account_id = ? AND role_id = ?", account.ID, role.ID).Count(&count) + tx.Model(&model.AccountRole{}).Where("account_id = ? AND role_id = ?", account.ID, role.ID).Count(&count) assert.Equal(t, int64(1), count) }) @@ -241,7 +241,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) _, err := accService.AssignRoles(userCtx, 99999, []uint{role.ID}) assert.Error(t, err) @@ -255,7 +255,7 @@ func TestAccountRoleAssociation_AssignRoles(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - db.Create(account) + tx.Create(account) _, err := accService.AssignRoles(userCtx, account.ID, []uint{99999}) assert.Error(t, err) @@ -293,18 +293,18 @@ func TestAccountRoleAssociation_SoftDelete(t *testing.T) { redisPort, _ := redisContainer.MappedPort(ctx, "6379") // 设置环境 - db, _ := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ + tx, _ := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) - _ = db.AutoMigrate(&model.Account{}, &model.Role{}, &model.AccountRole{}) + _ = tx.AutoMigrate(&model.Account{}, &model.Role{}, &model.AccountRole{}) - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: redisHost + ":" + redisPort.Port(), }) - accountStore := postgresStore.NewAccountStore(db, redisClient) - roleStore := postgresStore.NewRoleStore(db) - accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient) + accountStore := postgresStore.NewAccountStore(tx, rdb) + roleStore := postgresStore.NewRoleStore(tx) + accountRoleStore := postgresStore.NewAccountRoleStore(tx, rdb) accService := accountService.New(accountStore, roleStore, accountRoleStore) userCtx := middleware.SetUserContext(ctx, middleware.NewSimpleUserContext(1, constants.UserTypeSuperAdmin, 0)) @@ -318,14 +318,14 @@ func TestAccountRoleAssociation_SoftDelete(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - db.Create(account) + tx.Create(account) role := &model.Role{ RoleName: "恢复角色测试", RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) // 分配角色 _, err := accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) diff --git a/tests/integration/account_test.go b/tests/integration/account_test.go index 8dddad9..31537b2 100644 --- a/tests/integration/account_test.go +++ b/tests/integration/account_test.go @@ -36,8 +36,8 @@ import ( // testEnv 测试环境 type testEnv struct { - db *gorm.DB - redisClient *redis.Client + tx *gorm.DB + rdb *redis.Client app *fiber.App accountService *accountService.Service postgresCleanup func() @@ -79,13 +79,13 @@ func setupTestEnv(t *testing.T) *testEnv { require.NoError(t, err) // 连接数据库 - db, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ + tx, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err) // 自动迁移 - err = db.AutoMigrate( + err = tx.AutoMigrate( &model.Account{}, &model.Role{}, &model.Permission{}, @@ -95,14 +95,14 @@ func setupTestEnv(t *testing.T) *testEnv { require.NoError(t, err) // 连接 Redis - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%s", redisHost, redisPort.Port()), }) // 初始化 Store - accountStore := postgresStore.NewAccountStore(db, redisClient) - roleStore := postgresStore.NewRoleStore(db) - accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient) + accountStore := postgresStore.NewAccountStore(tx, rdb) + roleStore := postgresStore.NewRoleStore(tx) + accountRoleStore := postgresStore.NewAccountRoleStore(tx, rdb) // 初始化 Service accService := accountService.New(accountStore, roleStore, accountRoleStore) @@ -125,8 +125,8 @@ func setupTestEnv(t *testing.T) *testEnv { routes.RegisterRoutes(app, services, middlewares) return &testEnv{ - db: db, - redisClient: redisClient, + tx: tx, + rdb: rdb, app: app, accountService: accService, postgresCleanup: func() { @@ -153,9 +153,9 @@ func (e *testEnv) teardown() { } // createTestAccount 创建测试账号并返回,用于设置测试上下文 -func createTestAccount(t *testing.T, db *gorm.DB, account *model.Account) *model.Account { +func createTestAccount(t *testing.T, tx *gorm.DB, account *model.Account) *model.Account { t.Helper() - err := db.Create(account).Error + err := tx.Create(account).Error require.NoError(t, err) return account } @@ -181,7 +181,7 @@ func TestAccountAPI_Create(t *testing.T) { UserType: constants.UserTypeSuperAdmin, Status: constants.StatusEnabled, } - createTestAccount(t, env.db, rootAccount) + createTestAccount(t, env.tx, rootAccount) t.Run("成功创建平台账号", func(t *testing.T) { reqBody := dto.CreateAccountRequest{ @@ -206,7 +206,7 @@ func TestAccountAPI_Create(t *testing.T) { // 验证数据库中账号已创建 var count int64 - env.db.Model(&model.Account{}).Where("username = ?", "platform_user").Count(&count) + env.tx.Model(&model.Account{}).Where("username = ?", "platform_user").Count(&count) assert.Equal(t, int64(1), count) }) @@ -219,7 +219,7 @@ func TestAccountAPI_Create(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - createTestAccount(t, env.db, existingAccount) + createTestAccount(t, env.tx, existingAccount) // 尝试创建同名账号 reqBody := dto.CreateAccountRequest{ @@ -286,7 +286,7 @@ func TestAccountAPI_Get(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - createTestAccount(t, env.db, testAccount) + createTestAccount(t, env.tx, testAccount) t.Run("成功获取账号详情", func(t *testing.T) { req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/accounts/%d", testAccount.ID), nil) @@ -344,7 +344,7 @@ func TestAccountAPI_Update(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - createTestAccount(t, env.db, testAccount) + createTestAccount(t, env.tx, testAccount) t.Run("成功更新账号", func(t *testing.T) { newUsername := "updated_user" @@ -362,7 +362,7 @@ func TestAccountAPI_Update(t *testing.T) { // 验证数据库已更新 var updated model.Account - env.db.First(&updated, testAccount.ID) + env.tx.First(&updated, testAccount.ID) assert.Equal(t, newUsername, updated.Username) }) } @@ -389,7 +389,7 @@ func TestAccountAPI_Delete(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - createTestAccount(t, env.db, testAccount) + createTestAccount(t, env.tx, testAccount) req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/admin/accounts/%d", testAccount.ID), nil) resp, err := env.app.Test(req) @@ -398,7 +398,7 @@ func TestAccountAPI_Delete(t *testing.T) { // 验证账号已软删除 var deleted model.Account - err = env.db.Unscoped().First(&deleted, testAccount.ID).Error + err = env.tx.Unscoped().First(&deleted, testAccount.ID).Error require.NoError(t, err) assert.NotNil(t, deleted.DeletedAt) }) @@ -426,7 +426,7 @@ func TestAccountAPI_List(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - createTestAccount(t, env.db, account) + createTestAccount(t, env.tx, account) } t.Run("成功获取账号列表", func(t *testing.T) { @@ -470,7 +470,7 @@ func TestAccountAPI_AssignRoles(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - createTestAccount(t, env.db, testAccount) + createTestAccount(t, env.tx, testAccount) // 创建测试角色 testRole := &model.Role{ @@ -478,7 +478,7 @@ func TestAccountAPI_AssignRoles(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(testRole) + env.tx.Create(testRole) t.Run("成功分配角色", func(t *testing.T) { reqBody := dto.AssignRolesRequest{ @@ -495,7 +495,7 @@ func TestAccountAPI_AssignRoles(t *testing.T) { // 验证关联已创建 var count int64 - env.db.Model(&model.AccountRole{}).Where("account_id = ? AND role_id = ?", testAccount.ID, testRole.ID).Count(&count) + env.tx.Model(&model.AccountRole{}).Where("account_id = ? AND role_id = ?", testAccount.ID, testRole.ID).Count(&count) assert.Equal(t, int64(1), count) }) } @@ -521,7 +521,7 @@ func TestAccountAPI_GetRoles(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - createTestAccount(t, env.db, testAccount) + createTestAccount(t, env.tx, testAccount) // 创建并分配角色 testRole := &model.Role{ @@ -529,7 +529,7 @@ func TestAccountAPI_GetRoles(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(testRole) + env.tx.Create(testRole) accountRole := &model.AccountRole{ AccountID: testAccount.ID, @@ -538,7 +538,7 @@ func TestAccountAPI_GetRoles(t *testing.T) { Creator: 1, Updater: 1, } - env.db.Create(accountRole) + env.tx.Create(accountRole) t.Run("成功获取账号角色", func(t *testing.T) { req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/accounts/%d/roles", testAccount.ID), nil) @@ -574,7 +574,7 @@ func TestAccountAPI_RemoveRole(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - createTestAccount(t, env.db, testAccount) + createTestAccount(t, env.tx, testAccount) // 创建并分配角色 testRole := &model.Role{ @@ -582,7 +582,7 @@ func TestAccountAPI_RemoveRole(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(testRole) + env.tx.Create(testRole) accountRole := &model.AccountRole{ AccountID: testAccount.ID, @@ -591,7 +591,7 @@ func TestAccountAPI_RemoveRole(t *testing.T) { Creator: 1, Updater: 1, } - env.db.Create(accountRole) + env.tx.Create(accountRole) t.Run("成功移除角色", func(t *testing.T) { req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/admin/accounts/%d/roles/%d", testAccount.ID, testRole.ID), nil) @@ -601,7 +601,7 @@ func TestAccountAPI_RemoveRole(t *testing.T) { // 验证关联已软删除 var ar model.AccountRole - err = env.db.Unscoped().Where("account_id = ? AND role_id = ?", testAccount.ID, testRole.ID).First(&ar).Error + err = env.tx.Unscoped().Where("account_id = ? AND role_id = ?", testAccount.ID, testRole.ID).First(&ar).Error require.NoError(t, err) assert.NotNil(t, ar.DeletedAt) }) diff --git a/tests/integration/api_regression_test.go b/tests/integration/api_regression_test.go index ed9865e..514086a 100644 --- a/tests/integration/api_regression_test.go +++ b/tests/integration/api_regression_test.go @@ -33,8 +33,8 @@ import ( // regressionTestEnv 回归测试环境 type regressionTestEnv struct { - db *gorm.DB - redisClient *redis.Client + tx *gorm.DB + rdb *redis.Client app *fiber.App postgresCleanup func() redisCleanup func() @@ -75,13 +75,13 @@ func setupRegressionTestEnv(t *testing.T) *regressionTestEnv { require.NoError(t, err) // 连接数据库 - db, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ + tx, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err) // 自动迁移 - err = db.AutoMigrate( + err = tx.AutoMigrate( &model.Account{}, &model.Role{}, &model.Permission{}, @@ -91,21 +91,21 @@ func setupRegressionTestEnv(t *testing.T) *regressionTestEnv { require.NoError(t, err) // 连接 Redis - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%s", redisHost, redisPort.Port()), }) // 初始化所有 Store - accountStore := postgresStore.NewAccountStore(db, redisClient) - roleStore := postgresStore.NewRoleStore(db) - permStore := postgresStore.NewPermissionStore(db) - accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient) - rolePermStore := postgresStore.NewRolePermissionStore(db, redisClient) + accountStore := postgresStore.NewAccountStore(tx, rdb) + roleStore := postgresStore.NewRoleStore(tx) + permStore := postgresStore.NewPermissionStore(tx) + accountRoleStore := postgresStore.NewAccountRoleStore(tx, rdb) + rolePermStore := postgresStore.NewRolePermissionStore(tx, rdb) // 初始化所有 Service accService := accountService.New(accountStore, roleStore, accountRoleStore) roleSvc := roleService.New(roleStore, permStore, rolePermStore) - permSvc := permissionService.New(permStore, accountRoleStore, rolePermStore, redisClient) + permSvc := permissionService.New(permStore, accountRoleStore, rolePermStore, rdb) // 初始化所有 Handler accountHandler := admin.NewAccountHandler(accService) @@ -136,8 +136,8 @@ func setupRegressionTestEnv(t *testing.T) *regressionTestEnv { routes.RegisterRoutes(app, services, middlewares) return ®ressionTestEnv{ - db: db, - redisClient: redisClient, + tx: tx, + rdb: rdb, app: app, postgresCleanup: func() { if err := pgContainer.Terminate(ctx); err != nil { @@ -212,7 +212,7 @@ func TestAPIRegression_RouteModularization(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(account) + env.tx.Create(account) // 测试获取账号 req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/accounts/%d", account.ID), nil) @@ -234,7 +234,7 @@ func TestAPIRegression_RouteModularization(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(role) + env.tx.Create(role) // 测试获取角色 req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/roles/%d", role.ID), nil) @@ -257,7 +257,7 @@ func TestAPIRegression_RouteModularization(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - env.db.Create(perm) + env.tx.Create(perm) // 测试获取权限 req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/permissions/%d", perm.ID), nil) @@ -324,7 +324,7 @@ func TestAPIRegression_Pagination(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(account) + env.tx.Create(account) } t.Run("分页参数正常工作", func(t *testing.T) { diff --git a/tests/integration/error_handler_test.go b/tests/integration/error_handler_test.go index 7d3d3b9..f11dc85 100644 --- a/tests/integration/error_handler_test.go +++ b/tests/integration/error_handler_test.go @@ -253,7 +253,7 @@ func TestErrorHandler_ResponseFormatConsistency(t *testing.T) { }, { name: "数据库错误", - path: "/api/test/db-error", + path: "/api/test/tx-error", method: "GET", errorCode: errors.CodeDatabaseError, errorMsg: "数据库连接失败", @@ -687,7 +687,7 @@ func TestErrorClassification_SensitiveInfoHidden(t *testing.T) { }{ { name: "数据库连接错误", - path: "/api/test/db-connection", + path: "/api/test/tx-connection", errorCode: errors.CodeDatabaseError, sensitiveMsg: "connection refused: tcp 192.168.1.100:5432", expectedStatus: 500, diff --git a/tests/integration/health_test.go b/tests/integration/health_test.go index 5b04d8e..f263f84 100644 --- a/tests/integration/health_test.go +++ b/tests/integration/health_test.go @@ -21,7 +21,7 @@ func TestHealthCheckNormal(t *testing.T) { logger, _ := zap.NewDevelopment() // 初始化内存数据库 - db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) + tx, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) // 初始化 Redis 客户端(使用本地 Redis) @@ -35,7 +35,7 @@ func TestHealthCheckNormal(t *testing.T) { app := fiber.New() // 创建健康检查处理器 - healthHandler := handler.NewHealthHandler(db, rdb, logger) + healthHandler := handler.NewHealthHandler(tx, rdb, logger) app.Get("/health", healthHandler.Check) // 发送测试请求 @@ -59,7 +59,7 @@ func TestHealthCheckDatabaseDown(t *testing.T) { logger, _ := zap.NewDevelopment() // 初始化一个会失败的数据库连接 - db, err := gorm.Open(sqlite.Open("/invalid/path/test.db"), &gorm.Config{}) + tx, err := gorm.Open(sqlite.Open("/invalid/path/test.tx"), &gorm.Config{}) if err != nil { // 预期会失败 t.Log("数据库连接失败(预期行为)") @@ -76,7 +76,7 @@ func TestHealthCheckDatabaseDown(t *testing.T) { app := fiber.New() // 创建健康检查处理器 - healthHandler := handler.NewHealthHandler(db, rdb, logger) + healthHandler := handler.NewHealthHandler(tx, rdb, logger) app.Get("/health", healthHandler.Check) // 发送测试请求 @@ -95,7 +95,7 @@ func TestHealthCheckRedisDown(t *testing.T) { logger, _ := zap.NewDevelopment() // 初始化内存数据库 - db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) + tx, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) // 初始化一个连接到无效地址的 Redis 客户端 @@ -109,7 +109,7 @@ func TestHealthCheckRedisDown(t *testing.T) { app := fiber.New() // 创建健康检查处理器 - healthHandler := handler.NewHealthHandler(db, rdb, logger) + healthHandler := handler.NewHealthHandler(tx, rdb, logger) app.Get("/health", healthHandler.Check) // 发送测试请求 @@ -128,7 +128,7 @@ func TestHealthCheckDetailed(t *testing.T) { logger, _ := zap.NewDevelopment() // 初始化内存数据库 - db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) + tx, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) // 初始化 Redis 客户端 @@ -149,7 +149,7 @@ func TestHealthCheckDetailed(t *testing.T) { app := fiber.New() // 创建健康检查处理器 - healthHandler := handler.NewHealthHandler(db, rdb, logger) + healthHandler := handler.NewHealthHandler(tx, rdb, logger) app.Get("/health", healthHandler.Check) // 发送测试请求 diff --git a/tests/integration/migration_test.go b/tests/integration/migration_test.go index 877d0ca..40528c2 100644 --- a/tests/integration/migration_test.go +++ b/tests/integration/migration_test.go @@ -63,7 +63,7 @@ func TestMigration_UpAndDown(t *testing.T) { require.NoError(t, err, "执行向上迁移失败") // 验证表已创建 - db, err := gorm.Open(postgresDriver.Open(connStr), &gorm.Config{ + tx, err := gorm.Open(postgresDriver.Open(connStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err, "连接数据库失败") @@ -79,14 +79,14 @@ func TestMigration_UpAndDown(t *testing.T) { for _, table := range tables { var exists bool - err := db.Raw("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = ?)", table).Scan(&exists).Error + err := tx.Raw("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = ?)", table).Scan(&exists).Error assert.NoError(t, err) assert.True(t, exists, "表 %s 应该存在", table) } // 检查索引 var indexCount int64 - err = db.Raw(` + err = tx.Raw(` SELECT COUNT(*) FROM pg_indexes WHERE tablename = 'tb_account' AND indexname LIKE 'idx_account_%' @@ -94,7 +94,7 @@ func TestMigration_UpAndDown(t *testing.T) { assert.NoError(t, err) assert.Greater(t, indexCount, int64(0), "tb_account 表应该有索引") - sqlDB, _ := db.DB() + sqlDB, _ := tx.DB() if sqlDB != nil { _ = sqlDB.Close() } @@ -105,7 +105,7 @@ func TestMigration_UpAndDown(t *testing.T) { require.NoError(t, err, "执行向下迁移失败") // 验证表已删除 - db, err := gorm.Open(postgresDriver.Open(connStr), &gorm.Config{ + tx, err := gorm.Open(postgresDriver.Open(connStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err, "连接数据库失败") @@ -121,12 +121,12 @@ func TestMigration_UpAndDown(t *testing.T) { for _, table := range tables { var exists bool - err := db.Raw("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = ?)", table).Scan(&exists).Error + err := tx.Raw("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = ?)", table).Scan(&exists).Error assert.NoError(t, err) assert.False(t, exists, "表 %s 应该已删除", table) } - sqlDB, _ := db.DB() + sqlDB, _ := tx.DB() if sqlDB != nil { _ = sqlDB.Close() } @@ -200,12 +200,12 @@ func TestMigration_SoftDeleteSupport(t *testing.T) { require.NoError(t, err, "执行向上迁移失败") // 连接数据库验证 - db, err := gorm.Open(postgresDriver.Open(connStr), &gorm.Config{ + tx, err := gorm.Open(postgresDriver.Open(connStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err, "连接数据库失败") defer func() { - sqlDB, _ := db.DB() + sqlDB, _ := tx.DB() if sqlDB != nil { _ = sqlDB.Close() } @@ -223,7 +223,7 @@ func TestMigration_SoftDeleteSupport(t *testing.T) { for _, table := range tables { // 检查 deleted_at 列存在 var columnExists bool - err := db.Raw(` + err := tx.Raw(` SELECT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_name = ? AND column_name = 'deleted_at' diff --git a/tests/integration/permission_middleware_test.go b/tests/integration/permission_middleware_test.go index 32b68ae..badcac9 100644 --- a/tests/integration/permission_middleware_test.go +++ b/tests/integration/permission_middleware_test.go @@ -121,8 +121,9 @@ func TestPermissionMiddleware_Unauthorized(t *testing.T) { // // func TestPermissionMiddleware_Integration(t *testing.T) { // // 1. 初始化数据库和 Redis -// db, redisClient := testutils.SetupTestDB(t) -// defer testutils.TeardownTestDB(t, db, redisClient) +// tx := testutils.NewTestTransaction(t) +// rdb := testutils.GetTestRedis(t) +// testutils.CleanTestRedisKeys(t, rdb) // // // 2. 创建测试数据(用户、角色、权限) // // ... diff --git a/tests/integration/permission_test.go b/tests/integration/permission_test.go index 6f3c5c1..601b04c 100644 --- a/tests/integration/permission_test.go +++ b/tests/integration/permission_test.go @@ -36,8 +36,8 @@ import ( // permTestEnv 权限测试环境 type permTestEnv struct { - db *gorm.DB - redisClient *redis.Client + tx *gorm.DB + rdb *redis.Client app *fiber.App permissionService *permissionService.Service cleanup func() @@ -78,29 +78,29 @@ func setupPermTestEnv(t *testing.T) *permTestEnv { require.NoError(t, err) // 连接数据库 - db, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ + tx, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err) // 自动迁移 - err = db.AutoMigrate( + err = tx.AutoMigrate( &model.Permission{}, ) require.NoError(t, err) // 连接 Redis - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%s", redisHost, redisPort.Port()), }) // 初始化 Store - permStore := postgresStore.NewPermissionStore(db) - accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient) - rolePermStore := postgresStore.NewRolePermissionStore(db, redisClient) + permStore := postgresStore.NewPermissionStore(tx) + accountRoleStore := postgresStore.NewAccountRoleStore(tx, rdb) + rolePermStore := postgresStore.NewRolePermissionStore(tx, rdb) // 初始化 Service - permSvc := permissionService.New(permStore, accountRoleStore, rolePermStore, redisClient) + permSvc := permissionService.New(permStore, accountRoleStore, rolePermStore, rdb) // 初始化 Handler permHandler := admin.NewPermissionHandler(permSvc) @@ -120,8 +120,8 @@ func setupPermTestEnv(t *testing.T) *permTestEnv { routes.RegisterRoutes(app, services, middlewares) return &permTestEnv{ - db: db, - redisClient: redisClient, + tx: tx, + rdb: rdb, app: app, permissionService: permSvc, cleanup: func() { @@ -171,7 +171,7 @@ func TestPermissionAPI_Create(t *testing.T) { // 验证数据库中权限已创建 var count int64 - env.db.Model(&model.Permission{}).Where("perm_code = ?", "user:manage").Count(&count) + env.tx.Model(&model.Permission{}).Where("perm_code = ?", "user:manage").Count(&count) assert.Equal(t, int64(1), count) }) @@ -183,7 +183,7 @@ func TestPermissionAPI_Create(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - env.db.Create(existingPerm) + env.tx.Create(existingPerm) // 尝试创建相同编码的权限 reqBody := dto.CreatePermissionRequest{ @@ -213,7 +213,7 @@ func TestPermissionAPI_Create(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - env.db.Create(parentPerm) + env.tx.Create(parentPerm) // 创建子权限 reqBody := dto.CreatePermissionRequest{ @@ -233,7 +233,7 @@ func TestPermissionAPI_Create(t *testing.T) { // 验证父权限ID已设置 var child model.Permission - env.db.Where("perm_code = ?", "system:user:list").First(&child) + env.tx.Where("perm_code = ?", "system:user:list").First(&child) assert.NotNil(t, child.ParentID) assert.Equal(t, parentPerm.ID, *child.ParentID) }) @@ -259,7 +259,7 @@ func TestPermissionAPI_Get(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - env.db.Create(testPerm) + env.tx.Create(testPerm) t.Run("成功获取权限详情", func(t *testing.T) { req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/permissions/%d", testPerm.ID), nil) @@ -305,7 +305,7 @@ func TestPermissionAPI_Update(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - env.db.Create(testPerm) + env.tx.Create(testPerm) t.Run("成功更新权限", func(t *testing.T) { newName := "更新后权限" @@ -323,7 +323,7 @@ func TestPermissionAPI_Update(t *testing.T) { // 验证数据库已更新 var updated model.Permission - env.db.First(&updated, testPerm.ID) + env.tx.First(&updated, testPerm.ID) assert.Equal(t, newName, updated.PermName) }) } @@ -349,7 +349,7 @@ func TestPermissionAPI_Delete(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - env.db.Create(testPerm) + env.tx.Create(testPerm) req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/admin/permissions/%d", testPerm.ID), nil) resp, err := env.app.Test(req) @@ -358,7 +358,7 @@ func TestPermissionAPI_Delete(t *testing.T) { // 验证权限已软删除 var deleted model.Permission - err = env.db.Unscoped().First(&deleted, testPerm.ID).Error + err = env.tx.Unscoped().First(&deleted, testPerm.ID).Error require.NoError(t, err) assert.NotNil(t, deleted.DeletedAt) }) @@ -385,7 +385,7 @@ func TestPermissionAPI_List(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - env.db.Create(perm) + env.tx.Create(perm) } t.Run("成功获取权限列表", func(t *testing.T) { @@ -429,7 +429,7 @@ func TestPermissionAPI_GetTree(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - env.db.Create(rootPerm) + env.tx.Create(rootPerm) // 子权限 childPerm := &model.Permission{ @@ -439,7 +439,7 @@ func TestPermissionAPI_GetTree(t *testing.T) { ParentID: &rootPerm.ID, Status: constants.StatusEnabled, } - env.db.Create(childPerm) + env.tx.Create(childPerm) // 孙子权限 grandchildPerm := &model.Permission{ @@ -449,7 +449,7 @@ func TestPermissionAPI_GetTree(t *testing.T) { ParentID: &childPerm.ID, Status: constants.StatusEnabled, } - env.db.Create(grandchildPerm) + env.tx.Create(grandchildPerm) t.Run("成功获取权限树", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/admin/permissions/tree", nil) @@ -483,7 +483,7 @@ func TestPermissionAPI_GetTreeByRoleType(t *testing.T) { AvailableForRoleTypes: "1", Status: constants.StatusEnabled, } - env.db.Create(platformPerm) + env.tx.Create(platformPerm) customerPerm := &model.Permission{ PermName: "客户权限", @@ -492,7 +492,7 @@ func TestPermissionAPI_GetTreeByRoleType(t *testing.T) { AvailableForRoleTypes: "2", Status: constants.StatusEnabled, } - env.db.Create(customerPerm) + env.tx.Create(customerPerm) commonPerm := &model.Permission{ PermName: "通用权限", @@ -501,7 +501,7 @@ func TestPermissionAPI_GetTreeByRoleType(t *testing.T) { AvailableForRoleTypes: "1,2", Status: constants.StatusEnabled, } - env.db.Create(commonPerm) + env.tx.Create(commonPerm) t.Run("按角色类型过滤权限树-平台角色", func(t *testing.T) { req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/permissions/tree?available_for_role_type=%d", constants.RoleTypePlatform), nil) @@ -559,7 +559,7 @@ func TestPermissionAPI_FilterByAvailableForRoleTypes(t *testing.T) { AvailableForRoleTypes: "1", Status: constants.StatusEnabled, } - env.db.Create(platformPerm) + env.tx.Create(platformPerm) customerPerm := &model.Permission{ PermName: "客户专用权限", @@ -568,7 +568,7 @@ func TestPermissionAPI_FilterByAvailableForRoleTypes(t *testing.T) { AvailableForRoleTypes: "2", Status: constants.StatusEnabled, } - env.db.Create(customerPerm) + env.tx.Create(customerPerm) commonPerm := &model.Permission{ PermName: "通用权限", @@ -577,7 +577,7 @@ func TestPermissionAPI_FilterByAvailableForRoleTypes(t *testing.T) { AvailableForRoleTypes: "1,2", Status: constants.StatusEnabled, } - env.db.Create(commonPerm) + env.tx.Create(commonPerm) t.Run("过滤平台角色可用权限", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/admin/permissions?available_for_role_type=1", nil) diff --git a/tests/integration/platform_account_test.go b/tests/integration/platform_account_test.go index a619437..df414b5 100644 --- a/tests/integration/platform_account_test.go +++ b/tests/integration/platform_account_test.go @@ -26,12 +26,13 @@ import ( ) func TestPlatformAccountAPI_ListPlatformAccounts(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgresStore.NewAccountStore(db, redisClient) - roleStore := postgresStore.NewRoleStore(db) - accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient) + accountStore := postgresStore.NewAccountStore(tx, rdb) + roleStore := postgresStore.NewRoleStore(tx) + accountRoleStore := postgresStore.NewAccountRoleStore(tx, rdb) accService := accountService.New(accountStore, roleStore, accountRoleStore) accountHandler := admin.NewAccountHandler(accService) @@ -57,7 +58,7 @@ func TestPlatformAccountAPI_ListPlatformAccounts(t *testing.T) { UserType: constants.UserTypeSuperAdmin, Status: constants.StatusEnabled, } - db.Create(superAdmin) + tx.Create(superAdmin) platformUser := &model.Account{ Username: testutils.GenerateUsername("platform_user", 2), @@ -66,7 +67,7 @@ func TestPlatformAccountAPI_ListPlatformAccounts(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - db.Create(platformUser) + tx.Create(platformUser) agentUser := &model.Account{ Username: testutils.GenerateUsername("agent_user", 3), @@ -75,7 +76,7 @@ func TestPlatformAccountAPI_ListPlatformAccounts(t *testing.T) { UserType: constants.UserTypeAgent, Status: constants.StatusEnabled, } - db.Create(agentUser) + tx.Create(agentUser) t.Run("列表只返回平台账号和超级管理员", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/admin/platform-accounts?page=1&page_size=20", nil) @@ -93,7 +94,7 @@ func TestPlatformAccountAPI_ListPlatformAccounts(t *testing.T) { assert.GreaterOrEqual(t, len(items), 2) var count int64 - db.Model(&model.Account{}).Where("user_type IN ?", []int{1, 2}).Count(&count) + tx.Model(&model.Account{}).Where("user_type IN ?", []int{1, 2}).Count(&count) assert.GreaterOrEqual(t, count, int64(2)) }) @@ -114,12 +115,13 @@ func TestPlatformAccountAPI_ListPlatformAccounts(t *testing.T) { } func TestPlatformAccountAPI_UpdatePassword(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgresStore.NewAccountStore(db, redisClient) - roleStore := postgresStore.NewRoleStore(db) - accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient) + accountStore := postgresStore.NewAccountStore(tx, rdb) + roleStore := postgresStore.NewRoleStore(tx) + accountRoleStore := postgresStore.NewAccountRoleStore(tx, rdb) accService := accountService.New(accountStore, roleStore, accountRoleStore) accountHandler := admin.NewAccountHandler(accService) @@ -145,7 +147,7 @@ func TestPlatformAccountAPI_UpdatePassword(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - db.Create(testAccount) + tx.Create(testAccount) t.Run("成功修改密码", func(t *testing.T) { reqBody := dto.UpdatePasswordRequest{ @@ -165,7 +167,7 @@ func TestPlatformAccountAPI_UpdatePassword(t *testing.T) { assert.Equal(t, 0, result.Code) var updated model.Account - db.First(&updated, testAccount.ID) + tx.First(&updated, testAccount.ID) assert.NotEqual(t, "old_hashed_password", updated.Password) }) @@ -188,12 +190,13 @@ func TestPlatformAccountAPI_UpdatePassword(t *testing.T) { } func TestPlatformAccountAPI_UpdateStatus(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgresStore.NewAccountStore(db, redisClient) - roleStore := postgresStore.NewRoleStore(db) - accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient) + accountStore := postgresStore.NewAccountStore(tx, rdb) + roleStore := postgresStore.NewRoleStore(tx) + accountRoleStore := postgresStore.NewAccountRoleStore(tx, rdb) accService := accountService.New(accountStore, roleStore, accountRoleStore) accountHandler := admin.NewAccountHandler(accService) @@ -219,7 +222,7 @@ func TestPlatformAccountAPI_UpdateStatus(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - db.Create(testAccount) + tx.Create(testAccount) t.Run("成功禁用账号", func(t *testing.T) { reqBody := dto.UpdateStatusRequest{ @@ -234,7 +237,7 @@ func TestPlatformAccountAPI_UpdateStatus(t *testing.T) { assert.Equal(t, fiber.StatusOK, resp.StatusCode) var updated model.Account - db.First(&updated, testAccount.ID) + tx.First(&updated, testAccount.ID) assert.Equal(t, constants.StatusDisabled, updated.Status) }) @@ -251,18 +254,19 @@ func TestPlatformAccountAPI_UpdateStatus(t *testing.T) { assert.Equal(t, fiber.StatusOK, resp.StatusCode) var updated model.Account - db.First(&updated, testAccount.ID) + tx.First(&updated, testAccount.ID) assert.Equal(t, constants.StatusEnabled, updated.Status) }) } func TestPlatformAccountAPI_AssignRoles(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgresStore.NewAccountStore(db, redisClient) - roleStore := postgresStore.NewRoleStore(db) - accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient) + accountStore := postgresStore.NewAccountStore(tx, rdb) + roleStore := postgresStore.NewRoleStore(tx) + accountRoleStore := postgresStore.NewAccountRoleStore(tx, rdb) accService := accountService.New(accountStore, roleStore, accountRoleStore) accountHandler := admin.NewAccountHandler(accService) @@ -288,7 +292,7 @@ func TestPlatformAccountAPI_AssignRoles(t *testing.T) { UserType: constants.UserTypeSuperAdmin, Status: constants.StatusEnabled, } - db.Create(superAdmin) + tx.Create(superAdmin) platformUser := &model.Account{ Username: testutils.GenerateUsername("platform_user_role", 31), @@ -297,14 +301,14 @@ func TestPlatformAccountAPI_AssignRoles(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - db.Create(platformUser) + tx.Create(platformUser) testRole := &model.Role{ RoleName: testutils.GenerateUsername("测试角色", 30), RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(testRole) + tx.Create(testRole) t.Run("超级管理员禁止分配角色", func(t *testing.T) { reqBody := dto.AssignRolesRequest{ @@ -337,7 +341,7 @@ func TestPlatformAccountAPI_AssignRoles(t *testing.T) { assert.Equal(t, fiber.StatusOK, resp.StatusCode) var count int64 - db.Model(&model.AccountRole{}).Where("account_id = ? AND role_id = ?", platformUser.ID, testRole.ID).Count(&count) + tx.Model(&model.AccountRole{}).Where("account_id = ? AND role_id = ?", platformUser.ID, testRole.ID).Count(&count) assert.Equal(t, int64(1), count) }) @@ -354,7 +358,7 @@ func TestPlatformAccountAPI_AssignRoles(t *testing.T) { assert.Equal(t, fiber.StatusOK, resp.StatusCode) var count int64 - db.Model(&model.AccountRole{}).Where("account_id = ?", platformUser.ID).Count(&count) + tx.Model(&model.AccountRole{}).Where("account_id = ?", platformUser.ID).Count(&count) assert.Equal(t, int64(0), count) }) } diff --git a/tests/integration/role_permission_test.go b/tests/integration/role_permission_test.go index 74ed77b..8ac5804 100644 --- a/tests/integration/role_permission_test.go +++ b/tests/integration/role_permission_test.go @@ -54,25 +54,25 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { redisPort, err := redisContainer.MappedPort(ctx, "6379") require.NoError(t, err) - db, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ + tx, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err) - err = db.AutoMigrate( + err = tx.AutoMigrate( &model.Role{}, &model.Permission{}, &model.RolePermission{}, ) require.NoError(t, err) - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%s", redisHost, redisPort.Port()), }) - roleStore := postgresStore.NewRoleStore(db) - permStore := postgresStore.NewPermissionStore(db) - rolePermStore := postgresStore.NewRolePermissionStore(db, redisClient) + roleStore := postgresStore.NewRoleStore(tx) + permStore := postgresStore.NewPermissionStore(tx) + rolePermStore := postgresStore.NewRolePermissionStore(tx, rdb) roleSvc := roleService.New(roleStore, permStore, rolePermStore) // 创建测试用户上下文 @@ -85,7 +85,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) // 创建测试权限 perm := &model.Permission{ @@ -94,7 +94,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - db.Create(perm) + tx.Create(perm) // 分配权限 rps, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) @@ -111,7 +111,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) // 创建多个测试权限 permIDs := make([]uint, 3) @@ -122,7 +122,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - db.Create(perm) + tx.Create(perm) permIDs[i] = perm.ID } @@ -139,7 +139,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) // 创建并分配权限 perm := &model.Permission{ @@ -148,7 +148,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - db.Create(perm) + tx.Create(perm) _, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) require.NoError(t, err) @@ -167,7 +167,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) // 创建并分配权限 perm := &model.Permission{ @@ -176,7 +176,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - db.Create(perm) + tx.Create(perm) _, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) require.NoError(t, err) @@ -187,7 +187,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { // 验证权限已被软删除 var rp model.RolePermission - err = db.Unscoped().Where("role_id = ? AND perm_id = ?", role.ID, perm.ID).First(&rp).Error + err = tx.Unscoped().Where("role_id = ? AND perm_id = ?", role.ID, perm.ID).First(&rp).Error require.NoError(t, err) assert.NotNil(t, rp.DeletedAt) }) @@ -199,7 +199,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) // 创建测试权限 perm := &model.Permission{ @@ -208,7 +208,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - db.Create(perm) + tx.Create(perm) // 第一次分配 _, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) @@ -220,7 +220,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { // 验证只有一条记录 var count int64 - db.Model(&model.RolePermission{}).Where("role_id = ? AND perm_id = ?", role.ID, perm.ID).Count(&count) + tx.Model(&model.RolePermission{}).Where("role_id = ? AND perm_id = ?", role.ID, perm.ID).Count(&count) assert.Equal(t, int64(1), count) }) @@ -231,7 +231,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - db.Create(perm) + tx.Create(perm) _, err := roleSvc.AssignPermissions(userCtx, 99999, []uint{perm.ID}) assert.Error(t, err) @@ -243,7 +243,7 @@ func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) _, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{99999}) assert.Error(t, err) @@ -277,18 +277,18 @@ func TestRolePermissionAssociation_SoftDelete(t *testing.T) { redisHost, _ := redisContainer.Host(ctx) redisPort, _ := redisContainer.MappedPort(ctx, "6379") - db, _ := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ + tx, _ := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) - _ = db.AutoMigrate(&model.Role{}, &model.Permission{}, &model.RolePermission{}) + _ = tx.AutoMigrate(&model.Role{}, &model.Permission{}, &model.RolePermission{}) - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%s", redisHost, redisPort.Port()), }) - roleStore := postgresStore.NewRoleStore(db) - permStore := postgresStore.NewPermissionStore(db) - rolePermStore := postgresStore.NewRolePermissionStore(db, redisClient) + roleStore := postgresStore.NewRoleStore(tx) + permStore := postgresStore.NewPermissionStore(tx) + rolePermStore := postgresStore.NewRolePermissionStore(tx, rdb) roleSvc := roleService.New(roleStore, permStore, rolePermStore) userCtx := middleware.SetUserContext(ctx, middleware.NewSimpleUserContext(1, constants.UserTypeSuperAdmin, 0)) @@ -300,7 +300,7 @@ func TestRolePermissionAssociation_SoftDelete(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) perm := &model.Permission{ PermName: "恢复权限测试", @@ -308,7 +308,7 @@ func TestRolePermissionAssociation_SoftDelete(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - db.Create(perm) + tx.Create(perm) // 分配权限 _, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) @@ -336,7 +336,7 @@ func TestRolePermissionAssociation_SoftDelete(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) // 创建多个权限 permIDs := make([]uint, 5) @@ -347,7 +347,7 @@ func TestRolePermissionAssociation_SoftDelete(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - db.Create(perm) + tx.Create(perm) permIDs[i] = perm.ID } @@ -395,10 +395,10 @@ func TestRolePermissionAssociation_Cascade(t *testing.T) { pgConnStr, _ := pgContainer.ConnectionString(ctx, "sslmode=disable") // 设置环境 - db, _ := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ + tx, _ := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) - _ = db.AutoMigrate(&model.Role{}, &model.Permission{}, &model.RolePermission{}) + _ = tx.AutoMigrate(&model.Role{}, &model.Permission{}, &model.RolePermission{}) t.Run("验证无外键约束(关联表独立)", func(t *testing.T) { // 创建角色和权限 @@ -407,7 +407,7 @@ func TestRolePermissionAssociation_Cascade(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - db.Create(role) + tx.Create(role) perm := &model.Permission{ PermName: "级联测试权限", @@ -415,7 +415,7 @@ func TestRolePermissionAssociation_Cascade(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - db.Create(perm) + tx.Create(perm) // 创建关联 rp := &model.RolePermission{ @@ -423,19 +423,19 @@ func TestRolePermissionAssociation_Cascade(t *testing.T) { PermID: perm.ID, Status: constants.StatusEnabled, } - db.Create(rp) + tx.Create(rp) // 删除角色(软删除) - db.Delete(role) + tx.Delete(role) // 验证关联记录仍然存在(无外键约束) var count int64 - db.Model(&model.RolePermission{}).Where("role_id = ?", role.ID).Count(&count) + tx.Model(&model.RolePermission{}).Where("role_id = ?", role.ID).Count(&count) assert.Equal(t, int64(1), count, "关联记录应该仍然存在,因为没有外键约束") // 验证可以独立查询关联记录 var rpRecord model.RolePermission - err := db.Where("role_id = ? AND perm_id = ?", role.ID, perm.ID).First(&rpRecord).Error + err := tx.Where("role_id = ? AND perm_id = ?", role.ID, perm.ID).First(&rpRecord).Error assert.NoError(t, err, "应该能查询到关联记录") }) } diff --git a/tests/integration/role_test.go b/tests/integration/role_test.go index 84adb06..bbbd3c3 100644 --- a/tests/integration/role_test.go +++ b/tests/integration/role_test.go @@ -36,8 +36,8 @@ import ( // roleTestEnv 角色测试环境 type roleTestEnv struct { - db *gorm.DB - redisClient *redis.Client + tx *gorm.DB + rdb *redis.Client app *fiber.App roleService *roleService.Service postgresCleanup func() @@ -79,13 +79,13 @@ func setupRoleTestEnv(t *testing.T) *roleTestEnv { require.NoError(t, err) // 连接数据库 - db, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ + tx, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err) // 自动迁移 - err = db.AutoMigrate( + err = tx.AutoMigrate( &model.Account{}, &model.Role{}, &model.Permission{}, @@ -95,14 +95,14 @@ func setupRoleTestEnv(t *testing.T) *roleTestEnv { require.NoError(t, err) // 连接 Redis - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%s", redisHost, redisPort.Port()), }) // 初始化 Store - roleStore := postgresStore.NewRoleStore(db) - permissionStore := postgresStore.NewPermissionStore(db) - rolePermissionStore := postgresStore.NewRolePermissionStore(db, redisClient) + roleStore := postgresStore.NewRoleStore(tx) + permissionStore := postgresStore.NewPermissionStore(tx) + rolePermissionStore := postgresStore.NewRolePermissionStore(tx, rdb) // 初始化 Service roleSvc := roleService.New(roleStore, permissionStore, rolePermissionStore) @@ -125,8 +125,8 @@ func setupRoleTestEnv(t *testing.T) *roleTestEnv { routes.RegisterRoutes(app, services, middlewares) return &roleTestEnv{ - db: db, - redisClient: redisClient, + tx: tx, + rdb: rdb, app: app, roleService: roleSvc, postgresCleanup: func() { @@ -187,7 +187,7 @@ func TestRoleAPI_Create(t *testing.T) { // 验证数据库中角色已创建 var count int64 - env.db.Model(&model.Role{}).Where("role_name = ?", "测试角色").Count(&count) + env.tx.Model(&model.Role{}).Where("role_name = ?", "测试角色").Count(&count) assert.Equal(t, int64(1), count) }) @@ -229,7 +229,7 @@ func TestRoleAPI_Get(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(testRole) + env.tx.Create(testRole) t.Run("成功获取角色详情", func(t *testing.T) { req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/roles/%d", testRole.ID), nil) @@ -274,7 +274,7 @@ func TestRoleAPI_Update(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(testRole) + env.tx.Create(testRole) t.Run("成功更新角色", func(t *testing.T) { newName := "更新后角色" @@ -292,7 +292,7 @@ func TestRoleAPI_Update(t *testing.T) { // 验证数据库已更新 var updated model.Role - env.db.First(&updated, testRole.ID) + env.tx.First(&updated, testRole.ID) assert.Equal(t, newName, updated.RoleName) }) } @@ -317,7 +317,7 @@ func TestRoleAPI_Delete(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(testRole) + env.tx.Create(testRole) req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/admin/roles/%d", testRole.ID), nil) resp, err := env.app.Test(req) @@ -326,7 +326,7 @@ func TestRoleAPI_Delete(t *testing.T) { // 验证角色已软删除 var deleted model.Role - err = env.db.Unscoped().First(&deleted, testRole.ID).Error + err = env.tx.Unscoped().First(&deleted, testRole.ID).Error require.NoError(t, err) assert.NotNil(t, deleted.DeletedAt) }) @@ -352,7 +352,7 @@ func TestRoleAPI_List(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(role) + env.tx.Create(role) } t.Run("成功获取角色列表", func(t *testing.T) { @@ -387,7 +387,7 @@ func TestRoleAPI_AssignPermissions(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(testRole) + env.tx.Create(testRole) // 创建测试权限 testPerm := &model.Permission{ @@ -396,7 +396,7 @@ func TestRoleAPI_AssignPermissions(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - env.db.Create(testPerm) + env.tx.Create(testPerm) t.Run("成功分配权限", func(t *testing.T) { reqBody := dto.AssignPermissionsRequest{ @@ -413,7 +413,7 @@ func TestRoleAPI_AssignPermissions(t *testing.T) { // 验证关联已创建 var count int64 - env.db.Model(&model.RolePermission{}).Where("role_id = ? AND perm_id = ?", testRole.ID, testPerm.ID).Count(&count) + env.tx.Model(&model.RolePermission{}).Where("role_id = ? AND perm_id = ?", testRole.ID, testPerm.ID).Count(&count) assert.Equal(t, int64(1), count) }) } @@ -437,7 +437,7 @@ func TestRoleAPI_GetPermissions(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(testRole) + env.tx.Create(testRole) // 创建并分配权限 testPerm := &model.Permission{ @@ -446,14 +446,14 @@ func TestRoleAPI_GetPermissions(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - env.db.Create(testPerm) + env.tx.Create(testPerm) rolePerm := &model.RolePermission{ RoleID: testRole.ID, PermID: testPerm.ID, Status: constants.StatusEnabled, } - env.db.Create(rolePerm) + env.tx.Create(rolePerm) t.Run("成功获取角色权限", func(t *testing.T) { req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/roles/%d/permissions", testRole.ID), nil) @@ -487,7 +487,7 @@ func TestRoleAPI_RemovePermission(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(testRole) + env.tx.Create(testRole) // 创建并分配权限 testPerm := &model.Permission{ @@ -496,14 +496,14 @@ func TestRoleAPI_RemovePermission(t *testing.T) { PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } - env.db.Create(testPerm) + env.tx.Create(testPerm) rolePerm := &model.RolePermission{ RoleID: testRole.ID, PermID: testPerm.ID, Status: constants.StatusEnabled, } - env.db.Create(rolePerm) + env.tx.Create(rolePerm) t.Run("成功移除权限", func(t *testing.T) { req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/admin/roles/%d/permissions/%d", testRole.ID, testPerm.ID), nil) @@ -513,7 +513,7 @@ func TestRoleAPI_RemovePermission(t *testing.T) { // 验证关联已软删除 var rp model.RolePermission - err = env.db.Unscoped().Where("role_id = ? AND perm_id = ?", testRole.ID, testPerm.ID).First(&rp).Error + err = env.tx.Unscoped().Where("role_id = ? AND perm_id = ?", testRole.ID, testPerm.ID).First(&rp).Error require.NoError(t, err) assert.NotNil(t, rp.DeletedAt) }) @@ -538,7 +538,7 @@ func TestRoleAPI_UpdateStatus(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - env.db.Create(testRole) + env.tx.Create(testRole) t.Run("成功禁用角色", func(t *testing.T) { reqBody := dto.UpdateRoleStatusRequest{ @@ -560,7 +560,7 @@ func TestRoleAPI_UpdateStatus(t *testing.T) { // 验证数据库中状态已更新 var updated model.Role - env.db.First(&updated, testRole.ID) + env.tx.First(&updated, testRole.ID) assert.Equal(t, constants.StatusDisabled, updated.Status) }) @@ -584,7 +584,7 @@ func TestRoleAPI_UpdateStatus(t *testing.T) { // 验证数据库中状态已更新 var updated model.Role - env.db.First(&updated, testRole.ID) + env.tx.First(&updated, testRole.ID) assert.Equal(t, constants.StatusEnabled, updated.Status) }) diff --git a/tests/integration/shop_account_management_test.go b/tests/integration/shop_account_management_test.go index 83b8220..65a0da9 100644 --- a/tests/integration/shop_account_management_test.go +++ b/tests/integration/shop_account_management_test.go @@ -31,8 +31,8 @@ import ( // shopAccountTestEnv 商户账号测试环境 type shopAccountTestEnv struct { - db *gorm.DB - redisClient *redis.Client + tx *gorm.DB + rdb *redis.Client tokenManager *auth.TokenManager app *fiber.App adminToken string @@ -55,12 +55,12 @@ func setupShopAccountTestEnv(t *testing.T) *shopAccountTestEnv { zapLogger, _ := zap.NewDevelopment() dsn := "host=cxd.whcxd.cn port=16159 user=erp_pgsql password=erp_2025 dbname=junhong_cmp_test sslmode=disable TimeZone=Asia/Shanghai" - db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ + tx, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err) - err = db.AutoMigrate( + err = tx.AutoMigrate( &model.Account{}, &model.Role{}, &model.Permission{}, @@ -72,32 +72,32 @@ func setupShopAccountTestEnv(t *testing.T) *shopAccountTestEnv { ) require.NoError(t, err) - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "cxd.whcxd.cn:16299", Password: "cpNbWtAaqgo1YJmbMp3h", DB: 15, }) ctx := context.Background() - err = redisClient.Ping(ctx).Err() + err = rdb.Ping(ctx).Err() require.NoError(t, err) testPrefix := fmt.Sprintf("test:%s:", t.Name()) - keys, _ := redisClient.Keys(ctx, testPrefix+"*").Result() + keys, _ := rdb.Keys(ctx, testPrefix+"*").Result() if len(keys) > 0 { - redisClient.Del(ctx, keys...) + rdb.Del(ctx, keys...) } - tokenManager := auth.NewTokenManager(redisClient, 24*time.Hour, 7*24*time.Hour) + tokenManager := auth.NewTokenManager(rdb, 24*time.Hour, 7*24*time.Hour) - superAdmin := testutil.CreateSuperAdmin(t, db) - adminToken, _ := testutil.GenerateTestToken(t, redisClient, superAdmin, "web") + superAdmin := testutil.CreateSuperAdmin(t, tx) + adminToken, _ := testutil.GenerateTestToken(t, rdb, superAdmin, "web") - testShop := testutil.CreateTestShop(t, db, "测试商户", "TEST_SHOP", 1, nil) + testShop := testutil.CreateTestShop(t, tx, "测试商户", "TEST_SHOP", 1, nil) deps := &bootstrap.Dependencies{ - DB: db, - Redis: redisClient, + DB: tx, + Redis: rdb, Logger: zapLogger, TokenManager: tokenManager, } @@ -117,8 +117,8 @@ func setupShopAccountTestEnv(t *testing.T) *shopAccountTestEnv { routes.RegisterRoutes(app, handlers, middlewares) return &shopAccountTestEnv{ - db: db, - redisClient: redisClient, + tx: tx, + rdb: rdb, tokenManager: tokenManager, app: app, adminToken: adminToken, @@ -130,17 +130,17 @@ func setupShopAccountTestEnv(t *testing.T) *shopAccountTestEnv { // teardown 清理测试环境 func (e *shopAccountTestEnv) teardown() { - e.db.Exec("DELETE FROM tb_account WHERE username LIKE 'test%'") - e.db.Exec("DELETE FROM tb_shop WHERE shop_code LIKE 'TEST%'") + e.tx.Exec("DELETE FROM tb_account WHERE username LIKE 'test%'") + e.tx.Exec("DELETE FROM tb_shop WHERE shop_code LIKE 'TEST%'") ctx := context.Background() testPrefix := fmt.Sprintf("test:%s:", e.t.Name()) - keys, _ := e.redisClient.Keys(ctx, testPrefix+"*").Result() + keys, _ := e.rdb.Keys(ctx, testPrefix+"*").Result() if len(keys) > 0 { - e.redisClient.Del(ctx, keys...) + e.rdb.Del(ctx, keys...) } - e.redisClient.Close() + e.rdb.Close() } // TestShopAccount_CreateAccount 测试创建商户账号 @@ -177,7 +177,7 @@ func TestShopAccount_CreateAccount(t *testing.T) { // 验证数据库中的账号 var account model.Account - err = env.db.Where("username = ?", "agent001").First(&account).Error + err = env.tx.Where("username = ?", "agent001").First(&account).Error require.NoError(t, err) assert.Equal(t, constants.UserTypeAgent, account.UserType) assert.NotNil(t, account.ShopID) @@ -225,9 +225,9 @@ func TestShopAccount_ListAccounts(t *testing.T) { defer env.teardown() // 创建测试账号 - testutil.CreateAgentUser(t, env.db, env.testShop.ID) - testutil.CreateTestAccount(t, env.db, "agent2", "pass123", constants.UserTypeAgent, &env.testShop.ID, nil) - testutil.CreateTestAccount(t, env.db, "agent3", "pass123", constants.UserTypeAgent, &env.testShop.ID, nil) + testutil.CreateAgentUser(t, env.tx, env.testShop.ID) + testutil.CreateTestAccount(t, env.tx, "agent2", "pass123", constants.UserTypeAgent, &env.testShop.ID, nil) + testutil.CreateTestAccount(t, env.tx, "agent3", "pass123", constants.UserTypeAgent, &env.testShop.ID, nil) // 查询该商户的所有账号 req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/shop-accounts?shop_id=%d&page=1&size=10", env.testShop.ID), nil) @@ -260,7 +260,7 @@ func TestShopAccount_UpdateAccount(t *testing.T) { defer env.teardown() // 创建测试账号 - account := testutil.CreateAgentUser(t, env.db, env.testShop.ID) + account := testutil.CreateAgentUser(t, env.tx, env.testShop.ID) // 更新账号用户名 reqBody := dto.UpdateShopAccountRequest{ @@ -288,7 +288,7 @@ func TestShopAccount_UpdateAccount(t *testing.T) { // 验证数据库中的更新 var updatedAccount model.Account - err = env.db.First(&updatedAccount, account.ID).Error + err = env.tx.First(&updatedAccount, account.ID).Error require.NoError(t, err) assert.Equal(t, "updated_agent", updatedAccount.Username) assert.Equal(t, account.Phone, updatedAccount.Phone) // 手机号不应该改变 @@ -300,7 +300,7 @@ func TestShopAccount_UpdatePassword(t *testing.T) { defer env.teardown() // 创建测试账号 - account := testutil.CreateAgentUser(t, env.db, env.testShop.ID) + account := testutil.CreateAgentUser(t, env.tx, env.testShop.ID) // 重置密码 newPassword := "newpassword456" @@ -329,7 +329,7 @@ func TestShopAccount_UpdatePassword(t *testing.T) { // 验证新密码 var updatedAccount model.Account - err = env.db.First(&updatedAccount, account.ID).Error + err = env.tx.First(&updatedAccount, account.ID).Error require.NoError(t, err) err = bcrypt.CompareHashAndPassword([]byte(updatedAccount.Password), []byte(newPassword)) @@ -346,7 +346,7 @@ func TestShopAccount_UpdateStatus(t *testing.T) { defer env.teardown() // 创建测试账号(默认启用) - account := testutil.CreateAgentUser(t, env.db, env.testShop.ID) + account := testutil.CreateAgentUser(t, env.tx, env.testShop.ID) require.Equal(t, 1, account.Status) // 禁用账号 @@ -375,7 +375,7 @@ func TestShopAccount_UpdateStatus(t *testing.T) { // 验证账号已禁用 var disabledAccount model.Account - err = env.db.First(&disabledAccount, account.ID).Error + err = env.tx.First(&disabledAccount, account.ID).Error require.NoError(t, err) assert.Equal(t, 2, disabledAccount.Status) @@ -396,7 +396,7 @@ func TestShopAccount_UpdateStatus(t *testing.T) { // 验证账号已启用 var enabledAccount model.Account - err = env.db.First(&enabledAccount, account.ID).Error + err = env.tx.First(&enabledAccount, account.ID).Error require.NoError(t, err) assert.Equal(t, 1, enabledAccount.Status) } @@ -407,10 +407,10 @@ func TestShopAccount_DeleteShopDisablesAccounts(t *testing.T) { defer env.teardown() // 创建商户和多个账号 - shop := testutil.CreateTestShop(t, env.db, "待删除商户", "DEL_SHOP", 1, nil) - account1 := testutil.CreateTestAccount(t, env.db, "agent1", "pass123", constants.UserTypeAgent, &shop.ID, nil) - account2 := testutil.CreateTestAccount(t, env.db, "agent2", "pass123", constants.UserTypeAgent, &shop.ID, nil) - account3 := testutil.CreateTestAccount(t, env.db, "agent3", "pass123", constants.UserTypeAgent, &shop.ID, nil) + shop := testutil.CreateTestShop(t, env.tx, "待删除商户", "DEL_SHOP", 1, nil) + account1 := testutil.CreateTestAccount(t, env.tx, "agent1", "pass123", constants.UserTypeAgent, &shop.ID, nil) + account2 := testutil.CreateTestAccount(t, env.tx, "agent2", "pass123", constants.UserTypeAgent, &shop.ID, nil) + account3 := testutil.CreateTestAccount(t, env.tx, "agent3", "pass123", constants.UserTypeAgent, &shop.ID, nil) // 删除商户 req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/admin/shops/%d", shop.ID), nil) @@ -426,14 +426,14 @@ func TestShopAccount_DeleteShopDisablesAccounts(t *testing.T) { accounts := []*model.Account{account1, account2, account3} for _, acc := range accounts { var disabledAccount model.Account - err = env.db.First(&disabledAccount, acc.ID).Error + err = env.tx.First(&disabledAccount, acc.ID).Error require.NoError(t, err) assert.Equal(t, 2, disabledAccount.Status, "账号 %s 应该被禁用", acc.Username) } // 验证商户已软删除 var deletedShop model.Shop - err = env.db.Unscoped().First(&deletedShop, shop.ID).Error + err = env.tx.Unscoped().First(&deletedShop, shop.ID).Error require.NoError(t, err) assert.NotNil(t, deletedShop.DeletedAt) } @@ -460,11 +460,11 @@ func TestShopAccount_FilterByStatus(t *testing.T) { defer env.teardown() // 创建启用和禁用的账号 - _ = testutil.CreateTestAccount(t, env.db, "enabled_agent", "pass123", constants.UserTypeAgent, &env.testShop.ID, nil) - disabledAccount := testutil.CreateTestAccount(t, env.db, "disabled_agent", "pass123", constants.UserTypeAgent, &env.testShop.ID, nil) + _ = testutil.CreateTestAccount(t, env.tx, "enabled_agent", "pass123", constants.UserTypeAgent, &env.testShop.ID, nil) + disabledAccount := testutil.CreateTestAccount(t, env.tx, "disabled_agent", "pass123", constants.UserTypeAgent, &env.testShop.ID, nil) // 禁用第二个账号 - env.db.Model(&disabledAccount).Update("status", 2) + env.tx.Model(&disabledAccount).Update("status", 2) // 查询只包含启用的账号 req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/shop-accounts?shop_id=%d&status=1", env.testShop.ID), nil) diff --git a/tests/integration/shop_management_test.go b/tests/integration/shop_management_test.go index a7d6d64..3a128ca 100644 --- a/tests/integration/shop_management_test.go +++ b/tests/integration/shop_management_test.go @@ -29,8 +29,8 @@ import ( // shopManagementTestEnv 商户管理测试环境 type shopManagementTestEnv struct { - db *gorm.DB - redisClient *redis.Client + tx *gorm.DB + rdb *redis.Client tokenManager *auth.TokenManager app *fiber.App adminToken string @@ -52,12 +52,12 @@ func setupShopManagementTestEnv(t *testing.T) *shopManagementTestEnv { zapLogger, _ := zap.NewDevelopment() dsn := "host=cxd.whcxd.cn port=16159 user=erp_pgsql password=erp_2025 dbname=junhong_cmp_test sslmode=disable TimeZone=Asia/Shanghai" - db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ + tx, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err) - err = db.AutoMigrate( + err = tx.AutoMigrate( &model.Account{}, &model.Role{}, &model.Permission{}, @@ -69,30 +69,30 @@ func setupShopManagementTestEnv(t *testing.T) *shopManagementTestEnv { ) require.NoError(t, err) - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "cxd.whcxd.cn:16299", Password: "cpNbWtAaqgo1YJmbMp3h", DB: 15, }) ctx := context.Background() - err = redisClient.Ping(ctx).Err() + err = rdb.Ping(ctx).Err() require.NoError(t, err) testPrefix := fmt.Sprintf("test:%s:", t.Name()) - keys, _ := redisClient.Keys(ctx, testPrefix+"*").Result() + keys, _ := rdb.Keys(ctx, testPrefix+"*").Result() if len(keys) > 0 { - redisClient.Del(ctx, keys...) + rdb.Del(ctx, keys...) } - tokenManager := auth.NewTokenManager(redisClient, 24*time.Hour, 7*24*time.Hour) + tokenManager := auth.NewTokenManager(rdb, 24*time.Hour, 7*24*time.Hour) - superAdmin := testutil.CreateSuperAdmin(t, db) - adminToken, _ := testutil.GenerateTestToken(t, redisClient, superAdmin, "web") + superAdmin := testutil.CreateSuperAdmin(t, tx) + adminToken, _ := testutil.GenerateTestToken(t, rdb, superAdmin, "web") deps := &bootstrap.Dependencies{ - DB: db, - Redis: redisClient, + DB: tx, + Redis: rdb, Logger: zapLogger, TokenManager: tokenManager, } @@ -112,8 +112,8 @@ func setupShopManagementTestEnv(t *testing.T) *shopManagementTestEnv { routes.RegisterRoutes(app, handlers, middlewares) return &shopManagementTestEnv{ - db: db, - redisClient: redisClient, + tx: tx, + rdb: rdb, tokenManager: tokenManager, app: app, adminToken: adminToken, @@ -124,17 +124,17 @@ func setupShopManagementTestEnv(t *testing.T) *shopManagementTestEnv { // teardown 清理测试环境 func (e *shopManagementTestEnv) teardown() { - e.db.Exec("DELETE FROM tb_account WHERE username LIKE 'test%' OR username LIKE 'agent%' OR username LIKE 'superadmin%'") - e.db.Exec("DELETE FROM tb_shop WHERE shop_code LIKE 'TEST%' OR shop_code LIKE 'DUP%' OR shop_code LIKE 'SHOP_%' OR shop_code LIKE 'ORIG%' OR shop_code LIKE 'DEL%' OR shop_code LIKE 'MULTI%'") + e.tx.Exec("DELETE FROM tb_account WHERE username LIKE 'test%' OR username LIKE 'agent%' OR username LIKE 'superadmin%'") + e.tx.Exec("DELETE FROM tb_shop WHERE shop_code LIKE 'TEST%' OR shop_code LIKE 'DUP%' OR shop_code LIKE 'SHOP_%' OR shop_code LIKE 'ORIG%' OR shop_code LIKE 'DEL%' OR shop_code LIKE 'MULTI%'") ctx := context.Background() testPrefix := fmt.Sprintf("test:%s:", e.t.Name()) - keys, _ := e.redisClient.Keys(ctx, testPrefix+"*").Result() + keys, _ := e.rdb.Keys(ctx, testPrefix+"*").Result() if len(keys) > 0 { - e.redisClient.Del(ctx, keys...) + e.rdb.Del(ctx, keys...) } - e.redisClient.Close() + e.rdb.Close() } // TestShopManagement_CreateShop 测试创建商户 @@ -245,9 +245,9 @@ func TestShopManagement_ListShops(t *testing.T) { defer env.teardown() // 创建测试数据 - testutil.CreateTestShop(t, env.db, "商户A", "SHOP_A", 1, nil) - testutil.CreateTestShop(t, env.db, "商户B", "SHOP_B", 1, nil) - testutil.CreateTestShop(t, env.db, "商户C", "SHOP_C", 2, nil) + testutil.CreateTestShop(t, env.tx, "商户A", "SHOP_A", 1, nil) + testutil.CreateTestShop(t, env.tx, "商户B", "SHOP_B", 1, nil) + testutil.CreateTestShop(t, env.tx, "商户C", "SHOP_C", 2, nil) req := httptest.NewRequest("GET", "/api/admin/shops?page=1&size=10", nil) req.Header.Set("Authorization", "Bearer "+env.adminToken) @@ -279,7 +279,7 @@ func TestShopManagement_UpdateShop(t *testing.T) { defer env.teardown() // 创建测试商户 - shop := testutil.CreateTestShop(t, env.db, "原始商户", "ORIG001", 1, nil) + shop := testutil.CreateTestShop(t, env.tx, "原始商户", "ORIG001", 1, nil) // 更新商户 reqBody := dto.UpdateShopRequest{ @@ -318,7 +318,7 @@ func TestShopManagement_DeleteShop(t *testing.T) { defer env.teardown() // 创建测试商户 - shop := testutil.CreateTestShop(t, env.db, "待删除商户", "DEL001", 1, nil) + shop := testutil.CreateTestShop(t, env.tx, "待删除商户", "DEL001", 1, nil) // 删除商户 req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/admin/shops/%d", shop.ID), nil) @@ -343,7 +343,7 @@ func TestShopManagement_DeleteShop_WithMultipleAccounts(t *testing.T) { defer env.teardown() // 创建测试商户 - shop := testutil.CreateTestShop(t, env.db, "多账号商户", "MULTI001", 1, nil) + shop := testutil.CreateTestShop(t, env.tx, "多账号商户", "MULTI001", 1, nil) // 删除商户 req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/admin/shops/%d", shop.ID), nil) diff --git a/tests/integration/task_test.go b/tests/integration/task_test.go index 45cb07d..1185db6 100644 --- a/tests/integration/task_test.go +++ b/tests/integration/task_test.go @@ -26,14 +26,14 @@ type EmailPayload struct { // TestTaskSubmit 测试任务提交 func TestTaskSubmit(t *testing.T) { // 创建 Redis 客户端 - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() // 清理测试数据 ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) // 创建 Asynq 客户端 client := asynq.NewClient(asynq.RedisClientOpt{ @@ -69,13 +69,13 @@ func TestTaskSubmit(t *testing.T) { // TestTaskPriority 测试任务优先级 func TestTaskPriority(t *testing.T) { // 创建 Redis 客户端 - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) // 创建 Asynq 客户端 client := asynq.NewClient(asynq.RedisClientOpt{ @@ -115,13 +115,13 @@ func TestTaskPriority(t *testing.T) { // TestTaskRetry 测试任务重试机制 func TestTaskRetry(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", @@ -152,34 +152,34 @@ func TestTaskRetry(t *testing.T) { // TestTaskIdempotency 测试任务幂等性键 func TestTaskIdempotency(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) requestID := "idempotent-test-001" lockKey := constants.RedisTaskLockKey(requestID) // 第一次设置锁(模拟任务开始执行) - result, err := redisClient.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() + result, err := rdb.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() + result, err = rdb.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() require.NoError(t, err) assert.False(t, result, "第二次设置锁应该失败(幂等性)") // 验证锁存在 - exists, err := redisClient.Exists(ctx, lockKey).Result() + exists, err := rdb.Exists(ctx, lockKey).Result() require.NoError(t, err) assert.Equal(t, int64(1), exists) // 验证 TTL - ttl, err := redisClient.TTL(ctx, lockKey).Result() + ttl, err := rdb.TTL(ctx, lockKey).Result() require.NoError(t, err) assert.Greater(t, ttl.Hours(), 23.0) assert.LessOrEqual(t, ttl.Hours(), 24.0) @@ -187,13 +187,13 @@ func TestTaskIdempotency(t *testing.T) { // TestTaskStatusTracking 测试任务状态跟踪 func TestTaskStatusTracking(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) taskID := "task-123456" statusKey := constants.RedisTaskStatusKey(taskID) @@ -202,30 +202,30 @@ func TestTaskStatusTracking(t *testing.T) { statuses := []string{"pending", "processing", "completed"} for _, status := range statuses { - err := redisClient.Set(ctx, statusKey, status, 7*24*time.Hour).Err() + err := rdb.Set(ctx, statusKey, status, 7*24*time.Hour).Err() require.NoError(t, err) // 读取状态 - result, err := redisClient.Get(ctx, statusKey).Result() + result, err := rdb.Get(ctx, statusKey).Result() require.NoError(t, err) assert.Equal(t, status, result) } // 验证 TTL - ttl, err := redisClient.TTL(ctx, statusKey).Result() + ttl, err := rdb.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{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", diff --git a/tests/testutils/db.go b/tests/testutils/db.go new file mode 100644 index 0000000..f833db0 --- /dev/null +++ b/tests/testutils/db.go @@ -0,0 +1,223 @@ +package testutils + +import ( + "context" + "fmt" + "sync" + "testing" + + "github.com/redis/go-redis/v9" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm.io/gorm/logger" + + "github.com/break/junhong_cmp_fiber/internal/model" +) + +// 全局单例数据库和 Redis 连接 +// 使用 sync.Once 确保整个测试套件只创建一次连接,显著提升测试性能 +var ( + testDBOnce sync.Once + testDB *gorm.DB + testDBInitErr error + + testRedisOnce sync.Once + testRedis *redis.Client + testRedisInitErr error +) + +// 测试数据库配置 +// TODO: 未来可以从环境变量或配置文件加载 +const ( + testDBDSN = "host=cxd.whcxd.cn port=16159 user=erp_pgsql password=erp_2025 dbname=junhong_cmp_test sslmode=disable TimeZone=Asia/Shanghai" + testRedisAddr = "cxd.whcxd.cn:16299" + testRedisPasswd = "cpNbWtAaqgo1YJmbMp3h" + testRedisDB = 15 +) + +// GetTestDB 获取全局单例测试数据库连接 +// +// 特点: +// - 使用 sync.Once 确保整个测试套件只创建一次连接 +// - AutoMigrate 只在首次连接时执行一次 +// - 连接失败会跳过测试(不是致命错误) +// +// 用法: +// +// func TestXxx(t *testing.T) { +// db := testutils.GetTestDB(t) +// // db 是全局共享的连接,不要直接修改其状态 +// // 如需事务隔离,使用 NewTestTransaction(t) +// } +func GetTestDB(t *testing.T) *gorm.DB { + t.Helper() + + testDBOnce.Do(func() { + var err error + testDB, err = gorm.Open(postgres.Open(testDBDSN), &gorm.Config{ + Logger: logger.Default.LogMode(logger.Silent), + }) + if err != nil { + testDBInitErr = fmt.Errorf("无法连接测试数据库: %w", err) + return + } + + // AutoMigrate 只执行一次(幂等操作,但耗时约 100ms) + err = testDB.AutoMigrate( + &model.Account{}, + &model.Role{}, + &model.Permission{}, + &model.AccountRole{}, + &model.RolePermission{}, + &model.Shop{}, + &model.Enterprise{}, + &model.PersonalCustomer{}, + ) + if err != nil { + testDBInitErr = fmt.Errorf("数据库迁移失败: %w", err) + return + } + }) + + if testDBInitErr != nil { + t.Skipf("跳过测试:%v", testDBInitErr) + } + + return testDB +} + +// GetTestRedis 获取全局单例 Redis 连接 +// +// 特点: +// - 使用 sync.Once 确保整个测试套件只创建一次连接 +// - 连接失败会跳过测试(不是致命错误) +// +// 用法: +// +// func TestXxx(t *testing.T) { +// rdb := testutils.GetTestRedis(t) +// // rdb 是全局共享的连接 +// // 使用 CleanTestRedisKeys(t) 自动清理测试相关的 Redis 键 +// } +func GetTestRedis(t *testing.T) *redis.Client { + t.Helper() + + testRedisOnce.Do(func() { + testRedis = redis.NewClient(&redis.Options{ + Addr: testRedisAddr, + Password: testRedisPasswd, + DB: testRedisDB, + }) + + ctx := context.Background() + if err := testRedis.Ping(ctx).Err(); err != nil { + testRedisInitErr = fmt.Errorf("无法连接 Redis: %w", err) + return + } + }) + + if testRedisInitErr != nil { + t.Skipf("跳过测试:%v", testRedisInitErr) + } + + return testRedis +} + +// NewTestTransaction 创建测试事务,自动在测试结束时回滚 +// +// 特点: +// - 每个测试用例获得独立的事务,互不干扰 +// - 使用 t.Cleanup() 确保即使测试 panic 也能回滚 +// - 回滚后数据库状态与测试前完全一致 +// +// 用法: +// +// func TestXxx(t *testing.T) { +// tx := testutils.NewTestTransaction(t) +// // 所有数据库操作使用 tx 而非 db +// store := postgres.NewXxxStore(tx, rdb) +// // 测试结束后自动回滚,无需手动清理 +// } +// +// 注意: +// - 不要在子测试(t.Run)中调用此函数,因为子测试可能并行执行 +// - 如需在子测试中使用数据库,应在父测试中创建事务并传递 +func NewTestTransaction(t *testing.T) *gorm.DB { + t.Helper() + + db := GetTestDB(t) + tx := db.Begin() + if tx.Error != nil { + t.Fatalf("开启测试事务失败: %v", tx.Error) + } + + // 使用 t.Cleanup() 确保测试结束时自动回滚 + // 即使测试 panic 也能执行清理 + t.Cleanup(func() { + tx.Rollback() + }) + + return tx +} + +// CleanTestRedisKeys 清理当前测试的 Redis 键 +// +// 特点: +// - 使用测试名称作为键前缀,格式: test:{TestName}:* +// - 测试开始时清理已有键(防止脏数据) +// - 使用 t.Cleanup() 确保测试结束时自动清理 +// +// 用法: +// +// func TestXxx(t *testing.T) { +// rdb := testutils.GetTestRedis(t) +// testutils.CleanTestRedisKeys(t, rdb) +// // Redis 键使用测试专用前缀: test:TestXxx:your_key +// } +// +// 键命名规范: +// - 测试中创建的键应使用 GetTestRedisKeyPrefix(t) 作为前缀 +// - 例如: test:TestShopStore_Create:cache:shop:1 +func CleanTestRedisKeys(t *testing.T, rdb *redis.Client) { + t.Helper() + + ctx := context.Background() + testPrefix := GetTestRedisKeyPrefix(t) + + // 测试开始前清理已有键 + cleanKeys(ctx, rdb, testPrefix) + + // 测试结束时自动清理 + t.Cleanup(func() { + cleanKeys(ctx, rdb, testPrefix) + }) +} + +// GetTestRedisKeyPrefix 获取当前测试的 Redis 键前缀 +// +// 返回格式: test:{TestName}: +// 用于在测试中创建带前缀的 Redis 键,确保键不会与其他测试冲突 +// +// 用法: +// +// func TestXxx(t *testing.T) { +// prefix := testutils.GetTestRedisKeyPrefix(t) +// key := prefix + "my_cache_key" +// // key = "test:TestXxx:my_cache_key" +// } +func GetTestRedisKeyPrefix(t *testing.T) string { + t.Helper() + return fmt.Sprintf("test:%s:", t.Name()) +} + +// cleanKeys 清理匹配前缀的所有 Redis 键 +func cleanKeys(ctx context.Context, rdb *redis.Client, prefix string) { + keys, err := rdb.Keys(ctx, prefix+"*").Result() + if err != nil { + // 忽略 Redis 错误,不影响测试 + return + } + if len(keys) > 0 { + rdb.Del(ctx, keys...) + } +} diff --git a/tests/testutils/helpers.go b/tests/testutils/helpers.go index 0964192..a827a5c 100644 --- a/tests/testutils/helpers.go +++ b/tests/testutils/helpers.go @@ -3,35 +3,15 @@ package testutils import ( "path/filepath" "runtime" - "testing" - - "gorm.io/gorm" ) -// SetupTestDBWithStore 设置测试数据库并返回 AccountStore 和 cleanup 函数 -// 用于需要 store 接口的集成测试 -func SetupTestDBWithStore(t *testing.T) (*gorm.DB, func()) { - t.Helper() - - db, redisClient := SetupTestDB(t) - - cleanup := func() { - TeardownTestDB(t, db, redisClient) - } - - return db, cleanup -} - // GetMigrationsPath 获取数据库迁移文件的路径 -// 返回项目根目录下的 migrations 目录路径 func GetMigrationsPath() string { - // 获取当前文件路径 _, filename, _, ok := runtime.Caller(0) if !ok { panic("无法获取当前文件路径") } - // 从 tests/testutils/helpers.go 向上两级到项目根目录 projectRoot := filepath.Join(filepath.Dir(filename), "..", "..") migrationsPath := filepath.Join(projectRoot, "migrations") diff --git a/tests/testutils/setup.go b/tests/testutils/setup.go index 3254060..ef98298 100644 --- a/tests/testutils/setup.go +++ b/tests/testutils/setup.go @@ -1,87 +1,10 @@ package testutils import ( - "context" "fmt" - "testing" "time" - - "github.com/redis/go-redis/v9" - "gorm.io/driver/postgres" - "gorm.io/gorm" - "gorm.io/gorm/logger" - - "github.com/break/junhong_cmp_fiber/internal/model" ) -// SetupTestDB 设置测试数据库和 Redis(使用事务) -func SetupTestDB(t *testing.T) (*gorm.DB, *redis.Client) { - t.Helper() - - // 连接测试数据库(使用远程数据库) - dsn := "host=cxd.whcxd.cn port=16159 user=erp_pgsql password=erp_2025 dbname=junhong_cmp_test sslmode=disable TimeZone=Asia/Shanghai" - db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ - Logger: logger.Default.LogMode(logger.Silent), - }) - if err != nil { - t.Skipf("跳过测试:无法连接测试数据库: %v", err) - } - - err = db.AutoMigrate( - &model.Account{}, - &model.Role{}, - &model.Permission{}, - &model.AccountRole{}, - &model.RolePermission{}, - &model.Shop{}, - &model.Enterprise{}, - &model.PersonalCustomer{}, - ) - if err != nil { - t.Fatalf("数据库迁移失败: %v", err) - } - - txDB := db.Begin() - if txDB.Error != nil { - t.Fatalf("开启事务失败: %v", txDB.Error) - } - - redisClient := redis.NewClient(&redis.Options{ - Addr: "cxd.whcxd.cn:16299", - Password: "cpNbWtAaqgo1YJmbMp3h", - DB: 15, - }) - - ctx := context.Background() - if err := redisClient.Ping(ctx).Err(); err != nil { - t.Skipf("跳过测试:无法连接 Redis: %v", err) - } - - testPrefix := fmt.Sprintf("test:%s:", t.Name()) - keys, _ := redisClient.Keys(ctx, testPrefix+"*").Result() - if len(keys) > 0 { - redisClient.Del(ctx, keys...) - } - - return txDB, redisClient -} - -// TeardownTestDB 清理测试数据库(回滚事务) -func TeardownTestDB(t *testing.T, db *gorm.DB, redisClient *redis.Client) { - t.Helper() - - ctx := context.Background() - testPrefix := fmt.Sprintf("test:%s:", t.Name()) - keys, _ := redisClient.Keys(ctx, testPrefix+"*").Result() - if len(keys) > 0 { - redisClient.Del(ctx, keys...) - } - - db.Rollback() - - _ = redisClient.Close() -} - // GenerateUsername 生成测试用户名 func GenerateUsername(prefix string, index int) string { return fmt.Sprintf("%s_%d", prefix, index) diff --git a/tests/unit/account_model_test.go b/tests/unit/account_model_test.go index ca21986..309550b 100644 --- a/tests/unit/account_model_test.go +++ b/tests/unit/account_model_test.go @@ -15,10 +15,11 @@ import ( // TestAccountModel_Create 测试创建账号 func TestAccountModel_Create(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewAccountStore(db, redisClient) + store := postgres.NewAccountStore(tx, rdb) ctx := context.Background() t.Run("创建 root 账号", func(t *testing.T) { @@ -59,10 +60,11 @@ func TestAccountModel_Create(t *testing.T) { // TestAccountModel_GetByID 测试根据 ID 查询账号 func TestAccountModel_GetByID(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewAccountStore(db, redisClient) + store := postgres.NewAccountStore(tx, rdb) ctx := context.Background() // 创建测试账号 @@ -92,10 +94,11 @@ func TestAccountModel_GetByID(t *testing.T) { // TestAccountModel_GetByUsername 测试根据用户名查询账号 func TestAccountModel_GetByUsername(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewAccountStore(db, redisClient) + store := postgres.NewAccountStore(tx, rdb) ctx := context.Background() // 创建测试账号 @@ -123,10 +126,11 @@ func TestAccountModel_GetByUsername(t *testing.T) { // TestAccountModel_GetByPhone 测试根据手机号查询账号 func TestAccountModel_GetByPhone(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewAccountStore(db, redisClient) + store := postgres.NewAccountStore(tx, rdb) ctx := context.Background() // 创建测试账号 @@ -154,10 +158,11 @@ func TestAccountModel_GetByPhone(t *testing.T) { // TestAccountModel_Update 测试更新账号 func TestAccountModel_Update(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewAccountStore(db, redisClient) + store := postgres.NewAccountStore(tx, rdb) ctx := context.Background() // 创建测试账号 @@ -187,10 +192,11 @@ func TestAccountModel_Update(t *testing.T) { // TestAccountModel_List 测试查询账号列表 func TestAccountModel_List(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewAccountStore(db, redisClient) + store := postgres.NewAccountStore(tx, rdb) ctx := context.Background() // 创建多个测试账号 @@ -227,10 +233,11 @@ func TestAccountModel_List(t *testing.T) { // TestAccountModel_UniqueConstraints 测试唯一约束 func TestAccountModel_UniqueConstraints(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewAccountStore(db, redisClient) + store := postgres.NewAccountStore(tx, rdb) ctx := context.Background() // 创建测试账号 diff --git a/tests/unit/commission_withdrawal_service_test.go b/tests/unit/commission_withdrawal_service_test.go index 8137bfc..b16d690 100644 --- a/tests/unit/commission_withdrawal_service_test.go +++ b/tests/unit/commission_withdrawal_service_test.go @@ -22,16 +22,17 @@ func createWithdrawalTestContext(userID uint) context.Context { } func TestCommissionWithdrawalService_ListWithdrawalRequests(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) - walletStore := postgres.NewWalletStore(db, redisClient) - walletTransactionStore := postgres.NewWalletTransactionStore(db, redisClient) - commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) + walletStore := postgres.NewWalletStore(tx, rdb) + walletTransactionStore := postgres.NewWalletTransactionStore(tx, rdb) + commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(tx, rdb) - service := commission_withdrawal.New(db, shopStore, accountStore, walletStore, walletTransactionStore, commissionWithdrawalRequestStore) + service := commission_withdrawal.New(tx, shopStore, accountStore, walletStore, walletTransactionStore, commissionWithdrawalRequestStore) t.Run("查询提现申请列表-空结果", func(t *testing.T) { ctx := createWithdrawalTestContext(1) @@ -79,16 +80,17 @@ func TestCommissionWithdrawalService_ListWithdrawalRequests(t *testing.T) { } func TestCommissionWithdrawalService_Approve(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) - walletStore := postgres.NewWalletStore(db, redisClient) - walletTransactionStore := postgres.NewWalletTransactionStore(db, redisClient) - commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) + walletStore := postgres.NewWalletStore(tx, rdb) + walletTransactionStore := postgres.NewWalletTransactionStore(tx, rdb) + commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(tx, rdb) - service := commission_withdrawal.New(db, shopStore, accountStore, walletStore, walletTransactionStore, commissionWithdrawalRequestStore) + service := commission_withdrawal.New(tx, shopStore, accountStore, walletStore, walletTransactionStore, commissionWithdrawalRequestStore) t.Run("审批不存在的提现申请应失败", func(t *testing.T) { ctx := createWithdrawalTestContext(1) @@ -103,16 +105,17 @@ func TestCommissionWithdrawalService_Approve(t *testing.T) { } func TestCommissionWithdrawalService_Reject(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) - walletStore := postgres.NewWalletStore(db, redisClient) - walletTransactionStore := postgres.NewWalletTransactionStore(db, redisClient) - commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) + walletStore := postgres.NewWalletStore(tx, rdb) + walletTransactionStore := postgres.NewWalletTransactionStore(tx, rdb) + commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(tx, rdb) - service := commission_withdrawal.New(db, shopStore, accountStore, walletStore, walletTransactionStore, commissionWithdrawalRequestStore) + service := commission_withdrawal.New(tx, shopStore, accountStore, walletStore, walletTransactionStore, commissionWithdrawalRequestStore) t.Run("拒绝不存在的提现申请应失败", func(t *testing.T) { ctx := createWithdrawalTestContext(1) diff --git a/tests/unit/commission_withdrawal_setting_service_test.go b/tests/unit/commission_withdrawal_setting_service_test.go index 6d9a646..91b64f4 100644 --- a/tests/unit/commission_withdrawal_setting_service_test.go +++ b/tests/unit/commission_withdrawal_setting_service_test.go @@ -22,13 +22,14 @@ func createWithdrawalSettingTestContext(userID uint) context.Context { } func TestCommissionWithdrawalSettingService_Create(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - settingStore := postgres.NewCommissionWithdrawalSettingStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + settingStore := postgres.NewCommissionWithdrawalSettingStore(tx, rdb) - service := commission_withdrawal_setting.New(db, accountStore, settingStore) + service := commission_withdrawal_setting.New(tx, accountStore, settingStore) t.Run("新增提现配置", func(t *testing.T) { ctx := createWithdrawalSettingTestContext(1) @@ -63,13 +64,14 @@ func TestCommissionWithdrawalSettingService_Create(t *testing.T) { } func TestCommissionWithdrawalSettingService_ConfigSwitch(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - settingStore := postgres.NewCommissionWithdrawalSettingStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + settingStore := postgres.NewCommissionWithdrawalSettingStore(tx, rdb) - service := commission_withdrawal_setting.New(db, accountStore, settingStore) + service := commission_withdrawal_setting.New(tx, accountStore, settingStore) t.Run("配置切换-旧配置自动失效", func(t *testing.T) { ctx := createWithdrawalSettingTestContext(1) @@ -103,13 +105,14 @@ func TestCommissionWithdrawalSettingService_ConfigSwitch(t *testing.T) { } func TestCommissionWithdrawalSettingService_List(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - settingStore := postgres.NewCommissionWithdrawalSettingStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + settingStore := postgres.NewCommissionWithdrawalSettingStore(tx, rdb) - service := commission_withdrawal_setting.New(db, accountStore, settingStore) + service := commission_withdrawal_setting.New(tx, accountStore, settingStore) t.Run("查询配置列表-空结果", func(t *testing.T) { ctx := createWithdrawalSettingTestContext(1) @@ -152,13 +155,14 @@ func TestCommissionWithdrawalSettingService_List(t *testing.T) { } func TestCommissionWithdrawalSettingService_GetCurrent(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - settingStore := postgres.NewCommissionWithdrawalSettingStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + settingStore := postgres.NewCommissionWithdrawalSettingStore(tx, rdb) - service := commission_withdrawal_setting.New(db, accountStore, settingStore) + service := commission_withdrawal_setting.New(tx, accountStore, settingStore) t.Run("获取当前配置-无配置时应返回错误", func(t *testing.T) { ctx := createWithdrawalSettingTestContext(1) diff --git a/tests/unit/customer_account_service_test.go b/tests/unit/customer_account_service_test.go index 47147fb..3709f07 100644 --- a/tests/unit/customer_account_service_test.go +++ b/tests/unit/customer_account_service_test.go @@ -23,14 +23,15 @@ func createCustomerAccountTestContext(userID uint) context.Context { } func TestCustomerAccountService_List(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) - service := customer_account.New(db, accountStore, shopStore, enterpriseStore) + service := customer_account.New(tx, accountStore, shopStore, enterpriseStore) t.Run("查询账号列表-空结果", func(t *testing.T) { ctx := createCustomerAccountTestContext(1) @@ -59,7 +60,7 @@ func TestCustomerAccountService_List(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) createReq := &dto.CreateCustomerAccountReq{ @@ -96,7 +97,7 @@ func TestCustomerAccountService_List(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) createReq := &dto.CreateCustomerAccountReq{ @@ -122,14 +123,15 @@ func TestCustomerAccountService_List(t *testing.T) { } func TestCustomerAccountService_Create(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) - service := customer_account.New(db, accountStore, shopStore, enterpriseStore) + service := customer_account.New(tx, accountStore, shopStore, enterpriseStore) t.Run("新增代理商账号", func(t *testing.T) { ctx := createCustomerAccountTestContext(1) @@ -144,7 +146,7 @@ func TestCustomerAccountService_Create(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) req := &dto.CreateCustomerAccountReq{ @@ -176,7 +178,7 @@ func TestCustomerAccountService_Create(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) req1 := &dto.CreateCustomerAccountReq{ @@ -228,14 +230,15 @@ func TestCustomerAccountService_Create(t *testing.T) { } func TestCustomerAccountService_Update(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) - service := customer_account.New(db, accountStore, shopStore, enterpriseStore) + service := customer_account.New(tx, accountStore, shopStore, enterpriseStore) t.Run("编辑账号", func(t *testing.T) { ctx := createCustomerAccountTestContext(1) @@ -250,7 +253,7 @@ func TestCustomerAccountService_Update(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) createReq := &dto.CreateCustomerAccountReq{ @@ -286,14 +289,15 @@ func TestCustomerAccountService_Update(t *testing.T) { } func TestCustomerAccountService_UpdatePassword(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) - service := customer_account.New(db, accountStore, shopStore, enterpriseStore) + service := customer_account.New(tx, accountStore, shopStore, enterpriseStore) t.Run("修改密码", func(t *testing.T) { ctx := createCustomerAccountTestContext(1) @@ -308,7 +312,7 @@ func TestCustomerAccountService_UpdatePassword(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) createReq := &dto.CreateCustomerAccountReq{ @@ -324,7 +328,7 @@ func TestCustomerAccountService_UpdatePassword(t *testing.T) { require.NoError(t, err) var account model.Account - err = db.First(&account, created.ID).Error + err = tx.First(&account, created.ID).Error require.NoError(t, err) assert.NotEqual(t, "OldPass123", account.Password) assert.NotEqual(t, "NewPass456", account.Password) @@ -339,14 +343,15 @@ func TestCustomerAccountService_UpdatePassword(t *testing.T) { } func TestCustomerAccountService_UpdateStatus(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) - service := customer_account.New(db, accountStore, shopStore, enterpriseStore) + service := customer_account.New(tx, accountStore, shopStore, enterpriseStore) t.Run("禁用账号", func(t *testing.T) { ctx := createCustomerAccountTestContext(1) @@ -361,7 +366,7 @@ func TestCustomerAccountService_UpdateStatus(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) createReq := &dto.CreateCustomerAccountReq{ @@ -377,7 +382,7 @@ func TestCustomerAccountService_UpdateStatus(t *testing.T) { require.NoError(t, err) var account model.Account - err = db.First(&account, created.ID).Error + err = tx.First(&account, created.ID).Error require.NoError(t, err) assert.Equal(t, constants.StatusDisabled, account.Status) }) @@ -395,7 +400,7 @@ func TestCustomerAccountService_UpdateStatus(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) createReq := &dto.CreateCustomerAccountReq{ @@ -414,7 +419,7 @@ func TestCustomerAccountService_UpdateStatus(t *testing.T) { require.NoError(t, err) var account model.Account - err = db.First(&account, created.ID).Error + err = tx.First(&account, created.ID).Error require.NoError(t, err) assert.Equal(t, constants.StatusEnabled, account.Status) }) diff --git a/tests/unit/enterprise_card_service_test.go b/tests/unit/enterprise_card_service_test.go index 7ec8a7f..c887049 100644 --- a/tests/unit/enterprise_card_service_test.go +++ b/tests/unit/enterprise_card_service_test.go @@ -24,13 +24,14 @@ func createEnterpriseCardTestContext(userID uint, shopID uint) context.Context { } func TestEnterpriseCardService_AllocateCardsPreview(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) - enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(db, redisClient) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) + enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb) - service := enterprise_card.New(db, enterpriseStore, enterpriseCardAuthStore) + service := enterprise_card.New(tx, enterpriseStore, enterpriseCardAuthStore) t.Run("授权预检-企业不存在应失败", func(t *testing.T) { ctx := createEnterpriseCardTestContext(1, 1) @@ -64,7 +65,7 @@ func TestEnterpriseCardService_AllocateCardsPreview(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) req := &dto.AllocateCardsPreviewReq{ @@ -87,7 +88,7 @@ func TestEnterpriseCardService_AllocateCardsPreview(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) req := &dto.AllocateCardsPreviewReq{ @@ -112,7 +113,7 @@ func TestEnterpriseCardService_AllocateCardsPreview(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) shopID := uint(1) @@ -122,7 +123,7 @@ func TestEnterpriseCardService_AllocateCardsPreview(t *testing.T) { Status: 1, ShopID: &shopID, } - err = db.Create(card).Error + err = tx.Create(card).Error require.NoError(t, err) req := &dto.AllocateCardsPreviewReq{ @@ -138,13 +139,14 @@ func TestEnterpriseCardService_AllocateCardsPreview(t *testing.T) { } func TestEnterpriseCardService_AllocateCards(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) - enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(db, redisClient) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) + enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb) - service := enterprise_card.New(db, enterpriseStore, enterpriseCardAuthStore) + service := enterprise_card.New(tx, enterpriseStore, enterpriseCardAuthStore) t.Run("授权卡-企业不存在应失败", func(t *testing.T) { ctx := createEnterpriseCardTestContext(1, 1) @@ -167,7 +169,7 @@ func TestEnterpriseCardService_AllocateCards(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) shopID := uint(1) @@ -177,7 +179,7 @@ func TestEnterpriseCardService_AllocateCards(t *testing.T) { Status: 1, ShopID: &shopID, } - err = db.Create(card).Error + err = tx.Create(card).Error require.NoError(t, err) req := &dto.AllocateCardsReq{ @@ -200,7 +202,7 @@ func TestEnterpriseCardService_AllocateCards(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) shopID := uint(1) @@ -210,7 +212,7 @@ func TestEnterpriseCardService_AllocateCards(t *testing.T) { Status: 1, ShopID: &shopID, } - err = db.Create(card).Error + err = tx.Create(card).Error require.NoError(t, err) req := &dto.AllocateCardsReq{ @@ -224,7 +226,7 @@ func TestEnterpriseCardService_AllocateCards(t *testing.T) { require.NoError(t, err) var count int64 - db.Model(&model.EnterpriseCardAuthorization{}). + tx.Model(&model.EnterpriseCardAuthorization{}). Where("enterprise_id = ? AND iot_card_id = ?", ent.ID, card.ID). Count(&count) assert.Equal(t, int64(1), count) @@ -232,13 +234,14 @@ func TestEnterpriseCardService_AllocateCards(t *testing.T) { } func TestEnterpriseCardService_RecallCards(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) - enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(db, redisClient) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) + enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb) - service := enterprise_card.New(db, enterpriseStore, enterpriseCardAuthStore) + service := enterprise_card.New(tx, enterpriseStore, enterpriseCardAuthStore) t.Run("回收授权-企业不存在应失败", func(t *testing.T) { ctx := createEnterpriseCardTestContext(1, 1) @@ -261,7 +264,7 @@ func TestEnterpriseCardService_RecallCards(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) shopID := uint(1) @@ -271,7 +274,7 @@ func TestEnterpriseCardService_RecallCards(t *testing.T) { Status: 1, ShopID: &shopID, } - err = db.Create(card).Error + err = tx.Create(card).Error require.NoError(t, err) req := &dto.RecallCardsReq{ @@ -294,7 +297,7 @@ func TestEnterpriseCardService_RecallCards(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) shopID := uint(1) @@ -304,7 +307,7 @@ func TestEnterpriseCardService_RecallCards(t *testing.T) { Status: 1, ShopID: &shopID, } - err = db.Create(card).Error + err = tx.Create(card).Error require.NoError(t, err) allocReq := &dto.AllocateCardsReq{ @@ -324,13 +327,14 @@ func TestEnterpriseCardService_RecallCards(t *testing.T) { } func TestEnterpriseCardService_ListCards(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) - enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(db, redisClient) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) + enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb) - service := enterprise_card.New(db, enterpriseStore, enterpriseCardAuthStore) + service := enterprise_card.New(tx, enterpriseStore, enterpriseCardAuthStore) t.Run("查询企业卡列表-企业不存在应失败", func(t *testing.T) { ctx := createEnterpriseCardTestContext(1, 1) @@ -354,7 +358,7 @@ func TestEnterpriseCardService_ListCards(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) req := &dto.EnterpriseCardListReq{ @@ -378,7 +382,7 @@ func TestEnterpriseCardService_ListCards(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) shopID := uint(1) @@ -388,7 +392,7 @@ func TestEnterpriseCardService_ListCards(t *testing.T) { Status: 1, ShopID: &shopID, } - err = db.Create(card).Error + err = tx.Create(card).Error require.NoError(t, err) allocReq := &dto.AllocateCardsReq{ @@ -420,7 +424,7 @@ func TestEnterpriseCardService_ListCards(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) shopID := uint(1) @@ -430,7 +434,7 @@ func TestEnterpriseCardService_ListCards(t *testing.T) { Status: 1, ShopID: &shopID, } - err = db.Create(card).Error + err = tx.Create(card).Error require.NoError(t, err) allocReq := &dto.AllocateCardsReq{ @@ -453,13 +457,14 @@ func TestEnterpriseCardService_ListCards(t *testing.T) { } func TestEnterpriseCardService_SuspendAndResumeCard(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) - enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(db, redisClient) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) + enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb) - service := enterprise_card.New(db, enterpriseStore, enterpriseCardAuthStore) + service := enterprise_card.New(tx, enterpriseStore, enterpriseCardAuthStore) t.Run("停机-未授权的卡应失败", func(t *testing.T) { ctx := createEnterpriseCardTestContext(1, 1) @@ -471,7 +476,7 @@ func TestEnterpriseCardService_SuspendAndResumeCard(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) shopID := uint(1) @@ -481,7 +486,7 @@ func TestEnterpriseCardService_SuspendAndResumeCard(t *testing.T) { Status: 1, ShopID: &shopID, } - err = db.Create(card).Error + err = tx.Create(card).Error require.NoError(t, err) err = service.SuspendCard(ctx, ent.ID, card.ID) @@ -498,7 +503,7 @@ func TestEnterpriseCardService_SuspendAndResumeCard(t *testing.T) { } ent.Creator = 1 ent.Updater = 1 - err := db.Create(ent).Error + err := tx.Create(ent).Error require.NoError(t, err) shopID := uint(1) @@ -509,7 +514,7 @@ func TestEnterpriseCardService_SuspendAndResumeCard(t *testing.T) { NetworkStatus: 1, ShopID: &shopID, } - err = db.Create(card).Error + err = tx.Create(card).Error require.NoError(t, err) allocReq := &dto.AllocateCardsReq{ @@ -522,14 +527,14 @@ func TestEnterpriseCardService_SuspendAndResumeCard(t *testing.T) { require.NoError(t, err) var suspendedCard model.IotCard - db.First(&suspendedCard, card.ID) + tx.First(&suspendedCard, card.ID) assert.Equal(t, 0, suspendedCard.NetworkStatus) err = service.ResumeCard(ctx, ent.ID, card.ID) require.NoError(t, err) var resumedCard model.IotCard - db.First(&resumedCard, card.ID) + tx.First(&resumedCard, card.ID) assert.Equal(t, 1, resumedCard.NetworkStatus) }) } diff --git a/tests/unit/enterprise_service_test.go b/tests/unit/enterprise_service_test.go index 90f31b5..302848c 100644 --- a/tests/unit/enterprise_service_test.go +++ b/tests/unit/enterprise_service_test.go @@ -23,14 +23,15 @@ func createEnterpriseTestContext(userID uint) context.Context { } func TestEnterpriseService_Create(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) - service := enterprise.New(db, enterpriseStore, shopStore, accountStore) + service := enterprise.New(tx, enterpriseStore, shopStore, accountStore) t.Run("创建企业-含账号创建", func(t *testing.T) { ctx := createEnterpriseTestContext(1) @@ -123,14 +124,15 @@ func TestEnterpriseService_Create(t *testing.T) { } func TestEnterpriseService_Update(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) - service := enterprise.New(db, enterpriseStore, shopStore, accountStore) + service := enterprise.New(tx, enterpriseStore, shopStore, accountStore) t.Run("编辑企业", func(t *testing.T) { ctx := createEnterpriseTestContext(1) @@ -173,14 +175,15 @@ func TestEnterpriseService_Update(t *testing.T) { } func TestEnterpriseService_UpdateStatus(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) - service := enterprise.New(db, enterpriseStore, shopStore, accountStore) + service := enterprise.New(tx, enterpriseStore, shopStore, accountStore) t.Run("禁用企业-账号同步禁用", func(t *testing.T) { ctx := createEnterpriseTestContext(1) @@ -204,7 +207,7 @@ func TestEnterpriseService_UpdateStatus(t *testing.T) { assert.Equal(t, constants.StatusDisabled, ent.Status) var account model.Account - err = db.Where("enterprise_id = ?", createResult.Enterprise.ID).First(&account).Error + err = tx.Where("enterprise_id = ?", createResult.Enterprise.ID).First(&account).Error require.NoError(t, err) assert.Equal(t, constants.StatusDisabled, account.Status) }) @@ -234,7 +237,7 @@ func TestEnterpriseService_UpdateStatus(t *testing.T) { assert.Equal(t, constants.StatusEnabled, ent.Status) var account model.Account - err = db.Where("enterprise_id = ?", createResult.Enterprise.ID).First(&account).Error + err = tx.Where("enterprise_id = ?", createResult.Enterprise.ID).First(&account).Error require.NoError(t, err) assert.Equal(t, constants.StatusEnabled, account.Status) }) @@ -248,14 +251,15 @@ func TestEnterpriseService_UpdateStatus(t *testing.T) { } func TestEnterpriseService_UpdatePassword(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) - service := enterprise.New(db, enterpriseStore, shopStore, accountStore) + service := enterprise.New(tx, enterpriseStore, shopStore, accountStore) t.Run("修改企业账号密码", func(t *testing.T) { ctx := createEnterpriseTestContext(1) @@ -275,7 +279,7 @@ func TestEnterpriseService_UpdatePassword(t *testing.T) { require.NoError(t, err) var account model.Account - err = db.Where("enterprise_id = ?", createResult.Enterprise.ID).First(&account).Error + err = tx.Where("enterprise_id = ?", createResult.Enterprise.ID).First(&account).Error require.NoError(t, err) assert.NotEqual(t, "OldPass123", account.Password) assert.NotEqual(t, "NewPass456", account.Password) @@ -290,14 +294,15 @@ func TestEnterpriseService_UpdatePassword(t *testing.T) { } func TestEnterpriseService_List(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - enterpriseStore := postgres.NewEnterpriseStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + enterpriseStore := postgres.NewEnterpriseStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) - service := enterprise.New(db, enterpriseStore, shopStore, accountStore) + service := enterprise.New(tx, enterpriseStore, shopStore, accountStore) t.Run("查询企业列表-空结果", func(t *testing.T) { ctx := createEnterpriseTestContext(1) diff --git a/tests/unit/enterprise_store_test.go b/tests/unit/enterprise_store_test.go index 401b8fe..916f6a2 100644 --- a/tests/unit/enterprise_store_test.go +++ b/tests/unit/enterprise_store_test.go @@ -15,10 +15,11 @@ import ( // TestEnterpriseStore_Create 测试创建企业 func TestEnterpriseStore_Create(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewEnterpriseStore(db, redisClient) + store := postgres.NewEnterpriseStore(tx, rdb) ctx := context.Background() tests := []struct { @@ -79,10 +80,11 @@ func TestEnterpriseStore_Create(t *testing.T) { // TestEnterpriseStore_GetByID 测试根据 ID 查询企业 func TestEnterpriseStore_GetByID(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewEnterpriseStore(db, redisClient) + store := postgres.NewEnterpriseStore(tx, rdb) ctx := context.Background() // 创建测试企业 @@ -118,10 +120,11 @@ func TestEnterpriseStore_GetByID(t *testing.T) { // TestEnterpriseStore_GetByCode 测试根据企业编号查询 func TestEnterpriseStore_GetByCode(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewEnterpriseStore(db, redisClient) + store := postgres.NewEnterpriseStore(tx, rdb) ctx := context.Background() // 创建测试企业 @@ -156,10 +159,11 @@ func TestEnterpriseStore_GetByCode(t *testing.T) { // TestEnterpriseStore_Update 测试更新企业 func TestEnterpriseStore_Update(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewEnterpriseStore(db, redisClient) + store := postgres.NewEnterpriseStore(tx, rdb) ctx := context.Background() // 创建测试企业 @@ -212,10 +216,11 @@ func TestEnterpriseStore_Update(t *testing.T) { // TestEnterpriseStore_Delete 测试软删除企业 func TestEnterpriseStore_Delete(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewEnterpriseStore(db, redisClient) + store := postgres.NewEnterpriseStore(tx, rdb) ctx := context.Background() // 创建测试企业 @@ -247,10 +252,11 @@ func TestEnterpriseStore_Delete(t *testing.T) { // TestEnterpriseStore_List 测试查询企业列表 func TestEnterpriseStore_List(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewEnterpriseStore(db, redisClient) + store := postgres.NewEnterpriseStore(tx, rdb) ctx := context.Background() // 创建多个测试企业 @@ -293,10 +299,11 @@ func TestEnterpriseStore_List(t *testing.T) { // TestEnterpriseStore_GetByOwnerShopID 测试根据归属店铺查询企业 func TestEnterpriseStore_GetByOwnerShopID(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewEnterpriseStore(db, redisClient) + store := postgres.NewEnterpriseStore(tx, rdb) ctx := context.Background() shopID1 := uint(100) @@ -364,10 +371,11 @@ func TestEnterpriseStore_GetByOwnerShopID(t *testing.T) { // TestEnterpriseStore_UniqueConstraints 测试唯一约束 func TestEnterpriseStore_UniqueConstraints(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewEnterpriseStore(db, redisClient) + store := postgres.NewEnterpriseStore(tx, rdb) ctx := context.Background() // 创建测试企业 diff --git a/tests/unit/my_commission_service_test.go b/tests/unit/my_commission_service_test.go index 7b10590..4ca0fe8 100644 --- a/tests/unit/my_commission_service_test.go +++ b/tests/unit/my_commission_service_test.go @@ -24,18 +24,19 @@ func createMyCommissionTestContext(userID uint, shopID uint, userType int) conte } func TestMyCommissionService_GetCommissionSummary(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - walletStore := postgres.NewWalletStore(db, redisClient) - commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(db, redisClient) - commissionWithdrawalSettingStore := postgres.NewCommissionWithdrawalSettingStore(db, redisClient) - commissionRecordStore := postgres.NewCommissionRecordStore(db, redisClient) - walletTransactionStore := postgres.NewWalletTransactionStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + walletStore := postgres.NewWalletStore(tx, rdb) + commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(tx, rdb) + commissionWithdrawalSettingStore := postgres.NewCommissionWithdrawalSettingStore(tx, rdb) + commissionRecordStore := postgres.NewCommissionRecordStore(tx, rdb) + walletTransactionStore := postgres.NewWalletTransactionStore(tx, rdb) service := my_commission.New( - db, shopStore, walletStore, + tx, shopStore, walletStore, commissionWithdrawalRequestStore, commissionWithdrawalSettingStore, commissionRecordStore, walletTransactionStore, ) @@ -51,7 +52,7 @@ func TestMyCommissionService_GetCommissionSummary(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) ctx := createMyCommissionTestContext(1, shop.ID, constants.UserTypeAgent) @@ -79,18 +80,19 @@ func TestMyCommissionService_GetCommissionSummary(t *testing.T) { } func TestMyCommissionService_CreateWithdrawalRequest(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - walletStore := postgres.NewWalletStore(db, redisClient) - commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(db, redisClient) - commissionWithdrawalSettingStore := postgres.NewCommissionWithdrawalSettingStore(db, redisClient) - commissionRecordStore := postgres.NewCommissionRecordStore(db, redisClient) - walletTransactionStore := postgres.NewWalletTransactionStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + walletStore := postgres.NewWalletStore(tx, rdb) + commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(tx, rdb) + commissionWithdrawalSettingStore := postgres.NewCommissionWithdrawalSettingStore(tx, rdb) + commissionRecordStore := postgres.NewCommissionRecordStore(tx, rdb) + walletTransactionStore := postgres.NewWalletTransactionStore(tx, rdb) service := my_commission.New( - db, shopStore, walletStore, + tx, shopStore, walletStore, commissionWithdrawalRequestStore, commissionWithdrawalSettingStore, commissionRecordStore, walletTransactionStore, ) @@ -106,7 +108,7 @@ func TestMyCommissionService_CreateWithdrawalRequest(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) ctx := createMyCommissionTestContext(1, shop.ID, constants.UserTypeAgent) @@ -133,7 +135,7 @@ func TestMyCommissionService_CreateWithdrawalRequest(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) setting := &model.CommissionWithdrawalSetting{ @@ -144,7 +146,7 @@ func TestMyCommissionService_CreateWithdrawalRequest(t *testing.T) { } setting.Creator = 1 setting.Updater = 1 - err = db.Create(setting).Error + err = tx.Create(setting).Error require.NoError(t, err) ctx := createMyCommissionTestContext(1, shop.ID, constants.UserTypeAgent) @@ -171,7 +173,7 @@ func TestMyCommissionService_CreateWithdrawalRequest(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) wallet := &model.Wallet{ @@ -180,7 +182,7 @@ func TestMyCommissionService_CreateWithdrawalRequest(t *testing.T) { WalletType: constants.WalletTypeCommission, Balance: 5000, } - err = db.Create(wallet).Error + err = tx.Create(wallet).Error require.NoError(t, err) ctx := createMyCommissionTestContext(1, shop.ID, constants.UserTypeAgent) @@ -212,18 +214,19 @@ func TestMyCommissionService_CreateWithdrawalRequest(t *testing.T) { } func TestMyCommissionService_ListMyWithdrawalRequests(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - walletStore := postgres.NewWalletStore(db, redisClient) - commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(db, redisClient) - commissionWithdrawalSettingStore := postgres.NewCommissionWithdrawalSettingStore(db, redisClient) - commissionRecordStore := postgres.NewCommissionRecordStore(db, redisClient) - walletTransactionStore := postgres.NewWalletTransactionStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + walletStore := postgres.NewWalletStore(tx, rdb) + commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(tx, rdb) + commissionWithdrawalSettingStore := postgres.NewCommissionWithdrawalSettingStore(tx, rdb) + commissionRecordStore := postgres.NewCommissionRecordStore(tx, rdb) + walletTransactionStore := postgres.NewWalletTransactionStore(tx, rdb) service := my_commission.New( - db, shopStore, walletStore, + tx, shopStore, walletStore, commissionWithdrawalRequestStore, commissionWithdrawalSettingStore, commissionRecordStore, walletTransactionStore, ) @@ -239,7 +242,7 @@ func TestMyCommissionService_ListMyWithdrawalRequests(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) ctx := createMyCommissionTestContext(1, shop.ID, constants.UserTypeAgent) @@ -266,7 +269,7 @@ func TestMyCommissionService_ListMyWithdrawalRequests(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) ctx := createMyCommissionTestContext(1, shop.ID, constants.UserTypeAgent) @@ -297,18 +300,19 @@ func TestMyCommissionService_ListMyWithdrawalRequests(t *testing.T) { } func TestMyCommissionService_ListMyCommissionRecords(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - walletStore := postgres.NewWalletStore(db, redisClient) - commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(db, redisClient) - commissionWithdrawalSettingStore := postgres.NewCommissionWithdrawalSettingStore(db, redisClient) - commissionRecordStore := postgres.NewCommissionRecordStore(db, redisClient) - walletTransactionStore := postgres.NewWalletTransactionStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + walletStore := postgres.NewWalletStore(tx, rdb) + commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(tx, rdb) + commissionWithdrawalSettingStore := postgres.NewCommissionWithdrawalSettingStore(tx, rdb) + commissionRecordStore := postgres.NewCommissionRecordStore(tx, rdb) + walletTransactionStore := postgres.NewWalletTransactionStore(tx, rdb) service := my_commission.New( - db, shopStore, walletStore, + tx, shopStore, walletStore, commissionWithdrawalRequestStore, commissionWithdrawalSettingStore, commissionRecordStore, walletTransactionStore, ) @@ -324,7 +328,7 @@ func TestMyCommissionService_ListMyCommissionRecords(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) ctx := createMyCommissionTestContext(1, shop.ID, constants.UserTypeAgent) @@ -351,7 +355,7 @@ func TestMyCommissionService_ListMyCommissionRecords(t *testing.T) { } shop.Creator = 1 shop.Updater = 1 - err := db.Create(shop).Error + err := tx.Create(shop).Error require.NoError(t, err) ctx := createMyCommissionTestContext(1, shop.ID, constants.UserTypeAgent) diff --git a/tests/unit/permission_cache_test.go b/tests/unit/permission_cache_test.go index 87e56aa..00d2714 100644 --- a/tests/unit/permission_cache_test.go +++ b/tests/unit/permission_cache_test.go @@ -17,16 +17,17 @@ import ( ) func TestPermissionCache_FirstCallMissSecondHit(t *testing.T) { - db, rdb := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, rdb) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) ctx := context.Background() - accountStore := postgres.NewAccountStore(db, rdb) - roleStore := postgres.NewRoleStore(db) - permStore := postgres.NewPermissionStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, rdb) - rolePermStore := postgres.NewRolePermissionStore(db, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) + roleStore := postgres.NewRoleStore(tx) + permStore := postgres.NewPermissionStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) permSvc := permission.New(permStore, accountRoleStore, rolePermStore, rdb) @@ -99,16 +100,17 @@ func TestPermissionCache_FirstCallMissSecondHit(t *testing.T) { } func TestPermissionCache_ExpiredAfter30Minutes(t *testing.T) { - db, rdb := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, rdb) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) ctx := context.Background() - accountStore := postgres.NewAccountStore(db, rdb) - roleStore := postgres.NewRoleStore(db) - permStore := postgres.NewPermissionStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, rdb) - rolePermStore := postgres.NewRolePermissionStore(db, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) + roleStore := postgres.NewRoleStore(tx) + permStore := postgres.NewPermissionStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) permSvc := permission.New(permStore, accountRoleStore, rolePermStore, rdb) diff --git a/tests/unit/permission_check_test.go b/tests/unit/permission_check_test.go index fe47606..c7af9b7 100644 --- a/tests/unit/permission_check_test.go +++ b/tests/unit/permission_check_test.go @@ -26,13 +26,14 @@ func createContextWithUserType(userID uint, userType int) context.Context { } func TestPermissionService_CheckPermission_SuperAdmin(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - permStore := postgres.NewPermissionStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) - rolePermStore := postgres.NewRolePermissionStore(db, redisClient) - service := permission.New(permStore, accountRoleStore, rolePermStore, redisClient) + permStore := postgres.NewPermissionStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) + service := permission.New(permStore, accountRoleStore, rolePermStore, rdb) t.Run("超级管理员自动拥有所有权限", func(t *testing.T) { ctx := createContextWithUserType(1, constants.UserTypeSuperAdmin) @@ -44,14 +45,15 @@ func TestPermissionService_CheckPermission_SuperAdmin(t *testing.T) { } func TestPermissionService_CheckPermission_NormalUser(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - permStore := postgres.NewPermissionStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) - rolePermStore := postgres.NewRolePermissionStore(db, redisClient) - roleStore := postgres.NewRoleStore(db) - service := permission.New(permStore, accountRoleStore, rolePermStore, redisClient) + permStore := postgres.NewPermissionStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) + roleStore := postgres.NewRoleStore(tx) + service := permission.New(permStore, accountRoleStore, rolePermStore, rdb) ctx := createContextWithUserType(100, constants.UserTypePlatform) @@ -164,13 +166,14 @@ func TestPermissionService_CheckPermission_NormalUser(t *testing.T) { } func TestPermissionService_CheckPermission_NoRole(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - permStore := postgres.NewPermissionStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) - rolePermStore := postgres.NewRolePermissionStore(db, redisClient) - service := permission.New(permStore, accountRoleStore, rolePermStore, redisClient) + permStore := postgres.NewPermissionStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) + service := permission.New(permStore, accountRoleStore, rolePermStore, rdb) t.Run("用户无角色应返回false", func(t *testing.T) { ctx := createContextWithUserType(200, constants.UserTypePlatform) @@ -182,14 +185,15 @@ func TestPermissionService_CheckPermission_NoRole(t *testing.T) { } func TestPermissionService_CheckPermission_RoleNoPermission(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - permStore := postgres.NewPermissionStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) - rolePermStore := postgres.NewRolePermissionStore(db, redisClient) - roleStore := postgres.NewRoleStore(db) - service := permission.New(permStore, accountRoleStore, rolePermStore, redisClient) + permStore := postgres.NewPermissionStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) + roleStore := postgres.NewRoleStore(tx) + service := permission.New(permStore, accountRoleStore, rolePermStore, rdb) ctx := createContextWithUserType(300, constants.UserTypePlatform) diff --git a/tests/unit/permission_platform_filter_test.go b/tests/unit/permission_platform_filter_test.go index 59ba6ea..d9ed908 100644 --- a/tests/unit/permission_platform_filter_test.go +++ b/tests/unit/permission_platform_filter_test.go @@ -18,13 +18,14 @@ import ( // TestPermissionPlatformFilter_List 测试权限列表按 platform 过滤 func TestPermissionPlatformFilter_List(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - permissionStore := postgres.NewPermissionStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) - rolePermStore := postgres.NewRolePermissionStore(db, redisClient) - service := permission.New(permissionStore, accountRoleStore, rolePermStore, redisClient) + permissionStore := postgres.NewPermissionStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) + service := permission.New(permissionStore, accountRoleStore, rolePermStore, rdb) ctx := context.Background() ctx = middleware.SetUserContext(ctx, middleware.NewSimpleUserContext(1, constants.UserTypeSuperAdmin, 0)) @@ -38,7 +39,7 @@ func TestPermissionPlatformFilter_List(t *testing.T) { {PermName: "H5按钮", PermCode: "button:h5", PermType: constants.PermissionTypeButton, Platform: constants.PlatformH5, Status: constants.StatusEnabled}, } for _, perm := range permissions { - require.NoError(t, db.Create(perm).Error) + require.NoError(t, tx.Create(perm).Error) } // 测试查询全部权限(不过滤) @@ -104,13 +105,14 @@ func TestPermissionPlatformFilter_List(t *testing.T) { // TestPermissionPlatformFilter_CreateWithDefaultPlatform 测试创建权限时默认 platform 为 all func TestPermissionPlatformFilter_CreateWithDefaultPlatform(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - permissionStore := postgres.NewPermissionStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) - rolePermStore := postgres.NewRolePermissionStore(db, redisClient) - service := permission.New(permissionStore, accountRoleStore, rolePermStore, redisClient) + permissionStore := postgres.NewPermissionStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) + service := permission.New(permissionStore, accountRoleStore, rolePermStore, rdb) ctx := context.Background() ctx = middleware.SetUserContext(ctx, middleware.NewSimpleUserContext(1, constants.UserTypeSuperAdmin, 0)) @@ -130,13 +132,14 @@ func TestPermissionPlatformFilter_CreateWithDefaultPlatform(t *testing.T) { // TestPermissionPlatformFilter_CreateWithSpecificPlatform 测试创建权限时指定 platform func TestPermissionPlatformFilter_CreateWithSpecificPlatform(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - permissionStore := postgres.NewPermissionStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) - rolePermStore := postgres.NewRolePermissionStore(db, redisClient) - service := permission.New(permissionStore, accountRoleStore, rolePermStore, redisClient) + permissionStore := postgres.NewPermissionStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) + service := permission.New(permissionStore, accountRoleStore, rolePermStore, rdb) ctx := context.Background() ctx = middleware.SetUserContext(ctx, middleware.NewSimpleUserContext(1, constants.UserTypeSuperAdmin, 0)) @@ -169,13 +172,14 @@ func TestPermissionPlatformFilter_CreateWithSpecificPlatform(t *testing.T) { // TestPermissionPlatformFilter_Tree 测试权限树包含 platform 字段 func TestPermissionPlatformFilter_Tree(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - permissionStore := postgres.NewPermissionStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) - rolePermStore := postgres.NewRolePermissionStore(db, redisClient) - service := permission.New(permissionStore, accountRoleStore, rolePermStore, redisClient) + permissionStore := postgres.NewPermissionStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) + service := permission.New(permissionStore, accountRoleStore, rolePermStore, rdb) ctx := context.Background() ctx = middleware.SetUserContext(ctx, middleware.NewSimpleUserContext(1, constants.UserTypeSuperAdmin, 0)) @@ -188,7 +192,7 @@ func TestPermissionPlatformFilter_Tree(t *testing.T) { Platform: constants.PlatformWeb, Status: constants.StatusEnabled, } - require.NoError(t, db.Create(parent).Error) + require.NoError(t, tx.Create(parent).Error) child := &model.Permission{ PermName: "用户管理", @@ -198,7 +202,7 @@ func TestPermissionPlatformFilter_Tree(t *testing.T) { ParentID: &parent.ID, Status: constants.StatusEnabled, } - require.NoError(t, db.Create(child).Error) + require.NoError(t, tx.Create(child).Error) // 获取权限树 tree, err := service.GetTree(ctx, nil) diff --git a/tests/unit/permission_store_test.go b/tests/unit/permission_store_test.go index 1e9a20f..bd346ca 100644 --- a/tests/unit/permission_store_test.go +++ b/tests/unit/permission_store_test.go @@ -14,10 +14,11 @@ import ( ) func TestPermissionStore_List_AvailableForRoleTypes(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewPermissionStore(db) + store := postgres.NewPermissionStore(tx) ctx := context.Background() platformPerm := &model.Permission{ @@ -112,10 +113,11 @@ func TestPermissionStore_List_AvailableForRoleTypes(t *testing.T) { } func TestPermissionStore_GetAll_AvailableForRoleType(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewPermissionStore(db) + store := postgres.NewPermissionStore(tx) ctx := context.Background() platformPerm := &model.Permission{ @@ -188,10 +190,11 @@ func TestPermissionStore_GetAll_AvailableForRoleType(t *testing.T) { } func TestPermissionStore_GetByPlatform_AvailableForRoleType(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewPermissionStore(db) + store := postgres.NewPermissionStore(tx) ctx := context.Background() webPlatformPerm := &model.Permission{ diff --git a/tests/unit/personal_customer_store_test.go b/tests/unit/personal_customer_store_test.go index 1f3d88d..9eb01f8 100644 --- a/tests/unit/personal_customer_store_test.go +++ b/tests/unit/personal_customer_store_test.go @@ -15,10 +15,11 @@ import ( // TestPersonalCustomerStore_Create 测试创建个人客户 func TestPersonalCustomerStore_Create(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewPersonalCustomerStore(db, redisClient) + store := postgres.NewPersonalCustomerStore(tx, rdb) ctx := context.Background() tests := []struct { @@ -66,10 +67,11 @@ func TestPersonalCustomerStore_Create(t *testing.T) { // TestPersonalCustomerStore_GetByID 测试根据 ID 查询个人客户 func TestPersonalCustomerStore_GetByID(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewPersonalCustomerStore(db, redisClient) + store := postgres.NewPersonalCustomerStore(tx, rdb) ctx := context.Background() // 创建测试客户 @@ -97,10 +99,11 @@ func TestPersonalCustomerStore_GetByID(t *testing.T) { // TestPersonalCustomerStore_GetByPhone 测试根据手机号查询 func TestPersonalCustomerStore_GetByPhone(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewPersonalCustomerStore(db, redisClient) + store := postgres.NewPersonalCustomerStore(tx, rdb) ctx := context.Background() // 创建测试客户 @@ -120,7 +123,7 @@ func TestPersonalCustomerStore_GetByPhone(t *testing.T) { IsPrimary: true, Status: constants.StatusEnabled, } - err = db.Create(customerPhone).Error + err = tx.Create(customerPhone).Error require.NoError(t, err) t.Run("根据手机号查询", func(t *testing.T) { @@ -138,10 +141,11 @@ func TestPersonalCustomerStore_GetByPhone(t *testing.T) { // TestPersonalCustomerStore_GetByWxOpenID 测试根据微信 OpenID 查询 func TestPersonalCustomerStore_GetByWxOpenID(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewPersonalCustomerStore(db, redisClient) + store := postgres.NewPersonalCustomerStore(tx, rdb) ctx := context.Background() // 创建测试客户 @@ -169,10 +173,11 @@ func TestPersonalCustomerStore_GetByWxOpenID(t *testing.T) { // TestPersonalCustomerStore_Update 测试更新个人客户 func TestPersonalCustomerStore_Update(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewPersonalCustomerStore(db, redisClient) + store := postgres.NewPersonalCustomerStore(tx, rdb) ctx := context.Background() // 创建测试客户 @@ -224,10 +229,11 @@ func TestPersonalCustomerStore_Update(t *testing.T) { // TestPersonalCustomerStore_Delete 测试软删除个人客户 func TestPersonalCustomerStore_Delete(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewPersonalCustomerStore(db, redisClient) + store := postgres.NewPersonalCustomerStore(tx, rdb) ctx := context.Background() // 创建测试客户 @@ -252,10 +258,11 @@ func TestPersonalCustomerStore_Delete(t *testing.T) { // TestPersonalCustomerStore_List 测试查询客户列表 func TestPersonalCustomerStore_List(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewPersonalCustomerStore(db, redisClient) + store := postgres.NewPersonalCustomerStore(tx, rdb) ctx := context.Background() // 创建多个测试客户 @@ -291,10 +298,11 @@ func TestPersonalCustomerStore_List(t *testing.T) { // TestPersonalCustomerStore_UniqueConstraints 测试唯一约束 func TestPersonalCustomerStore_UniqueConstraints(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewPersonalCustomerStore(db, redisClient) + store := postgres.NewPersonalCustomerStore(tx, rdb) ctx := context.Background() // 创建测试客户 diff --git a/tests/unit/queue_test.go b/tests/unit/queue_test.go index 7be3da9..856ee06 100644 --- a/tests/unit/queue_test.go +++ b/tests/unit/queue_test.go @@ -16,13 +16,13 @@ import ( // TestQueueClientEnqueue 测试任务入队 func TestQueueClientEnqueue(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", @@ -47,13 +47,13 @@ func TestQueueClientEnqueue(t *testing.T) { // TestQueueClientEnqueueWithOptions 测试带选项的任务入队 func TestQueueClientEnqueueWithOptions(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", @@ -136,13 +136,13 @@ func TestQueueClientEnqueueWithOptions(t *testing.T) { // TestQueueClientTaskUniqueness 测试任务唯一性 func TestQueueClientTaskUniqueness(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", @@ -224,13 +224,13 @@ func TestTaskPayloadSizeLimit(t *testing.T) { // Redis 默认支持最大 512MB,但实际应用中不建议超过 1MB } - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", @@ -268,13 +268,13 @@ func TestTaskPayloadSizeLimit(t *testing.T) { // TestTaskScheduling 测试任务调度 func TestTaskScheduling(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", @@ -320,13 +320,13 @@ func TestTaskScheduling(t *testing.T) { // TestQueueInspectorStats 测试队列统计 func TestQueueInspectorStats(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", @@ -363,13 +363,13 @@ func TestQueueInspectorStats(t *testing.T) { // TestTaskRetention 测试任务保留策略 func TestTaskRetention(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", @@ -395,13 +395,13 @@ func TestTaskRetention(t *testing.T) { // TestQueueDraining 测试队列暂停和恢复 func TestQueueDraining(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) inspector := asynq.NewInspector(asynq.RedisClientOpt{ Addr: "localhost:6379", @@ -429,13 +429,13 @@ func TestQueueDraining(t *testing.T) { // TestTaskCancellation 测试任务取消 func TestTaskCancellation(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", @@ -471,13 +471,13 @@ func TestTaskCancellation(t *testing.T) { // TestBatchTaskEnqueue 测试批量任务入队 func TestBatchTaskEnqueue(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", @@ -512,13 +512,13 @@ func TestBatchTaskEnqueue(t *testing.T) { // TestTaskGrouping 测试任务分组 func TestTaskGrouping(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer func() { _ = redisClient.Close() }() + defer func() { _ = rdb.Close() }() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) client := asynq.NewClient(asynq.RedisClientOpt{ Addr: "localhost:6379", diff --git a/tests/unit/role_assignment_limit_test.go b/tests/unit/role_assignment_limit_test.go index 95bb716..ff8c89d 100644 --- a/tests/unit/role_assignment_limit_test.go +++ b/tests/unit/role_assignment_limit_test.go @@ -17,12 +17,13 @@ import ( // TestRoleAssignmentLimit_PlatformUser 测试平台用户可以分配多个角色(无限制) func TestRoleAssignmentLimit_PlatformUser(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - roleStore := postgres.NewRoleStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + roleStore := postgres.NewRoleStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) service := account.New(accountStore, roleStore, accountRoleStore) ctx := context.Background() @@ -36,7 +37,7 @@ func TestRoleAssignmentLimit_PlatformUser(t *testing.T) { UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } - require.NoError(t, db.Create(platformUser).Error) + require.NoError(t, tx.Create(platformUser).Error) // 创建 3 个平台角色 roles := []*model.Role{ @@ -45,7 +46,7 @@ func TestRoleAssignmentLimit_PlatformUser(t *testing.T) { {RoleName: "财务", RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled}, } for _, role := range roles { - require.NoError(t, db.Create(role).Error) + require.NoError(t, tx.Create(role).Error) } // 为平台用户分配 3 个角色(应该成功,因为平台用户无限制) @@ -57,12 +58,13 @@ func TestRoleAssignmentLimit_PlatformUser(t *testing.T) { // TestRoleAssignmentLimit_AgentUser 测试代理账号只能分配一个角色 func TestRoleAssignmentLimit_AgentUser(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - roleStore := postgres.NewRoleStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + roleStore := postgres.NewRoleStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) service := account.New(accountStore, roleStore, accountRoleStore) ctx := context.Background() @@ -76,7 +78,7 @@ func TestRoleAssignmentLimit_AgentUser(t *testing.T) { UserType: constants.UserTypeAgent, Status: constants.StatusEnabled, } - require.NoError(t, db.Create(agentAccount).Error) + require.NoError(t, tx.Create(agentAccount).Error) // 创建 2 个客户角色 roles := []*model.Role{ @@ -84,7 +86,7 @@ func TestRoleAssignmentLimit_AgentUser(t *testing.T) { {RoleName: "二级代理", RoleType: constants.RoleTypeCustomer, Status: constants.StatusEnabled}, } for _, role := range roles { - require.NoError(t, db.Create(role).Error) + require.NoError(t, tx.Create(role).Error) } // 先分配第一个角色(应该成功) @@ -100,12 +102,13 @@ func TestRoleAssignmentLimit_AgentUser(t *testing.T) { // TestRoleAssignmentLimit_EnterpriseUser 测试企业账号只能分配一个角色 func TestRoleAssignmentLimit_EnterpriseUser(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - roleStore := postgres.NewRoleStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + roleStore := postgres.NewRoleStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) service := account.New(accountStore, roleStore, accountRoleStore) ctx := context.Background() @@ -119,7 +122,7 @@ func TestRoleAssignmentLimit_EnterpriseUser(t *testing.T) { UserType: constants.UserTypeEnterprise, Status: constants.StatusEnabled, } - require.NoError(t, db.Create(enterpriseAccount).Error) + require.NoError(t, tx.Create(enterpriseAccount).Error) // 创建 2 个客户角色 roles := []*model.Role{ @@ -127,7 +130,7 @@ func TestRoleAssignmentLimit_EnterpriseUser(t *testing.T) { {RoleName: "企业高级", RoleType: constants.RoleTypeCustomer, Status: constants.StatusEnabled}, } for _, role := range roles { - require.NoError(t, db.Create(role).Error) + require.NoError(t, tx.Create(role).Error) } // 先分配第一个角色(应该成功) @@ -143,12 +146,13 @@ func TestRoleAssignmentLimit_EnterpriseUser(t *testing.T) { // TestRoleAssignmentLimit_SuperAdmin 测试超级管理员不允许分配角色 func TestRoleAssignmentLimit_SuperAdmin(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - roleStore := postgres.NewRoleStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + roleStore := postgres.NewRoleStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) service := account.New(accountStore, roleStore, accountRoleStore) ctx := context.Background() @@ -162,7 +166,7 @@ func TestRoleAssignmentLimit_SuperAdmin(t *testing.T) { UserType: constants.UserTypeSuperAdmin, Status: constants.StatusEnabled, } - require.NoError(t, db.Create(superAdmin).Error) + require.NoError(t, tx.Create(superAdmin).Error) // 创建一个平台角色 role := &model.Role{ @@ -170,7 +174,7 @@ func TestRoleAssignmentLimit_SuperAdmin(t *testing.T) { RoleType: constants.RoleTypePlatform, Status: constants.StatusEnabled, } - require.NoError(t, db.Create(role).Error) + require.NoError(t, tx.Create(role).Error) // 尝试为超级管理员分配角色(应该失败) _, err := service.AssignRoles(ctx, superAdmin.ID, []uint{role.ID}) diff --git a/tests/unit/role_service_test.go b/tests/unit/role_service_test.go index 73c3d22..cdc04a7 100644 --- a/tests/unit/role_service_test.go +++ b/tests/unit/role_service_test.go @@ -14,12 +14,13 @@ import ( ) func TestRoleService_AssignPermissions_ValidateAvailableForRoleTypes(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - roleStore := postgres.NewRoleStore(db) - permStore := postgres.NewPermissionStore(db) - rolePermStore := postgres.NewRolePermissionStore(db, redisClient) + roleStore := postgres.NewRoleStore(tx) + permStore := postgres.NewPermissionStore(tx) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) service := role.New(roleStore, permStore, rolePermStore) ctx := createContextWithUserID(1) @@ -133,12 +134,13 @@ func TestRoleService_AssignPermissions_ValidateAvailableForRoleTypes(t *testing. } func TestRoleService_UpdateStatus(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - roleStore := postgres.NewRoleStore(db) - permStore := postgres.NewPermissionStore(db) - rolePermStore := postgres.NewRolePermissionStore(db, redisClient) + roleStore := postgres.NewRoleStore(tx) + permStore := postgres.NewPermissionStore(tx) + rolePermStore := postgres.NewRolePermissionStore(tx, rdb) service := role.New(roleStore, permStore, rolePermStore) ctx := createContextWithUserID(1) diff --git a/tests/unit/shop_account_service_test.go b/tests/unit/shop_account_service_test.go index 980cfd3..c62409e 100644 --- a/tests/unit/shop_account_service_test.go +++ b/tests/unit/shop_account_service_test.go @@ -18,11 +18,12 @@ import ( // TestShopAccountService_Create 测试创建商户账号 func TestShopAccountService_Create(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) service := shop_account.New(accountStore, shopStore) t.Run("创建商户账号成功", func(t *testing.T) { @@ -130,11 +131,12 @@ func TestShopAccountService_Create(t *testing.T) { // TestShopAccountService_Update 测试更新商户账号 func TestShopAccountService_Update(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) service := shop_account.New(accountStore, shopStore) t.Run("更新商户账号成功", func(t *testing.T) { @@ -204,11 +206,12 @@ func TestShopAccountService_Update(t *testing.T) { // TestShopAccountService_UpdatePassword 测试更新密码 func TestShopAccountService_UpdatePassword(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) service := shop_account.New(accountStore, shopStore) t.Run("更新密码成功", func(t *testing.T) { @@ -276,11 +279,12 @@ func TestShopAccountService_UpdatePassword(t *testing.T) { // TestShopAccountService_UpdateStatus 测试更新状态 func TestShopAccountService_UpdateStatus(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) service := shop_account.New(accountStore, shopStore) t.Run("更新状态成功", func(t *testing.T) { @@ -348,11 +352,12 @@ func TestShopAccountService_UpdateStatus(t *testing.T) { // TestShopAccountService_List 测试查询商户账号列表 func TestShopAccountService_List(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - shopStore := postgres.NewShopStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + shopStore := postgres.NewShopStore(tx, rdb) service := shop_account.New(accountStore, shopStore) t.Run("查询商户账号列表", func(t *testing.T) { diff --git a/tests/unit/shop_commission_service_test.go b/tests/unit/shop_commission_service_test.go index f6e861f..c67bbd7 100644 --- a/tests/unit/shop_commission_service_test.go +++ b/tests/unit/shop_commission_service_test.go @@ -23,14 +23,15 @@ func createCommissionTestContext(userID uint) context.Context { } func TestShopCommissionService_ListShopCommissionSummary(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) - walletStore := postgres.NewWalletStore(db, redisClient) - commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(db, redisClient) - commissionRecordStore := postgres.NewCommissionRecordStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) + walletStore := postgres.NewWalletStore(tx, rdb) + commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(tx, rdb) + commissionRecordStore := postgres.NewCommissionRecordStore(tx, rdb) service := shop_commission.New(shopStore, accountStore, walletStore, commissionWithdrawalRequestStore, commissionRecordStore) @@ -94,14 +95,15 @@ func TestShopCommissionService_ListShopCommissionSummary(t *testing.T) { } func TestShopCommissionService_ListShopWithdrawalRequests(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) - walletStore := postgres.NewWalletStore(db, redisClient) - commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(db, redisClient) - commissionRecordStore := postgres.NewCommissionRecordStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) + walletStore := postgres.NewWalletStore(tx, rdb) + commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(tx, rdb) + commissionRecordStore := postgres.NewCommissionRecordStore(tx, rdb) service := shop_commission.New(shopStore, accountStore, walletStore, commissionWithdrawalRequestStore, commissionRecordStore) @@ -148,14 +150,15 @@ func TestShopCommissionService_ListShopWithdrawalRequests(t *testing.T) { } func TestShopCommissionService_ListShopCommissionRecords(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) - walletStore := postgres.NewWalletStore(db, redisClient) - commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(db, redisClient) - commissionRecordStore := postgres.NewCommissionRecordStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) + walletStore := postgres.NewWalletStore(tx, rdb) + commissionWithdrawalRequestStore := postgres.NewCommissionWithdrawalRequestStore(tx, rdb) + commissionRecordStore := postgres.NewCommissionRecordStore(tx, rdb) service := shop_commission.New(shopStore, accountStore, walletStore, commissionWithdrawalRequestStore, commissionRecordStore) diff --git a/tests/unit/shop_service_test.go b/tests/unit/shop_service_test.go index 6cad7de..c4c6d04 100644 --- a/tests/unit/shop_service_test.go +++ b/tests/unit/shop_service_test.go @@ -18,11 +18,12 @@ import ( // TestShopService_Create 测试创建店铺 func TestShopService_Create(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) service := shop.New(shopStore, accountStore) t.Run("创建一级店铺成功", func(t *testing.T) { @@ -236,11 +237,12 @@ func TestShopService_Create(t *testing.T) { // TestShopService_Update 测试更新店铺 func TestShopService_Update(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) service := shop.New(shopStore, accountStore) t.Run("更新店铺信息成功", func(t *testing.T) { @@ -365,11 +367,12 @@ func TestShopService_Update(t *testing.T) { // TestShopService_Disable 测试禁用店铺 func TestShopService_Disable(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) service := shop.New(shopStore, accountStore) t.Run("禁用店铺成功", func(t *testing.T) { @@ -428,11 +431,12 @@ func TestShopService_Disable(t *testing.T) { // TestShopService_Enable 测试启用店铺 func TestShopService_Enable(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) service := shop.New(shopStore, accountStore) t.Run("启用店铺成功", func(t *testing.T) { @@ -500,11 +504,12 @@ func TestShopService_Enable(t *testing.T) { // TestShopService_GetByID 测试获取店铺详情 func TestShopService_GetByID(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) service := shop.New(shopStore, accountStore) t.Run("获取存在的店铺", func(t *testing.T) { @@ -548,11 +553,12 @@ func TestShopService_GetByID(t *testing.T) { // TestShopService_List 测试查询店铺列表 func TestShopService_List(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) service := shop.New(shopStore, accountStore) t.Run("查询店铺列表", func(t *testing.T) { @@ -584,11 +590,12 @@ func TestShopService_List(t *testing.T) { // TestShopService_GetSubordinateShopIDs 测试获取下级店铺 ID 列表 func TestShopService_GetSubordinateShopIDs(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) service := shop.New(shopStore, accountStore) t.Run("获取下级店铺 ID 列表", func(t *testing.T) { @@ -648,11 +655,12 @@ func TestShopService_GetSubordinateShopIDs(t *testing.T) { // TestShopService_Delete 测试删除店铺 func TestShopService_Delete(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - shopStore := postgres.NewShopStore(db, redisClient) - accountStore := postgres.NewAccountStore(db, redisClient) + shopStore := postgres.NewShopStore(tx, rdb) + accountStore := postgres.NewAccountStore(tx, rdb) service := shop.New(shopStore, accountStore) t.Run("删除店铺成功", func(t *testing.T) { diff --git a/tests/unit/shop_store_test.go b/tests/unit/shop_store_test.go index 0ef5904..db9246d 100644 --- a/tests/unit/shop_store_test.go +++ b/tests/unit/shop_store_test.go @@ -15,10 +15,11 @@ import ( // TestShopStore_Create 测试创建店铺 func TestShopStore_Create(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewShopStore(db, redisClient) + store := postgres.NewShopStore(tx, rdb) ctx := context.Background() tests := []struct { @@ -77,10 +78,11 @@ func TestShopStore_Create(t *testing.T) { // TestShopStore_GetByID 测试根据 ID 查询店铺 func TestShopStore_GetByID(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewShopStore(db, redisClient) + store := postgres.NewShopStore(tx, rdb) ctx := context.Background() // 创建测试店铺 @@ -115,10 +117,11 @@ func TestShopStore_GetByID(t *testing.T) { // TestShopStore_GetByCode 测试根据店铺编号查询 func TestShopStore_GetByCode(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewShopStore(db, redisClient) + store := postgres.NewShopStore(tx, rdb) ctx := context.Background() // 创建测试店铺 @@ -152,10 +155,11 @@ func TestShopStore_GetByCode(t *testing.T) { // TestShopStore_Update 测试更新店铺 func TestShopStore_Update(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewShopStore(db, redisClient) + store := postgres.NewShopStore(tx, rdb) ctx := context.Background() // 创建测试店铺 @@ -205,10 +209,11 @@ func TestShopStore_Update(t *testing.T) { // TestShopStore_Delete 测试软删除店铺 func TestShopStore_Delete(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewShopStore(db, redisClient) + store := postgres.NewShopStore(tx, rdb) ctx := context.Background() // 创建测试店铺 @@ -239,10 +244,11 @@ func TestShopStore_Delete(t *testing.T) { // TestShopStore_List 测试查询店铺列表 func TestShopStore_List(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewShopStore(db, redisClient) + store := postgres.NewShopStore(tx, rdb) ctx := context.Background() // 创建多个测试店铺 @@ -284,10 +290,11 @@ func TestShopStore_List(t *testing.T) { // TestShopStore_GetSubordinateShopIDs 测试递归查询下级店铺 ID func TestShopStore_GetSubordinateShopIDs(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewShopStore(db, redisClient) + store := postgres.NewShopStore(tx, rdb) ctx := context.Background() // 创建店铺层级结构 @@ -403,10 +410,11 @@ func TestShopStore_GetSubordinateShopIDs(t *testing.T) { // TestShopStore_UniqueConstraints 测试唯一约束 func TestShopStore_UniqueConstraints(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewShopStore(db, redisClient) + store := postgres.NewShopStore(tx, rdb) ctx := context.Background() // 创建测试店铺 diff --git a/tests/unit/soft_delete_test.go b/tests/unit/soft_delete_test.go index d76d3e6..65d1f3f 100644 --- a/tests/unit/soft_delete_test.go +++ b/tests/unit/soft_delete_test.go @@ -16,10 +16,11 @@ import ( // TestAccountSoftDelete 测试账号软删除功能 func TestAccountSoftDelete(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - store := postgres.NewAccountStore(db, redisClient) + store := postgres.NewAccountStore(tx, rdb) ctx := context.Background() // 创建测试账号 @@ -45,7 +46,7 @@ func TestAccountSoftDelete(t *testing.T) { t.Run("使用 Unscoped 可以查到已删除账号", func(t *testing.T) { var found model.Account - err := db.Unscoped().First(&found, account.ID).Error + err := tx.Unscoped().First(&found, account.ID).Error require.NoError(t, err) assert.Equal(t, account.Username, found.Username) assert.NotNil(t, found.DeletedAt) @@ -68,10 +69,11 @@ func TestAccountSoftDelete(t *testing.T) { // TestRoleSoftDelete 测试角色软删除功能 func TestRoleSoftDelete(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - roleStore := postgres.NewRoleStore(db) + roleStore := postgres.NewRoleStore(tx) ctx := context.Background() // 创建测试角色 @@ -96,7 +98,7 @@ func TestRoleSoftDelete(t *testing.T) { t.Run("使用 Unscoped 可以查到已删除角色", func(t *testing.T) { var found model.Role - err := db.Unscoped().First(&found, role.ID).Error + err := tx.Unscoped().First(&found, role.ID).Error require.NoError(t, err) assert.Equal(t, role.RoleName, found.RoleName) assert.NotNil(t, found.DeletedAt) @@ -105,10 +107,11 @@ func TestRoleSoftDelete(t *testing.T) { // TestPermissionSoftDelete 测试权限软删除功能 func TestPermissionSoftDelete(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - permissionStore := postgres.NewPermissionStore(db) + permissionStore := postgres.NewPermissionStore(tx) ctx := context.Background() // 创建测试权限 @@ -146,12 +149,13 @@ func TestPermissionSoftDelete(t *testing.T) { // TestAccountRoleSoftDelete 测试账号-角色关联软删除功能 func TestAccountRoleSoftDelete(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - accountStore := postgres.NewAccountStore(db, redisClient) - roleStore := postgres.NewRoleStore(db) - accountRoleStore := postgres.NewAccountRoleStore(db, redisClient) + accountStore := postgres.NewAccountStore(tx, rdb) + roleStore := postgres.NewRoleStore(tx) + accountRoleStore := postgres.NewAccountRoleStore(tx, rdb) ctx := context.Background() // 创建测试账号 @@ -216,12 +220,13 @@ func TestAccountRoleSoftDelete(t *testing.T) { // TestRolePermissionSoftDelete 测试角色-权限关联软删除功能 func TestRolePermissionSoftDelete(t *testing.T) { - db, redisClient := testutils.SetupTestDB(t) - defer testutils.TeardownTestDB(t, db, redisClient) + tx := testutils.NewTestTransaction(t) + rdb := testutils.GetTestRedis(t) + testutils.CleanTestRedisKeys(t, rdb) - roleStore := postgres.NewRoleStore(db) - permissionStore := postgres.NewPermissionStore(db) - rolePermissionStore := postgres.NewRolePermissionStore(db, redisClient) + roleStore := postgres.NewRoleStore(tx) + permissionStore := postgres.NewPermissionStore(tx) + rolePermissionStore := postgres.NewRolePermissionStore(tx, rdb) ctx := context.Background() // 创建测试角色 diff --git a/tests/unit/task_handler_test.go b/tests/unit/task_handler_test.go index 1e5c19f..9158358 100644 --- a/tests/unit/task_handler_test.go +++ b/tests/unit/task_handler_test.go @@ -25,33 +25,33 @@ type MockEmailPayload struct { // TestHandlerIdempotency 测试处理器幂等性逻辑 func TestHandlerIdempotency(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer redisClient.Close() + defer rdb.Close() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) requestID := "test-req-001" lockKey := constants.RedisTaskLockKey(requestID) // 测试场景1: 第一次执行(未加锁) t.Run("First Execution - Should Acquire Lock", func(t *testing.T) { - result, err := redisClient.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() + result, err := rdb.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() require.NoError(t, err) assert.True(t, result, "第一次执行应该成功获取锁") }) // 测试场景2: 重复执行(已加锁) t.Run("Duplicate Execution - Should Skip", func(t *testing.T) { - result, err := redisClient.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() + result, err := rdb.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() require.NoError(t, err) assert.False(t, result, "重复执行应该跳过(锁已存在)") }) // 清理 - redisClient.Del(ctx, lockKey) + rdb.Del(ctx, lockKey) } // TestHandlerErrorHandling 测试处理器错误处理 @@ -218,13 +218,13 @@ func TestPayloadDeserialization(t *testing.T) { // TestTaskStatusTransition 测试任务状态转换 func TestTaskStatusTransition(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer redisClient.Close() + defer rdb.Close() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) taskID := "task-transition-001" statusKey := constants.RedisTaskStatusKey(taskID) @@ -245,7 +245,7 @@ func TestTaskStatusTransition(t *testing.T) { t.Run("Transition to "+tr.status, func(t *testing.T) { // 检查状态转换是否合法 if isValidTransition(currentStatus, tr.status) == tr.valid { - err := redisClient.Set(ctx, statusKey, tr.status, 7*24*time.Hour).Err() + err := rdb.Set(ctx, statusKey, tr.status, 7*24*time.Hour).Err() require.NoError(t, err) currentStatus = tr.status } else { @@ -281,13 +281,13 @@ func isValidTransition(from, to string) bool { // TestConcurrentTaskExecution 测试并发任务执行 func TestConcurrentTaskExecution(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer redisClient.Close() + defer rdb.Close() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) // 模拟多个并发任务尝试获取同一个锁 requestID := "concurrent-test-001" @@ -301,7 +301,7 @@ func TestConcurrentTaskExecution(t *testing.T) { // 并发执行 for i := 0; i < concurrency; i++ { go func() { - result, err := redisClient.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() + result, err := rdb.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() if err == nil && result { successCount++ } @@ -364,19 +364,19 @@ func TestTaskTimeout(t *testing.T) { // TestLockExpiration 测试锁过期机制 func TestLockExpiration(t *testing.T) { - redisClient := redis.NewClient(&redis.Options{ + rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) - defer redisClient.Close() + defer rdb.Close() ctx := context.Background() - redisClient.FlushDB(ctx) + rdb.FlushDB(ctx) requestID := "expiration-test-001" lockKey := constants.RedisTaskLockKey(requestID) // 设置短 TTL 的锁 - result, err := redisClient.SetNX(ctx, lockKey, "1", 100*time.Millisecond).Result() + result, err := rdb.SetNX(ctx, lockKey, "1", 100*time.Millisecond).Result() require.NoError(t, err) assert.True(t, result) @@ -384,7 +384,7 @@ func TestLockExpiration(t *testing.T) { time.Sleep(200 * time.Millisecond) // 验证锁已过期,可以重新获取 - result, err = redisClient.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() + result, err = rdb.SetNX(ctx, lockKey, "1", 24*time.Hour).Result() require.NoError(t, err) assert.True(t, result, "锁过期后应该可以重新获取") }