Files
fn-serverless/api/datastore/sql/migratex/migrate_test.go
Reed Allman a481191db2 migratex api uses tx now instead of db (#939)
* migratex api uses tx now instead of db

we want to be able to do external queries outside of the migration itself
inside of the same transaction for version checking. if we don't do this, we
risk the case where we set the version to the latest but we don't run the
table creates at all, so we have a db that thinks it's up to date but doesn't
even have any tables, and on subsequent boots if a migration slides in then
the migrations will run when there are no tables. it was unlikely, but now
it's dead.

* tx friendly table exists check

the previous existence checker for dbs was relying on getting back errors
about the db not existing. if we use this in a tx, it makes the whole tx
invalid for postgres. so, now we have count the table queries which return a 1
or a 0 instead of a 1 or an error so that we can check existence inside of a
transaction. voila.
2018-04-13 15:21:54 -07:00

101 lines
2.0 KiB
Go

package migratex
import (
"context"
"errors"
"fmt"
"testing"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
)
const testsqlite3 = "file::memory:?mode=memory&cache=shared"
type tm struct{}
func (t *tm) Up(ctx context.Context, tx *sqlx.Tx) error {
_, err := tx.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS foo (
bar bigint NOT NULL PRIMARY KEY
)`)
return err
}
func (t *tm) Down(ctx context.Context, tx *sqlx.Tx) error {
_, err := tx.ExecContext(ctx, "DROP TABLE foo")
return err
}
func (t *tm) Version() int64 { return 1 }
func TestMigrateUp(t *testing.T) {
x := new(tm)
db, err := sqlx.Open("sqlite3", testsqlite3)
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
do := func() error {
tx, err := db.Beginx()
if err != nil {
return err
}
defer tx.Commit()
version, dirty, err := Version(ctx, tx)
if version != NilVersion || err != nil || dirty {
return fmt.Errorf("version err: %v %v", err, dirty)
}
if version != NilVersion {
return errors.New("found existing version in db, nuke it")
}
err = Up(ctx, tx, []Migration{x})
if err != nil {
return err
}
version, dirty, err = Version(ctx, tx)
if err != nil || dirty {
return fmt.Errorf("version err: %v %v", err, dirty)
}
if version != x.Version() {
return errors.New("version did not update, migration should have ran.")
}
return nil
}
err = do()
if err != nil {
t.Fatalf("couldn't run migrations: %v", err)
}
do = func() error {
// make sure the table is there.
// TODO find a db agnostic way of doing this.
// query := db.Rebind(`SELECT foo FROM sqlite_master WHERE type = 'table'`)
query := db.Rebind(`SELECT name FROM sqlite_master where type='table' AND name='foo'`)
var result string
err = db.QueryRowContext(ctx, query).Scan(&result)
if err != nil {
return fmt.Errorf("foo check: %v", err)
}
if result != "foo" {
return fmt.Errorf("migration version worked but migration didn't work: %v", result)
}
return nil
}
err = do()
if err != nil {
t.Fatalf("migration check failed: %v", err)
}
}