github.com/decred/dcrlnd@v0.7.6/channeldb/meta_test.go (about) 1 package channeldb 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "testing" 8 9 "github.com/decred/dcrlnd/kvdb" 10 "github.com/go-errors/errors" 11 ) 12 13 // applyMigration is a helper test function that encapsulates the general steps 14 // which are needed to properly check the result of applying migration function. 15 func applyMigration(t *testing.T, beforeMigration, afterMigration func(d *DB), 16 migrationFunc migration, shouldFail bool, dryRun bool) { 17 18 cdb, cleanUp, err := MakeTestDB() 19 defer cleanUp() 20 if err != nil { 21 t.Fatal(err) 22 } 23 cdb.dryRun = dryRun 24 25 // Create a test node that will be our source node. 26 testNode, err := createTestVertex(cdb) 27 if err != nil { 28 t.Fatal(err) 29 } 30 graph := cdb.ChannelGraph() 31 if err := graph.SetSourceNode(testNode); err != nil { 32 t.Fatal(err) 33 } 34 35 // beforeMigration usually used for populating the database 36 // with test data. 37 beforeMigration(cdb) 38 39 // Create test meta info with zero database version and put it on disk. 40 // Than creating the version list pretending that new version was added. 41 meta := &Meta{DbVersionNumber: 0} 42 if err := cdb.PutMeta(meta); err != nil { 43 t.Fatalf("unable to store meta data: %v", err) 44 } 45 46 versions := []version{ 47 { 48 number: 0, 49 migration: nil, 50 }, 51 { 52 number: 1, 53 migration: migrationFunc, 54 }, 55 } 56 57 defer func() { 58 if r := recover(); r != nil { 59 if dryRun && r != ErrDryRunMigrationOK { 60 t.Fatalf("expected dry run migration OK") 61 } 62 err = errors.New(r) 63 } 64 65 if err == nil && shouldFail { 66 t.Fatal("error wasn't received on migration stage") 67 } else if err != nil && !shouldFail { 68 t.Fatalf("error was received on migration stage: %v", err) 69 } 70 71 // afterMigration usually used for checking the database state and 72 // throwing the error if something went wrong. 73 afterMigration(cdb) 74 }() 75 76 // Sync with the latest version - applying migration function. 77 err = cdb.syncVersions(versions) 78 if err != nil { 79 log.Error(err) 80 } 81 } 82 83 // TestVersionFetchPut checks the propernces of fetch/put methods 84 // and also initialization of meta data in case if don't have any in 85 // database. 86 func TestVersionFetchPut(t *testing.T) { 87 t.Parallel() 88 89 db, cleanUp, err := MakeTestDB() 90 defer cleanUp() 91 if err != nil { 92 t.Fatal(err) 93 } 94 95 meta, err := db.FetchMeta(nil) 96 if err != nil { 97 t.Fatal(err) 98 } 99 100 if meta.DbVersionNumber != getLatestDBVersion(dbVersions) { 101 t.Fatal("initialization of meta information wasn't performed") 102 } 103 104 newVersion := getLatestDBVersion(dbVersions) + 1 105 meta.DbVersionNumber = newVersion 106 107 if err := db.PutMeta(meta); err != nil { 108 t.Fatalf("update of meta failed %v", err) 109 } 110 111 meta, err = db.FetchMeta(nil) 112 if err != nil { 113 t.Fatal(err) 114 } 115 116 if meta.DbVersionNumber != newVersion { 117 t.Fatal("update of meta information wasn't performed") 118 } 119 } 120 121 // TestOrderOfMigrations checks that migrations are applied in proper order. 122 func TestOrderOfMigrations(t *testing.T) { 123 t.Parallel() 124 125 appliedMigration := -1 126 versions := []version{ 127 {0, nil}, 128 {1, nil}, 129 {2, func(tx kvdb.RwTx) error { 130 appliedMigration = 2 131 return nil 132 }}, 133 {3, func(tx kvdb.RwTx) error { 134 appliedMigration = 3 135 return nil 136 }}, 137 } 138 139 // Retrieve the migration that should be applied to db, as far as 140 // current version is 1, we skip zero and first versions. 141 migrations, _ := getMigrationsToApply(versions, 1) 142 143 if len(migrations) != 2 { 144 t.Fatal("incorrect number of migrations to apply") 145 } 146 147 // Apply first migration. 148 migrations[0](nil) 149 150 // Check that first migration corresponds to the second version. 151 if appliedMigration != 2 { 152 t.Fatal("incorrect order of applying migrations") 153 } 154 155 // Apply second migration. 156 migrations[1](nil) 157 158 // Check that second migration corresponds to the third version. 159 if appliedMigration != 3 { 160 t.Fatal("incorrect order of applying migrations") 161 } 162 } 163 164 // TestGlobalVersionList checks that there is no mistake in global version list 165 // in terms of version ordering. 166 func TestGlobalVersionList(t *testing.T) { 167 t.Parallel() 168 169 if dbVersions == nil { 170 t.Fatal("can't find versions list") 171 } 172 173 if len(dbVersions) == 0 { 174 t.Fatal("db versions list is empty") 175 } 176 177 prev := dbVersions[0].number 178 for i := 1; i < len(dbVersions); i++ { 179 version := dbVersions[i].number 180 181 if version == prev { 182 t.Fatal("duplicates db versions") 183 } 184 if version < prev { 185 t.Fatal("order of db versions is wrong") 186 } 187 188 prev = version 189 } 190 } 191 192 // TestMigrationWithPanic asserts that if migration logic panics, we will return 193 // to the original state unaltered. 194 func TestMigrationWithPanic(t *testing.T) { 195 t.Parallel() 196 197 bucketPrefix := []byte("somebucket") 198 keyPrefix := []byte("someprefix") 199 beforeMigration := []byte("beforemigration") 200 afterMigration := []byte("aftermigration") 201 202 beforeMigrationFunc := func(d *DB) { 203 // Insert data in database and in order then make sure that the 204 // key isn't changes in case of panic or fail. 205 err := kvdb.Update(d, func(tx kvdb.RwTx) error { 206 bucket, err := tx.CreateTopLevelBucket(bucketPrefix) 207 if err != nil { 208 return err 209 } 210 211 return bucket.Put(keyPrefix, beforeMigration) 212 }, func() {}) 213 if err != nil { 214 t.Fatalf("unable to insert: %v", err) 215 } 216 } 217 218 // Create migration function which changes the initially created data and 219 // throw the panic, in this case we pretending that something goes. 220 migrationWithPanic := func(tx kvdb.RwTx) error { 221 bucket, err := tx.CreateTopLevelBucket(bucketPrefix) 222 if err != nil { 223 return err 224 } 225 226 bucket.Put(keyPrefix, afterMigration) 227 panic("panic!") 228 } 229 230 // Check that version of database and data wasn't changed. 231 afterMigrationFunc := func(d *DB) { 232 meta, err := d.FetchMeta(nil) 233 if err != nil { 234 t.Fatal(err) 235 } 236 237 if meta.DbVersionNumber != 0 { 238 t.Fatal("migration panicked but version is changed") 239 } 240 241 err = kvdb.Update(d, func(tx kvdb.RwTx) error { 242 bucket, err := tx.CreateTopLevelBucket(bucketPrefix) 243 if err != nil { 244 return err 245 } 246 247 value := bucket.Get(keyPrefix) 248 if !bytes.Equal(value, beforeMigration) { 249 return errors.New("migration failed but data is " + 250 "changed") 251 } 252 253 return nil 254 }, func() {}) 255 if err != nil { 256 t.Fatal(err) 257 } 258 } 259 260 applyMigration(t, 261 beforeMigrationFunc, 262 afterMigrationFunc, 263 migrationWithPanic, 264 true, 265 false) 266 } 267 268 // TestMigrationWithFatal asserts that migrations which fail do not modify the 269 // database. 270 func TestMigrationWithFatal(t *testing.T) { 271 t.Parallel() 272 273 bucketPrefix := []byte("somebucket") 274 keyPrefix := []byte("someprefix") 275 beforeMigration := []byte("beforemigration") 276 afterMigration := []byte("aftermigration") 277 278 beforeMigrationFunc := func(d *DB) { 279 err := kvdb.Update(d, func(tx kvdb.RwTx) error { 280 bucket, err := tx.CreateTopLevelBucket(bucketPrefix) 281 if err != nil { 282 return err 283 } 284 285 return bucket.Put(keyPrefix, beforeMigration) 286 }, func() {}) 287 if err != nil { 288 t.Fatalf("unable to insert pre migration key: %v", err) 289 } 290 } 291 292 // Create migration function which changes the initially created data and 293 // return the error, in this case we pretending that something goes 294 // wrong. 295 migrationWithFatal := func(tx kvdb.RwTx) error { 296 bucket, err := tx.CreateTopLevelBucket(bucketPrefix) 297 if err != nil { 298 return err 299 } 300 301 bucket.Put(keyPrefix, afterMigration) 302 return errors.New("some error") 303 } 304 305 // Check that version of database and initial data wasn't changed. 306 afterMigrationFunc := func(d *DB) { 307 meta, err := d.FetchMeta(nil) 308 if err != nil { 309 t.Fatal(err) 310 } 311 312 if meta.DbVersionNumber != 0 { 313 t.Fatal("migration failed but version is changed") 314 } 315 316 err = kvdb.Update(d, func(tx kvdb.RwTx) error { 317 bucket, err := tx.CreateTopLevelBucket(bucketPrefix) 318 if err != nil { 319 return err 320 } 321 322 value := bucket.Get(keyPrefix) 323 if !bytes.Equal(value, beforeMigration) { 324 return errors.New("migration failed but data is " + 325 "changed") 326 } 327 328 return nil 329 }, func() {}) 330 if err != nil { 331 t.Fatal(err) 332 } 333 } 334 335 applyMigration(t, 336 beforeMigrationFunc, 337 afterMigrationFunc, 338 migrationWithFatal, 339 true, 340 false) 341 } 342 343 // TestMigrationWithoutErrors asserts that a successful migration has its 344 // changes applied to the database. 345 func TestMigrationWithoutErrors(t *testing.T) { 346 t.Parallel() 347 348 bucketPrefix := []byte("somebucket") 349 keyPrefix := []byte("someprefix") 350 beforeMigration := []byte("beforemigration") 351 afterMigration := []byte("aftermigration") 352 353 // Populate database with initial data. 354 beforeMigrationFunc := func(d *DB) { 355 err := kvdb.Update(d, func(tx kvdb.RwTx) error { 356 bucket, err := tx.CreateTopLevelBucket(bucketPrefix) 357 if err != nil { 358 return err 359 } 360 361 return bucket.Put(keyPrefix, beforeMigration) 362 }, func() {}) 363 if err != nil { 364 t.Fatalf("unable to update db pre migration: %v", err) 365 } 366 } 367 368 // Create migration function which changes the initially created data. 369 migrationWithoutErrors := func(tx kvdb.RwTx) error { 370 bucket, err := tx.CreateTopLevelBucket(bucketPrefix) 371 if err != nil { 372 return err 373 } 374 375 bucket.Put(keyPrefix, afterMigration) 376 return nil 377 } 378 379 // Check that version of database and data was properly changed. 380 afterMigrationFunc := func(d *DB) { 381 meta, err := d.FetchMeta(nil) 382 if err != nil { 383 t.Fatal(err) 384 } 385 386 if meta.DbVersionNumber != 1 { 387 t.Fatal("version number isn't changed after " + 388 "successfully applied migration") 389 } 390 391 err = kvdb.Update(d, func(tx kvdb.RwTx) error { 392 bucket, err := tx.CreateTopLevelBucket(bucketPrefix) 393 if err != nil { 394 return err 395 } 396 397 value := bucket.Get(keyPrefix) 398 if !bytes.Equal(value, afterMigration) { 399 return errors.New("migration wasn't applied " + 400 "properly") 401 } 402 403 return nil 404 }, func() {}) 405 if err != nil { 406 t.Fatal(err) 407 } 408 } 409 410 applyMigration(t, 411 beforeMigrationFunc, 412 afterMigrationFunc, 413 migrationWithoutErrors, 414 false, 415 false) 416 } 417 418 // TestMigrationReversion tests after performing a migration to a higher 419 // database version, opening the database with a lower latest db version returns 420 // ErrDBReversion. 421 func TestMigrationReversion(t *testing.T) { 422 t.Parallel() 423 424 tempDirName, err := ioutil.TempDir("", "channeldb") 425 defer func() { 426 os.RemoveAll(tempDirName) 427 }() 428 if err != nil { 429 t.Fatalf("unable to create temp dir: %v", err) 430 } 431 432 backend, cleanup, err := kvdb.GetTestBackend(tempDirName, "cdb") 433 if err != nil { 434 t.Fatalf("unable to get test db backend: %v", err) 435 } 436 437 cdb, err := CreateWithBackend(backend) 438 if err != nil { 439 cleanup() 440 t.Fatalf("unable to open channeldb: %v", err) 441 } 442 443 // Update the database metadata to point to one more than the highest 444 // known version. 445 err = kvdb.Update(cdb, func(tx kvdb.RwTx) error { 446 newMeta := &Meta{ 447 DbVersionNumber: getLatestDBVersion(dbVersions) + 1, 448 } 449 450 return putMeta(newMeta, tx) 451 }, func() {}) 452 453 // Close the database. Even if we succeeded, our next step is to reopen. 454 cdb.Close() 455 cleanup() 456 457 if err != nil { 458 t.Fatalf("unable to increase db version: %v", err) 459 } 460 461 backend, cleanup, err = kvdb.GetTestBackend(tempDirName, "cdb") 462 if err != nil { 463 t.Fatalf("unable to get test db backend: %v", err) 464 } 465 defer cleanup() 466 467 _, err = CreateWithBackend(backend) 468 if err != ErrDBReversion { 469 t.Fatalf("unexpected error when opening channeldb, "+ 470 "want: %v, got: %v", ErrDBReversion, err) 471 } 472 } 473 474 // TestMigrationDryRun ensures that opening the database in dry run migration 475 // mode will fail and not commit the migration. 476 func TestMigrationDryRun(t *testing.T) { 477 t.Parallel() 478 479 // Nothing to do, will inspect version number. 480 beforeMigrationFunc := func(d *DB) {} 481 482 // Check that version of database version is not modified. 483 afterMigrationFunc := func(d *DB) { 484 err := kvdb.View(d, func(tx kvdb.RTx) error { 485 meta, err := d.FetchMeta(nil) 486 if err != nil { 487 t.Fatal(err) 488 } 489 490 if meta.DbVersionNumber != 0 { 491 t.Fatal("dry run migration was not aborted") 492 } 493 494 return nil 495 }, func() {}) 496 if err != nil { 497 t.Fatalf("unable to apply after func: %v", err) 498 } 499 } 500 501 applyMigration(t, 502 beforeMigrationFunc, 503 afterMigrationFunc, 504 func(kvdb.RwTx) error { return nil }, 505 true, 506 true) 507 }