lnd.xprv/channeldb/meta_test.go
Olaoluwa Osuntokun b70d1f8cfe
channeldb: move .SyncVersions() into .Open(), minor migration cleanups
This commit unexports the SyncVerions PR, in favor of making it private
and moving it into the .Open() method. With this change, callers no
longer need worry about ensuring the database version is up to sync
before usage, as it will happen automatically once the database is
opened.

This commit also unexports some additional variables within the package
that don’t need be consumed by the public, and exports the
DbVersionNumber attribute on the meta struct. Finally some minor
formatting fixes have neen carried out to ensure the new code more
closely matches the style of the rest of the codebase.
2016-11-22 16:00:02 -06:00

369 lines
8.4 KiB
Go

package channeldb
import (
"bytes"
"testing"
"github.com/boltdb/bolt"
"github.com/go-errors/errors"
)
// TestVersionFetchPut checks the propernces of fetch/put methods
// and also initialization of meta data in case if don't have any in
// database.
func TestVersionFetchPut(t *testing.T) {
db, cleanUp, err := makeTestDB()
if err != nil {
t.Fatal(err)
}
defer cleanUp()
meta, err := db.FetchMeta(nil)
if err != nil {
t.Fatal(err)
}
if meta.DbVersionNumber != getLatestDBVersion(dbVersions) {
t.Fatal("initialization of meta information wasn't performed")
}
var newVersion uint32 = getLatestDBVersion(dbVersions) + 1
meta.DbVersionNumber = newVersion
if err := db.PutMeta(meta, nil); err != nil {
t.Fatalf("update of meta failed %v", err)
}
meta, err = db.FetchMeta(nil)
if err != nil {
t.Fatal(err)
}
if meta.DbVersionNumber != newVersion {
t.Fatal("update of meta information wasn't performed")
}
}
// TestOrderOfMigrations checks that migrations are applied in proper order.
func TestOrderOfMigrations(t *testing.T) {
appliedMigration := -1
versions := []version{
{0, nil},
{1, nil},
{2, func(tx *bolt.Tx) error {
appliedMigration = 2
return nil
}},
{3, func(tx *bolt.Tx) error {
appliedMigration = 3
return nil
}},
}
// Retrieve the migration that should be applied to db, as far as
// current version is 1, we skip zero and first versions.
migrations := getMigrationsToApply(versions, 1)
if len(migrations) != 2 {
t.Fatal("incorrect number of migrations to apply")
}
// Apply first migration.
migrations[0](nil)
// Check that first migration corresponds to the second version.
if appliedMigration != 2 {
t.Fatal("incorrect order of applying migrations")
}
// Apply second migration.
migrations[1](nil)
// Check that second migration corresponds to the third version.
if appliedMigration != 3 {
t.Fatal("incorrect order of applying migrations")
}
}
// TestGlobalVersionList checks that there is no mistake in global version list
// in terms of version ordering.
func TestGlobalVersionList(t *testing.T) {
if dbVersions == nil {
t.Fatal("can't find versions list")
}
if len(dbVersions) == 0 {
t.Fatal("db versions list is empty")
}
prev := dbVersions[0].number
for i := 1; i < len(dbVersions); i++ {
version := dbVersions[i].number
if version == prev {
t.Fatal("duplicates db versions")
}
if version < prev {
t.Fatal("order of db versions is wrong")
}
prev = version
}
}
// applyMigration is a helper test function that encapsulates the general steps
// which are needed to properly check the result of applying migration function.
func applyMigration(t *testing.T, beforeMigration, afterMigration func(d *DB),
migrationFunc migration, shouldFail bool) {
cdb, cleanUp, err := makeTestDB()
if err != nil {
t.Fatal(err)
}
defer cleanUp()
// beforeMigration usually used for populating the database
// with test data.
beforeMigration(cdb)
// Create test meta info with zero database version and put it on disk.
// Than creating the version list pretending that new version was added.
meta := &Meta{DbVersionNumber: 0}
cdb.PutMeta(meta, nil)
versions := []version{
{
number: 0,
migration: nil,
},
{
number: 1,
migration: migrationFunc,
},
}
defer func() {
if r := recover(); r != nil {
err = errors.New(r)
}
if err == nil && shouldFail {
t.Fatal("error wasn't received on migration stage")
} else if err != nil && !shouldFail {
t.Fatal("error was received on migration stage")
}
// afterMigration usually used for checking the database state and
// throwing the error if something went wrong.
afterMigration(cdb)
}()
// Sync with the latest version - applying migration function.
err = cdb.syncVersions(versions)
}
func TestMigrationWithPanic(t *testing.T) {
bucketPrefix := []byte("somebucket")
keyPrefix := []byte("someprefix")
beforeMigration := []byte("beforemigration")
afterMigration := []byte("aftermigration")
beforeMigrationFunc := func(d *DB) {
// Insert data in database and in order then make sure that the
// key isn't changes in case of panic or fail.
d.store.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(bucketPrefix)
if err != nil {
return err
}
bucket.Put(keyPrefix, beforeMigration)
return nil
})
}
// Create migration function which changes the initialy created data and
// throw the panic, in this case we pretending that something goes.
migrationWithPanic := func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(bucketPrefix)
if err != nil {
return err
}
bucket.Put(keyPrefix, afterMigration)
panic("panic!")
}
// Check that version of database and data wasn't changed.
afterMigrationFunc := func(d *DB) {
meta, err := d.FetchMeta(nil)
if err != nil {
t.Fatal(err)
}
if meta.DbVersionNumber != 0 {
t.Fatal("migration paniced but version is changed")
}
err = d.store.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(bucketPrefix)
if err != nil {
return err
}
value := bucket.Get(keyPrefix)
if !bytes.Equal(value, beforeMigration) {
return errors.New("migration failed but data is " +
"changed")
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
applyMigration(t,
beforeMigrationFunc,
afterMigrationFunc,
migrationWithPanic,
true)
}
func TestMigrationWithFatal(t *testing.T) {
bucketPrefix := []byte("somebucket")
keyPrefix := []byte("someprefix")
beforeMigration := []byte("beforemigration")
afterMigration := []byte("aftermigration")
beforeMigrationFunc := func(d *DB) {
d.store.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(bucketPrefix)
if err != nil {
return err
}
bucket.Put(keyPrefix, beforeMigration)
return nil
})
}
// Create migration function which changes the initialy created data and
// return the error, in this case we pretending that somthing goes
// wrong.
migrationWithFatal := func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(bucketPrefix)
if err != nil {
return err
}
bucket.Put(keyPrefix, afterMigration)
return errors.New("some error!")
}
// Check that version of database and initial data wasn't changed.
afterMigrationFunc := func(d *DB) {
meta, err := d.FetchMeta(nil)
if err != nil {
t.Fatal(err)
}
if meta.DbVersionNumber != 0 {
t.Fatal("migration failed but version is changed")
}
err = d.store.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(bucketPrefix)
if err != nil {
return err
}
value := bucket.Get(keyPrefix)
if !bytes.Equal(value, beforeMigration) {
return errors.New("migration failed but data is " +
"changed")
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
applyMigration(t,
beforeMigrationFunc,
afterMigrationFunc,
migrationWithFatal,
true)
}
func TestMigrationWithoutErrors(t *testing.T) {
bucketPrefix := []byte("somebucket")
keyPrefix := []byte("someprefix")
beforeMigration := []byte("beforemigration")
afterMigration := []byte("aftermigration")
// Populate database with initial data.
beforeMigrationFunc := func(d *DB) {
d.store.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(bucketPrefix)
if err != nil {
return err
}
bucket.Put(keyPrefix, beforeMigration)
return nil
})
}
// Create migration function which changes the initialy created data.
migrationWithoutErrors := func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(bucketPrefix)
if err != nil {
return err
}
bucket.Put(keyPrefix, afterMigration)
return nil
}
// Check that version of database and data was properly changed.
afterMigrationFunc := func(d *DB) {
meta, err := d.FetchMeta(nil)
if err != nil {
t.Fatal(err)
}
if meta.DbVersionNumber != 1 {
t.Fatal("version number isn't changed after " +
"succesfully aplied migration")
}
err = d.store.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists(bucketPrefix)
if err != nil {
return err
}
value := bucket.Get(keyPrefix)
if !bytes.Equal(value, afterMigration) {
return errors.New("migration wasn't applyied " +
"properly")
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
applyMigration(t,
beforeMigrationFunc,
afterMigrationFunc,
migrationWithoutErrors,
false)
}