89 lines
2.4 KiB
Go
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, "`", "``") + "`"
|
||
|
|
}
|