sessions/internal/bundb/bundb_test.go

148 lines
3.3 KiB
Go

package bundb_test
import (
"context"
"database/sql"
"errors"
"fmt"
"net"
"net/url"
"os"
"strings"
"testing"
"time"
"unicode"
"gitea.dwysokinski.me/twhelp/sessions/internal/bundb/migrations"
"github.com/cenkalti/backoff/v4"
"github.com/google/uuid"
"github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect/pgdialect"
"github.com/uptrace/bun/driver/pgdriver"
)
func newDB(tb testing.TB) *bun.DB {
tb.Helper()
if dsn, ok := os.LookupEnv("TESTS_DB_DSN"); ok {
return newDBWithDSN(tb, dsn)
}
q := url.Values{}
q.Add("sslmode", "disable")
dsn := &url.URL{
Scheme: "postgres",
User: url.UserPassword("postgres", "postgres"),
Path: "twhelp",
RawQuery: q.Encode(),
}
pool, err := dockertest.NewPool("")
require.NoError(tb, err, "couldn't connect to docker")
pool.MaxWait = 20 * time.Second
pw, _ := dsn.User.Password()
resource, err := pool.RunWithOptions(&dockertest.RunOptions{
Repository: "postgres",
Tag: "14.5",
Env: []string{
fmt.Sprintf("POSTGRES_USER=%s", dsn.User.Username()),
fmt.Sprintf("POSTGRES_PASSWORD=%s", pw),
fmt.Sprintf("POSTGRES_DB=%s", dsn.Path),
},
}, func(config *docker.HostConfig) {
config.AutoRemove = true
config.RestartPolicy = docker.RestartPolicy{
Name: "no",
}
})
require.NoError(tb, err, "couldn't start resource")
tb.Cleanup(func() {
_ = pool.Purge(resource)
})
assert.NoError(tb, resource.Expire(60))
dsn.Host = getHostPort(tb, resource, "5432/tcp")
return newDBWithDSN(tb, dsn.String())
}
func newDBWithDSN(tb testing.TB, dsn string) *bun.DB {
tb.Helper()
schema := generateSchema()
sqldb := sql.OpenDB(
pgdriver.NewConnector(
pgdriver.WithDSN(dsn),
pgdriver.WithConnParams(map[string]any{
"search_path": schema,
}),
),
)
bunDB := bun.NewDB(sqldb, pgdialect.New())
tb.Cleanup(func() {
_ = bunDB.Close()
})
require.NoError(tb, retry(bunDB.Ping), "couldn't ping DB")
_, err := bunDB.Exec("CREATE SCHEMA ?", bun.Safe(schema))
require.NoError(tb, err, "couldn't create schema")
runMigrations(tb, bunDB)
return bunDB
}
func getHostPort(tb testing.TB, resource *dockertest.Resource, id string) string {
tb.Helper()
dockerURL := os.Getenv("DOCKER_HOST")
if dockerURL == "" {
return resource.GetHostPort(id)
}
u, err := url.Parse(dockerURL)
require.NoError(tb, err)
return net.JoinHostPort(u.Hostname(), resource.GetPort(id))
}
func runMigrations(tb testing.TB, db *bun.DB) {
tb.Helper()
migrator := migrations.NewMigrator(db)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
require.NoError(tb, migrator.Init(ctx), "couldn't migrate (1)")
_, err := migrator.Migrate(ctx)
require.NoError(tb, err, "couldn't migrate (2)")
}
func generateSchema() string {
return strings.TrimFunc(strings.ReplaceAll(uuid.NewString(), "-", "_"), unicode.IsNumber)
}
func retry(op func() error) error {
bo := backoff.NewExponentialBackOff()
bo.MaxInterval = time.Second * 5
bo.MaxElapsedTime = 30 * time.Second
if err := backoff.Retry(op, bo); err != nil {
if bo.NextBackOff() == backoff.Stop {
return errors.New("reached retry deadline")
}
return err
}
return nil
}