Dawid Wysokiński
b6130b9fc9
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #9
158 lines
3.6 KiB
Go
158 lines
3.6 KiB
Go
package bundb_test
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
"unicode"
|
|
|
|
"gitea.dwysokinski.me/twhelp/dcbot/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/dbfixture"
|
|
"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]interface{}{
|
|
"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 loadFixtures(tb testing.TB, bunDB *bun.DB) *dbfixture.Fixture {
|
|
tb.Helper()
|
|
|
|
fixture := dbfixture.New(bunDB)
|
|
err := fixture.Load(context.Background(), os.DirFS("testdata"), "fixture.yml")
|
|
require.NoError(tb, err, "couldn't load fixtures")
|
|
return fixture
|
|
}
|
|
|
|
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
|
|
}
|