github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/migration/migration_test.go (about)

     1  package migration_test
     2  
     3  import (
     4  	"database/sql"
     5  	"io/ioutil"
     6  	"math/rand"
     7  	"strconv"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  
    12  	"code.cloudfoundry.org/lager"
    13  
    14  	"github.com/pf-qiu/concourse/v6/atc/db/lock"
    15  	"github.com/pf-qiu/concourse/v6/atc/db/migration"
    16  	"github.com/pf-qiu/concourse/v6/atc/db/migration/migrationfakes"
    17  	"github.com/lib/pq"
    18  
    19  	. "github.com/onsi/ginkgo"
    20  	. "github.com/onsi/gomega"
    21  )
    22  
    23  const initialSchemaVersion = 1510262030
    24  const upgradedSchemaVersion = 1510670987
    25  
    26  var _ = Describe("Migration", func() {
    27  	var (
    28  		err         error
    29  		db          *sql.DB
    30  		lockDB      *sql.DB
    31  		lockFactory lock.LockFactory
    32  		bindata     *migrationfakes.FakeBindata
    33  		fakeLogFunc = func(logger lager.Logger, id lock.LockID) {}
    34  	)
    35  
    36  	BeforeEach(func() {
    37  		db, err = sql.Open("postgres", postgresRunner.DataSourceName())
    38  		Expect(err).NotTo(HaveOccurred())
    39  
    40  		lockDB, err = sql.Open("postgres", postgresRunner.DataSourceName())
    41  		Expect(err).NotTo(HaveOccurred())
    42  
    43  		lockFactory = lock.NewLockFactory(lockDB, fakeLogFunc, fakeLogFunc)
    44  
    45  		bindata = new(migrationfakes.FakeBindata)
    46  		bindata.AssetStub = asset
    47  	})
    48  
    49  	AfterEach(func() {
    50  		_ = db.Close()
    51  		_ = lockDB.Close()
    52  	})
    53  
    54  	Context("Migration test run", func() {
    55  		It("Runs all the migrations", func() {
    56  			migrator := migration.NewMigrator(db, lockFactory)
    57  
    58  			err := migrator.Up(nil, nil)
    59  			Expect(err).NotTo(HaveOccurred())
    60  		})
    61  	})
    62  
    63  	Context("Version Check", func() {
    64  		It("CurrentVersion reports the current version stored in the database", func() {
    65  			bindata.AssetNamesReturns([]string{
    66  				"1000_some_migration.up.sql",
    67  				"1510262030_initial_schema.up.sql",
    68  				"1510670987_update_unique_constraint_for_resource_caches.up.sql",
    69  				"2000000000_latest_migration_does_not_matter.up.sql",
    70  			})
    71  			bindata.AssetStub = func(name string) ([]byte, error) {
    72  				if name == "1000_some_migration.up.sql" {
    73  					return []byte{}, nil
    74  				} else if name == "2000000000_latest_migration_does_not_matter.up.sql" {
    75  					return []byte{}, nil
    76  				}
    77  				return asset(name)
    78  			}
    79  
    80  			myDatabaseVersion := 1234567890
    81  
    82  			SetupMigrationsHistoryTableToExistAtVersion(db, myDatabaseVersion)
    83  
    84  			migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
    85  
    86  			version, err := migrator.CurrentVersion()
    87  			Expect(err).NotTo(HaveOccurred())
    88  			Expect(version).To(Equal(myDatabaseVersion))
    89  		})
    90  
    91  		It("SupportedVersion reports the highest supported migration version", func() {
    92  
    93  			SetupMigrationsHistoryTableToExistAtVersion(db, initialSchemaVersion)
    94  
    95  			bindata.AssetNamesReturns([]string{
    96  				"1000_some_migration.up.sql",
    97  				"1510262030_initial_schema.up.sql",
    98  				"1510670987_update_unique_constraint_for_resource_caches.up.sql",
    99  				"300000_this_is_to_prove_we_dont_use_string_sort.up.sql",
   100  				"2000000000_latest_migration.up.sql",
   101  			})
   102  			migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   103  
   104  			version, err := migrator.SupportedVersion()
   105  			Expect(err).NotTo(HaveOccurred())
   106  			Expect(version).To(Equal(2000000000))
   107  		})
   108  
   109  		It("Ignores files it can't parse", func() {
   110  
   111  			SetupMigrationsHistoryTableToExistAtVersion(db, initialSchemaVersion)
   112  
   113  			bindata.AssetNamesReturns([]string{
   114  				"1000_some_migration.up.sql",
   115  				"1510262030_initial_schema.up.sql",
   116  				"1510670987_update_unique_constraint_for_resource_caches.up.sql",
   117  				"300000_this_is_to_prove_we_dont_use_string_sort.up.sql",
   118  				"2000000000_latest_migration.up.sql",
   119  				"migrations.go",
   120  			})
   121  			migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   122  
   123  			version, err := migrator.SupportedVersion()
   124  			Expect(err).NotTo(HaveOccurred())
   125  			Expect(version).To(Equal(2000000000))
   126  		})
   127  	})
   128  
   129  	Context("Upgrade", func() {
   130  		Context("old schema_migrations table exist", func() {
   131  			var dirty bool
   132  
   133  			JustBeforeEach(func() {
   134  				SetupSchemaMigrationsTable(db, 8878, dirty)
   135  			})
   136  
   137  			Context("dirty state is true", func() {
   138  				BeforeEach(func() {
   139  					dirty = true
   140  				})
   141  
   142  				It("errors", func() {
   143  
   144  					Expect(err).NotTo(HaveOccurred())
   145  
   146  					migrator := migration.NewMigrator(db, lockFactory)
   147  
   148  					err = migrator.Up(nil, nil)
   149  					Expect(err).To(HaveOccurred())
   150  					Expect(err.Error()).To(ContainSubstring("database is in a dirty state"))
   151  
   152  					var newTableCreated bool
   153  					err = db.QueryRow("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name='migrations_history')").Scan(&newTableCreated)
   154  					Expect(newTableCreated).To(BeFalse())
   155  				})
   156  			})
   157  
   158  			Context("dirty state is false", func() {
   159  				BeforeEach(func() {
   160  					dirty = false
   161  				})
   162  
   163  				It("populate migrations_history table with starting version from schema_migrations table", func() {
   164  					startTime := time.Now()
   165  					migrator := migration.NewMigrator(db, lockFactory)
   166  
   167  					err = migrator.Up(nil, nil)
   168  					Expect(err).NotTo(HaveOccurred())
   169  
   170  					var (
   171  						version   int
   172  						isDirty   bool
   173  						timeStamp pq.NullTime
   174  						status    string
   175  						direction string
   176  					)
   177  					err = db.QueryRow("SELECT * from migrations_history ORDER BY tstamp ASC LIMIT 1").Scan(&version, &timeStamp, &direction, &status, &isDirty)
   178  					Expect(version).To(Equal(8878))
   179  					Expect(isDirty).To(BeFalse())
   180  					Expect(timeStamp.Time.After(startTime)).To(Equal(true))
   181  					Expect(direction).To(Equal("up"))
   182  					Expect(status).To(Equal("passed"))
   183  				})
   184  
   185  				Context("when the migrations_history table already exists", func() {
   186  					It("does not repopulate the migrations_history table", func() {
   187  						SetupMigrationsHistoryTableToExistAtVersion(db, 8878)
   188  						startTime := time.Now()
   189  						migrator := migration.NewMigrator(db, lockFactory)
   190  
   191  						err = migrator.Up(nil, nil)
   192  						Expect(err).NotTo(HaveOccurred())
   193  
   194  						var timeStamp pq.NullTime
   195  						rows, err := db.Query("SELECT tstamp FROM migrations_history WHERE version=8878")
   196  						Expect(err).NotTo(HaveOccurred())
   197  						var numRows = 0
   198  						for rows.Next() {
   199  							err = rows.Scan(&timeStamp)
   200  							numRows++
   201  						}
   202  						Expect(numRows).To(Equal(1))
   203  						Expect(timeStamp.Time.Before(startTime)).To(Equal(true))
   204  					})
   205  				})
   206  			})
   207  		})
   208  
   209  		Context("sql migrations", func() {
   210  			It("runs a migration", func() {
   211  				simpleMigrationFilename := "1000_test_table_created.up.sql"
   212  				bindata.AssetReturns([]byte(`
   213  						BEGIN;
   214  						CREATE TABLE some_table (id integer);
   215  						COMMIT;
   216  						`), nil)
   217  
   218  				bindata.AssetNamesReturns([]string{
   219  					simpleMigrationFilename,
   220  				})
   221  
   222  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   223  
   224  				migrations, err := migrator.Migrations()
   225  				Expect(err).NotTo(HaveOccurred())
   226  				Expect(len(migrations)).To(Equal(1))
   227  
   228  				err = migrator.Up(nil, nil)
   229  				Expect(err).NotTo(HaveOccurred())
   230  
   231  				By("Creating the table in the database")
   232  				var exists string
   233  				err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM information_schema.tables where table_name = 'some_table')").Scan(&exists)
   234  				Expect(err).NotTo(HaveOccurred())
   235  				Expect(exists).To(Equal("true"))
   236  
   237  				By("Updating the migrations_history table")
   238  				ExpectDatabaseMigrationVersionToEqual(migrator, 1000)
   239  			})
   240  
   241  			It("ignores migrations before the current version", func() {
   242  				SetupMigrationsHistoryTableToExistAtVersion(db, 1000)
   243  
   244  				simpleMigrationFilename := "1000_test_table_created.up.sql"
   245  				bindata.AssetStub = func(name string) ([]byte, error) {
   246  					if name == simpleMigrationFilename {
   247  						return []byte(`
   248  						BEGIN;
   249  						CREATE TABLE some_table (id integer);
   250  						COMMIT;
   251  						`), nil
   252  					}
   253  					return asset(name)
   254  				}
   255  				bindata.AssetNamesReturns([]string{
   256  					simpleMigrationFilename,
   257  				})
   258  
   259  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   260  				err := migrator.Up(nil, nil)
   261  				Expect(err).NotTo(HaveOccurred())
   262  
   263  				By("Not creating the database referenced in the migration")
   264  				var exists string
   265  				err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM information_schema.tables where table_name = 'some_table')").Scan(&exists)
   266  				Expect(err).NotTo(HaveOccurred())
   267  				Expect(exists).To(Equal("false"))
   268  			})
   269  
   270  			It("runs the up migrations in ascending order", func() {
   271  				addTableMigrationFilename := "1000_test_table_created.up.sql"
   272  				removeTableMigrationFilename := "1001_test_table_created.up.sql"
   273  
   274  				bindata.AssetStub = func(name string) ([]byte, error) {
   275  					if name == addTableMigrationFilename {
   276  						return []byte(`
   277  						BEGIN;
   278  						CREATE TABLE some_table (id integer);
   279  						COMMIT;
   280  						`), nil
   281  					} else if name == removeTableMigrationFilename {
   282  						return []byte(`
   283  						BEGIN;
   284  						DROP TABLE some_table;
   285  						COMMIT;
   286  						`), nil
   287  					}
   288  					return asset(name)
   289  				}
   290  
   291  				bindata.AssetNamesReturns([]string{
   292  					removeTableMigrationFilename,
   293  					addTableMigrationFilename,
   294  				})
   295  
   296  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   297  				err := migrator.Up(nil, nil)
   298  				Expect(err).NotTo(HaveOccurred())
   299  
   300  			})
   301  
   302  			Context("when sql migrations fail", func() {
   303  				BeforeEach(func() {
   304  					bindata.AssetNamesReturns([]string{
   305  						"1510262030_initial_schema.up.sql",
   306  						"1525724789_drop_reaper_addr_from_workers.up.sql",
   307  					})
   308  				})
   309  
   310  				It("rolls back and leaves the database clean", func() {
   311  					migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   312  					err := migrator.Up(nil, nil)
   313  					Expect(err).To(HaveOccurred())
   314  					Expect(err.Error()).To(ContainSubstring("failed and was rolled back"))
   315  					ExpectDatabaseMigrationVersionToEqual(migrator, initialSchemaVersion)
   316  					ExpectMigrationToHaveFailed(db, 1525724789, false)
   317  				})
   318  			})
   319  
   320  			It("Doesn't fail if there are no migrations to run", func() {
   321  				bindata.AssetNamesReturns([]string{
   322  					"1510262030_initial_schema.up.sql",
   323  				})
   324  
   325  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   326  				err := migrator.Up(nil, nil)
   327  				Expect(err).NotTo(HaveOccurred())
   328  
   329  				err = migrator.Up(nil, nil)
   330  				Expect(err).NotTo(HaveOccurred())
   331  
   332  				ExpectDatabaseMigrationVersionToEqual(migrator, initialSchemaVersion)
   333  
   334  				ExpectMigrationVersionTableNotToExist(db)
   335  
   336  				ExpectToBeAbleToInsertData(db)
   337  			})
   338  
   339  			It("Locks the database so multiple ATCs don't all run migrations at the same time", func() {
   340  				SetupMigrationsHistoryTableToExistAtVersion(db, 1510262030)
   341  
   342  				SetupSchemaFromFile(db, "migrations/1510262030_initial_schema.up.sql")
   343  
   344  				bindata.AssetNamesReturns([]string{
   345  					"1510262030_initial_schema.up.sql",
   346  				})
   347  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   348  
   349  				var wg sync.WaitGroup
   350  				wg.Add(3)
   351  
   352  				go TryRunUpAndVerifyResult(db, migrator, &wg)
   353  				go TryRunUpAndVerifyResult(db, migrator, &wg)
   354  				go TryRunUpAndVerifyResult(db, migrator, &wg)
   355  
   356  				wg.Wait()
   357  			})
   358  		})
   359  
   360  		Context("golang migrations", func() {
   361  			It("runs a migration with Migrate", func() {
   362  
   363  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   364  				bindata.AssetNamesReturns([]string{
   365  					"1510262030_initial_schema.up.sql",
   366  					"1516643303_update_auth_providers.up.go",
   367  				})
   368  
   369  				By("applying the initial migration")
   370  				err := migrator.Migrate(nil, nil, 1510262030)
   371  				var columnExists string
   372  				err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM information_schema.columns where table_name = 'teams' AND column_name='basic_auth')").Scan(&columnExists)
   373  				Expect(err).NotTo(HaveOccurred())
   374  				Expect(columnExists).To(Equal("true"))
   375  
   376  				err = migrator.Migrate(nil, nil, 1516643303)
   377  				Expect(err).NotTo(HaveOccurred())
   378  
   379  				By("applying the go migration")
   380  				err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM information_schema.columns where table_name = 'teams' AND column_name='basic_auth')").Scan(&columnExists)
   381  				Expect(err).NotTo(HaveOccurred())
   382  				Expect(columnExists).To(Equal("false"))
   383  
   384  				By("updating the schema migrations table")
   385  				ExpectDatabaseMigrationVersionToEqual(migrator, 1516643303)
   386  			})
   387  
   388  			It("runs a migration with Up", func() {
   389  
   390  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   391  				bindata.AssetNamesReturns([]string{
   392  					"1510262030_initial_schema.up.sql",
   393  					"1516643303_update_auth_providers.up.go",
   394  				})
   395  
   396  				err := migrator.Up(nil, nil)
   397  				Expect(err).NotTo(HaveOccurred())
   398  
   399  				By("applying the migration")
   400  				var columnExists string
   401  				err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM information_schema.columns where table_name = 'teams' AND column_name='basic_auth')").Scan(&columnExists)
   402  				Expect(err).NotTo(HaveOccurred())
   403  				Expect(columnExists).To(Equal("false"))
   404  
   405  				By("updating the schema migrations table")
   406  				ExpectDatabaseMigrationVersionToEqual(migrator, 1516643303)
   407  			})
   408  		})
   409  	})
   410  
   411  	Context("Downgrade", func() {
   412  		Context("Downgrades to a version that uses the old mattes/migrate schema_migrations table", func() {
   413  			It("Downgrades to a given version and write it to a new created schema_migrations table", func() {
   414  				bindata.AssetNamesReturns([]string{
   415  					"1510262030_initial_schema.up.sql",
   416  					"1510670987_update_unique_constraint_for_resource_caches.up.sql",
   417  					"1510670987_update_unique_constraint_for_resource_caches.down.sql",
   418  				})
   419  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   420  
   421  				err := migrator.Up(nil, nil)
   422  				Expect(err).NotTo(HaveOccurred())
   423  
   424  				currentVersion, err := migrator.CurrentVersion()
   425  				Expect(err).NotTo(HaveOccurred())
   426  				Expect(currentVersion).To(Equal(upgradedSchemaVersion))
   427  
   428  				err = migrator.Migrate(nil, nil, initialSchemaVersion)
   429  				Expect(err).NotTo(HaveOccurred())
   430  
   431  				currentVersion, err = migrator.CurrentVersion()
   432  				Expect(err).NotTo(HaveOccurred())
   433  				Expect(currentVersion).To(Equal(initialSchemaVersion))
   434  
   435  				ExpectDatabaseVersionToEqual(db, initialSchemaVersion, "schema_migrations")
   436  
   437  				ExpectToBeAbleToInsertData(db)
   438  			})
   439  
   440  			It("Downgrades to a given version and write it to the existing schema_migrations table with dirty true", func() {
   441  
   442  				bindata.AssetNamesReturns([]string{
   443  					"1510262030_initial_schema.up.sql",
   444  					"1510670987_update_unique_constraint_for_resource_caches.up.sql",
   445  					"1510670987_update_unique_constraint_for_resource_caches.down.sql",
   446  				})
   447  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   448  
   449  				err := migrator.Up(nil, nil)
   450  				Expect(err).NotTo(HaveOccurred())
   451  
   452  				currentVersion, err := migrator.CurrentVersion()
   453  				Expect(err).NotTo(HaveOccurred())
   454  				Expect(currentVersion).To(Equal(upgradedSchemaVersion))
   455  
   456  				SetupSchemaMigrationsTable(db, 8878, true)
   457  
   458  				err = migrator.Migrate(nil, nil, initialSchemaVersion)
   459  				Expect(err).NotTo(HaveOccurred())
   460  
   461  				currentVersion, err = migrator.CurrentVersion()
   462  				Expect(err).NotTo(HaveOccurred())
   463  				Expect(currentVersion).To(Equal(initialSchemaVersion))
   464  
   465  				ExpectDatabaseVersionToEqual(db, initialSchemaVersion, "schema_migrations")
   466  
   467  				ExpectToBeAbleToInsertData(db)
   468  			})
   469  		})
   470  
   471  		Context("Downgrades to a version with new migrations_history table", func() {
   472  			It("Downgrades to a given version", func() {
   473  				bindata.AssetNamesReturns([]string{
   474  					"1510262030_initial_schema.up.sql",
   475  					"1510670987_update_unique_constraint_for_resource_caches.up.sql",
   476  					"1510670987_update_unique_constraint_for_resource_caches.down.sql",
   477  				})
   478  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   479  
   480  				err := migrator.Up(nil, nil)
   481  				Expect(err).NotTo(HaveOccurred())
   482  
   483  				currentVersion, err := migrator.CurrentVersion()
   484  				Expect(err).NotTo(HaveOccurred())
   485  				Expect(currentVersion).To(Equal(upgradedSchemaVersion))
   486  
   487  				err = migrator.Migrate(nil, nil, initialSchemaVersion)
   488  				Expect(err).NotTo(HaveOccurred())
   489  
   490  				currentVersion, err = migrator.CurrentVersion()
   491  				Expect(err).NotTo(HaveOccurred())
   492  				Expect(currentVersion).To(Equal(initialSchemaVersion))
   493  
   494  				ExpectToBeAbleToInsertData(db)
   495  			})
   496  
   497  			It("Doesn't fail if already at the requested version", func() {
   498  				bindata.AssetNamesReturns([]string{
   499  					"1510262030_initial_schema.up.sql",
   500  					"1510670987_update_unique_constraint_for_resource_caches.up.sql",
   501  				})
   502  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   503  
   504  				err := migrator.Migrate(nil, nil, upgradedSchemaVersion)
   505  				Expect(err).NotTo(HaveOccurred())
   506  
   507  				currentVersion, err := migrator.CurrentVersion()
   508  				Expect(err).NotTo(HaveOccurred())
   509  				Expect(currentVersion).To(Equal(upgradedSchemaVersion))
   510  
   511  				err = migrator.Migrate(nil, nil, upgradedSchemaVersion)
   512  				Expect(err).NotTo(HaveOccurred())
   513  
   514  				currentVersion, err = migrator.CurrentVersion()
   515  				Expect(err).NotTo(HaveOccurred())
   516  				Expect(currentVersion).To(Equal(upgradedSchemaVersion))
   517  
   518  				ExpectToBeAbleToInsertData(db)
   519  			})
   520  
   521  			It("Locks the database so multiple consumers don't run downgrade at the same time", func() {
   522  				migrator := migration.NewMigratorForMigrations(db, lockFactory, bindata)
   523  				bindata.AssetNamesReturns([]string{
   524  					"1510262030_initial_schema.up.sql",
   525  					"1510670987_update_unique_constraint_for_resource_caches.up.sql",
   526  					"1510670987_update_unique_constraint_for_resource_caches.down.sql",
   527  				})
   528  
   529  				err := migrator.Up(nil, nil)
   530  				Expect(err).NotTo(HaveOccurred())
   531  
   532  				var wg sync.WaitGroup
   533  				wg.Add(3)
   534  
   535  				go TryRunMigrateAndVerifyResult(db, migrator, initialSchemaVersion, &wg)
   536  				go TryRunMigrateAndVerifyResult(db, migrator, initialSchemaVersion, &wg)
   537  				go TryRunMigrateAndVerifyResult(db, migrator, initialSchemaVersion, &wg)
   538  
   539  				wg.Wait()
   540  			})
   541  		})
   542  	})
   543  
   544  })
   545  
   546  func TryRunUpAndVerifyResult(db *sql.DB, migrator migration.Migrator, wg *sync.WaitGroup) {
   547  	defer GinkgoRecover()
   548  	defer wg.Done()
   549  
   550  	err := migrator.Up(nil, nil)
   551  	Expect(err).NotTo(HaveOccurred())
   552  
   553  	ExpectDatabaseMigrationVersionToEqual(migrator, initialSchemaVersion)
   554  
   555  	ExpectToBeAbleToInsertData(db)
   556  }
   557  
   558  func TryRunMigrateAndVerifyResult(db *sql.DB, migrator migration.Migrator, version int, wg *sync.WaitGroup) {
   559  	defer GinkgoRecover()
   560  	defer wg.Done()
   561  
   562  	err := migrator.Migrate(nil, nil, version)
   563  	Expect(err).NotTo(HaveOccurred())
   564  
   565  	ExpectDatabaseMigrationVersionToEqual(migrator, version)
   566  
   567  	ExpectToBeAbleToInsertData(db)
   568  }
   569  
   570  func SetupMigrationsHistoryTableToExistAtVersion(db *sql.DB, version int) {
   571  	_, err := db.Exec(`CREATE TABLE migrations_history(version bigint, tstamp timestamp with time zone, direction varchar, status varchar, dirty boolean)`)
   572  	Expect(err).NotTo(HaveOccurred())
   573  
   574  	_, err = db.Exec(`INSERT INTO migrations_history(version, tstamp, direction, status, dirty) VALUES($1, current_timestamp, 'up', 'passed', false)`, version)
   575  	Expect(err).NotTo(HaveOccurred())
   576  }
   577  
   578  func SetupSchemaMigrationsTable(db *sql.DB, version int, dirty bool) {
   579  	_, err := db.Exec("CREATE TABLE IF NOT EXISTS schema_migrations (version bigint, dirty boolean)")
   580  	Expect(err).NotTo(HaveOccurred())
   581  	_, err = db.Exec("INSERT INTO schema_migrations (version, dirty) VALUES ($1, $2)", version, dirty)
   582  	Expect(err).NotTo(HaveOccurred())
   583  }
   584  
   585  func SetupSchemaFromFile(db *sql.DB, path string) {
   586  	migrations, err := ioutil.ReadFile(path)
   587  	Expect(err).NotTo(HaveOccurred())
   588  
   589  	for _, migration := range strings.Split(string(migrations), ";") {
   590  		_, err = db.Exec(migration)
   591  		Expect(err).NotTo(HaveOccurred())
   592  	}
   593  }
   594  
   595  func ExpectDatabaseMigrationVersionToEqual(migrator migration.Migrator, expectedVersion int) {
   596  	var dbVersion int
   597  	dbVersion, err := migrator.CurrentVersion()
   598  	Expect(err).NotTo(HaveOccurred())
   599  	Expect(dbVersion).To(Equal(expectedVersion))
   600  }
   601  
   602  func ExpectToBeAbleToInsertData(dbConn *sql.DB) {
   603  	rand.Seed(time.Now().UnixNano())
   604  
   605  	teamID := rand.Intn(10000)
   606  	_, err := dbConn.Exec("INSERT INTO teams(id, name) VALUES ($1, $2)", teamID, strconv.Itoa(teamID))
   607  	Expect(err).NotTo(HaveOccurred())
   608  
   609  	pipelineID := rand.Intn(10000)
   610  	_, err = dbConn.Exec("INSERT INTO pipelines(id, team_id, name) VALUES ($1, $2, $3)", pipelineID, teamID, strconv.Itoa(pipelineID))
   611  	Expect(err).NotTo(HaveOccurred())
   612  
   613  	jobID := rand.Intn(10000)
   614  	_, err = dbConn.Exec("INSERT INTO jobs(id, pipeline_id, name, config) VALUES ($1, $2, $3, '{}')", jobID, pipelineID, strconv.Itoa(jobID))
   615  	Expect(err).NotTo(HaveOccurred())
   616  }
   617  
   618  func ExpectMigrationToHaveFailed(dbConn *sql.DB, failedVersion int, expectDirty bool) {
   619  	var status string
   620  	var dirty bool
   621  	err := dbConn.QueryRow("SELECT status, dirty FROM migrations_history WHERE version=$1 ORDER BY tstamp desc LIMIT 1", failedVersion).Scan(&status, &dirty)
   622  	Expect(err).NotTo(HaveOccurred())
   623  	Expect(status).To(Equal("failed"))
   624  	Expect(dirty).To(Equal(expectDirty))
   625  }