github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/db/migrations/state/utils_test.go (about)

     1  package migrations_test
     2  
     3  import (
     4  	"database/sql"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/0xPolygon/supernets2-node/db"
     9  	"github.com/0xPolygon/supernets2-node/log"
    10  	"github.com/0xPolygon/supernets2-node/test/dbutils"
    11  	"github.com/gobuffalo/packr/v2"
    12  	"github.com/jackc/pgx/v4"
    13  	"github.com/jackc/pgx/v4/stdlib"
    14  	migrate "github.com/rubenv/sql-migrate"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  /*
    19  	Considerations tricks and tips for migration file testing:
    20  
    21  	- Functionality of the DB is tested by the rest of the packages, migration tests only have to check persistence across migrations (both UP and DOWN)
    22  	- It's recommended to use real data (from testnet/mainnet), but modifying NULL fields to check that those are migrated properly
    23  	- It's recommended to use some SQL tool (such as DBeaver) that generates insert queries from existing rows
    24  	- Any new migration file could be tested using the existing `migrationTester` interface. Check `0002_test.go` for an example
    25  */
    26  
    27  func init() {
    28  	log.Init(log.Config{
    29  		Level:   "debug",
    30  		Outputs: []string{"stderr"},
    31  	})
    32  }
    33  
    34  type migrationTester interface {
    35  	// InsertData used to insert data in the affected tables of the migration that is being tested
    36  	// data will be inserted with the schema as it was previous the migration that is being tested
    37  	InsertData(*sql.DB) error
    38  	// RunAssertsAfterMigrationUp this function will be called after running the migration is being tested
    39  	// and should assert that the data inserted in the function InsertData is persisted properly
    40  	RunAssertsAfterMigrationUp(*testing.T, *sql.DB)
    41  	// RunAssertsAfterMigrationDown this function will be called after reverting the migration that is being tested
    42  	// and should assert that the data inserted in the function InsertData is persisted properly
    43  	RunAssertsAfterMigrationDown(*testing.T, *sql.DB)
    44  }
    45  
    46  var (
    47  	stateDBCfg      = dbutils.NewStateConfigFromEnv()
    48  	packrMigrations = map[string]*packr.Box{
    49  		db.StateMigrationName: packr.New(db.StateMigrationName, "./migrations/state"),
    50  		db.PoolMigrationName:  packr.New(db.PoolMigrationName, "./migrations/pool"),
    51  	}
    52  )
    53  
    54  func runMigrationTest(t *testing.T, migrationNumber int, miter migrationTester) {
    55  	// Initialize an empty DB
    56  	d, err := initCleanSQLDB()
    57  	require.NoError(t, err)
    58  	require.NoError(t, runMigrationsDown(d, 0, db.StateMigrationName))
    59  	// Run migrations until migration to test
    60  	require.NoError(t, runMigrationsUp(d, migrationNumber-1, db.StateMigrationName))
    61  	// Insert data into table(s) affected by migration
    62  	require.NoError(t, miter.InsertData(d))
    63  	// Run migration that is being tested
    64  	require.NoError(t, runMigrationsUp(d, 1, db.StateMigrationName))
    65  	// Check that data is persisted properly after migration up
    66  	miter.RunAssertsAfterMigrationUp(t, d)
    67  	// Revert migration to test
    68  	require.NoError(t, runMigrationsDown(d, 1, db.StateMigrationName))
    69  	// Check that data is persisted properly after migration down
    70  	miter.RunAssertsAfterMigrationDown(t, d)
    71  }
    72  
    73  func initCleanSQLDB() (*sql.DB, error) {
    74  	// run migrations
    75  	if err := db.RunMigrationsDown(stateDBCfg, db.StateMigrationName); err != nil {
    76  		return nil, err
    77  	}
    78  	c, err := pgx.ParseConfig(fmt.Sprintf("postgres://%s:%s@%s:%s/%s", stateDBCfg.User, stateDBCfg.Password, stateDBCfg.Host, stateDBCfg.Port, stateDBCfg.Name))
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	sqlDB := stdlib.OpenDB(*c)
    83  	return sqlDB, nil
    84  }
    85  
    86  func runMigrationsUp(d *sql.DB, n int, packrName string) error {
    87  	box, ok := packrMigrations[packrName]
    88  	if !ok {
    89  		return fmt.Errorf("packr box not found with name: %v", packrName)
    90  	}
    91  
    92  	var migrations = &migrate.PackrMigrationSource{Box: box}
    93  	nMigrations, err := migrate.ExecMax(d, "postgres", migrations, migrate.Up, n)
    94  	if err != nil {
    95  		return err
    96  	}
    97  	if nMigrations != n {
    98  		return fmt.Errorf("Unexpected amount of migrations: expected: %d, actual: %d", n, nMigrations)
    99  	}
   100  	return nil
   101  }
   102  
   103  func runMigrationsDown(d *sql.DB, n int, packrName string) error {
   104  	box, ok := packrMigrations[packrName]
   105  	if !ok {
   106  		return fmt.Errorf("packr box not found with name: %v", packrName)
   107  	}
   108  
   109  	var migrations = &migrate.PackrMigrationSource{Box: box}
   110  	nMigrations, err := migrate.ExecMax(d, "postgres", migrations, migrate.Down, n)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	if nMigrations != n {
   115  		return fmt.Errorf("Unexpected amount of migrations: expected: %d, actual: %d", n, nMigrations)
   116  	}
   117  	return nil
   118  }