mirror of https://github.com/pressly/goose.git
test: improve goose cli tests (#589)
parent
6c1d92b9fa
commit
958c950f66
|
@ -0,0 +1,214 @@
|
|||
package goose_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pressly/goose/v3/internal/check"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
const (
|
||||
// gooseTestBinaryVersion is utilized in conjunction with a linker variable to set the version
|
||||
// of a binary created solely for testing purposes. It is used to test the --version flag.
|
||||
gooseTestBinaryVersion = "v0.0.0"
|
||||
)
|
||||
|
||||
func TestFullBinary(t *testing.T) {
|
||||
t.Parallel()
|
||||
cli := buildGooseCLI(t, false)
|
||||
out, err := cli.run("--version")
|
||||
check.NoError(t, err)
|
||||
check.Equal(t, out, "goose version: "+gooseTestBinaryVersion+"\n")
|
||||
}
|
||||
|
||||
func TestLiteBinary(t *testing.T) {
|
||||
t.Parallel()
|
||||
cli := buildGooseCLI(t, true)
|
||||
|
||||
t.Run("binary_version", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
out, err := cli.run("--version")
|
||||
check.NoError(t, err)
|
||||
check.Equal(t, out, "goose version: "+gooseTestBinaryVersion+"\n")
|
||||
})
|
||||
t.Run("default_binary", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
total := countSQLFiles(t, "testdata/migrations")
|
||||
commands := []struct {
|
||||
cmd string
|
||||
out string
|
||||
}{
|
||||
{"up", "goose: successfully migrated database to version: " + strconv.Itoa(total)},
|
||||
{"version", "goose: version " + strconv.Itoa(total)},
|
||||
{"down", "OK"},
|
||||
{"version", "goose: version " + strconv.Itoa(total-1)},
|
||||
{"status", ""},
|
||||
{"reset", "OK"},
|
||||
{"version", "goose: version 0"},
|
||||
}
|
||||
for _, c := range commands {
|
||||
out, err := cli.run("-dir=testdata/migrations", "sqlite3", filepath.Join(dir, "sql.db"), c.cmd)
|
||||
check.NoError(t, err)
|
||||
check.Contains(t, out, c.out)
|
||||
}
|
||||
})
|
||||
t.Run("gh_issue_532", func(t *testing.T) {
|
||||
// https://github.com/pressly/goose/issues/532
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
total := countSQLFiles(t, "testdata/migrations")
|
||||
_, err := cli.run("-dir=testdata/migrations", "sqlite3", filepath.Join(dir, "sql.db"), "up")
|
||||
check.NoError(t, err)
|
||||
out, err := cli.run("-dir=testdata/migrations", "sqlite3", filepath.Join(dir, "sql.db"), "up")
|
||||
check.NoError(t, err)
|
||||
check.Contains(t, out, "goose: no migrations to run. current version: "+strconv.Itoa(total))
|
||||
out, err = cli.run("-dir=testdata/migrations", "sqlite3", filepath.Join(dir, "sql.db"), "version")
|
||||
check.NoError(t, err)
|
||||
check.Contains(t, out, "goose: version "+strconv.Itoa(total))
|
||||
})
|
||||
t.Run("gh_issue_293", func(t *testing.T) {
|
||||
// https://github.com/pressly/goose/issues/293
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
total := countSQLFiles(t, "testdata/migrations")
|
||||
commands := []struct {
|
||||
cmd string
|
||||
out string
|
||||
}{
|
||||
{"up", "goose: successfully migrated database to version: " + strconv.Itoa(total)},
|
||||
{"version", "goose: version " + strconv.Itoa(total)},
|
||||
{"down", "OK"},
|
||||
{"down", "OK"},
|
||||
{"version", "goose: version " + strconv.Itoa(total-2)},
|
||||
{"up", "goose: successfully migrated database to version: " + strconv.Itoa(total)},
|
||||
{"status", ""},
|
||||
}
|
||||
for _, c := range commands {
|
||||
out, err := cli.run("-dir=testdata/migrations", "sqlite3", filepath.Join(dir, "sql.db"), c.cmd)
|
||||
check.NoError(t, err)
|
||||
check.Contains(t, out, c.out)
|
||||
}
|
||||
})
|
||||
t.Run("gh_issue_336", func(t *testing.T) {
|
||||
// https://github.com/pressly/goose/issues/336
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
_, err := cli.run("-dir="+dir, "sqlite3", filepath.Join(dir, "sql.db"), "up")
|
||||
check.HasError(t, err)
|
||||
check.Contains(t, err.Error(), "goose run: no migration files found")
|
||||
})
|
||||
t.Run("create_and_fix", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
createEmptyFile(t, dir, "00001_alpha.sql")
|
||||
createEmptyFile(t, dir, "00003_bravo.sql")
|
||||
createEmptyFile(t, dir, "20230826163141_charlie.sql")
|
||||
createEmptyFile(t, dir, "20230826163151_delta.go")
|
||||
total, err := os.ReadDir(dir)
|
||||
check.NoError(t, err)
|
||||
check.Number(t, len(total), 4)
|
||||
migrationFiles := []struct {
|
||||
name string
|
||||
fileType string
|
||||
}{
|
||||
{"echo", "sql"},
|
||||
{"foxtrot", "go"},
|
||||
{"golf", ""},
|
||||
}
|
||||
for i, f := range migrationFiles {
|
||||
args := []string{"-dir=" + dir, "create", f.name}
|
||||
if f.fileType != "" {
|
||||
args = append(args, f.fileType)
|
||||
}
|
||||
out, err := cli.run(args...)
|
||||
check.NoError(t, err)
|
||||
check.Contains(t, out, "Created new file")
|
||||
// ensure different timestamps, granularity is 1 second
|
||||
if i < len(migrationFiles)-1 {
|
||||
time.Sleep(1100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
total, err = os.ReadDir(dir)
|
||||
check.NoError(t, err)
|
||||
check.Number(t, len(total), 7)
|
||||
out, err := cli.run("-dir="+dir, "fix")
|
||||
check.NoError(t, err)
|
||||
check.Contains(t, out, "RENAMED")
|
||||
files, err := os.ReadDir(dir)
|
||||
check.NoError(t, err)
|
||||
check.Number(t, len(files), 7)
|
||||
expected := []string{
|
||||
"00001_alpha.sql",
|
||||
"00003_bravo.sql",
|
||||
"00004_charlie.sql",
|
||||
"00005_delta.go",
|
||||
"00006_echo.sql",
|
||||
"00007_foxtrot.go",
|
||||
"00008_golf.go",
|
||||
}
|
||||
for i, f := range files {
|
||||
check.Equal(t, f.Name(), expected[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type gooseBinary struct {
|
||||
binaryPath string
|
||||
}
|
||||
|
||||
func (g gooseBinary) run(params ...string) (string, error) {
|
||||
cmd := exec.Command(g.binaryPath, params...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to run goose command: %v\nout: %v", err, string(out))
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
// buildGooseCLI builds goose test binary, which is used for testing goose CLI. It is built with all
|
||||
// drivers enabled, unless lite is true, in which case all drivers are disabled except sqlite3
|
||||
func buildGooseCLI(t *testing.T, lite bool) gooseBinary {
|
||||
binName := "goose-test"
|
||||
dir := t.TempDir()
|
||||
output := filepath.Join(dir, binName)
|
||||
// usage: go build [-o output] [build flags] [packages]
|
||||
args := []string{
|
||||
"build",
|
||||
"-o", output,
|
||||
"-ldflags=-s -w -X main.version=" + gooseTestBinaryVersion,
|
||||
}
|
||||
if lite {
|
||||
args = append(args, "-tags=no_clickhouse no_mssql no_mysql no_vertica no_postgres")
|
||||
}
|
||||
args = append(args, "./cmd/goose")
|
||||
build := exec.Command("go", args...)
|
||||
out, err := build.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to build %s binary: %v: %s", binName, err, string(out))
|
||||
}
|
||||
return gooseBinary{
|
||||
binaryPath: output,
|
||||
}
|
||||
}
|
||||
|
||||
func countSQLFiles(t *testing.T, dir string) int {
|
||||
t.Helper()
|
||||
files, err := filepath.Glob(filepath.Join(dir, "*.sql"))
|
||||
check.NoError(t, err)
|
||||
return len(files)
|
||||
}
|
||||
|
||||
func createEmptyFile(t *testing.T, dir, name string) {
|
||||
t.Helper()
|
||||
path := filepath.Join(dir, name)
|
||||
f, err := os.Create(path)
|
||||
check.NoError(t, err)
|
||||
defer f.Close()
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package goose_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"embed"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/pressly/goose/v3"
|
||||
"github.com/pressly/goose/v3/internal/check"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
//go:embed testdata/migrations/*.sql
|
||||
var embedMigrations embed.FS
|
||||
|
||||
func TestEmbeddedMigrations(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
// not using t.Parallel here to avoid races
|
||||
db, err := sql.Open("sqlite", filepath.Join(dir, "sql_embed.db"))
|
||||
check.NoError(t, err)
|
||||
|
||||
db.SetMaxOpenConns(1)
|
||||
|
||||
migrationFiles, err := fs.ReadDir(embedMigrations, "testdata/migrations")
|
||||
check.NoError(t, err)
|
||||
total := len(migrationFiles)
|
||||
|
||||
// decouple from existing structure
|
||||
fsys, err := fs.Sub(embedMigrations, "testdata/migrations")
|
||||
check.NoError(t, err)
|
||||
|
||||
goose.SetBaseFS(fsys)
|
||||
t.Cleanup(func() { goose.SetBaseFS(nil) })
|
||||
check.NoError(t, goose.SetDialect("sqlite3"))
|
||||
|
||||
t.Run("migration_cycle", func(t *testing.T) {
|
||||
err := goose.Up(db, ".")
|
||||
check.NoError(t, err)
|
||||
ver, err := goose.GetDBVersion(db)
|
||||
check.NoError(t, err)
|
||||
check.Number(t, ver, total)
|
||||
err = goose.Reset(db, ".")
|
||||
check.NoError(t, err)
|
||||
ver, err = goose.GetDBVersion(db)
|
||||
check.NoError(t, err)
|
||||
check.Number(t, ver, 0)
|
||||
})
|
||||
t.Run("create_uses_os_fs", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
err := goose.Create(db, dir, "test", "sql")
|
||||
check.NoError(t, err)
|
||||
paths, _ := filepath.Glob(filepath.Join(dir, "*test.sql"))
|
||||
check.NumberNotZero(t, len(paths))
|
||||
err = goose.Fix(dir)
|
||||
check.NoError(t, err)
|
||||
_, err = os.Stat(filepath.Join(dir, "00001_test.sql"))
|
||||
check.NoError(t, err)
|
||||
})
|
||||
}
|
330
goose_test.go
330
goose_test.go
|
@ -1,330 +0,0 @@
|
|||
package goose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pressly/goose/v3/internal/check"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
const (
|
||||
binName = "goose-test"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if runtime.GOOS == "windows" {
|
||||
log.Fatalf("this test is not supported on Windows")
|
||||
}
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
args := []string{
|
||||
"build",
|
||||
"-ldflags=-s -w",
|
||||
// disable all drivers except sqlite3
|
||||
"-tags=no_clickhouse no_mssql no_mysql no_vertica no_postgres",
|
||||
"-o", binName,
|
||||
"./cmd/goose",
|
||||
}
|
||||
build := exec.Command("go", args...)
|
||||
out, err := build.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to build %s binary: %v: %s", binName, err, string(out))
|
||||
}
|
||||
result := m.Run()
|
||||
defer func() { os.Exit(result) }()
|
||||
if err := os.Remove(filepath.Join(dir, binName)); err != nil {
|
||||
log.Printf("failed to remove binary: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultBinary(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
commands := []string{
|
||||
"go build -o ./bin/goose ./cmd/goose",
|
||||
"./bin/goose -dir=examples/sql-migrations sqlite3 sql.db up",
|
||||
"./bin/goose -dir=examples/sql-migrations sqlite3 sql.db version",
|
||||
"./bin/goose -dir=examples/sql-migrations sqlite3 sql.db down",
|
||||
"./bin/goose -dir=examples/sql-migrations sqlite3 sql.db status",
|
||||
"./bin/goose --version",
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := os.Remove("./bin/goose"); err != nil {
|
||||
t.Logf("failed to remove %s resources: %v", t.Name(), err)
|
||||
}
|
||||
if err := os.Remove("./sql.db"); err != nil {
|
||||
t.Logf("failed to remove %s resources: %v", t.Name(), err)
|
||||
}
|
||||
})
|
||||
|
||||
for _, cmd := range commands {
|
||||
args := strings.Split(cmd, " ")
|
||||
command := args[0]
|
||||
var params []string
|
||||
if len(args) > 1 {
|
||||
params = args[1:]
|
||||
}
|
||||
|
||||
cmd := exec.Command(command, params...)
|
||||
cmd.Env = os.Environ()
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("%s:\n%v\n\n%s", err, cmd, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue532(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
migrationsDir := filepath.Join("examples", "sql-migrations")
|
||||
count := countSQLFiles(t, migrationsDir)
|
||||
check.Number(t, count, 3)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
dirFlag := "--dir=" + migrationsDir
|
||||
|
||||
tt := []struct {
|
||||
command string
|
||||
output string
|
||||
}{
|
||||
{"up", ""},
|
||||
{"up", "goose: no migrations to run. current version: 3"},
|
||||
{"version", "goose: version 3"},
|
||||
}
|
||||
for _, tc := range tt {
|
||||
params := []string{dirFlag, "sqlite3", filepath.Join(tempDir, "sql.db"), tc.command}
|
||||
got, err := runGoose(params...)
|
||||
check.NoError(t, err)
|
||||
if tc.output == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(strings.TrimSpace(got), tc.output) {
|
||||
t.Logf("output mismatch for command: %q", tc.command)
|
||||
t.Logf("got\n%s", strings.TrimSpace(got))
|
||||
t.Log("====")
|
||||
t.Logf("want\n%s", tc.output)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue293(t *testing.T) {
|
||||
t.Parallel()
|
||||
// https://github.com/pressly/goose/issues/293
|
||||
commands := []string{
|
||||
"go build -o ./bin/goose293 ./cmd/goose",
|
||||
"./bin/goose293 -dir=examples/sql-migrations sqlite3 issue_293.db up",
|
||||
"./bin/goose293 -dir=examples/sql-migrations sqlite3 issue_293.db version",
|
||||
"./bin/goose293 -dir=examples/sql-migrations sqlite3 issue_293.db down",
|
||||
"./bin/goose293 -dir=examples/sql-migrations sqlite3 issue_293.db down",
|
||||
"./bin/goose293 -dir=examples/sql-migrations sqlite3 issue_293.db version",
|
||||
"./bin/goose293 -dir=examples/sql-migrations sqlite3 issue_293.db up",
|
||||
"./bin/goose293 -dir=examples/sql-migrations sqlite3 issue_293.db status",
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := os.Remove("./bin/goose293"); err != nil {
|
||||
t.Logf("failed to remove %s resources: %v", t.Name(), err)
|
||||
}
|
||||
if err := os.Remove("./issue_293.db"); err != nil {
|
||||
t.Logf("failed to remove %s resources: %v", t.Name(), err)
|
||||
}
|
||||
})
|
||||
for _, cmd := range commands {
|
||||
args := strings.Split(cmd, " ")
|
||||
command := args[0]
|
||||
var params []string
|
||||
if len(args) > 1 {
|
||||
params = args[1:]
|
||||
}
|
||||
|
||||
cmd := exec.Command(command, params...)
|
||||
cmd.Env = os.Environ()
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("%s:\n%v\n\n%s", err, cmd, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue336(t *testing.T) {
|
||||
t.Parallel()
|
||||
// error when no migrations are found
|
||||
// https://github.com/pressly/goose/issues/336
|
||||
|
||||
tempDir := t.TempDir()
|
||||
params := []string{"--dir=" + tempDir, "sqlite3", filepath.Join(tempDir, "sql.db"), "up"}
|
||||
|
||||
_, err := runGoose(params...)
|
||||
check.HasError(t, err)
|
||||
check.Contains(t, err.Error(), "no migration files found")
|
||||
}
|
||||
|
||||
func TestLiteBinary(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
t.Cleanup(func() {
|
||||
if err := os.Remove("./bin/lite-goose"); err != nil {
|
||||
t.Logf("failed to remove %s resources: %v", t.Name(), err)
|
||||
}
|
||||
})
|
||||
|
||||
// this has to be done outside of the loop
|
||||
// since go only supports space separated tags list.
|
||||
cmd := exec.Command("go", "build", "-tags='no_postgres no_mysql no_sqlite3'", "-o", "./bin/lite-goose", "./cmd/goose")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("%s:\n%v\n\n%s", err, cmd, out)
|
||||
}
|
||||
|
||||
commands := []string{
|
||||
fmt.Sprintf("./bin/lite-goose -dir=%s create user_indices sql", dir),
|
||||
fmt.Sprintf("./bin/lite-goose -dir=%s fix", dir),
|
||||
}
|
||||
|
||||
for _, cmd := range commands {
|
||||
args := strings.Split(cmd, " ")
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Env = os.Environ()
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("%s:\n%v\n\n%s", err, cmd, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomBinary(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
commands := []string{
|
||||
"go build -o ./bin/custom-goose ./examples/go-migrations",
|
||||
"./bin/custom-goose -dir=examples/go-migrations sqlite3 go.db up",
|
||||
"./bin/custom-goose -dir=examples/go-migrations sqlite3 go.db version",
|
||||
"./bin/custom-goose -dir=examples/go-migrations sqlite3 go.db down",
|
||||
"./bin/custom-goose -dir=examples/go-migrations sqlite3 go.db status",
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := os.Remove("./go.db"); err != nil {
|
||||
t.Logf("failed to remove %s resources: %v", t.Name(), err)
|
||||
}
|
||||
})
|
||||
|
||||
for _, cmd := range commands {
|
||||
args := strings.Split(cmd, " ")
|
||||
out, err := exec.Command(args[0], args[1:]...).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("%s:\n%v\n\n%s", err, cmd, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed examples/sql-migrations/*.sql
|
||||
var migrations embed.FS
|
||||
|
||||
func TestEmbeddedMigrations(t *testing.T) {
|
||||
// not using t.Parallel here to avoid races
|
||||
db, err := sql.Open("sqlite", "sql_embed.db")
|
||||
if err != nil {
|
||||
t.Fatalf("Database open failed: %s", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := os.Remove("./sql_embed.db"); err != nil {
|
||||
t.Logf("failed to remove %s resources: %v", t.Name(), err)
|
||||
}
|
||||
})
|
||||
|
||||
db.SetMaxOpenConns(1)
|
||||
|
||||
// decouple from existing structure
|
||||
fsys, err := fs.Sub(migrations, "examples/sql-migrations")
|
||||
if err != nil {
|
||||
t.Fatalf("SubFS make failed: %s", err)
|
||||
}
|
||||
|
||||
SetBaseFS(fsys)
|
||||
check.NoError(t, SetDialect("sqlite3"))
|
||||
t.Cleanup(func() { SetBaseFS(nil) })
|
||||
|
||||
t.Run("Migration cycle", func(t *testing.T) {
|
||||
if err := Up(db, "."); err != nil {
|
||||
t.Errorf("Failed to run 'up' migrations: %s", err)
|
||||
}
|
||||
|
||||
ver, err := GetDBVersion(db)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get migrations version: %s", err)
|
||||
}
|
||||
|
||||
if ver != 3 {
|
||||
t.Errorf("Expected version 3 after 'up', got %d", ver)
|
||||
}
|
||||
|
||||
if err := Reset(db, "."); err != nil {
|
||||
t.Errorf("Failed to run 'down' migrations: %s", err)
|
||||
}
|
||||
|
||||
ver, err = GetDBVersion(db)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get migrations version: %s", err)
|
||||
}
|
||||
|
||||
if ver != 0 {
|
||||
t.Errorf("Expected version 0 after 'reset', got %d", ver)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Create uses os fs", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
if err := Create(db, tmpDir, "test", "sql"); err != nil {
|
||||
t.Errorf("Failed to create migration: %s", err)
|
||||
}
|
||||
|
||||
paths, _ := filepath.Glob(filepath.Join(tmpDir, "*test.sql"))
|
||||
if len(paths) == 0 {
|
||||
t.Errorf("Failed to find created migration")
|
||||
}
|
||||
|
||||
if err := Fix(tmpDir); err != nil {
|
||||
t.Errorf("Failed to 'fix' migrations: %s", err)
|
||||
}
|
||||
|
||||
_, err = os.Stat(filepath.Join(tmpDir, "00001_test.sql"))
|
||||
if err != nil {
|
||||
t.Errorf("Failed to locate fixed migration: %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func runGoose(params ...string) (string, error) {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cmdPath := filepath.Join(dir, binName)
|
||||
cmd := exec.Command(cmdPath, params...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%v\n%v", err, string(out))
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func countSQLFiles(t *testing.T, dir string) int {
|
||||
t.Helper()
|
||||
files, err := filepath.Glob(filepath.Join(dir, "*.sql"))
|
||||
check.NoError(t, err)
|
||||
return len(files)
|
||||
}
|
|
@ -73,7 +73,7 @@ func Bool(t *testing.T, got, want bool) {
|
|||
func Contains(t *testing.T, got, want string) {
|
||||
t.Helper()
|
||||
if !strings.Contains(got, want) {
|
||||
t.Errorf("failed to find substring %q in string value %q", got, want)
|
||||
t.Errorf("failed to find substring:\n%s\n\nin string value:\n%s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
-- +goose Up
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE users;
|
|
@ -0,0 +1,12 @@
|
|||
-- +goose Up
|
||||
CREATE TABLE posts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
author_id INTEGER NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (author_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE posts;
|
|
@ -0,0 +1,13 @@
|
|||
-- +goose Up
|
||||
CREATE TABLE comments (
|
||||
id INTEGER PRIMARY KEY,
|
||||
post_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (post_id) REFERENCES posts(id),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE comments;
|
|
@ -0,0 +1,23 @@
|
|||
-- +goose Up
|
||||
INSERT INTO users (id, username, email)
|
||||
VALUES
|
||||
(1, 'john_doe', 'john@example.com'),
|
||||
(2, 'jane_smith', 'jane@example.com'),
|
||||
(3, 'alice_wonderland', 'alice@example.com');
|
||||
|
||||
INSERT INTO posts (id, title, content, author_id)
|
||||
VALUES
|
||||
(1, 'Introduction to SQL', 'SQL is a powerful language for managing databases...', 1),
|
||||
(2, 'Data Modeling Techniques', 'Choosing the right data model is crucial...', 2),
|
||||
(3, 'Advanced Query Optimization', 'Optimizing queries can greatly improve...', 1);
|
||||
|
||||
INSERT INTO comments (id, post_id, user_id, content)
|
||||
VALUES
|
||||
(1, 1, 3, 'Great introduction! Looking forward to more.'),
|
||||
(2, 1, 2, 'SQL can be a bit tricky at first, but practice helps.'),
|
||||
(3, 2, 1, 'You covered normalization really well in this post.');
|
||||
|
||||
-- +goose Down
|
||||
DELETE FROM comments;
|
||||
DELETE FROM posts;
|
||||
DELETE FROM users;
|
|
@ -0,0 +1,15 @@
|
|||
-- +goose NO TRANSACTION
|
||||
|
||||
-- +goose Up
|
||||
CREATE VIEW posts_view AS
|
||||
SELECT
|
||||
p.id,
|
||||
p.title,
|
||||
p.content,
|
||||
p.created_at,
|
||||
u.username AS author
|
||||
FROM posts p
|
||||
JOIN users u ON p.author_id = u.id;
|
||||
|
||||
-- +goose Down
|
||||
DROP VIEW posts_view;
|
Loading…
Reference in New Issue