Created
March 11, 2026 01:04
-
-
Save wreulicke/035a50d8bd4a7a7c0dd0e5f97519cf2a to your computer and use it in GitHub Desktop.
psqldef の regression を調べるためのテストコード
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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