package db import ( "context" "os" "strings" "sync" "testing" "github.com/jackc/pgx/v5/pgxpool" ) var ( migrateOnceMu sync.Mutex migratedDSNs = map[string]error{} ) // RunMigrationsOnceForTest 同一 DSN 只迁一次,避免并行包抢同一隔离库。 func RunMigrationsOnceForTest(ctx context.Context, pool *pgxpool.Pool, dsn string) error { key := strings.TrimSpace(dsn) migrateOnceMu.Lock() defer migrateOnceMu.Unlock() if err, ok := migratedDSNs[key]; ok { return err } err := RunMigrations(ctx, pool, ResolveMigrationsDir()) migratedDSNs[key] = err return err } const testDatabaseEnv = "TEST_DATABASE_URL" // RequireTestDatabaseURL 集成测试必须显式提供隔离测试库,禁止默认连开发库。 func RequireTestDatabaseURL(t *testing.T) string { t.Helper() dsn := strings.TrimSpace(os.Getenv(testDatabaseEnv)) if dsn == "" { t.Fatalf("%s must be set for PostgreSQL integration tests (refusing default dev database)", testDatabaseEnv) } return dsn } // SkipIfNoTestDatabaseURL 无隔离测试库时跳过(单元测试包内可选 PG 用例)。 func SkipIfNoTestDatabaseURL(t *testing.T) string { t.Helper() dsn := strings.TrimSpace(os.Getenv(testDatabaseEnv)) if dsn == "" { t.Skipf("%s not set; skipping PostgreSQL integration test", testDatabaseEnv) } return dsn } // RefuseSharedEnvDSN fails if the DSN targets a shared env database. func RefuseSharedEnvDSN(t *testing.T, dsn string) { t.Helper() if strings.Contains(dsn, "5433") || strings.Contains(dsn, "agent_system_dev") || (strings.Contains(dsn, "5434") && strings.Contains(dsn, "agent_system_test")) || strings.Contains(dsn, "5435") { t.Fatal("TEST_DATABASE_URL points at a shared env port; refuse agent_system_dev/test/main") } }