You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
3.2 KiB
Go
132 lines
3.2 KiB
Go
package containers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/testcontainers/testcontainers-go/modules/postgres"
|
|
|
|
sharedpostgres "knowfoolery/backend/shared/infra/database/postgres"
|
|
)
|
|
|
|
const (
|
|
testPostgresUser = "knowfoolery"
|
|
testPostgresPassword = "knowfoolery"
|
|
testPostgresDB = "knowfoolery_test"
|
|
)
|
|
|
|
// PostgresInstance contains connection details for a disposable Postgres test container.
|
|
type PostgresInstance struct {
|
|
Host string
|
|
Port int
|
|
User string
|
|
Password string
|
|
Database string
|
|
}
|
|
|
|
// URL returns a connection URL for pgx/stdlib clients.
|
|
func (p PostgresInstance) URL() string {
|
|
return fmt.Sprintf(
|
|
"postgresql://%s:%s@%s:%d/%s?sslmode=disable",
|
|
p.User,
|
|
p.Password,
|
|
p.Host,
|
|
p.Port,
|
|
p.Database,
|
|
)
|
|
}
|
|
|
|
// Config converts container details to the shared postgres config type.
|
|
func (p PostgresInstance) Config() sharedpostgres.Config {
|
|
cfg := sharedpostgres.DefaultConfig()
|
|
cfg.Host = p.Host
|
|
cfg.Port = p.Port
|
|
cfg.User = p.User
|
|
cfg.Password = p.Password
|
|
cfg.Database = p.Database
|
|
cfg.SSLMode = "disable"
|
|
return cfg
|
|
}
|
|
|
|
// StartPostgres boots a disposable Postgres container and registers cleanup with t.Cleanup.
|
|
func StartPostgres(t *testing.T) PostgresInstance {
|
|
t.Helper()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
|
|
t.Cleanup(cancel)
|
|
|
|
var (
|
|
container *postgres.PostgresContainer
|
|
err error
|
|
panicErr error
|
|
)
|
|
func() {
|
|
defer func() {
|
|
if recovered := recover(); recovered != nil {
|
|
panicErr = fmt.Errorf("%v", recovered)
|
|
}
|
|
}()
|
|
container, err = postgres.Run(
|
|
ctx,
|
|
"postgres:16-alpine",
|
|
postgres.WithDatabase(testPostgresDB),
|
|
postgres.WithUsername(testPostgresUser),
|
|
postgres.WithPassword(testPostgresPassword),
|
|
postgres.BasicWaitStrategies(),
|
|
)
|
|
}()
|
|
if panicErr != nil {
|
|
if isDockerUnavailable(panicErr) {
|
|
t.Skipf("docker unavailable for testcontainers: %v", panicErr)
|
|
}
|
|
t.Fatalf("start postgres testcontainer panic: %v", panicErr)
|
|
}
|
|
if err != nil {
|
|
if isDockerUnavailable(err) {
|
|
t.Skipf("docker unavailable for testcontainers: %v", err)
|
|
}
|
|
t.Fatalf("start postgres testcontainer: %v", err)
|
|
}
|
|
|
|
t.Cleanup(func() {
|
|
stopCtx, stopCancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer stopCancel()
|
|
if termErr := container.Terminate(stopCtx); termErr != nil {
|
|
t.Fatalf("terminate postgres testcontainer: %v", termErr)
|
|
}
|
|
})
|
|
|
|
host, err := container.Host(ctx)
|
|
if err != nil {
|
|
t.Fatalf("resolve postgres host: %v", err)
|
|
}
|
|
mappedPort, err := container.MappedPort(ctx, "5432/tcp")
|
|
if err != nil {
|
|
t.Fatalf("resolve postgres mapped port: %v", err)
|
|
}
|
|
port, err := strconv.Atoi(mappedPort.Port())
|
|
if err != nil {
|
|
t.Fatalf("parse postgres mapped port: %v", err)
|
|
}
|
|
|
|
return PostgresInstance{
|
|
Host: host,
|
|
Port: port,
|
|
User: testPostgresUser,
|
|
Password: testPostgresPassword,
|
|
Database: testPostgresDB,
|
|
}
|
|
}
|
|
|
|
func isDockerUnavailable(err error) bool {
|
|
msg := strings.ToLower(err.Error())
|
|
return strings.Contains(msg, "permission denied") ||
|
|
strings.Contains(msg, "cannot connect to the docker daemon") ||
|
|
strings.Contains(msg, "docker.sock") ||
|
|
strings.Contains(msg, "connection refused")
|
|
}
|