storj/private/migrate/versions_test.go
paul cannon 378b863b2b private,satellite: unite all the "temp db schema" things
first, so that they all work the same way, because it's getting
complicated, and second, so that we can do the appropriate thing
instead of CREATE SCHEMA for cockroachdb.

Change-Id: I27fbaeeb6223a3e06d97bcf692a2d014b31465f7
2019-12-05 15:36:59 +00:00

331 lines
7.5 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package migrate_test
import (
"database/sql"
"fmt"
"io/ioutil"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/storj/private/dbutil/pgutil/pgtest"
"storj.io/storj/private/dbutil/tempdb"
"storj.io/storj/private/migrate"
"storj.io/storj/private/testcontext"
)
func TestBasicMigrationSqliteNoRebind(t *testing.T) {
db, err := sql.Open("sqlite3", ":memory:")
require.NoError(t, err)
defer func() { assert.NoError(t, db.Close()) }()
basicMigration(t, db, db)
}
func TestBasicMigrationSqlite(t *testing.T) {
db, err := sql.Open("sqlite3", ":memory:")
require.NoError(t, err)
defer func() { assert.NoError(t, db.Close()) }()
basicMigration(t, db, &sqliteDB{DB: db})
}
func TestBasicMigrationPostgres(t *testing.T) {
if *pgtest.ConnStr == "" {
t.Skipf("postgres flag missing, example:\n-postgres-test-db=%s", pgtest.DefaultConnStr)
}
testBasicMigrationGeneric(t, *pgtest.ConnStr)
}
func TestBasicMigrationCockroach(t *testing.T) {
if *pgtest.CrdbConnStr == "" {
t.Skipf("cockroach flag missing, example:\n-cockroach-test-db=%s", pgtest.DefaultCrdbConnStr)
}
testBasicMigrationGeneric(t, *pgtest.CrdbConnStr)
}
func testBasicMigrationGeneric(t *testing.T, connStr string) {
db, err := tempdb.OpenUnique(connStr, "create-")
if err != nil {
t.Fatal(err)
}
defer func() { assert.NoError(t, db.Close()) }()
basicMigration(t, db.DB, &postgresDB{DB: db.DB})
}
func basicMigration(t *testing.T, db *sql.DB, testDB migrate.DB) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
dbName := strings.ToLower(`versions_` + t.Name())
defer func() { assert.NoError(t, dropTables(db, dbName, "users")) }()
err := ioutil.WriteFile(ctx.File("alpha.txt"), []byte("test"), 0644)
require.NoError(t, err)
m := migrate.Migration{
Table: dbName,
Steps: []*migrate.Step{
{
DB: testDB,
Description: "Initialize Table",
Version: 1,
Action: migrate.SQL{
`CREATE TABLE users (id int)`,
`INSERT INTO users (id) VALUES (1)`,
},
},
{
DB: testDB,
Description: "Move files",
Version: 2,
Action: migrate.Func(func(log *zap.Logger, _ migrate.DB, tx *sql.Tx) error {
return os.Rename(ctx.File("alpha.txt"), ctx.File("beta.txt"))
}),
},
},
}
dbVersion, err := m.CurrentVersion(nil, testDB)
assert.NoError(t, err)
assert.Equal(t, dbVersion, -1)
err = m.Run(zap.NewNop())
assert.NoError(t, err)
dbVersion, err = m.CurrentVersion(nil, testDB)
assert.NoError(t, err)
assert.Equal(t, dbVersion, 2)
m2 := migrate.Migration{
Table: dbName,
Steps: []*migrate.Step{
{
DB: testDB,
Version: 3,
},
},
}
dbVersion, err = m2.CurrentVersion(nil, testDB)
assert.NoError(t, err)
assert.Equal(t, dbVersion, 2)
var version int
err = db.QueryRow(`SELECT MAX(version) FROM ` + dbName).Scan(&version)
assert.NoError(t, err)
assert.Equal(t, 2, version)
var id int
err = db.QueryRow(`SELECT MAX(id) FROM users`).Scan(&id)
assert.NoError(t, err)
assert.Equal(t, 1, id)
// file not exists
_, err = os.Stat(ctx.File("alpha.txt"))
assert.Error(t, err)
// file exists
_, err = os.Stat(ctx.File("beta.txt"))
assert.NoError(t, err)
data, err := ioutil.ReadFile(ctx.File("beta.txt"))
assert.NoError(t, err)
assert.Equal(t, []byte("test"), data)
}
func TestMultipleMigrationSqlite(t *testing.T) {
db, err := sql.Open("sqlite3", ":memory:")
require.NoError(t, err)
defer func() { assert.NoError(t, db.Close()) }()
multipleMigration(t, db, &sqliteDB{DB: db})
}
func TestMultipleMigrationPostgres(t *testing.T) {
if *pgtest.ConnStr == "" {
t.Skipf("postgres flag missing, example:\n-postgres-test-db=%s", pgtest.DefaultConnStr)
}
db, err := sql.Open("postgres", *pgtest.ConnStr)
require.NoError(t, err)
defer func() { assert.NoError(t, db.Close()) }()
multipleMigration(t, db, &postgresDB{DB: db})
}
func multipleMigration(t *testing.T, db *sql.DB, testDB migrate.DB) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
dbName := strings.ToLower(`versions_` + t.Name())
defer func() { assert.NoError(t, dropTables(db, dbName)) }()
steps := 0
m := migrate.Migration{
Table: dbName,
Steps: []*migrate.Step{
{
DB: testDB,
Description: "Step 1",
Version: 1,
Action: migrate.Func(func(log *zap.Logger, _ migrate.DB, tx *sql.Tx) error {
steps++
return nil
}),
},
{
DB: testDB,
Description: "Step 2",
Version: 2,
Action: migrate.Func(func(log *zap.Logger, _ migrate.DB, tx *sql.Tx) error {
steps++
return nil
}),
},
},
}
err := m.Run(zap.NewNop())
assert.NoError(t, err)
assert.Equal(t, 2, steps)
m.Steps = append(m.Steps, &migrate.Step{
DB: testDB,
Description: "Step 3",
Version: 3,
Action: migrate.Func(func(log *zap.Logger, _ migrate.DB, tx *sql.Tx) error {
steps++
return nil
}),
})
err = m.Run(zap.NewNop())
assert.NoError(t, err)
var version int
err = db.QueryRow(`SELECT MAX(version) FROM ` + dbName).Scan(&version)
assert.NoError(t, err)
assert.Equal(t, 3, version)
assert.Equal(t, 3, steps)
}
func TestFailedMigrationSqlite(t *testing.T) {
db, err := sql.Open("sqlite3", ":memory:")
require.NoError(t, err)
defer func() { assert.NoError(t, db.Close()) }()
failedMigration(t, db, &sqliteDB{DB: db})
}
func TestFailedMigrationPostgres(t *testing.T) {
if *pgtest.ConnStr == "" {
t.Skipf("postgres flag missing, example:\n-postgres-test-db=%s", pgtest.DefaultConnStr)
}
db, err := sql.Open("postgres", *pgtest.ConnStr)
require.NoError(t, err)
defer func() { assert.NoError(t, db.Close()) }()
failedMigration(t, db, &postgresDB{DB: db})
}
func failedMigration(t *testing.T, db *sql.DB, testDB migrate.DB) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
dbName := strings.ToLower(`versions_` + t.Name())
defer func() { assert.NoError(t, dropTables(db, dbName)) }()
m := migrate.Migration{
Table: dbName,
Steps: []*migrate.Step{
{
DB: testDB,
Description: "Step 1",
Version: 1,
Action: migrate.Func(func(log *zap.Logger, _ migrate.DB, tx *sql.Tx) error {
return fmt.Errorf("migration failed")
}),
},
},
}
err := m.Run(zap.NewNop())
require.Error(t, err, "migration failed")
var version sql.NullInt64
err = db.QueryRow(`SELECT MAX(version) FROM ` + dbName).Scan(&version)
assert.NoError(t, err)
assert.Equal(t, false, version.Valid)
}
func TestTargetVersion(t *testing.T) {
m := migrate.Migration{
Table: "test",
Steps: []*migrate.Step{
{
Description: "Step 1",
Version: 1,
Action: migrate.SQL{},
},
{
Description: "Step 2",
Version: 2,
Action: migrate.SQL{},
},
{
Description: "Step 2.2",
Version: 2,
Action: migrate.SQL{},
},
{
Description: "Step 3",
Version: 3,
Action: migrate.SQL{},
},
},
}
testedMigration := m.TargetVersion(2)
assert.Equal(t, 3, len(testedMigration.Steps))
}
func TestInvalidStepsOrder(t *testing.T) {
m := migrate.Migration{
Table: "test",
Steps: []*migrate.Step{
{
Version: 0,
},
{
Version: 1,
},
{
Version: 4,
},
{
Version: 2,
},
},
}
err := m.ValidateSteps()
require.Error(t, err, "migrate: steps have incorrect order")
}
func dropTables(db *sql.DB, names ...string) error {
var errlist errs.Group
for _, name := range names {
_, err := db.Exec(`DROP TABLE ` + name)
errlist.Add(err)
}
return errlist.Err()
}