541ad708c7
In this commit, we add a new sub-package to the walletdb package: migration. In this package, we define a new interface, Manager, which will expose all of the necessary functions required to abstract the migration logic of different sub-services within the wallet, like the address and transaction managers. The implementations of this interface will then be able to use the migration logic within the Upgrade function with no additional complexity.
343 lines
6.5 KiB
Go
343 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)
|
|
}
|
|
}
|