github.com/decred/dcrlnd@v0.7.6/channeldb/meta_test.go (about)

     1  package channeldb
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/decred/dcrlnd/kvdb"
    10  	"github.com/go-errors/errors"
    11  )
    12  
    13  // applyMigration is a helper test function that encapsulates the general steps
    14  // which are needed to properly check the result of applying migration function.
    15  func applyMigration(t *testing.T, beforeMigration, afterMigration func(d *DB),
    16  	migrationFunc migration, shouldFail bool, dryRun bool) {
    17  
    18  	cdb, cleanUp, err := MakeTestDB()
    19  	defer cleanUp()
    20  	if err != nil {
    21  		t.Fatal(err)
    22  	}
    23  	cdb.dryRun = dryRun
    24  
    25  	// Create a test node that will be our source node.
    26  	testNode, err := createTestVertex(cdb)
    27  	if err != nil {
    28  		t.Fatal(err)
    29  	}
    30  	graph := cdb.ChannelGraph()
    31  	if err := graph.SetSourceNode(testNode); err != nil {
    32  		t.Fatal(err)
    33  	}
    34  
    35  	// beforeMigration usually used for populating the database
    36  	// with test data.
    37  	beforeMigration(cdb)
    38  
    39  	// Create test meta info with zero database version and put it on disk.
    40  	// Than creating the version list pretending that new version was added.
    41  	meta := &Meta{DbVersionNumber: 0}
    42  	if err := cdb.PutMeta(meta); err != nil {
    43  		t.Fatalf("unable to store meta data: %v", err)
    44  	}
    45  
    46  	versions := []version{
    47  		{
    48  			number:    0,
    49  			migration: nil,
    50  		},
    51  		{
    52  			number:    1,
    53  			migration: migrationFunc,
    54  		},
    55  	}
    56  
    57  	defer func() {
    58  		if r := recover(); r != nil {
    59  			if dryRun && r != ErrDryRunMigrationOK {
    60  				t.Fatalf("expected dry run migration OK")
    61  			}
    62  			err = errors.New(r)
    63  		}
    64  
    65  		if err == nil && shouldFail {
    66  			t.Fatal("error wasn't received on migration stage")
    67  		} else if err != nil && !shouldFail {
    68  			t.Fatalf("error was received on migration stage: %v", err)
    69  		}
    70  
    71  		// afterMigration usually used for checking the database state and
    72  		// throwing the error if something went wrong.
    73  		afterMigration(cdb)
    74  	}()
    75  
    76  	// Sync with the latest version - applying migration function.
    77  	err = cdb.syncVersions(versions)
    78  	if err != nil {
    79  		log.Error(err)
    80  	}
    81  }
    82  
    83  // TestVersionFetchPut checks the propernces of fetch/put methods
    84  // and also initialization of meta data in case if don't have any in
    85  // database.
    86  func TestVersionFetchPut(t *testing.T) {
    87  	t.Parallel()
    88  
    89  	db, cleanUp, err := MakeTestDB()
    90  	defer cleanUp()
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	meta, err := db.FetchMeta(nil)
    96  	if err != nil {
    97  		t.Fatal(err)
    98  	}
    99  
   100  	if meta.DbVersionNumber != getLatestDBVersion(dbVersions) {
   101  		t.Fatal("initialization of meta information wasn't performed")
   102  	}
   103  
   104  	newVersion := getLatestDBVersion(dbVersions) + 1
   105  	meta.DbVersionNumber = newVersion
   106  
   107  	if err := db.PutMeta(meta); err != nil {
   108  		t.Fatalf("update of meta failed %v", err)
   109  	}
   110  
   111  	meta, err = db.FetchMeta(nil)
   112  	if err != nil {
   113  		t.Fatal(err)
   114  	}
   115  
   116  	if meta.DbVersionNumber != newVersion {
   117  		t.Fatal("update of meta information wasn't performed")
   118  	}
   119  }
   120  
   121  // TestOrderOfMigrations checks that migrations are applied in proper order.
   122  func TestOrderOfMigrations(t *testing.T) {
   123  	t.Parallel()
   124  
   125  	appliedMigration := -1
   126  	versions := []version{
   127  		{0, nil},
   128  		{1, nil},
   129  		{2, func(tx kvdb.RwTx) error {
   130  			appliedMigration = 2
   131  			return nil
   132  		}},
   133  		{3, func(tx kvdb.RwTx) error {
   134  			appliedMigration = 3
   135  			return nil
   136  		}},
   137  	}
   138  
   139  	// Retrieve the migration that should be applied to db, as far as
   140  	// current version is 1, we skip zero and first versions.
   141  	migrations, _ := getMigrationsToApply(versions, 1)
   142  
   143  	if len(migrations) != 2 {
   144  		t.Fatal("incorrect number of migrations to apply")
   145  	}
   146  
   147  	// Apply first migration.
   148  	migrations[0](nil)
   149  
   150  	// Check that first migration corresponds to the second version.
   151  	if appliedMigration != 2 {
   152  		t.Fatal("incorrect order of applying migrations")
   153  	}
   154  
   155  	// Apply second migration.
   156  	migrations[1](nil)
   157  
   158  	// Check that second migration corresponds to the third version.
   159  	if appliedMigration != 3 {
   160  		t.Fatal("incorrect order of applying migrations")
   161  	}
   162  }
   163  
   164  // TestGlobalVersionList checks that there is no mistake in global version list
   165  // in terms of version ordering.
   166  func TestGlobalVersionList(t *testing.T) {
   167  	t.Parallel()
   168  
   169  	if dbVersions == nil {
   170  		t.Fatal("can't find versions list")
   171  	}
   172  
   173  	if len(dbVersions) == 0 {
   174  		t.Fatal("db versions list is empty")
   175  	}
   176  
   177  	prev := dbVersions[0].number
   178  	for i := 1; i < len(dbVersions); i++ {
   179  		version := dbVersions[i].number
   180  
   181  		if version == prev {
   182  			t.Fatal("duplicates db versions")
   183  		}
   184  		if version < prev {
   185  			t.Fatal("order of db versions is wrong")
   186  		}
   187  
   188  		prev = version
   189  	}
   190  }
   191  
   192  // TestMigrationWithPanic asserts that if migration logic panics, we will return
   193  // to the original state unaltered.
   194  func TestMigrationWithPanic(t *testing.T) {
   195  	t.Parallel()
   196  
   197  	bucketPrefix := []byte("somebucket")
   198  	keyPrefix := []byte("someprefix")
   199  	beforeMigration := []byte("beforemigration")
   200  	afterMigration := []byte("aftermigration")
   201  
   202  	beforeMigrationFunc := func(d *DB) {
   203  		// Insert data in database and in order then make sure that the
   204  		// key isn't changes in case of panic or fail.
   205  		err := kvdb.Update(d, func(tx kvdb.RwTx) error {
   206  			bucket, err := tx.CreateTopLevelBucket(bucketPrefix)
   207  			if err != nil {
   208  				return err
   209  			}
   210  
   211  			return bucket.Put(keyPrefix, beforeMigration)
   212  		}, func() {})
   213  		if err != nil {
   214  			t.Fatalf("unable to insert: %v", err)
   215  		}
   216  	}
   217  
   218  	// Create migration function which changes the initially created data and
   219  	// throw the panic, in this case we pretending that something goes.
   220  	migrationWithPanic := func(tx kvdb.RwTx) error {
   221  		bucket, err := tx.CreateTopLevelBucket(bucketPrefix)
   222  		if err != nil {
   223  			return err
   224  		}
   225  
   226  		bucket.Put(keyPrefix, afterMigration)
   227  		panic("panic!")
   228  	}
   229  
   230  	// Check that version of database and data wasn't changed.
   231  	afterMigrationFunc := func(d *DB) {
   232  		meta, err := d.FetchMeta(nil)
   233  		if err != nil {
   234  			t.Fatal(err)
   235  		}
   236  
   237  		if meta.DbVersionNumber != 0 {
   238  			t.Fatal("migration panicked but version is changed")
   239  		}
   240  
   241  		err = kvdb.Update(d, func(tx kvdb.RwTx) error {
   242  			bucket, err := tx.CreateTopLevelBucket(bucketPrefix)
   243  			if err != nil {
   244  				return err
   245  			}
   246  
   247  			value := bucket.Get(keyPrefix)
   248  			if !bytes.Equal(value, beforeMigration) {
   249  				return errors.New("migration failed but data is " +
   250  					"changed")
   251  			}
   252  
   253  			return nil
   254  		}, func() {})
   255  		if err != nil {
   256  			t.Fatal(err)
   257  		}
   258  	}
   259  
   260  	applyMigration(t,
   261  		beforeMigrationFunc,
   262  		afterMigrationFunc,
   263  		migrationWithPanic,
   264  		true,
   265  		false)
   266  }
   267  
   268  // TestMigrationWithFatal asserts that migrations which fail do not modify the
   269  // database.
   270  func TestMigrationWithFatal(t *testing.T) {
   271  	t.Parallel()
   272  
   273  	bucketPrefix := []byte("somebucket")
   274  	keyPrefix := []byte("someprefix")
   275  	beforeMigration := []byte("beforemigration")
   276  	afterMigration := []byte("aftermigration")
   277  
   278  	beforeMigrationFunc := func(d *DB) {
   279  		err := kvdb.Update(d, func(tx kvdb.RwTx) error {
   280  			bucket, err := tx.CreateTopLevelBucket(bucketPrefix)
   281  			if err != nil {
   282  				return err
   283  			}
   284  
   285  			return bucket.Put(keyPrefix, beforeMigration)
   286  		}, func() {})
   287  		if err != nil {
   288  			t.Fatalf("unable to insert pre migration key: %v", err)
   289  		}
   290  	}
   291  
   292  	// Create migration function which changes the initially created data and
   293  	// return the error, in this case we pretending that something goes
   294  	// wrong.
   295  	migrationWithFatal := func(tx kvdb.RwTx) error {
   296  		bucket, err := tx.CreateTopLevelBucket(bucketPrefix)
   297  		if err != nil {
   298  			return err
   299  		}
   300  
   301  		bucket.Put(keyPrefix, afterMigration)
   302  		return errors.New("some error")
   303  	}
   304  
   305  	// Check that version of database and initial data wasn't changed.
   306  	afterMigrationFunc := func(d *DB) {
   307  		meta, err := d.FetchMeta(nil)
   308  		if err != nil {
   309  			t.Fatal(err)
   310  		}
   311  
   312  		if meta.DbVersionNumber != 0 {
   313  			t.Fatal("migration failed but version is changed")
   314  		}
   315  
   316  		err = kvdb.Update(d, func(tx kvdb.RwTx) error {
   317  			bucket, err := tx.CreateTopLevelBucket(bucketPrefix)
   318  			if err != nil {
   319  				return err
   320  			}
   321  
   322  			value := bucket.Get(keyPrefix)
   323  			if !bytes.Equal(value, beforeMigration) {
   324  				return errors.New("migration failed but data is " +
   325  					"changed")
   326  			}
   327  
   328  			return nil
   329  		}, func() {})
   330  		if err != nil {
   331  			t.Fatal(err)
   332  		}
   333  	}
   334  
   335  	applyMigration(t,
   336  		beforeMigrationFunc,
   337  		afterMigrationFunc,
   338  		migrationWithFatal,
   339  		true,
   340  		false)
   341  }
   342  
   343  // TestMigrationWithoutErrors asserts that a successful migration has its
   344  // changes applied to the database.
   345  func TestMigrationWithoutErrors(t *testing.T) {
   346  	t.Parallel()
   347  
   348  	bucketPrefix := []byte("somebucket")
   349  	keyPrefix := []byte("someprefix")
   350  	beforeMigration := []byte("beforemigration")
   351  	afterMigration := []byte("aftermigration")
   352  
   353  	// Populate database with initial data.
   354  	beforeMigrationFunc := func(d *DB) {
   355  		err := kvdb.Update(d, func(tx kvdb.RwTx) error {
   356  			bucket, err := tx.CreateTopLevelBucket(bucketPrefix)
   357  			if err != nil {
   358  				return err
   359  			}
   360  
   361  			return bucket.Put(keyPrefix, beforeMigration)
   362  		}, func() {})
   363  		if err != nil {
   364  			t.Fatalf("unable to update db pre migration: %v", err)
   365  		}
   366  	}
   367  
   368  	// Create migration function which changes the initially created data.
   369  	migrationWithoutErrors := func(tx kvdb.RwTx) error {
   370  		bucket, err := tx.CreateTopLevelBucket(bucketPrefix)
   371  		if err != nil {
   372  			return err
   373  		}
   374  
   375  		bucket.Put(keyPrefix, afterMigration)
   376  		return nil
   377  	}
   378  
   379  	// Check that version of database and data was properly changed.
   380  	afterMigrationFunc := func(d *DB) {
   381  		meta, err := d.FetchMeta(nil)
   382  		if err != nil {
   383  			t.Fatal(err)
   384  		}
   385  
   386  		if meta.DbVersionNumber != 1 {
   387  			t.Fatal("version number isn't changed after " +
   388  				"successfully applied migration")
   389  		}
   390  
   391  		err = kvdb.Update(d, func(tx kvdb.RwTx) error {
   392  			bucket, err := tx.CreateTopLevelBucket(bucketPrefix)
   393  			if err != nil {
   394  				return err
   395  			}
   396  
   397  			value := bucket.Get(keyPrefix)
   398  			if !bytes.Equal(value, afterMigration) {
   399  				return errors.New("migration wasn't applied " +
   400  					"properly")
   401  			}
   402  
   403  			return nil
   404  		}, func() {})
   405  		if err != nil {
   406  			t.Fatal(err)
   407  		}
   408  	}
   409  
   410  	applyMigration(t,
   411  		beforeMigrationFunc,
   412  		afterMigrationFunc,
   413  		migrationWithoutErrors,
   414  		false,
   415  		false)
   416  }
   417  
   418  // TestMigrationReversion tests after performing a migration to a higher
   419  // database version, opening the database with a lower latest db version returns
   420  // ErrDBReversion.
   421  func TestMigrationReversion(t *testing.T) {
   422  	t.Parallel()
   423  
   424  	tempDirName, err := ioutil.TempDir("", "channeldb")
   425  	defer func() {
   426  		os.RemoveAll(tempDirName)
   427  	}()
   428  	if err != nil {
   429  		t.Fatalf("unable to create temp dir: %v", err)
   430  	}
   431  
   432  	backend, cleanup, err := kvdb.GetTestBackend(tempDirName, "cdb")
   433  	if err != nil {
   434  		t.Fatalf("unable to get test db backend: %v", err)
   435  	}
   436  
   437  	cdb, err := CreateWithBackend(backend)
   438  	if err != nil {
   439  		cleanup()
   440  		t.Fatalf("unable to open channeldb: %v", err)
   441  	}
   442  
   443  	// Update the database metadata to point to one more than the highest
   444  	// known version.
   445  	err = kvdb.Update(cdb, func(tx kvdb.RwTx) error {
   446  		newMeta := &Meta{
   447  			DbVersionNumber: getLatestDBVersion(dbVersions) + 1,
   448  		}
   449  
   450  		return putMeta(newMeta, tx)
   451  	}, func() {})
   452  
   453  	// Close the database. Even if we succeeded, our next step is to reopen.
   454  	cdb.Close()
   455  	cleanup()
   456  
   457  	if err != nil {
   458  		t.Fatalf("unable to increase db version: %v", err)
   459  	}
   460  
   461  	backend, cleanup, err = kvdb.GetTestBackend(tempDirName, "cdb")
   462  	if err != nil {
   463  		t.Fatalf("unable to get test db backend: %v", err)
   464  	}
   465  	defer cleanup()
   466  
   467  	_, err = CreateWithBackend(backend)
   468  	if err != ErrDBReversion {
   469  		t.Fatalf("unexpected error when opening channeldb, "+
   470  			"want: %v, got: %v", ErrDBReversion, err)
   471  	}
   472  }
   473  
   474  // TestMigrationDryRun ensures that opening the database in dry run migration
   475  // mode will fail and not commit the migration.
   476  func TestMigrationDryRun(t *testing.T) {
   477  	t.Parallel()
   478  
   479  	// Nothing to do, will inspect version number.
   480  	beforeMigrationFunc := func(d *DB) {}
   481  
   482  	// Check that version of database version is not modified.
   483  	afterMigrationFunc := func(d *DB) {
   484  		err := kvdb.View(d, func(tx kvdb.RTx) error {
   485  			meta, err := d.FetchMeta(nil)
   486  			if err != nil {
   487  				t.Fatal(err)
   488  			}
   489  
   490  			if meta.DbVersionNumber != 0 {
   491  				t.Fatal("dry run migration was not aborted")
   492  			}
   493  
   494  			return nil
   495  		}, func() {})
   496  		if err != nil {
   497  			t.Fatalf("unable to apply after func: %v", err)
   498  		}
   499  	}
   500  
   501  	applyMigration(t,
   502  		beforeMigrationFunc,
   503  		afterMigrationFunc,
   504  		func(kvdb.RwTx) error { return nil },
   505  		true,
   506  		true)
   507  }