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 }