supabase-cli/internal/db/test/test.go

100 lines
2.8 KiB
Go

package test
import (
"context"
_ "embed"
"fmt"
"os"
"path/filepath"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/go-errors/errors"
"github.com/jackc/pgconn"
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v4"
"github.com/spf13/afero"
"github.com/spf13/viper"
"github.com/supabase/cli/internal/utils"
cliConfig "github.com/supabase/cli/pkg/config"
)
const (
ENABLE_PGTAP = "create extension if not exists pgtap with schema extensions"
DISABLE_PGTAP = "drop extension if exists pgtap"
)
func Run(ctx context.Context, testFiles []string, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
// Build test command
cmd := []string{"pg_prove", "--ext", ".pg", "--ext", ".sql", "-r"}
for _, fp := range testFiles {
relPath, err := filepath.Rel(utils.DbTestsDir, fp)
if err != nil {
return errors.Errorf("failed to resolve relative path: %w", err)
}
cmd = append(cmd, relPath)
}
if viper.GetBool("DEBUG") {
cmd = append(cmd, "--verbose")
}
// Mount tests directory into container as working directory
srcPath, err := filepath.Abs(utils.DbTestsDir)
if err != nil {
return errors.Errorf("failed to resolve absolute path: %w", err)
}
dstPath := "/tmp"
binds := []string{fmt.Sprintf("%s:%s:ro", srcPath, dstPath)}
// Enable pgTAP if not already exists
alreadyExists := false
options = append(options, func(cc *pgx.ConnConfig) {
cc.OnNotice = func(pc *pgconn.PgConn, n *pgconn.Notice) {
alreadyExists = n.Code == pgerrcode.DuplicateObject
}
})
conn, err := utils.ConnectByConfig(ctx, config, options...)
if err != nil {
return err
}
defer conn.Close(context.Background())
if _, err := conn.Exec(ctx, ENABLE_PGTAP); err != nil {
return errors.Errorf("failed to enable pgTAP: %w", err)
}
if !alreadyExists {
defer func() {
if _, err := conn.Exec(ctx, DISABLE_PGTAP); err != nil {
fmt.Fprintln(os.Stderr, "failed to disable pgTAP:", err)
}
}()
}
// Use custom network when connecting to local database
// disable selinux via security-opt to allow pg-tap to work properly
hostConfig := container.HostConfig{Binds: binds, SecurityOpt: []string{"label:disable"}}
if utils.IsLocalDatabase(config) {
config.Host = utils.DbAliases[0]
config.Port = 5432
} else {
hostConfig.NetworkMode = network.NetworkHost
}
// Run pg_prove on volume mount
return utils.DockerRunOnceWithConfig(
ctx,
container.Config{
Image: cliConfig.Images.PgProve,
Env: []string{
"PGHOST=" + config.Host,
fmt.Sprintf("PGPORT=%d", config.Port),
"PGUSER=" + config.User,
"PGPASSWORD=" + config.Password,
"PGDATABASE=" + config.Database,
},
Cmd: cmd,
WorkingDir: dstPath,
},
hostConfig,
network.NetworkingConfig{},
"",
os.Stdout,
os.Stderr,
)
}