github.com/CloudCom/goose@v0.0.0-20151110184009-e03c3249c21b/lib/goose/migrate_test.go (about)

     1  package goose
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	_ "github.com/go-sql-driver/mysql"
    10  	_ "github.com/lib/pq"
    11  	_ "github.com/mattn/go-sqlite3"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func getSqlite3Driver(t *testing.T) DBDriver {
    18  	return DBDriver{
    19  		Name:    "sqlite3",
    20  		Dialect: Sqlite3Dialect{},
    21  		OpenStr: ":memory:",
    22  	}
    23  }
    24  
    25  func getMysqlDriver(t *testing.T) DBDriver {
    26  	dsn := os.Getenv("MYSQL_DATABASE_DSN")
    27  	if dsn == "" {
    28  		t.SkipNow()
    29  	}
    30  	return DBDriver{
    31  		Name:    "mysql",
    32  		Dialect: MySqlDialect{},
    33  		OpenStr: dsn,
    34  	}
    35  }
    36  
    37  func getPostgresDriver(t *testing.T) DBDriver {
    38  	dsn := os.Getenv("POSTGRES_DATABASE_DSN")
    39  	if dsn == "" {
    40  		t.SkipNow()
    41  	}
    42  	return DBDriver{
    43  		Name:    "postgres",
    44  		Dialect: PostgresDialect{},
    45  		OpenStr: dsn,
    46  	}
    47  }
    48  
    49  func getRedshiftDriver(t *testing.T) DBDriver {
    50  	dsn := os.Getenv("REDSHIFT_DATABASE_DSN")
    51  	if dsn == "" {
    52  		t.SkipNow()
    53  	}
    54  	return DBDriver{
    55  		Name:    "postgres",
    56  		Dialect: RedshiftDialect{},
    57  		OpenStr: dsn,
    58  	}
    59  }
    60  
    61  func TestMigrationSorterLen(t *testing.T) {
    62  	ms := migrationSorter{
    63  		{Version: 1},
    64  		{Version: 2},
    65  		{Version: 4},
    66  		{Version: 3},
    67  	}
    68  	l := ms.Len()
    69  	if l != 4 {
    70  		t.Errorf("expected ms.Len() == 4, but got %d\n", l)
    71  	}
    72  }
    73  
    74  func TestMigrationSorterSwap(t *testing.T) {
    75  	ms := migrationSorter{
    76  		{Version: 1},
    77  		{Version: 2},
    78  		{Version: 4},
    79  		{Version: 3},
    80  	}
    81  	ms.Swap(1, 2)
    82  	if ms[1].Version != 4 {
    83  		t.Errorf("expected ms[1].Version == 4, but got %d\n", ms[1].Version)
    84  	}
    85  	if ms[2].Version != 2 {
    86  		t.Errorf("expected ms[2].Version == 2, but got %d\n", ms[1].Version)
    87  	}
    88  }
    89  
    90  func TestMigrationSorterLess(t *testing.T) {
    91  	ms := migrationSorter{
    92  		{Version: 1},
    93  		{Version: 2},
    94  		{Version: 4},
    95  		{Version: 3},
    96  	}
    97  	v := ms.Less(2, 3)
    98  	if v != false {
    99  		t.Errorf("expected ms.Less(2,3) == false, but got %v\n", v)
   100  	}
   101  	v = ms.Less(3, 2)
   102  	if v != true {
   103  		t.Errorf("expected ms.Less(3,2) == true, but got %v\n", v)
   104  	}
   105  }
   106  
   107  func setupMigrationsDir(migrationMap map[string][2]string) (string, func()) {
   108  	td, err := ioutil.TempDir("", "goose-test-")
   109  	if err != nil {
   110  		panic(err)
   111  	}
   112  
   113  	dbPath := filepath.Join(td, "db")
   114  	migrationsPath := filepath.Join(dbPath, "migrations")
   115  	os.MkdirAll(migrationsPath, 0700)
   116  
   117  	for name, migrations := range migrationMap {
   118  		migStr := `-- +goose Up
   119  ` + migrations[0] + `
   120  
   121  -- +goose Down
   122  ` + migrations[1] + `
   123  `
   124  		if err := ioutil.WriteFile(filepath.Join(migrationsPath, name), []byte(migStr), 0600); err != nil {
   125  			panic(err)
   126  		}
   127  	}
   128  
   129  	return migrationsPath, func() { os.RemoveAll(td) }
   130  }
   131  
   132  func TestCollectMigrations(t *testing.T) {
   133  	md, mdCleanup := setupMigrationsDir(map[string][2]string{
   134  		"20010203040506_first.sql":  [2]string{"SELECT 1;", "SELECT 1;"},
   135  		"20010203040507_second.sql": [2]string{"SELECT 2;", "SELECT 2;"},
   136  		"20010203040508_third.sql":  [2]string{"SELECT 3;", "SELECT 3;"},
   137  	})
   138  	defer mdCleanup()
   139  
   140  	migs, err := CollectMigrations(md)
   141  	require.NoError(t, err)
   142  
   143  	assert.Len(t, migs, 3)
   144  	assert.Contains(t, migs, &Migration{
   145  		Version:   20010203040506,
   146  		IsApplied: false,
   147  		Source:    filepath.Join(md, "20010203040506_first.sql"),
   148  	})
   149  	assert.Contains(t, migs, &Migration{
   150  		Version:   20010203040507,
   151  		IsApplied: false,
   152  		Source:    filepath.Join(md, "20010203040507_second.sql"),
   153  	})
   154  	assert.Contains(t, migs, &Migration{
   155  		Version:   20010203040508,
   156  		IsApplied: false,
   157  		Source:    filepath.Join(md, "20010203040508_third.sql"),
   158  	})
   159  }
   160  
   161  func testRunMigrationsOnDb(t *testing.T, driver DBDriver) {
   162  	md, mdCleanup := setupMigrationsDir(map[string][2]string{
   163  		"20010203040506_setup.sql": [2]string{"CREATE TABLE test(value VARCHAR(20));", "DROP TABLE test;"},
   164  		"20010203040507_one.sql":   [2]string{"INSERT INTO test(value) VALUES('one');", "DELETE FROM test WHERE value = 'one';"},
   165  		"20010203040508_two.sql":   [2]string{"INSERT INTO test(value) VALUES('two');", "DELETE FROM test WHERE value = 'two';"},
   166  	})
   167  	defer mdCleanup()
   168  	conf := &DBConf{
   169  		Driver:        driver,
   170  		MigrationsDir: md,
   171  	}
   172  
   173  	db, err := OpenDBFromDBConf(conf)
   174  	require.NoError(t, err)
   175  
   176  	db.Exec("DROP TABLE goose_db_version")
   177  	db.Exec("DROP TABLE test")
   178  
   179  	err = RunMigrationsOnDb(conf, conf.MigrationsDir, 20010203040508, db)
   180  	require.NoError(t, err)
   181  
   182  	rows, err := db.Query("SELECT value FROM test")
   183  	require.NoError(t, err)
   184  	defer rows.Close()
   185  	var values []string
   186  	for rows.Next() {
   187  		var value string
   188  		err := rows.Scan(&value)
   189  		require.NoError(t, err)
   190  		values = append(values, value)
   191  	}
   192  
   193  	assert.Len(t, values, 2)
   194  	assert.Contains(t, values, "one")
   195  	assert.Contains(t, values, "two")
   196  }
   197  func TestRunMigrationsOnDb_sqlite3(t *testing.T) {
   198  	testRunMigrationsOnDb(t, getSqlite3Driver(t))
   199  }
   200  func TestRunMigrationsOnDb_mysql(t *testing.T) {
   201  	testRunMigrationsOnDb(t, getMysqlDriver(t))
   202  }
   203  func TestRunMigrationsOnDb_postgres(t *testing.T) {
   204  	testRunMigrationsOnDb(t, getPostgresDriver(t))
   205  }
   206  func TestRunMigrationsOnDb_redshift(t *testing.T) {
   207  	testRunMigrationsOnDb(t, getRedshiftDriver(t))
   208  }
   209  
   210  func testRunMigrationsOnDb_missingMiddle(t *testing.T, driver DBDriver) {
   211  	md, mdCleanup := setupMigrationsDir(map[string][2]string{
   212  		"20010203040506_setup.sql": [2]string{"CREATE TABLE test(value VARCHAR(20));", "DROP TABLE test;"},
   213  		"20010203040507_one.sql":   [2]string{"INSERT INTO test(value) VALUES('one');", "DELETE FROM test WHERE value = 'one';"},
   214  		"20010203040508_two.sql":   [2]string{"INSERT INTO test(value) VALUES('two');", "DELETE FROM test WHERE value = 'two';"},
   215  	})
   216  	defer mdCleanup()
   217  	conf := &DBConf{
   218  		Driver:        driver,
   219  		MigrationsDir: md,
   220  	}
   221  
   222  	db, err := OpenDBFromDBConf(conf)
   223  	require.NoError(t, err)
   224  
   225  	db.Exec("DROP TABLE goose_db_version")
   226  	db.Exec("DROP TABLE test")
   227  
   228  	// make the middle migration disappear for a moment
   229  	err = os.Rename(filepath.Join(md, "20010203040507_one.sql"), filepath.Join(md, "20010203040507_one.sql_"))
   230  	require.NoError(t, err)
   231  
   232  	err = RunMigrationsOnDb(conf, conf.MigrationsDir, 20010203040508, db)
   233  	require.NoError(t, err)
   234  
   235  	rows, err := db.Query("SELECT value FROM test")
   236  	require.NoError(t, err)
   237  	defer rows.Close()
   238  	var values []string
   239  	for rows.Next() {
   240  		var value string
   241  		err := rows.Scan(&value)
   242  		require.NoError(t, err)
   243  		values = append(values, value)
   244  	}
   245  
   246  	assert.Len(t, values, 1)
   247  	assert.Contains(t, values, "two")
   248  
   249  	// now put it back
   250  	err = os.Rename(filepath.Join(md, "20010203040507_one.sql_"), filepath.Join(md, "20010203040507_one.sql"))
   251  	require.NoError(t, err)
   252  
   253  	err = RunMigrationsOnDb(conf, conf.MigrationsDir, 20010203040508, db)
   254  	require.NoError(t, err)
   255  
   256  	rows, err = db.Query("SELECT value FROM test")
   257  	require.NoError(t, err)
   258  	defer rows.Close()
   259  	values = []string{}
   260  	for rows.Next() {
   261  		var value string
   262  		err := rows.Scan(&value)
   263  		require.NoError(t, err)
   264  		values = append(values, value)
   265  	}
   266  
   267  	assert.Len(t, values, 2)
   268  	assert.Contains(t, values, "one")
   269  	assert.Contains(t, values, "two")
   270  }
   271  func TestRunMigrationsOnDb_missingMiddle_sqlite3(t *testing.T) {
   272  	testRunMigrationsOnDb_missingMiddle(t, getSqlite3Driver(t))
   273  }
   274  func TestRunMigrationsOnDb_missingMiddle_mysql(t *testing.T) {
   275  	testRunMigrationsOnDb_missingMiddle(t, getMysqlDriver(t))
   276  }
   277  func TestRunMigrationsOnDb_missingMiddle_postgres(t *testing.T) {
   278  	testRunMigrationsOnDb_missingMiddle(t, getPostgresDriver(t))
   279  }
   280  func TestRunMigrationsOnDb_missingMiddle_redshift(t *testing.T) {
   281  	testRunMigrationsOnDb_missingMiddle(t, getRedshiftDriver(t))
   282  }
   283  
   284  func testRunMigrationsOnDb_down(t *testing.T, driver DBDriver) {
   285  	md, mdCleanup := setupMigrationsDir(map[string][2]string{
   286  		"20010203040506_setup.sql": [2]string{"CREATE TABLE test(value VARCHAR(20));", "DROP TABLE test;"},
   287  		"20010203040507_one.sql":   [2]string{"INSERT INTO test(value) VALUES('one');", "DELETE FROM test WHERE value = 'one';"},
   288  		"20010203040508_two.sql":   [2]string{"INSERT INTO test(value) VALUES('two');", "DELETE FROM test WHERE value = 'two';"},
   289  	})
   290  	defer mdCleanup()
   291  	conf := &DBConf{
   292  		Driver:        driver,
   293  		MigrationsDir: md,
   294  	}
   295  
   296  	db, err := OpenDBFromDBConf(conf)
   297  	require.NoError(t, err)
   298  
   299  	db.Exec("DROP TABLE goose_db_version")
   300  	db.Exec("DROP TABLE test")
   301  
   302  	// up
   303  	err = RunMigrationsOnDb(conf, conf.MigrationsDir, 20010203040508, db)
   304  	require.NoError(t, err)
   305  
   306  	// down
   307  	err = RunMigrationsOnDb(conf, conf.MigrationsDir, 20010203040507, db)
   308  	require.NoError(t, err)
   309  
   310  	rows, err := db.Query("SELECT value FROM test")
   311  	require.NoError(t, err)
   312  	defer rows.Close()
   313  	var values []string
   314  	for rows.Next() {
   315  		var value string
   316  		err := rows.Scan(&value)
   317  		require.NoError(t, err)
   318  		values = append(values, value)
   319  	}
   320  
   321  	assert.Len(t, values, 1)
   322  	assert.Contains(t, values, "one")
   323  }
   324  func TestRunMigrationsOnDb_down_sqlite3(t *testing.T) {
   325  	testRunMigrationsOnDb_down(t, getSqlite3Driver(t))
   326  }
   327  func TestRunMigrationsOnDb_down_mysql(t *testing.T) {
   328  	testRunMigrationsOnDb_down(t, getMysqlDriver(t))
   329  }
   330  func TestRunMigrationsOnDb_down_postgres(t *testing.T) {
   331  	testRunMigrationsOnDb_down(t, getPostgresDriver(t))
   332  }
   333  func TestRunMigrationsOnDb_down_redshift(t *testing.T) {
   334  	testRunMigrationsOnDb_down(t, getRedshiftDriver(t))
   335  }
   336  
   337  func testRunMigrationsOnDb_upDownUp(t *testing.T, driver DBDriver) {
   338  	md, mdCleanup := setupMigrationsDir(map[string][2]string{
   339  		"20010203040506_setup.sql": [2]string{"CREATE TABLE test(value VARCHAR(20));", "DROP TABLE test;"},
   340  		"20010203040507_one.sql":   [2]string{"INSERT INTO test(value) VALUES('one');", "DELETE FROM test WHERE value = 'one';"},
   341  		"20010203040508_two.sql":   [2]string{"INSERT INTO test(value) VALUES('two');", "DELETE FROM test WHERE value = 'two';"},
   342  	})
   343  	defer mdCleanup()
   344  	conf := &DBConf{
   345  		Driver:        driver,
   346  		MigrationsDir: md,
   347  	}
   348  
   349  	db, err := OpenDBFromDBConf(conf)
   350  	require.NoError(t, err)
   351  
   352  	db.Exec("DROP TABLE goose_db_version")
   353  	db.Exec("DROP TABLE test")
   354  
   355  	// up
   356  	err = RunMigrationsOnDb(conf, conf.MigrationsDir, 20010203040508, db)
   357  	require.NoError(t, err)
   358  
   359  	// down
   360  	err = RunMigrationsOnDb(conf, conf.MigrationsDir, 0, db)
   361  	require.NoError(t, err)
   362  
   363  	rows, err := db.Query("SELECT value FROM test")
   364  	require.Error(t, err) // table won't exist
   365  
   366  	// up
   367  	err = RunMigrationsOnDb(conf, conf.MigrationsDir, 20010203040508, db)
   368  	require.NoError(t, err)
   369  
   370  	rows, err = db.Query("SELECT value FROM test")
   371  	require.NoError(t, err)
   372  	defer rows.Close()
   373  	var values []string
   374  	for rows.Next() {
   375  		var value string
   376  		err := rows.Scan(&value)
   377  		require.NoError(t, err)
   378  		values = append(values, value)
   379  	}
   380  
   381  	assert.Len(t, values, 2)
   382  	assert.Contains(t, values, "one")
   383  	assert.Contains(t, values, "two")
   384  }
   385  func TestRunMigrationsOnDb_upDownUp_sqlite3(t *testing.T) {
   386  	testRunMigrationsOnDb_upDownUp(t, getSqlite3Driver(t))
   387  }
   388  func TestRunMigrationsOnDb_upDownUp_mysql(t *testing.T) {
   389  	testRunMigrationsOnDb_upDownUp(t, getMysqlDriver(t))
   390  }
   391  func TestRunMigrationsOnDb_upDownUp_postgres(t *testing.T) {
   392  	testRunMigrationsOnDb_upDownUp(t, getPostgresDriver(t))
   393  }
   394  func TestRunMigrationsOnDb_upDownUp_redshift(t *testing.T) {
   395  	testRunMigrationsOnDb_upDownUp(t, getRedshiftDriver(t))
   396  }