fix(backend): secure production startup and migrations

This commit is contained in:
2026-07-22 13:11:30 +08:00
parent 0daea4def9
commit 7980943660
13 changed files with 220 additions and 43 deletions

View File

@@ -0,0 +1,68 @@
//go:build integration
// Package testutil contains opt-in helpers for isolated integration databases.
package testutil
import (
"crypto/rand"
"encoding/hex"
"fmt"
"net/url"
"os"
"strings"
"testing"
"github.com/stretchr/testify/require"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
// OpenIsolatedPostgres creates a random schema and binds every pooled test
// connection to it through search_path. It never reads the production DSN.
func OpenIsolatedPostgres(t testing.TB, config *gorm.Config) *gorm.DB {
t.Helper()
dsn := strings.TrimSpace(os.Getenv("TEST_DATABASE_URL"))
require.NotEmpty(t, dsn, "TEST_DATABASE_URL is required for integration tests and must point to an isolated database")
schema := randomSchemaName(t)
admin, err := gorm.Open(postgres.Open(dsn), config)
require.NoError(t, err)
adminSQL, err := admin.DB()
require.NoError(t, err)
require.NoError(t, admin.Exec(fmt.Sprintf(`CREATE SCHEMA "%s"`, schema)).Error)
schemaDSN, err := postgresSchemaDSN(dsn, schema)
require.NoError(t, err)
database, err := gorm.Open(postgres.Open(schemaDSN), config)
require.NoError(t, err)
isolationSQL, err := database.DB()
require.NoError(t, err)
t.Cleanup(func() {
_ = isolationSQL.Close()
_ = admin.Exec(fmt.Sprintf(`DROP SCHEMA IF EXISTS "%s" CASCADE`, schema)).Error
_ = adminSQL.Close()
})
return database
}
func randomSchemaName(t testing.TB) string {
t.Helper()
value := make([]byte, 12)
_, err := rand.Read(value)
require.NoError(t, err)
return "senlin_test_" + hex.EncodeToString(value)
}
func postgresSchemaDSN(dsn, schema string) (string, error) {
if strings.Contains(dsn, "://") {
parsed, err := url.Parse(dsn)
if err != nil {
return "", err
}
query := parsed.Query()
query.Set("search_path", schema)
parsed.RawQuery = query.Encode()
return parsed.String(), nil
}
return strings.TrimSpace(dsn) + " search_path=" + schema, nil
}