344 lines
6.5 KiB
Go
344 lines
6.5 KiB
Go
|
package migration_test
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/btcsuite/btcwallet/walletdb"
|
||
|
"github.com/btcsuite/btcwallet/walletdb/migration"
|
||
|
"github.com/davecgh/go-spew/spew"
|
||
|
)
|
||
|
|
||
|
type mockMigrationManager struct {
|
||
|
currentVersion uint32
|
||
|
versions []migration.Version
|
||
|
}
|
||
|
|
||
|
var _ migration.Manager = (*mockMigrationManager)(nil)
|
||
|
|
||
|
func (m *mockMigrationManager) Name() string {
|
||
|
return "mock"
|
||
|
}
|
||
|
|
||
|
func (m *mockMigrationManager) Namespace() walletdb.ReadWriteBucket {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (m *mockMigrationManager) CurrentVersion(_ walletdb.ReadBucket) (uint32, error) {
|
||
|
return m.currentVersion, nil
|
||
|
}
|
||
|
|
||
|
func (m *mockMigrationManager) SetVersion(_ walletdb.ReadWriteBucket, version uint32) error {
|
||
|
m.currentVersion = version
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (m *mockMigrationManager) Versions() []migration.Version {
|
||
|
return m.versions
|
||
|
}
|
||
|
|
||
|
// TestGetLatestVersion ensures that we can properly retrieve the latest version
|
||
|
// from a slice of versions.
|
||
|
func TestGetLatestVersion(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
tests := []struct {
|
||
|
versions []migration.Version
|
||
|
latestVersion uint32
|
||
|
}{
|
||
|
{
|
||
|
versions: []migration.Version{},
|
||
|
latestVersion: 0,
|
||
|
},
|
||
|
{
|
||
|
versions: []migration.Version{
|
||
|
{
|
||
|
Number: 1,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
},
|
||
|
latestVersion: 1,
|
||
|
},
|
||
|
{
|
||
|
versions: []migration.Version{
|
||
|
{
|
||
|
Number: 1,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 2,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
},
|
||
|
latestVersion: 2,
|
||
|
},
|
||
|
{
|
||
|
versions: []migration.Version{
|
||
|
{
|
||
|
Number: 2,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 0,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 1,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
},
|
||
|
latestVersion: 2,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, test := range tests {
|
||
|
latestVersion := migration.GetLatestVersion(test.versions)
|
||
|
if latestVersion != test.latestVersion {
|
||
|
t.Fatalf("test %d: expected latest version %d, got %d",
|
||
|
i, test.latestVersion, latestVersion)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestVersionsToApply ensures that the proper versions that needs to be applied
|
||
|
// are returned given the current version.
|
||
|
func TestVersionsToApply(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
tests := []struct {
|
||
|
currentVersion uint32
|
||
|
versions []migration.Version
|
||
|
versionsToApply []migration.Version
|
||
|
}{
|
||
|
{
|
||
|
currentVersion: 0,
|
||
|
versions: []migration.Version{
|
||
|
{
|
||
|
Number: 0,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
},
|
||
|
versionsToApply: nil,
|
||
|
},
|
||
|
{
|
||
|
currentVersion: 1,
|
||
|
versions: []migration.Version{
|
||
|
{
|
||
|
Number: 0,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
},
|
||
|
versionsToApply: nil,
|
||
|
},
|
||
|
{
|
||
|
currentVersion: 0,
|
||
|
versions: []migration.Version{
|
||
|
{
|
||
|
Number: 0,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 1,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 2,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
},
|
||
|
versionsToApply: []migration.Version{
|
||
|
{
|
||
|
Number: 1,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 2,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
currentVersion: 0,
|
||
|
versions: []migration.Version{
|
||
|
{
|
||
|
Number: 2,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 0,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 1,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
},
|
||
|
versionsToApply: []migration.Version{
|
||
|
{
|
||
|
Number: 1,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 2,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, test := range tests {
|
||
|
versionsToApply := migration.VersionsToApply(
|
||
|
test.currentVersion, test.versions,
|
||
|
)
|
||
|
|
||
|
if !reflect.DeepEqual(versionsToApply, test.versionsToApply) {
|
||
|
t.Fatalf("test %d: versions to apply mismatch\n"+
|
||
|
"expected: %v\ngot: %v", i,
|
||
|
spew.Sdump(test.versionsToApply),
|
||
|
spew.Sdump(versionsToApply))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestUpgradeRevert ensures that we are not able to revert to a previous
|
||
|
// version.
|
||
|
func TestUpgradeRevert(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
m := &mockMigrationManager{
|
||
|
currentVersion: 1,
|
||
|
versions: []migration.Version{
|
||
|
{
|
||
|
Number: 0,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
if err := migration.Upgrade(m); err != migration.ErrReversion {
|
||
|
t.Fatalf("expected Upgrade to fail with ErrReversion, got %v",
|
||
|
err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestUpgradeSameVersion ensures that no upgrades happen if the current version
|
||
|
// matches the latest.
|
||
|
func TestUpgradeSameVersion(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
m := &mockMigrationManager{
|
||
|
currentVersion: 1,
|
||
|
versions: []migration.Version{
|
||
|
{
|
||
|
Number: 0,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 1,
|
||
|
Migration: func(walletdb.ReadWriteBucket) error {
|
||
|
return errors.New("migration should " +
|
||
|
"not happen due to already " +
|
||
|
"being on the latest version")
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
if err := migration.Upgrade(m); err != nil {
|
||
|
t.Fatalf("unable to upgrade: %v", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestUpgradeNewVersion ensures that we can properly upgrade to a newer version
|
||
|
// if available.
|
||
|
func TestUpgradeNewVersion(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
versions := []migration.Version{
|
||
|
{
|
||
|
Number: 0,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 1,
|
||
|
Migration: func(walletdb.ReadWriteBucket) error {
|
||
|
return nil
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
m := &mockMigrationManager{
|
||
|
currentVersion: 0,
|
||
|
versions: versions,
|
||
|
}
|
||
|
|
||
|
if err := migration.Upgrade(m); err != nil {
|
||
|
t.Fatalf("unable to upgrade: %v", err)
|
||
|
}
|
||
|
|
||
|
latestVersion := migration.GetLatestVersion(versions)
|
||
|
if m.currentVersion != latestVersion {
|
||
|
t.Fatalf("expected current version to match latest: "+
|
||
|
"current=%d vs latest=%d", m.currentVersion,
|
||
|
latestVersion)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TestUpgradeMultipleVersions ensures that we can go through multiple upgrades
|
||
|
// in-order to reach the latest version.
|
||
|
func TestUpgradeMultipleVersions(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
previousVersion := uint32(0)
|
||
|
versions := []migration.Version{
|
||
|
{
|
||
|
Number: previousVersion,
|
||
|
Migration: nil,
|
||
|
},
|
||
|
{
|
||
|
Number: 1,
|
||
|
Migration: func(walletdb.ReadWriteBucket) error {
|
||
|
if previousVersion != 0 {
|
||
|
return fmt.Errorf("expected previous "+
|
||
|
"version to be %d, got %d", 0,
|
||
|
previousVersion)
|
||
|
}
|
||
|
|
||
|
previousVersion = 1
|
||
|
return nil
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
Number: 2,
|
||
|
Migration: func(walletdb.ReadWriteBucket) error {
|
||
|
if previousVersion != 1 {
|
||
|
return fmt.Errorf("expected previous "+
|
||
|
"version to be %d, got %d", 1,
|
||
|
previousVersion)
|
||
|
}
|
||
|
|
||
|
previousVersion = 2
|
||
|
return nil
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
m := &mockMigrationManager{
|
||
|
currentVersion: 0,
|
||
|
versions: versions,
|
||
|
}
|
||
|
|
||
|
if err := migration.Upgrade(m); err != nil {
|
||
|
t.Fatalf("unable to upgrade: %v", err)
|
||
|
}
|
||
|
|
||
|
latestVersion := migration.GetLatestVersion(versions)
|
||
|
if m.currentVersion != latestVersion {
|
||
|
t.Fatalf("expected current version to match latest: "+
|
||
|
"current=%d vs latest=%d", m.currentVersion,
|
||
|
latestVersion)
|
||
|
}
|
||
|
}
|