Files
affiliate_dash/backend/internal/testdb/testdb.go
T
yml2213 d423fc652c chore(build): 新增 make test/lint/fmt 并修复 testdb 连接串缺陷
- make test 自动拉起并等待 PostgreSQL 就绪;测试用临时 schema 隔离,不污染开发库
- 新增 make test-db / lint / fmt
- 修复 testdb.withSearchPath:原实现用 url.Values.Encode() 重编码整个查询串,
  把 TimeZone=Asia/Shanghai 变成 Asia%2FShanghai,pgx 原样传给服务端后报
  "unknown time zone Asia%2FShanghai",导致数据库起来后集成测试依然全挂。
  改为只追加 search_path,并补测试锁定该行为。
2026-10-06 19:42:49 +08:00

85 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package testdb
import (
"fmt"
"net/url"
"os"
"strings"
"testing"
"time"
"affiliate_dash/internal/database"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// New 使用真实 PostgreSQL 创建测试隔离 schema,避免 SQLite 与生产 SQL 行为不一致。
func New(t testing.TB, _ ...interface{}) *gorm.DB {
t.Helper()
dsn := os.Getenv("TEST_DATABASE_URL")
if dsn == "" {
dsn = os.Getenv("DATABASE_URL")
}
if dsn == "" {
dsn = "postgres://affiliate:affiliate_dev_password@127.0.0.1:15432/affiliate_dash?sslmode=disable"
}
cfg := &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)}
adminDB, err := gorm.Open(postgres.Open(dsn), cfg)
if err != nil {
t.Fatalf("连接 PostgreSQL 测试库失败: %v", err)
}
adminSQL, err := adminDB.DB()
if err != nil {
t.Fatalf("获取 PostgreSQL 连接失败: %v", err)
}
schema := fmt.Sprintf("test_%d", time.Now().UnixNano())
if err := adminDB.Exec(`CREATE SCHEMA "` + schema + `"`).Error; err != nil {
t.Fatalf("创建测试 schema 失败: %v", err)
}
t.Cleanup(func() {
_ = adminDB.Exec(`DROP SCHEMA IF EXISTS "` + schema + `" CASCADE`).Error
_ = adminSQL.Close()
})
db, err := gorm.Open(postgres.Open(withSearchPath(dsn, schema)), cfg)
if err != nil {
t.Fatalf("连接测试 schema 失败: %v", err)
}
sqlDB, err := db.DB()
if err != nil {
t.Fatalf("获取测试 schema 连接失败: %v", err)
}
t.Cleanup(func() { _ = sqlDB.Close() })
if err := database.Migrate(db); err != nil {
t.Fatalf("迁移测试 schema 失败: %v", err)
}
return db
}
// withSearchPath 给 DSN 追加 search_path,让每个测试用例落在自己的临时 schema 里。
//
// 注意:这里刻意只「追加」而不用 url.Values.Encode() 重新编码整个查询串。
// 重新编码会把 TimeZone=Asia/Shanghai 变成 Asia%2FShanghai,pgx 会把这个值原样
// 传给服务端并报错 "unknown time zone Asia%2FShanghai",导致所有集成测试连不上库。
func withSearchPath(dsn, schema string) string {
parsed, err := url.Parse(dsn)
if err == nil && strings.HasPrefix(parsed.Scheme, "postgres") {
separator := "&"
if parsed.RawQuery == "" {
separator = "?"
}
// url.URL.String() 原样输出 RawQuery,不会再做一次百分号编码。
parsed.RawQuery = parsed.RawQuery + separator + "search_path=" + url.QueryEscape(schema)
return parsed.String()
}
if strings.TrimSpace(dsn) == "" {
return dsn
}
return dsn + " search_path=" + schema
}