Files
cmautobuy/admin/internal/testutil/mysql.go
T

89 lines
2.4 KiB
Go

// Package testutil 为 Admin 测试创建彼此隔离的 MySQL 8 数据库。
package testutil
import (
"crypto/rand"
"database/sql"
"encoding/hex"
"fmt"
"os"
"strings"
"cmautobuy/admin/config"
"cmautobuy/admin/repository"
)
// TestingT 是本包需要的 testing.T 最小接口,避免生产包依赖 testing。
type TestingT interface {
Helper()
Cleanup(func())
Fatalf(format string, args ...any)
Errorf(format string, args ...any)
Skipf(format string, args ...any)
}
// OpenMySQL 为一个测试创建独立数据库,测试结束后只删除该数据库。
func OpenMySQL(t TestingT) *sql.DB {
t.Helper()
if os.Getenv("CMAUTOBUY_MYSQL_TEST") != "1" {
t.Skipf("未设置 CMAUTOBUY_MYSQL_TEST=1,跳过真实 MySQL 8 测试")
}
baseConfig, err := config.LoadDatabaseFromEnv()
if err != nil {
t.Fatalf("读取 MySQL 测试配置失败: %v", err)
}
if !strings.HasSuffix(baseConfig.Name, "_test") {
t.Fatalf("MySQL 测试基础库名必须以 _test 结尾,实际 %q", baseConfig.Name)
}
adminDB, err := repository.OpenMySQL(baseConfig)
if err != nil {
t.Fatalf("连接 MySQL 测试基础库失败: %v", err)
}
databaseName := testDatabaseName(baseConfig.Name)
quotedName := quoteIdentifier(databaseName)
if _, err := adminDB.Exec("CREATE DATABASE " + quotedName +
" CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci"); err != nil {
adminDB.Close()
t.Fatalf("创建隔离 MySQL 测试库失败: %v", err)
}
testConfig := baseConfig
testConfig.Name = databaseName
db, err := repository.OpenMySQL(testConfig)
if err != nil {
adminDB.Exec("DROP DATABASE " + quotedName)
adminDB.Close()
t.Fatalf("连接隔离 MySQL 测试库失败: %v", err)
}
if err := repository.MigrateMySQL(db); err != nil {
db.Close()
adminDB.Exec("DROP DATABASE " + quotedName)
adminDB.Close()
t.Fatalf("初始化隔离 MySQL 测试库失败: %v", err)
}
t.Cleanup(func() {
db.Close()
if _, err := adminDB.Exec("DROP DATABASE " + quotedName); err != nil {
t.Errorf("清理隔离 MySQL 测试库 %s 失败: %v", databaseName, err)
}
adminDB.Close()
})
return db
}
func testDatabaseName(base string) string {
random := make([]byte, 8)
if _, err := rand.Read(random); err != nil {
panic(fmt.Sprintf("生成 MySQL 测试库随机名失败: %v", err))
}
if len(base) > 40 {
base = base[:40]
}
return base + "_" + hex.EncodeToString(random)
}
func quoteIdentifier(name string) string {
return "`" + strings.ReplaceAll(name, "`", "``") + "`"
}