2019-02-12 16:57:26 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package migrate_test
|
|
|
|
|
|
|
|
import (
|
2020-01-13 13:44:55 +00:00
|
|
|
"context"
|
2019-02-12 16:57:26 +00:00
|
|
|
"database/sql"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
2019-12-27 11:48:47 +00:00
|
|
|
"storj.io/common/testcontext"
|
2021-04-23 10:52:40 +01:00
|
|
|
"storj.io/private/dbutil/pgtest"
|
|
|
|
"storj.io/private/dbutil/tempdb"
|
|
|
|
"storj.io/private/tagsql"
|
2019-11-14 19:46:15 +00:00
|
|
|
"storj.io/storj/private/migrate"
|
2019-02-12 16:57:26 +00:00
|
|
|
)
|
|
|
|
|
2019-09-20 17:26:07 +01:00
|
|
|
func TestBasicMigrationSqliteNoRebind(t *testing.T) {
|
2020-01-13 13:18:48 +00:00
|
|
|
ctx := testcontext.New(t)
|
|
|
|
defer ctx.Cleanup()
|
2020-01-17 18:22:44 +00:00
|
|
|
|
2020-10-29 08:11:02 +00:00
|
|
|
db, err := tagsql.Open(ctx, "sqlite3", ":memory:")
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer func() { assert.NoError(t, db.Close()) }()
|
|
|
|
|
2020-01-19 14:41:23 +00:00
|
|
|
basicMigration(ctx, t, db, db)
|
2019-09-20 17:26:07 +01:00
|
|
|
}
|
|
|
|
|
2019-02-12 16:57:26 +00:00
|
|
|
func TestBasicMigrationSqlite(t *testing.T) {
|
2020-01-13 13:18:48 +00:00
|
|
|
ctx := testcontext.New(t)
|
|
|
|
defer ctx.Cleanup()
|
2020-01-17 18:22:44 +00:00
|
|
|
|
2020-10-29 08:11:02 +00:00
|
|
|
db, err := tagsql.Open(ctx, "sqlite3", ":memory:")
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer func() { assert.NoError(t, db.Close()) }()
|
|
|
|
|
2020-01-19 14:41:23 +00:00
|
|
|
basicMigration(ctx, t, db, &sqliteDB{DB: db})
|
2019-02-12 16:57:26 +00:00
|
|
|
}
|
|
|
|
|
2020-04-27 20:34:42 +01:00
|
|
|
func TestBasicMigration(t *testing.T) {
|
|
|
|
pgtest.Run(t, func(ctx *testcontext.Context, t *testing.T, connstr string) {
|
|
|
|
db, err := tempdb.OpenUnique(ctx, connstr, "create-")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer func() { assert.NoError(t, db.Close()) }()
|
2020-01-17 18:22:44 +00:00
|
|
|
|
2020-04-27 20:34:42 +01:00
|
|
|
basicMigration(ctx, t, db.DB, &postgresDB{DB: db.DB})
|
|
|
|
})
|
2019-02-12 16:57:26 +00:00
|
|
|
}
|
|
|
|
|
2020-01-30 19:38:25 +00:00
|
|
|
func basicMigration(ctx *testcontext.Context, t *testing.T, db tagsql.DB, testDB tagsql.DB) {
|
2020-10-13 13:47:55 +01:00
|
|
|
dbName := strings.ToLower(`versions_` + strings.ReplaceAll(t.Name(), "/", "_"))
|
2020-01-17 18:22:44 +00:00
|
|
|
defer func() { assert.NoError(t, dropTables(ctx, db, dbName, "users")) }()
|
2019-02-12 16:57:26 +00:00
|
|
|
|
2020-06-05 10:47:39 +01:00
|
|
|
/* #nosec G306 */ // This is a test besides the file contains just test data.
|
2022-10-11 12:39:08 +01:00
|
|
|
err := os.WriteFile(ctx.File("alpha.txt"), []byte("test"), 0644)
|
2019-02-12 16:57:26 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
m := migrate.Migration{
|
|
|
|
Table: dbName,
|
|
|
|
Steps: []*migrate.Step{
|
|
|
|
{
|
2020-10-09 13:35:34 +01:00
|
|
|
DB: &testDB,
|
2019-02-12 16:57:26 +00:00
|
|
|
Description: "Initialize Table",
|
|
|
|
Version: 1,
|
|
|
|
Action: migrate.SQL{
|
|
|
|
`CREATE TABLE users (id int)`,
|
|
|
|
`INSERT INTO users (id) VALUES (1)`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-10-09 13:35:34 +01:00
|
|
|
DB: &testDB,
|
2019-02-12 16:57:26 +00:00
|
|
|
Description: "Move files",
|
|
|
|
Version: 2,
|
2020-01-30 19:38:25 +00:00
|
|
|
Action: migrate.Func(func(_ context.Context, log *zap.Logger, _ tagsql.DB, tx tagsql.Tx) error {
|
2019-02-12 16:57:26 +00:00
|
|
|
return os.Rename(ctx.File("alpha.txt"), ctx.File("beta.txt"))
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-01-13 13:44:55 +00:00
|
|
|
dbVersion, err := m.CurrentVersion(ctx, nil, testDB)
|
2019-11-19 20:52:57 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, dbVersion, -1)
|
|
|
|
|
2020-01-13 13:44:55 +00:00
|
|
|
err = m.Run(ctx, zap.NewNop())
|
2019-02-12 16:57:26 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2020-01-13 13:44:55 +00:00
|
|
|
dbVersion, err = m.CurrentVersion(ctx, nil, testDB)
|
2019-11-19 20:52:57 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, dbVersion, 2)
|
|
|
|
|
|
|
|
m2 := migrate.Migration{
|
|
|
|
Table: dbName,
|
|
|
|
Steps: []*migrate.Step{
|
|
|
|
{
|
2020-10-09 13:35:34 +01:00
|
|
|
DB: &testDB,
|
2019-11-19 20:52:57 +00:00
|
|
|
Version: 3,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2020-01-13 13:44:55 +00:00
|
|
|
dbVersion, err = m2.CurrentVersion(ctx, nil, testDB)
|
2019-11-19 20:52:57 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, dbVersion, 2)
|
|
|
|
|
2019-02-12 16:57:26 +00:00
|
|
|
var version int
|
2020-06-05 10:47:39 +01:00
|
|
|
/* #nosec G202 */ // This is a test besides the dbName value is generated in
|
|
|
|
// a controlled way
|
2020-01-17 18:22:44 +00:00
|
|
|
err = db.QueryRow(ctx, `SELECT MAX(version) FROM `+dbName).Scan(&version)
|
2019-02-12 16:57:26 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 2, version)
|
|
|
|
|
|
|
|
var id int
|
2020-01-17 18:22:44 +00:00
|
|
|
err = db.QueryRow(ctx, `SELECT MAX(id) FROM users`).Scan(&id)
|
2019-02-12 16:57:26 +00:00
|
|
|
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)
|
2022-10-11 12:39:08 +01:00
|
|
|
data, err := os.ReadFile(ctx.File("beta.txt"))
|
2019-02-12 16:57:26 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, []byte("test"), data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMultipleMigrationSqlite(t *testing.T) {
|
2020-10-29 08:11:02 +00:00
|
|
|
ctx := testcontext.New(t)
|
|
|
|
defer ctx.Cleanup()
|
|
|
|
|
|
|
|
db, err := tagsql.Open(ctx, "sqlite3", ":memory:")
|
2019-02-12 16:57:26 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
defer func() { assert.NoError(t, db.Close()) }()
|
2020-01-19 14:41:23 +00:00
|
|
|
|
2020-10-29 08:11:02 +00:00
|
|
|
multipleMigration(ctx, t, db, &sqliteDB{DB: db})
|
2019-02-12 16:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestMultipleMigrationPostgres(t *testing.T) {
|
2020-10-29 08:11:02 +00:00
|
|
|
ctx := testcontext.New(t)
|
|
|
|
defer ctx.Cleanup()
|
|
|
|
|
2020-04-27 20:34:42 +01:00
|
|
|
connstr := pgtest.PickPostgres(t)
|
2019-02-12 16:57:26 +00:00
|
|
|
|
2020-10-29 08:11:02 +00:00
|
|
|
db, err := tagsql.Open(ctx, "pgx", connstr)
|
2019-02-12 16:57:26 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
defer func() { assert.NoError(t, db.Close()) }()
|
|
|
|
|
2020-10-29 08:11:02 +00:00
|
|
|
multipleMigration(ctx, t, db, &postgresDB{DB: db})
|
2019-02-12 16:57:26 +00:00
|
|
|
}
|
|
|
|
|
2020-10-29 08:11:02 +00:00
|
|
|
func multipleMigration(ctx context.Context, t *testing.T, db tagsql.DB, testDB tagsql.DB) {
|
2019-02-12 16:57:26 +00:00
|
|
|
dbName := strings.ToLower(`versions_` + t.Name())
|
2020-01-17 18:22:44 +00:00
|
|
|
defer func() { assert.NoError(t, dropTables(ctx, db, dbName)) }()
|
2019-02-12 16:57:26 +00:00
|
|
|
|
|
|
|
steps := 0
|
|
|
|
m := migrate.Migration{
|
|
|
|
Table: dbName,
|
|
|
|
Steps: []*migrate.Step{
|
|
|
|
{
|
2020-10-09 13:35:34 +01:00
|
|
|
DB: &testDB,
|
2019-02-12 16:57:26 +00:00
|
|
|
Description: "Step 1",
|
|
|
|
Version: 1,
|
2020-01-30 19:38:25 +00:00
|
|
|
Action: migrate.Func(func(ctx context.Context, log *zap.Logger, _ tagsql.DB, tx tagsql.Tx) error {
|
2019-02-12 16:57:26 +00:00
|
|
|
steps++
|
|
|
|
return nil
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
{
|
2020-10-09 13:35:34 +01:00
|
|
|
DB: &testDB,
|
2019-02-12 16:57:26 +00:00
|
|
|
Description: "Step 2",
|
|
|
|
Version: 2,
|
2020-01-30 19:38:25 +00:00
|
|
|
Action: migrate.Func(func(ctx context.Context, log *zap.Logger, _ tagsql.DB, tx tagsql.Tx) error {
|
2019-02-12 16:57:26 +00:00
|
|
|
steps++
|
|
|
|
return nil
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-01-13 13:44:55 +00:00
|
|
|
err := m.Run(ctx, zap.NewNop())
|
2019-02-12 16:57:26 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 2, steps)
|
|
|
|
|
|
|
|
m.Steps = append(m.Steps, &migrate.Step{
|
2020-10-09 13:35:34 +01:00
|
|
|
DB: &testDB,
|
2019-02-12 16:57:26 +00:00
|
|
|
Description: "Step 3",
|
|
|
|
Version: 3,
|
2020-01-30 19:38:25 +00:00
|
|
|
Action: migrate.Func(func(ctx context.Context, log *zap.Logger, _ tagsql.DB, tx tagsql.Tx) error {
|
2019-02-12 16:57:26 +00:00
|
|
|
steps++
|
|
|
|
return nil
|
|
|
|
}),
|
|
|
|
})
|
2020-01-13 13:44:55 +00:00
|
|
|
err = m.Run(ctx, zap.NewNop())
|
2019-02-12 16:57:26 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
var version int
|
2020-06-05 10:47:39 +01:00
|
|
|
/* #nosec G202 */ // This is a test besides the dbName value is generated in
|
|
|
|
// a controlled way
|
2020-01-17 18:22:44 +00:00
|
|
|
err = db.QueryRow(ctx, `SELECT MAX(version) FROM `+dbName).Scan(&version)
|
2019-02-12 16:57:26 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 3, version)
|
|
|
|
|
|
|
|
assert.Equal(t, 3, steps)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFailedMigrationSqlite(t *testing.T) {
|
2020-10-29 08:11:02 +00:00
|
|
|
ctx := testcontext.New(t)
|
|
|
|
defer ctx.Cleanup()
|
|
|
|
|
|
|
|
db, err := tagsql.Open(ctx, "sqlite3", ":memory:")
|
2019-02-12 16:57:26 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
defer func() { assert.NoError(t, db.Close()) }()
|
|
|
|
|
2020-10-29 08:11:02 +00:00
|
|
|
failedMigration(ctx, t, db, &sqliteDB{DB: db})
|
2019-02-12 16:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestFailedMigrationPostgres(t *testing.T) {
|
2020-10-29 08:11:02 +00:00
|
|
|
ctx := testcontext.New(t)
|
|
|
|
defer ctx.Cleanup()
|
|
|
|
|
2020-04-27 20:34:42 +01:00
|
|
|
connstr := pgtest.PickPostgres(t)
|
2019-02-12 16:57:26 +00:00
|
|
|
|
2020-10-29 08:11:02 +00:00
|
|
|
db, err := tagsql.Open(ctx, "pgx", connstr)
|
2019-02-12 16:57:26 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
defer func() { assert.NoError(t, db.Close()) }()
|
|
|
|
|
2020-10-29 08:11:02 +00:00
|
|
|
failedMigration(ctx, t, db, &postgresDB{DB: db})
|
2019-02-12 16:57:26 +00:00
|
|
|
}
|
|
|
|
|
2020-10-29 08:11:02 +00:00
|
|
|
func failedMigration(ctx context.Context, t *testing.T, db tagsql.DB, testDB tagsql.DB) {
|
2019-02-12 16:57:26 +00:00
|
|
|
dbName := strings.ToLower(`versions_` + t.Name())
|
2020-01-17 18:22:44 +00:00
|
|
|
defer func() { assert.NoError(t, dropTables(ctx, db, dbName)) }()
|
2019-02-12 16:57:26 +00:00
|
|
|
|
|
|
|
m := migrate.Migration{
|
|
|
|
Table: dbName,
|
|
|
|
Steps: []*migrate.Step{
|
|
|
|
{
|
2020-10-09 13:35:34 +01:00
|
|
|
DB: &testDB,
|
2019-02-12 16:57:26 +00:00
|
|
|
Description: "Step 1",
|
|
|
|
Version: 1,
|
2020-01-30 19:38:25 +00:00
|
|
|
Action: migrate.Func(func(ctx context.Context, log *zap.Logger, _ tagsql.DB, tx tagsql.Tx) error {
|
2019-02-12 16:57:26 +00:00
|
|
|
return fmt.Errorf("migration failed")
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-01-13 13:44:55 +00:00
|
|
|
err := m.Run(ctx, zap.NewNop())
|
2019-02-12 16:57:26 +00:00
|
|
|
require.Error(t, err, "migration failed")
|
|
|
|
|
|
|
|
var version sql.NullInt64
|
2020-06-05 10:47:39 +01:00
|
|
|
/* #nosec G202 */ // This is a test besides the dbName value is generated in
|
|
|
|
// a controlled way
|
2020-01-17 18:22:44 +00:00
|
|
|
err = db.QueryRow(ctx, `SELECT MAX(version) FROM `+dbName).Scan(&version)
|
2019-02-12 16:57:26 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, false, version.Valid)
|
|
|
|
}
|
|
|
|
|
2019-02-19 09:39:04 +00:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2019-02-12 16:57:26 +00:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
|
2020-01-17 18:22:44 +00:00
|
|
|
func dropTables(ctx context.Context, db tagsql.DB, names ...string) error {
|
2019-02-12 16:57:26 +00:00
|
|
|
var errlist errs.Group
|
|
|
|
for _, name := range names {
|
2020-01-17 18:22:44 +00:00
|
|
|
_, err := db.Exec(ctx, `DROP TABLE `+name)
|
2019-02-12 16:57:26 +00:00
|
|
|
errlist.Add(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return errlist.Err()
|
|
|
|
}
|