Skip to content

Instantly share code, notes, and snippets.

@wreulicke
Created March 11, 2026 01:04
Show Gist options
  • Select an option

  • Save wreulicke/035a50d8bd4a7a7c0dd0e5f97519cf2a to your computer and use it in GitHub Desktop.

Select an option

Save wreulicke/035a50d8bd4a7a7c0dd0e5f97519cf2a to your computer and use it in GitHub Desktop.
psqldef の regression を調べるためのテストコード
package main
import (
"archive/tar"
"archive/zip"
"bytes"
"compress/gzip"
"context"
"debug/buildinfo"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"os/exec"
"strings"
"testing"
"github.com/docker/go-connections/nat"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/postgres"
"github.com/testcontainers/testcontainers-go/wait"
)
func setupPostgresContainer(t *testing.T, initSqlPath string) (*postgres.PostgresContainer, string) {
t.Helper()
options := []testcontainers.ContainerCustomizer{
testcontainers.WithAdditionalWaitStrategy(
wait.ForLog("database system is ready to accept connections").WithOccurrence(2),
wait.ForListeningPort("5432/tcp")),
}
if initSqlPath != "" {
options = append(options, postgres.WithInitScripts(initSqlPath))
}
c, err := postgres.Run(t.Context(), "postgres:15", options...)
if err != nil {
t.Fatalf("failed to start postgres container: %v", err)
}
port, err := c.MappedPort(t.Context(), nat.Port("5432/tcp"))
if err != nil {
t.Fatalf("failed to get mapped port: %v", err)
}
t.Cleanup(func() {
if err := c.Terminate(context.Background()); err != nil {
t.Errorf("failed to terminate container: %v", err)
}
})
return c, port.Port()
}
func setupPsqldef(sqldefVersion string) error {
info, err := buildinfo.ReadFile(os.Args[0])
if err != nil {
return fmt.Errorf("cannot read buildinfo: %w", err)
}
curOs := ""
curArch := ""
for _, s := range info.Settings {
if s.Key == "GOOS" {
curOs = s.Value
}
if s.Key == "GOARCH" {
curArch = s.Value
}
}
if curOs == "" || curArch == "" {
return errors.New("cannot detect GOOS or GOARCH")
}
archive := ".tar.gz"
if curOs != "linux" {
archive = ".zip"
}
psqldefLink := "https://github.com/sqldef/sqldef/releases/download/" + sqldefVersion + "/psqldef_" + curOs + "_" + curArch + archive
u, err := url.Parse(psqldefLink)
if err != nil {
return fmt.Errorf("cannot parse psqldefLink: %w", err)
}
psqldefPath := "build/psqldef-" + sqldefVersion
_ = os.MkdirAll("build", 0o750)
psqldefFile, err := os.Create(psqldefPath)
if err != nil {
return fmt.Errorf("cannot create file: %w", err)
}
defer func() { _ = psqldefFile.Close() }()
resp, err := http.DefaultClient.Do(&http.Request{
Method: http.MethodGet,
URL: u,
Body: http.NoBody,
})
if err != nil {
return fmt.Errorf("cannot download psqldef: %w", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to download psqldef: url: %s, status %d", psqldefLink, resp.StatusCode)
}
if archive == ".zip" {
var buf bytes.Buffer
_, err := io.Copy(&buf, resp.Body)
if err != nil {
return fmt.Errorf("cannot read zip body: %w", err)
}
z, err := zip.NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
if err != nil {
return fmt.Errorf("cannot read zip archive: %w", err)
}
for _, f := range z.File {
if !strings.HasSuffix(f.Name, "psqldef") {
continue
}
rc, err := f.Open()
if err != nil {
return fmt.Errorf("cannot open psqldef file in zip: %w", err)
}
_, err = io.Copy(psqldefFile, rc) //nolint:gosec // ignore G110
if err != nil {
return fmt.Errorf("cannot copy psqldef binary: %w", err)
}
err = os.Chmod(psqldefFile.Name(), 0o755) //nolint:gosec // we need to make it executable
if err != nil {
return fmt.Errorf("cannot chmod psqldef binary: %w", err)
}
break
}
return nil
} else if archive == ".tar.gz" {
g, err := gzip.NewReader(resp.Body)
if err != nil {
return fmt.Errorf("cannot create gzip reader: %w", err)
}
defer func() { _ = g.Close() }()
tr := tar.NewReader(g)
for {
h, err := tr.Next()
if err == io.EOF {
return nil
}
if err != nil {
return fmt.Errorf("cannot read tar: %w", err)
}
if h.Typeflag != tar.TypeReg {
continue
}
if !strings.HasSuffix(h.Name, "psqldef") {
continue
}
break
}
_, err = io.Copy(psqldefFile, tr) //nolint:gosec // ignore G110
if err != nil {
return fmt.Errorf("cannot copy psqldef binary: %w", err)
}
err = os.Chmod(psqldefFile.Name(), 0o755) //nolint:gosec // we need to make it executable
if err != nil {
return fmt.Errorf("cannot chmod psqldef binary: %w", err)
}
return nil
}
return errors.New("unsupported archive format")
}
func TestRegressionTest(t *testing.T) {
t.Parallel()
sqldefVersions := []string{
"v3.10.0",
"v3.9.8",
"v3.9.7",
"v3.9.6",
"v3.9.5",
"v3.9.4",
"v3.9.3",
"v3.9.2",
"v3.9.1",
"v3.9.0",
"v3.8.14",
"v3.8.13",
"v3.8.12",
"v3.8.11",
"v3.8.10",
"v3.8.9",
"v3.8.8",
"v3.8.7",
"v3.8.6",
"v3.8.5",
"v3.8.4",
"v3.8.3",
"v3.8.2",
"v3.8.1",
"v3.8.0",
"v3.7.11",
"v3.7.10",
"v3.7.9",
"v3.7.8",
"v3.7.7",
"v3.7.6",
"v3.7.5",
"v3.7.4",
"v3.7.3",
"v3.7.2",
"v3.7.1",
"v3.7.0",
"v3.6.7",
"v3.6.6",
"v3.6.5",
"v3.6.4",
"v3.6.3",
"v3.6.2",
"v3.6.1",
"v3.6.0",
"v3.5.1",
"v3.5.0",
"v3.4.0",
"v3.3.0",
"v3.2.2",
"v3.2.1",
"v3.2.0",
"v3.1.18",
"v3.1.17",
"v3.1.16",
"v3.1.15",
"v3.1.14",
"v3.1.13",
"v3.1.12",
"v3.1.11",
"v3.1.10",
"v3.1.9",
"v3.1.8",
"v3.1.7",
"v3.1.6",
"v3.1.5",
"v3.1.4",
"v3.1.3",
"v3.1.2",
"v3.1.1",
"v3.1.0",
"v3.0.8",
"v3.0.7",
"v3.0.6",
"v3.0.5",
"v3.0.4",
"v3.0.3",
"v3.0.2",
"v3.0.1",
"v3.0.0",
"v2.5.0",
"v2.4.8",
"v2.4.7",
"v2.4.6",
"v2.4.5",
"v2.4.4",
"v2.4.3-1",
"v2.4.2",
"v2.4.1",
"v2.4.0",
"v2.3.0",
"v2.2.0",
"v2.1.0",
"v2.0.11",
"v2.0.10",
"v2.0.9",
"v2.0.8",
"v2.0.7",
"v2.0.6",
"v2.0.5",
"v2.0.4",
"v2.0.3",
"v2.0.2",
"v2.0.0",
"v1.0.7",
"v1.0.6",
"v1.0.5",
"v1.0.4",
"v1.0.3",
"v1.0.2",
"v1.0.1",
"v1.0.0",
"v0.17.32",
"v0.17.31",
"v0.17.30",
"v0.17.29",
"v0.17.28",
"v0.17.27",
"v0.17.26",
"v0.17.25",
"v0.17.24",
"v0.17.23",
"v0.17.22",
"v0.17.21",
"v0.17.20",
"v0.17.19",
"v0.17.18",
"v0.17.17",
"v0.17.16",
"v0.17.15",
"v0.17.14",
"v0.17.13",
"v0.17.12",
"v0.17.11",
"v0.17.10",
"v0.17.9",
"v0.17.8",
"v0.17.7",
"v0.17.6",
"v0.17.5",
"v0.17.4",
"v0.17.3",
"v0.17.2",
"v0.17.1",
"v0.16.15",
"v0.16.14",
"v0.16.13",
"v0.16.12",
"v0.16.11",
"v0.16.10",
"v0.16.9",
"v0.16.8",
"v0.16.7",
"v0.16.6",
"v0.16.5",
"v0.16.3",
"v0.16.2",
"v0.16.1",
}
for _, v := range sqldefVersions {
t.Run(fmt.Sprintf("sqldef-%s", v), func(t *testing.T) {
t.Parallel()
err := setupPsqldef(v)
if err != nil {
t.Fatalf("failed to setup psqldef: %v", err)
}
_, p := setupPostgresContainer(t, "")
err = runPsqldef("build/psqldef-"+v, "postgres", "postgres", "localhost", p, "postgres", "schema.sql")
if err != nil {
t.Fatalf("psqldef failed: %v", err)
}
})
}
}
func runPsqldef(psqldefPath, user, password, host, port, dbName, schemaPath string) error {
c := exec.Command(psqldefPath,
"-U", user,
"-W", password,
"-h", host,
"-p", port,
"-f", schemaPath,
dbName,
)
c.Stdout = os.Stdout
c.Stderr = os.Stderr
return c.Run()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment