github.com/ethersphere/bee/v2@v2.2.0/pkg/storage/migration/migration_test.go (about)

     1  // Copyright 2022 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package migration_test
     6  
     7  import (
     8  	"encoding/binary"
     9  	"errors"
    10  	"math"
    11  	"math/rand"
    12  	"strconv"
    13  	"testing"
    14  
    15  	storage "github.com/ethersphere/bee/v2/pkg/storage"
    16  	"github.com/ethersphere/bee/v2/pkg/storage/inmemstore"
    17  	"github.com/ethersphere/bee/v2/pkg/storage/migration"
    18  	"github.com/ethersphere/bee/v2/pkg/storage/storagetest"
    19  	"github.com/ethersphere/bee/v2/pkg/storage/storageutil"
    20  )
    21  
    22  var (
    23  	errStep = errors.New("step error")
    24  )
    25  
    26  func TestLatestVersion(t *testing.T) {
    27  	t.Parallel()
    28  
    29  	const expectedLatestVersion = 8
    30  	steps := migration.Steps{
    31  		8: func() error { return nil },
    32  		7: func() error { return nil },
    33  		6: func() error { return nil },
    34  	}
    35  
    36  	latestVersion := migration.LatestVersion(steps)
    37  
    38  	if latestVersion != expectedLatestVersion {
    39  		t.Errorf("got %d, expected %d", latestVersion, expectedLatestVersion)
    40  	}
    41  }
    42  
    43  func TestGetSetVersion(t *testing.T) {
    44  	t.Parallel()
    45  
    46  	t.Run("Version", func(t *testing.T) {
    47  		t.Parallel()
    48  
    49  		s := inmemstore.New()
    50  
    51  		gotVersion, err := migration.Version(s, "migration")
    52  		if err != nil {
    53  			t.Errorf("Version() unexpected error: %v", err)
    54  		}
    55  		if gotVersion != 0 {
    56  			t.Errorf("expect version to be 0, got %v", gotVersion)
    57  		}
    58  	})
    59  
    60  	t.Run("SetVersion", func(t *testing.T) {
    61  		t.Parallel()
    62  
    63  		s := inmemstore.New()
    64  
    65  		const version = 10
    66  
    67  		err := migration.SetVersion(s, version, "migration")
    68  		if err != nil {
    69  			t.Errorf("SetVersion() unexpected error: %v", err)
    70  		}
    71  
    72  		gotVersion, err := migration.Version(s, "migration")
    73  		if err != nil {
    74  			t.Errorf("Version() unexpected error: %v", err)
    75  		}
    76  		if gotVersion != version {
    77  			t.Errorf("expect version to be %d, got %d", version, gotVersion)
    78  		}
    79  	})
    80  }
    81  
    82  func TestValidateVersions(t *testing.T) {
    83  	t.Parallel()
    84  	objT1 := &obj{id: 111, val: 1}
    85  	objT2 := &obj{id: 222, val: 2}
    86  	objT3 := &obj{id: 333, val: 3}
    87  
    88  	s := inmemstore.New()
    89  
    90  	tests := []struct {
    91  		name    string
    92  		input   migration.Steps
    93  		wantErr bool
    94  	}{
    95  		{
    96  			name:    "empty",
    97  			input:   migration.Steps{},
    98  			wantErr: true,
    99  		},
   100  		{
   101  			name: "missing version 3",
   102  			input: migration.Steps{
   103  				1: func() error {
   104  					return s.Put(objT1)
   105  				},
   106  				2: func() error {
   107  					return s.Put(objT2)
   108  				},
   109  				4: func() error {
   110  					return s.Put(objT3)
   111  				},
   112  			},
   113  			wantErr: true,
   114  		},
   115  		{
   116  			name: "not missing",
   117  			input: migration.Steps{
   118  				1: func() error {
   119  					return s.Put(objT1)
   120  				},
   121  				2: func() error {
   122  					return s.Put(objT2)
   123  				},
   124  				3: func() error {
   125  					return s.Put(objT3)
   126  				},
   127  			},
   128  			wantErr: false,
   129  		},
   130  		{
   131  			name: "desc order versions",
   132  			input: migration.Steps{
   133  				3: func() error {
   134  					return s.Put(objT1)
   135  				},
   136  				2: func() error {
   137  					return s.Put(objT2)
   138  				},
   139  				1: func() error {
   140  					return s.Put(objT3)
   141  				},
   142  			},
   143  			wantErr: false,
   144  		},
   145  		{
   146  			name: "desc order version missing",
   147  			input: migration.Steps{
   148  				4: func() error {
   149  					return s.Put(objT1)
   150  				},
   151  				2: func() error {
   152  					return s.Put(objT2)
   153  				},
   154  				1: func() error {
   155  					return s.Put(objT3)
   156  				},
   157  			},
   158  			wantErr: true,
   159  		},
   160  	}
   161  	for _, tt := range tests {
   162  		tt := tt
   163  		t.Run(tt.name, func(t *testing.T) {
   164  			t.Parallel()
   165  			if err := migration.ValidateVersions(tt.input); (err != nil) != tt.wantErr {
   166  				t.Errorf("ValidateVersions() unexpected error: %v, wantErr : %v", err, tt.wantErr)
   167  			}
   168  		})
   169  	}
   170  }
   171  
   172  func TestMigrate(t *testing.T) {
   173  	t.Parallel()
   174  	objT1 := &obj{id: 111, val: 1}
   175  	objT2 := &obj{id: 222, val: 2}
   176  	objT3 := &obj{id: 333, val: 3}
   177  
   178  	t.Run("migration: 0 to 3", func(t *testing.T) {
   179  		t.Parallel()
   180  
   181  		s := inmemstore.New()
   182  
   183  		steps := migration.Steps{
   184  			1: func() error {
   185  				return s.Put(objT1)
   186  			},
   187  			2: func() error {
   188  				return s.Put(objT2)
   189  			},
   190  			3: func() error {
   191  				return s.Put(objT3)
   192  			},
   193  		}
   194  
   195  		if err := migration.Migrate(s, "migration", steps); err != nil {
   196  			t.Errorf("Migrate() unexpected error: %v", err)
   197  		}
   198  
   199  		newVersion, err := migration.Version(s, "migration")
   200  		if err != nil {
   201  			t.Errorf("Version() unexpected error: %v", err)
   202  		}
   203  		if newVersion != 3 {
   204  			t.Errorf("new version = %v must be 3", newVersion)
   205  		}
   206  
   207  		assertObjectExists(t, s, objT1, objT2, objT3)
   208  	})
   209  
   210  	t.Run("migration: 5 to 8", func(t *testing.T) {
   211  		t.Parallel()
   212  
   213  		s := inmemstore.New()
   214  
   215  		steps := migration.Steps{
   216  			8: func() error {
   217  				return s.Put(objT1)
   218  			},
   219  			7: func() error {
   220  				return s.Put(objT2)
   221  			},
   222  			6: func() error {
   223  				return s.Put(objT3)
   224  			},
   225  		}
   226  
   227  		err := migration.SetVersion(s, 5, "migration")
   228  		if err != nil {
   229  			t.Errorf("SetVersion() unexpected error: %v", err)
   230  		}
   231  
   232  		if err := migration.Migrate(s, "migration", steps); err != nil {
   233  			t.Errorf("Migrate() unexpected error: %v", err)
   234  		}
   235  
   236  		newVersion, err := migration.Version(s, "migration")
   237  		if err != nil {
   238  			t.Errorf("Version() unexpected error: %v", err)
   239  		}
   240  		if newVersion != 8 {
   241  			t.Errorf("new version = %v must be 8", newVersion)
   242  		}
   243  
   244  		assertObjectExists(t, s, objT1, objT2, objT3)
   245  	})
   246  
   247  	t.Run("migration: 5 to 8 with steps error", func(t *testing.T) {
   248  		t.Parallel()
   249  
   250  		s := inmemstore.New()
   251  
   252  		steps := migration.Steps{
   253  			8: func() error {
   254  				return s.Put(objT1)
   255  			},
   256  			7: func() error {
   257  				return errStep
   258  			},
   259  			6: func() error {
   260  				return s.Put(objT3)
   261  			},
   262  		}
   263  
   264  		err := migration.SetVersion(s, 5, "migration")
   265  		if err != nil {
   266  			t.Errorf("SetVersion() unexpected error: %v", err)
   267  		}
   268  
   269  		if err := migration.Migrate(s, "migration", steps); err != nil && !errors.Is(err, errStep) {
   270  			t.Errorf("Migrate() unexpected error: %v", err)
   271  		}
   272  
   273  		newVersion, err := migration.Version(s, "migration")
   274  		if err != nil {
   275  			t.Errorf("Version() unexpected error: %v", err)
   276  		}
   277  		// since we have error on step 7, we should be on version 6
   278  		if newVersion != 6 {
   279  			t.Errorf("new version = %v must be 6", newVersion)
   280  		}
   281  	})
   282  }
   283  
   284  func assertObjectExists(t *testing.T, s storage.BatchStore, keys ...storage.Key) {
   285  	t.Helper()
   286  
   287  	for _, key := range keys {
   288  		if isExist, _ := s.Has(key); !isExist {
   289  			t.Errorf("key = %v doesn't exists", key)
   290  		}
   291  	}
   292  }
   293  
   294  func TestTagIDAddressItem_MarshalAndUnmarshal(t *testing.T) {
   295  	t.Parallel()
   296  
   297  	tests := []struct {
   298  		name string
   299  		test *storagetest.ItemMarshalAndUnmarshalTest
   300  	}{
   301  		{
   302  			name: "zero values",
   303  			test: &storagetest.ItemMarshalAndUnmarshalTest{
   304  				Item:    &migration.StorageVersionItem{},
   305  				Factory: func() storage.Item { return new(migration.StorageVersionItem) },
   306  			},
   307  		},
   308  		{
   309  			name: "max value",
   310  			test: &storagetest.ItemMarshalAndUnmarshalTest{
   311  				Item:    &migration.StorageVersionItem{Version: math.MaxUint64},
   312  				Factory: func() storage.Item { return new(migration.StorageVersionItem) },
   313  			},
   314  		},
   315  		{
   316  			name: "invalid size",
   317  			test: &storagetest.ItemMarshalAndUnmarshalTest{
   318  				Item: &storagetest.ItemStub{
   319  					MarshalBuf:   []byte{0xFF},
   320  					UnmarshalBuf: []byte{0xFF},
   321  				},
   322  				Factory:      func() storage.Item { return new(migration.StorageVersionItem) },
   323  				UnmarshalErr: migration.ErrStorageVersionItemUnmarshalInvalidSize,
   324  			},
   325  		},
   326  		{
   327  			name: "random value",
   328  			test: &storagetest.ItemMarshalAndUnmarshalTest{
   329  				Item:    &migration.StorageVersionItem{Version: rand.Uint64()},
   330  				Factory: func() storage.Item { return new(migration.StorageVersionItem) },
   331  			},
   332  		}}
   333  
   334  	for _, tc := range tests {
   335  		tc := tc
   336  		t.Run(tc.name, func(t *testing.T) {
   337  			t.Parallel()
   338  			storagetest.TestItemMarshalAndUnmarshal(t, tc.test)
   339  		})
   340  	}
   341  }
   342  
   343  type obj struct {
   344  	id  int
   345  	val int
   346  }
   347  
   348  func newObjFactory() storage.Item { return &obj{} }
   349  
   350  func (o *obj) ID() string     { return strconv.Itoa(o.id) }
   351  func (obj) Namespace() string { return "obj" }
   352  
   353  func (o *obj) Marshal() ([]byte, error) {
   354  	buf := make([]byte, 16)
   355  	binary.LittleEndian.PutUint64(buf, uint64(o.id))
   356  	binary.LittleEndian.PutUint64(buf, uint64(o.val))
   357  	return buf, nil
   358  }
   359  
   360  func (o *obj) Unmarshal(buf []byte) error {
   361  	if len(buf) != 16 {
   362  		return errors.New("invalid length")
   363  	}
   364  	o.id = int(binary.LittleEndian.Uint64(buf))
   365  	o.val = int(binary.LittleEndian.Uint64(buf))
   366  	return nil
   367  }
   368  
   369  func (o *obj) Clone() storage.Item {
   370  	if o == nil {
   371  		return nil
   372  	}
   373  	return &obj{
   374  		id:  o.id,
   375  		val: o.val,
   376  	}
   377  }
   378  
   379  func (o obj) String() string {
   380  	return storageutil.JoinFields(o.Namespace(), o.ID())
   381  }