74 lines
2.0 KiB
Go
74 lines
2.0 KiB
Go
// Package pgtest provisions isolated PostgreSQL schemas for tests.
|
|
//
|
|
// Set CHESS_TEST_DSN to a database the test role may create schemas in, e.g.
|
|
//
|
|
// CHESS_TEST_DSN='postgres://chess_test:secret@127.0.0.1:5432/chess_test?sslmode=disable'
|
|
//
|
|
// Tests that need a database are skipped when it is unset.
|
|
package pgtest
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"database/sql"
|
|
"encoding/hex"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
_ "github.com/jackc/pgx/v5/stdlib"
|
|
)
|
|
|
|
// EnvDSN names the environment variable holding the base test DSN.
|
|
const EnvDSN = "CHESS_TEST_DSN"
|
|
|
|
// DSN creates an empty schema, registers its removal as test cleanup, and
|
|
// returns the base DSN with search_path pinned to that schema.
|
|
func DSN(t testing.TB) string {
|
|
t.Helper()
|
|
base := os.Getenv(EnvDSN)
|
|
if base == "" {
|
|
t.Skipf("%s is not set; skipping PostgreSQL test", EnvDSN)
|
|
}
|
|
|
|
suffix := make([]byte, 6)
|
|
rand.Read(suffix)
|
|
schema := "chess_test_" + hex.EncodeToString(suffix)
|
|
|
|
admin, err := sql.Open("pgx", base)
|
|
if err != nil {
|
|
t.Fatalf("open test database: %v", err)
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
if _, err := admin.ExecContext(ctx, "CREATE SCHEMA "+schema); err != nil {
|
|
admin.Close()
|
|
t.Fatalf("create test schema: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
defer admin.Close()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
if _, err := admin.ExecContext(ctx, "DROP SCHEMA "+schema+" CASCADE"); err != nil {
|
|
t.Errorf("drop test schema %s: %v", schema, err)
|
|
}
|
|
})
|
|
return WithParam(base, "search_path", schema)
|
|
}
|
|
|
|
// WithParam adds a runtime parameter to a URL or keyword/value DSN.
|
|
func WithParam(dsn, key, value string) string {
|
|
if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
|
|
u, err := url.Parse(dsn)
|
|
if err == nil {
|
|
query := u.Query()
|
|
query.Set(key, value)
|
|
u.RawQuery = query.Encode()
|
|
return u.String()
|
|
}
|
|
}
|
|
return dsn + " " + key + "=" + value
|
|
}
|