package sql import ( "context" "net/url" "os" "testing" "github.com/fnproject/fn/api/datastore/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" _ "github.com/fnproject/fn/api/datastore/sql/mysql" _ "github.com/fnproject/fn/api/datastore/sql/postgres" _ "github.com/fnproject/fn/api/datastore/sql/sqlite" 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) *SQLStore { 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 ds } f2 := func(t *testing.T) models.Datastore { ds := f(t) return datastoreutil.NewValidator(ds) } t.Run(u.Scheme, func(t *testing.T) { datastoretest.RunAllTests(t, f2, datastoretest.NewBasicResourceProvider()) }) // 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) *SQLStore { ds, err := newDS(ctx, u) if err != nil { t.Fatal(err) } ds.clear() if err != nil { t.Fatal(err) } return ds } f2 := func(t *testing.T) models.Datastore { ds := f(t) return datastoreutil.NewValidator(ds) } // test fresh w/o migrations t.Run(u.Scheme, func(t *testing.T) { datastoretest.RunAllTests(t, f2, datastoretest.NewBasicResourceProvider()) }) // also test sql implements logstore logstoretest.Test(t, f(t)) f = func(t *testing.T) *SQLStore { 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 ds } f2 = func(t *testing.T) models.Datastore { ds := f(t) return datastoreutil.NewValidator(ds) } // test that migrations work & things work with them t.Run(u.Scheme, func(t *testing.T) { datastoretest.RunAllTests(t, f2, datastoretest.NewBasicResourceProvider()) }) // 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) } } func TestClose(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) } os.RemoveAll("sqlite_test_dir") ds, err := newDS(ctx, u) if err != nil { t.Fatal(err) } if err := ds.Close(); err != nil { t.Fatalf("Failed to close datastore: %v", err) } }