Files
fn-serverless/api/datastore/sql/sql_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

131 lines
3.0 KiB
Go

package sql
import (
"context"
"net/url"
"os"
"testing"
"github.com/fnproject/fn/api/datastore/internal/datastoretest"
"github.com/fnproject/fn/api/datastore/internal/datastoreutil"
"github.com/fnproject/fn/api/datastore/sql/migratex"
"github.com/fnproject/fn/api/datastore/sql/migrations"
logstoretest "github.com/fnproject/fn/api/logs/testing"
"github.com/fnproject/fn/api/models"
"github.com/jmoiron/sqlx"
)
// since New with fresh dbs skips all migrations:
// * open a fresh db on latest version
// * run all down migrations
// * run all up migrations
// [ then run tests against that db ]
func newWithMigrations(ctx context.Context, url *url.URL) (*sqlStore, error) {
ds, err := newDS(ctx, url)
if err != nil {
return nil, err
}
err = ds.Tx(func(tx *sqlx.Tx) error {
return migratex.Down(ctx, tx, migrations.Migrations)
})
if err != nil {
return nil, err
}
// go through New, to ensure our Up logic works in there...
ds, err = newDS(ctx, url)
if err != nil {
return nil, err
}
return ds, nil
}
func TestDatastore(t *testing.T) {
ctx := context.Background()
defer os.RemoveAll("sqlite_test_dir")
u, err := url.Parse("sqlite3://sqlite_test_dir")
if err != nil {
t.Fatal(err)
}
f := func(t *testing.T) models.Datastore {
os.RemoveAll("sqlite_test_dir")
ds, err := newDS(ctx, u)
if err != nil {
t.Fatal(err)
}
// we don't want to test the validator, really
return datastoreutil.NewValidator(ds)
}
datastoretest.Test(t, f)
// also logs
logstoretest.Test(t, f(t))
// NOTE: sqlite3 does not like ALTER TABLE DROP COLUMN so do not run
// migration tests against it, only pg and mysql -- should prove UP migrations
// will likely work for sqlite3, but may need separate testing by devs :(
// if being run from test script (CI) poke around for pg and mysql containers
// to run tests against them too. this runs with a fresh db first run, then
// will down migrate all migrations, up migrate, and run tests again.
both := func(u *url.URL) {
f := func(t *testing.T) models.Datastore {
ds, err := newDS(ctx, u)
if err != nil {
t.Fatal(err)
}
ds.clear()
if err != nil {
t.Fatal(err)
}
return datastoreutil.NewValidator(ds)
}
// test fresh w/o migrations
datastoretest.Test(t, f)
// also test sql implements logstore
logstoretest.Test(t, f(t))
f = func(t *testing.T) models.Datastore {
t.Log("with migrations now!")
ds, err := newWithMigrations(ctx, u)
if err != nil {
t.Fatal(err)
}
ds.clear()
if err != nil {
t.Fatal(err)
}
return datastoreutil.NewValidator(ds)
}
// test that migrations work & things work with them
datastoretest.Test(t, f)
// also test sql implements logstore
logstoretest.Test(t, f(t))
}
if pg := os.Getenv("POSTGRES_URL"); pg != "" {
u, err := url.Parse(pg)
if err != nil {
t.Fatal(err)
}
both(u)
}
if mysql := os.Getenv("MYSQL_URL"); mysql != "" {
u, err := url.Parse(mysql)
if err != nil {
t.Fatal(err)
}
both(u)
}
}