func WithName(name string) func(*PostgresDB) {
	return func(db *PostgresDB) {
		db.dbName = name
	}
}
WithName is used to specify the name of the test Postgres database. By default
the database name is "test-postgres-db"
package integration
import (
	"context"
	"database/sql"
	"fmt"
	"time"
)
var (
	errConnectionTimeout = testDatabaseError{
		message: "database connection timed out",
	}
)
type testDatabaseError struct{ message string }
func (e testDatabaseError) Error() string {
	return fmt.Sprintf("testing database error: %s", e.message)
}
type PostgresDB struct {
	host                string
	port                int
	dbName              string
	dbUser              string
	dbPassword          string
	dbVersion           string
	migrateUpFunction   func(*sql.DB) error
	migrateDownFunction func(*sql.DB) error
	timeout             time.Duration
}
func NewTestPostgresDB(opts ...option) (PostgresDB, error) {
	port, err := GetOpenPortInRange(35000, portRangeMax)
	if err != nil {
		return PostgresDB{}, err
	}
	db := PostgresDB{
		port:       port,
		dbName:     "test-postgres-db",
		dbUser:     "postgres",
		dbPassword: "postgres",
		dbVersion:  "latest",
		timeout:    time.Second * 10,
	}
	for _, opt := range opts {
		opt(&db)
	}
	return db, nil
}
func (db PostgresDB) Reset() error {
	dbSQL, err := sql.Open("postgres", db.GetDSN())
	if err != nil {
		return err
	}
	defer dbSQL.Close()
	if db.migrateDownFunction != nil {
		db.migrateDownFunction(dbSQL)
	} else {
		resetQuery := "DROP SCHEMA public CASCADE;" +
			"CREATE SCHEMA public;" +
			"GRANT ALL ON SCHEMA public TO postgres;" +
			"GRANT ALL ON SCHEMA public TO public;"
		if _, err := dbSQL.Exec(resetQuery); err != nil {
			return err
		}
	}
	if db.migrateUpFunction != nil {
		if err := db.migrateUpFunction(dbSQL); err != nil {
			return err
		}
	}
	return nil
}
func (db PostgresDB) RunAsDockerContainer() (func() error, error) {
	cleanup, err := RunContainer(
		fmt.Sprintf("postgres:%s", db.dbVersion),
		[]string{
			fmt.Sprintf("--publish=%d:5432", db.port),
			fmt.Sprintf("--env=POSTGRES_DB=%s", db.dbName),
			fmt.Sprintf("--env=POSTGRES_PASSWORD=%s", db.dbPassword),
			fmt.Sprintf("--env=POSTGRES_USER=%s", db.dbUser),
			"--detach",
			"--rm",
		},
		[]string{},
	)
	if err != nil {
		return nil, err
	}
	if err := db.CheckConnection(); err != nil {
		cleanup()
		return nil, err
	}
	return cleanup, nil
}
func (db PostgresDB) CheckConnection() error {
	ctx, cancel := context.WithTimeout(context.Background(), db.timeout)
	defer cancel()
	errStream := make(chan error)
	go func() {
		for {
			select {
			case <-time.After(500 * time.Millisecond):
				driver, _ := sql.Open("postgres", db.GetDSN())
				if err := driver.Ping(); err == nil {
					errStream <- nil
					return
				}
			case <-ctx.Done():
				errStream <- errConnectionTimeout
			}
		}
	}()
	if err := <-errStream; err != nil {
		return err
	}
	return nil
}
func (db PostgresDB) GetDSN() string {
	return fmt.Sprintf(
		"host=localhost port=%d user=%s password=%s sslmode=disable dbname=%s",
		db.port, db.dbUser, db.dbPassword, db.dbName,
	)
}
type option func(*PostgresDB)