From 77a911e4c58ca290f27420861ad786fedaf22c7a Mon Sep 17 00:00:00 2001 From: Rafael Espinoza Date: Wed, 5 May 2021 23:19:07 -0700 Subject: [PATCH] test: Adjust driver tests to accept custom migration content Most DBs can use a set of default queries. But those queries aren't valid for cassandra. Allow a driver to supply DB preparation queries. Add test input validation Make a copy of test inputs rather than mutate it in place (the invalid sql test did this). --- drivers/cassandra/cassandra_test.go | 17 ++- drivers/mysql/mysql_test.go | 2 +- drivers/postgres/postgres_test.go | 2 +- drivers/sqlite3/sqlite3_test.go | 2 +- internal/stub/driver_test.go | 2 +- internal/test/apply_migration.go | 170 +++++++++++++++++++--------- internal/test/info.go | 18 ++- internal/test/migrate.go | 17 +-- internal/test/test.go | 102 +++++++---------- 9 files changed, 198 insertions(+), 134 deletions(-) diff --git a/drivers/cassandra/cassandra_test.go b/drivers/cassandra/cassandra_test.go index 2bdaf5b..b02f898 100644 --- a/drivers/cassandra/cassandra_test.go +++ b/drivers/cassandra/cassandra_test.go @@ -8,5 +8,20 @@ import ( ) func Test(t *testing.T) { - test.RunDriverTests(t, cassandra.NewDriver()) + queries := test.Queries{ + CreateFoos: test.MigrationContent{ + Forward: "CREATE TABLE foos (id int PRIMARY KEY);", + Reverse: "DROP TABLE foos;", + }, + CreateBars: test.MigrationContent{ + Forward: "CREATE TABLE bars (id int PRIMARY KEY);", + Reverse: "DROP TABLE bars;", + }, + AlterFoos: test.MigrationContent{ + Forward: "ALTER TABLE foos ADD a varchar;", + Reverse: "ALTER TABLE foos DROP a;", + }, + } + + test.RunDriverTests(t, cassandra.NewDriver(), queries) } diff --git a/drivers/mysql/mysql_test.go b/drivers/mysql/mysql_test.go index be90a0e..b3b4b96 100644 --- a/drivers/mysql/mysql_test.go +++ b/drivers/mysql/mysql_test.go @@ -8,5 +8,5 @@ import ( ) func Test(t *testing.T) { - test.RunDriverTests(t, mysql.NewDriver()) + test.RunDriverTests(t, mysql.NewDriver(), test.DefaultQueries) } diff --git a/drivers/postgres/postgres_test.go b/drivers/postgres/postgres_test.go index 51b3252..1e375e7 100644 --- a/drivers/postgres/postgres_test.go +++ b/drivers/postgres/postgres_test.go @@ -8,5 +8,5 @@ import ( ) func Test(t *testing.T) { - test.RunDriverTests(t, postgres.NewDriver()) + test.RunDriverTests(t, postgres.NewDriver(), test.DefaultQueries) } diff --git a/drivers/sqlite3/sqlite3_test.go b/drivers/sqlite3/sqlite3_test.go index 1dc27cc..03b9369 100644 --- a/drivers/sqlite3/sqlite3_test.go +++ b/drivers/sqlite3/sqlite3_test.go @@ -8,5 +8,5 @@ import ( ) func Test(t *testing.T) { - test.RunDriverTests(t, sqlite3.NewDriver()) + test.RunDriverTests(t, sqlite3.NewDriver(), test.DefaultQueries) } diff --git a/internal/stub/driver_test.go b/internal/stub/driver_test.go index 96a5621..a95de54 100644 --- a/internal/stub/driver_test.go +++ b/internal/stub/driver_test.go @@ -8,5 +8,5 @@ import ( ) func Test(t *testing.T) { - test.RunDriverTests(t, &stub.Driver{}) + test.RunDriverTests(t, &stub.Driver{}, test.DefaultQueries) } diff --git a/internal/test/apply_migration.go b/internal/test/apply_migration.go index 8790871..64b6d9b 100644 --- a/internal/test/apply_migration.go +++ b/internal/test/apply_migration.go @@ -7,7 +7,7 @@ import ( "github.com/rafaelespinoza/godfish" ) -func testApplyMigration(t *testing.T, driver godfish.Driver) { +func testApplyMigration(t *testing.T, driver godfish.Driver, queries Queries) { // testSetupState is the database state before calling ApplyMigration. type testSetupState struct { // migrateTo is the version that the DB should be at. @@ -62,6 +62,21 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { } } + var defaultStubs = []testDriverStub{ + { + content: queries.CreateFoos, + version: formattedTime("12340102030405"), + }, + { + content: queries.AlterFoos, + version: formattedTime("23450102030405"), + }, + { + content: queries.CreateBars, + version: formattedTime("34560102030405"), + }, + } + t.Run("setup state: no migration files", func(t *testing.T) { runTest( t, @@ -99,7 +114,7 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t, testSetupState{ migrateTo: skipMigration, - stubs: _DefaultTestDriverStubs, + stubs: defaultStubs, }, testInput{ direction: godfish.DirForward, @@ -114,7 +129,7 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t, testSetupState{ migrateTo: skipMigration, - stubs: _DefaultTestDriverStubs, + stubs: defaultStubs, }, testInput{ direction: godfish.DirReverse, @@ -133,7 +148,7 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t, testSetupState{ migrateTo: "12340102030405", - stubs: _DefaultTestDriverStubs, + stubs: defaultStubs, }, testInput{ direction: godfish.DirForward, @@ -148,7 +163,7 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t, testSetupState{ migrateTo: "23450102030405", - stubs: _DefaultTestDriverStubs, + stubs: defaultStubs, }, testInput{ direction: godfish.DirForward, @@ -163,7 +178,7 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t, testSetupState{ migrateTo: "34560102030405", - stubs: _DefaultTestDriverStubs, + stubs: defaultStubs, }, testInput{ direction: godfish.DirForward, @@ -181,7 +196,7 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t, testSetupState{ migrateTo: "34560102030405", - stubs: _DefaultTestDriverStubs, + stubs: defaultStubs, }, testInput{ direction: godfish.DirReverse, @@ -196,7 +211,7 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t, testSetupState{ migrateTo: "23450102030405", - stubs: _DefaultTestDriverStubs, + stubs: defaultStubs, }, testInput{ direction: godfish.DirReverse, @@ -211,7 +226,23 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t, testSetupState{ migrateTo: "12340102030405", - stubs: _DefaultTestDriverStubs, + stubs: []testDriverStub{ + { + content: MigrationContent{Forward: queries.CreateFoos.Forward}, + version: formattedTime("12340102030405"), + }, + { + content: MigrationContent{ + Forward: queries.AlterFoos.Forward, + Reverse: queries.AlterFoos.Reverse, + }, + version: formattedTime("23450102030405"), + }, + { + content: MigrationContent{Forward: queries.CreateBars.Forward}, + version: formattedTime("34560102030405"), + }, + }, }, testInput{ direction: godfish.DirReverse, @@ -233,22 +264,15 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { migrateTo: "34560102030405", stubs: []testDriverStub{ { - content: struct{ forward, reverse string }{ - forward: `CREATE TABLE foos (id int);`, - }, + content: queries.CreateFoos, version: formattedTime("12340102030405"), }, { - content: struct{ forward, reverse string }{ - forward: `CREATE TABLE bars (id int);`, - }, + content: queries.CreateBars, version: formattedTime("23450102030405"), }, { - content: struct{ forward, reverse string }{ - forward: `ALTER TABLE foos ADD COLUMN a varchar(255);`, - reverse: `ALTER TABLE foos DROP COLUMN a;`, - }, + content: queries.AlterFoos, version: formattedTime("34560102030405"), }, }, @@ -264,11 +288,29 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { }) t.Run("migration file does not exist", func(t *testing.T) { + var stubs = []testDriverStub{ + { + content: MigrationContent{Forward: queries.CreateFoos.Forward}, + version: formattedTime("12340102030405"), + }, + { + content: MigrationContent{ + Forward: queries.AlterFoos.Forward, + Reverse: queries.AlterFoos.Reverse, + }, + version: formattedTime("23450102030405"), + }, + { + content: MigrationContent{Forward: queries.CreateBars.Forward}, + version: formattedTime("34560102030405"), + }, + } + runTest( t, testSetupState{ migrateTo: "34560102030405", - stubs: _DefaultTestDriverStubs, + stubs: stubs, }, testInput{ direction: godfish.DirForward, @@ -284,7 +326,7 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t, testSetupState{ migrateTo: "34560102030405", - stubs: _DefaultTestDriverStubs, + stubs: stubs, }, testInput{ direction: godfish.DirReverse, @@ -296,17 +338,17 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { }, ) + // targeted migration only exists in forward direction. + // target > available. runTest( t, testSetupState{ migrateTo: "34560102030405", - stubs: _DefaultTestDriverStubs, + stubs: stubs, }, testInput{ direction: godfish.DirReverse, - // target migration only exists in forward direction. - // target > available. - version: "34560102030405", + version: "34560102030405", }, expectedOutput{ appliedVersions: []string{"12340102030405", "23450102030405", "34560102030405"}, @@ -314,17 +356,17 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { }, ) + // targeted migration only exists in forward direction. + // target < available. runTest( t, testSetupState{ migrateTo: "34560102030405", - stubs: _DefaultTestDriverStubs, + stubs: stubs, }, testInput{ direction: godfish.DirReverse, - // target migration only exists in forward direction. - // target < available. - version: "12340102030405", + version: "12340102030405", }, expectedOutput{ appliedVersions: []string{"12340102030405", "23450102030405", "34560102030405"}, @@ -334,13 +376,16 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { }) t.Run("Error during migration execution", func(t *testing.T) { + stubs := make([]testDriverStub, len(defaultStubs)) + copy(stubs, defaultStubs) + runTest( t, testSetupState{ migrateTo: "34560102030405", - stubs: append(_DefaultTestDriverStubs, testDriverStub{ - content: struct{ forward, reverse string }{ - forward: "invalid SQL", + stubs: append(stubs, testDriverStub{ + content: MigrationContent{ + Forward: "invalid SQL", }, version: formattedTime("45670102030405"), }), @@ -363,22 +408,15 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { migrateTo: "2345", stubs: []testDriverStub{ { - content: struct{ forward, reverse string }{ - forward: `CREATE TABLE foos (id int);`, - }, + content: queries.CreateFoos, version: formattedTime("1234"), }, { - content: struct{ forward, reverse string }{ - forward: `CREATE TABLE bars (id int);`, - }, + content: queries.CreateBars, version: formattedTime("2345"), }, { - content: struct{ forward, reverse string }{ - forward: `ALTER TABLE foos ADD COLUMN a varchar(255);`, - reverse: `ALTER TABLE foos DROP COLUMN a;`, - }, + content: queries.AlterFoos, version: formattedTime("3456"), }, }, @@ -400,13 +438,13 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { migrateTo: skipMigration, stubs: []testDriverStub{ { - content: struct{ forward, reverse string }{ - forward: strings.Join([]string{ - "CREATE TABLE foos (id int);", + content: MigrationContent{ + Forward: strings.Join([]string{ + queries.CreateFoos.Forward, "", - "CREATE TABLE bars (id int); ", + queries.CreateBars.Forward, "", - "ALTER TABLE foos ADD COLUMN a varchar(255) ;", + queries.AlterFoos.Forward, " ", "", }, "\n"), @@ -431,11 +469,11 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { migrateTo: skipMigration, stubs: []testDriverStub{ { - content: struct{ forward, reverse string }{ - forward: strings.Join([]string{ - "CREATE TABLE foos (id int);", + content: MigrationContent{ + Forward: strings.Join([]string{ + queries.CreateFoos.Forward, "invalid SQL;", - "ALTER TABLE foos ADD COLUMN a varchar(255);", + queries.AlterFoos.Forward, }, "\n"), }, version: formattedTime("12340102030405"), @@ -455,11 +493,22 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t.Run("Alternate filenames", func(t *testing.T) { t.Run("migrate rollback", func(t *testing.T) { + var stubs = []testDriverStub{ + { + content: queries.CreateFoos, + indirectives: struct{ forward, reverse godfish.Indirection }{ + forward: godfish.Indirection{Label: "migrate"}, + reverse: godfish.Indirection{Label: "rollback"}, + }, + version: formattedTime("12340102030405"), + }, + } + runTest( t, testSetupState{ migrateTo: skipMigration, - stubs: _StubsWithMigrateRollbackIndirectives, + stubs: stubs, }, testInput{ direction: godfish.DirForward, @@ -474,7 +523,7 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t, testSetupState{ migrateTo: "12340102030405", - stubs: _StubsWithMigrateRollbackIndirectives, + stubs: stubs, }, testInput{ direction: godfish.DirReverse, @@ -487,11 +536,22 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { }) t.Run("up down", func(t *testing.T) { + var stubs = []testDriverStub{ + { + content: queries.CreateFoos, + indirectives: struct{ forward, reverse godfish.Indirection }{ + forward: godfish.Indirection{Label: "up"}, + reverse: godfish.Indirection{Label: "down"}, + }, + version: formattedTime("12340102030405"), + }, + } + runTest( t, testSetupState{ migrateTo: skipMigration, - stubs: _StubsWithUpDownIndirectives, + stubs: stubs, }, testInput{ direction: godfish.DirForward, @@ -506,7 +566,7 @@ func testApplyMigration(t *testing.T, driver godfish.Driver) { t, testSetupState{ migrateTo: "12340102030405", - stubs: _StubsWithUpDownIndirectives, + stubs: stubs, }, testInput{ direction: godfish.DirReverse, diff --git a/internal/test/info.go b/internal/test/info.go index f301c93..ba32478 100644 --- a/internal/test/info.go +++ b/internal/test/info.go @@ -6,8 +6,22 @@ import ( "github.com/rafaelespinoza/godfish" ) -func testInfo(t *testing.T, driver godfish.Driver) { - path, err := setup(driver, t.Name(), _DefaultTestDriverStubs, "34560102030405") +func testInfo(t *testing.T, driver godfish.Driver, queries Queries) { + stubs := []testDriverStub{ + { + content: queries.CreateFoos, + version: formattedTime("12340102030405"), + }, + { + content: queries.AlterFoos, + version: formattedTime("23450102030405"), + }, + { + content: queries.CreateBars, + version: formattedTime("34560102030405"), + }, + } + path, err := setup(driver, t.Name(), stubs, "34560102030405") if err != nil { t.Errorf("could not setup test; %v", err) return diff --git a/internal/test/migrate.go b/internal/test/migrate.go index f55a4d3..6c6cd4f 100644 --- a/internal/test/migrate.go +++ b/internal/test/migrate.go @@ -6,27 +6,18 @@ import ( "github.com/rafaelespinoza/godfish" ) -func testMigrate(t *testing.T, driver godfish.Driver) { +func testMigrate(t *testing.T, driver godfish.Driver, queries Queries) { stubs := []testDriverStub{ { - content: struct{ forward, reverse string }{ - forward: `CREATE TABLE foos (id int);`, - reverse: `DROP TABLE foos;`, - }, + content: queries.CreateFoos, version: formattedTime("12340102030405"), }, { - content: struct{ forward, reverse string }{ - forward: `CREATE TABLE bars (id int);`, - reverse: `DROP TABLE bars;`, - }, + content: queries.CreateBars, version: formattedTime("23450102030405"), }, { - content: struct{ forward, reverse string }{ - forward: `ALTER TABLE foos ADD COLUMN a varchar(255);`, - reverse: `ALTER TABLE foos DROP COLUMN a;`, - }, + content: queries.AlterFoos, version: formattedTime("34560102030405"), }, } diff --git a/internal/test/test.go b/internal/test/test.go index 8b3688a..e9bfec8 100644 --- a/internal/test/test.go +++ b/internal/test/test.go @@ -11,12 +11,46 @@ import ( ) // RunDriverTests tests an implementation of the godfish.Driver interface. -func RunDriverTests(t *testing.T, driver godfish.Driver) { - t.Run("Migrate", func(t *testing.T) { testMigrate(t, driver) }) - t.Run("Info", func(t *testing.T) { testInfo(t, driver) }) - t.Run("ApplyMigration", func(t *testing.T) { testApplyMigration(t, driver) }) +// Callers should supply a set of valid queries q; most DBs can just use the +// DefaultQueries. +func RunDriverTests(t *testing.T, d godfish.Driver, q Queries) { + for _, query := range []MigrationContent{q.CreateFoos, q.CreateBars, q.AlterFoos} { + if query.Forward == "" || query.Reverse == "" { + // Should also be valid queries, but the database will decide that. + t.Fatalf("all %T fields should be non-empty", query) + } + } + + t.Run("Migrate", func(t *testing.T) { testMigrate(t, d, q) }) + t.Run("Info", func(t *testing.T) { testInfo(t, d, q) }) + t.Run("ApplyMigration", func(t *testing.T) { testApplyMigration(t, d, q) }) +} + +// Queries are named DB queries to use in the tests. +type Queries struct { + CreateFoos MigrationContent + CreateBars MigrationContent + AlterFoos MigrationContent +} + +// DefaultQueries should be sufficient to use for most DBs in RunDriverTests. +var DefaultQueries = Queries{ + CreateFoos: MigrationContent{ + Forward: "CREATE TABLE foos (id int);", + Reverse: "DROP TABLE foos;", + }, + CreateBars: MigrationContent{ + Forward: "CREATE TABLE bars (id int); ", + Reverse: "DROP TABLE bars;", + }, + AlterFoos: MigrationContent{ + Forward: "ALTER TABLE foos ADD COLUMN a varchar(255) ;", + Reverse: "ALTER TABLE foos DROP COLUMN a;", + }, } +type MigrationContent struct{ Forward, Reverse string } + const dsnKey = "DB_DSN" func mustDSN() string { @@ -78,56 +112,6 @@ func teardown(driver godfish.Driver, path string, tablesToDrop ...string) { driver.Close() } -var ( - _DefaultTestDriverStubs = []testDriverStub{ - { - content: struct{ forward, reverse string }{ - forward: `CREATE TABLE foos (id int);`, - }, - version: formattedTime("12340102030405"), - }, - { - content: struct{ forward, reverse string }{ - forward: `ALTER TABLE foos ADD COLUMN a varchar(255);`, - reverse: `ALTER TABLE foos DROP COLUMN a;`, - }, - version: formattedTime("23450102030405"), - }, - { - content: struct{ forward, reverse string }{ - forward: `CREATE TABLE bars (id int);`, - }, - version: formattedTime("34560102030405"), - }, - } - _StubsWithMigrateRollbackIndirectives = []testDriverStub{ - { - content: struct{ forward, reverse string }{ - forward: "CREATE TABLE foos (id int);", - reverse: "DROP TABLE foos;", - }, - indirectives: struct{ forward, reverse godfish.Indirection }{ - forward: godfish.Indirection{Label: "migrate"}, - reverse: godfish.Indirection{Label: "rollback"}, - }, - version: formattedTime("12340102030405"), - }, - } - _StubsWithUpDownIndirectives = []testDriverStub{ - { - content: struct{ forward, reverse string }{ - forward: "CREATE TABLE foos (id int);", - reverse: "DROP TABLE foos;", - }, - indirectives: struct{ forward, reverse godfish.Indirection }{ - forward: godfish.Indirection{Label: "up"}, - reverse: godfish.Indirection{Label: "down"}, - }, - version: formattedTime("12340102030405"), - }, - } -) - type formattedTime string func (v formattedTime) Before(u godfish.Version) bool { @@ -148,7 +132,7 @@ var _ godfish.Version = (*formattedTime)(nil) // testDriverStub encompasses some data to use with interface tests. type testDriverStub struct { migration godfish.Migration - content struct{ forward, reverse string } + content MigrationContent indirectives struct{ forward, reverse godfish.Indirection } version godfish.Version } @@ -185,9 +169,9 @@ func generateMigrationFiles(pathToTestDir string, stubs []testDriverStub) error var params *godfish.MigrationParams var reversible bool - if stub.content.forward != "" && stub.content.reverse != "" { + if stub.content.Forward != "" && stub.content.Reverse != "" { reversible = true - } else if stub.content.forward == "" { + } else if stub.content.Forward == "" { panic(fmt.Errorf("test setup should have content in forward direction")) } @@ -224,12 +208,12 @@ func generateMigrationFiles(pathToTestDir string, stubs []testDriverStub) error // migrations where each Direction is in the order: // [forward, reverse] if j == 0 { - if _, err = file.WriteString(stub.content.forward); err != nil { + if _, err = file.WriteString(stub.content.Forward); err != nil { return err } continue } - if _, err = file.WriteString(stub.content.reverse); err != nil { + if _, err = file.WriteString(stub.content.Reverse); err != nil { return err } }