github.com/decred/dcrlnd@v0.7.6/kvdb/readwrite_bucket_test.go (about) 1 package kvdb 2 3 import ( 4 "fmt" 5 "math" 6 "testing" 7 8 "github.com/btcsuite/btcwallet/walletdb" 9 "github.com/stretchr/testify/require" 10 ) 11 12 func testBucketCreation(t *testing.T, db walletdb.DB) { 13 err := Update(db, func(tx walletdb.ReadWriteTx) error { 14 // empty bucket name 15 b, err := tx.CreateTopLevelBucket(nil) 16 require.Error(t, walletdb.ErrBucketNameRequired, err) 17 require.Nil(t, b) 18 19 // empty bucket name 20 b, err = tx.CreateTopLevelBucket([]byte("")) 21 require.Error(t, walletdb.ErrBucketNameRequired, err) 22 require.Nil(t, b) 23 24 // "apple" 25 apple, err := tx.CreateTopLevelBucket([]byte("apple")) 26 require.NoError(t, err) 27 require.NotNil(t, apple) 28 29 // Check bucket tx. 30 require.Equal(t, tx, apple.Tx()) 31 32 // "apple" already created 33 b, err = tx.CreateTopLevelBucket([]byte("apple")) 34 require.NoError(t, err) 35 require.NotNil(t, b) 36 37 // "apple/banana" 38 banana, err := apple.CreateBucket([]byte("banana")) 39 require.NoError(t, err) 40 require.NotNil(t, banana) 41 42 banana, err = apple.CreateBucketIfNotExists([]byte("banana")) 43 require.NoError(t, err) 44 require.NotNil(t, banana) 45 46 // Try creating "apple/banana" again 47 b, err = apple.CreateBucket([]byte("banana")) 48 require.Error(t, walletdb.ErrBucketExists, err) 49 require.Nil(t, b) 50 51 // "apple/mango" 52 mango, err := apple.CreateBucket([]byte("mango")) 53 require.Nil(t, err) 54 require.NotNil(t, mango) 55 56 // "apple/banana/pear" 57 pear, err := banana.CreateBucket([]byte("pear")) 58 require.Nil(t, err) 59 require.NotNil(t, pear) 60 61 // empty bucket 62 require.Nil(t, apple.NestedReadWriteBucket(nil)) 63 require.Nil(t, apple.NestedReadWriteBucket([]byte(""))) 64 65 // "apple/pear" doesn't exist 66 require.Nil(t, apple.NestedReadWriteBucket([]byte("pear"))) 67 68 // "apple/banana" exits 69 require.NotNil(t, apple.NestedReadWriteBucket([]byte("banana"))) 70 require.NotNil(t, apple.NestedReadBucket([]byte("banana"))) 71 return nil 72 }, func() {}) 73 74 require.Nil(t, err) 75 } 76 77 func testBucketDeletion(t *testing.T, db walletdb.DB) { 78 err := Update(db, func(tx walletdb.ReadWriteTx) error { 79 // "apple" 80 apple, err := tx.CreateTopLevelBucket([]byte("apple")) 81 require.Nil(t, err) 82 require.NotNil(t, apple) 83 84 // "apple/banana" 85 banana, err := apple.CreateBucket([]byte("banana")) 86 require.Nil(t, err) 87 require.NotNil(t, banana) 88 89 kvs := []KV{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}} 90 91 for _, kv := range kvs { 92 require.NoError(t, banana.Put([]byte(kv.key), []byte(kv.val))) 93 require.Equal(t, []byte(kv.val), banana.Get([]byte(kv.key))) 94 } 95 96 // Delete a k/v from "apple/banana" 97 require.NoError(t, banana.Delete([]byte("key2"))) 98 // Try getting/putting/deleting invalid k/v's. 99 require.Nil(t, banana.Get(nil)) 100 require.Error(t, walletdb.ErrKeyRequired, banana.Put(nil, []byte("val"))) 101 require.Error(t, walletdb.ErrKeyRequired, banana.Delete(nil)) 102 103 // Try deleting a k/v that doesn't exist. 104 require.NoError(t, banana.Delete([]byte("nokey"))) 105 106 // "apple/pear" 107 pear, err := apple.CreateBucket([]byte("pear")) 108 require.Nil(t, err) 109 require.NotNil(t, pear) 110 111 // Put some values into "apple/pear" 112 for _, kv := range kvs { 113 require.Nil(t, pear.Put([]byte(kv.key), []byte(kv.val))) 114 require.Equal(t, []byte(kv.val), pear.Get([]byte(kv.key))) 115 } 116 117 // Create nested bucket "apple/pear/cherry" 118 cherry, err := pear.CreateBucket([]byte("cherry")) 119 require.Nil(t, err) 120 require.NotNil(t, cherry) 121 122 // Put some values into "apple/pear/cherry" 123 for _, kv := range kvs { 124 require.NoError(t, cherry.Put([]byte(kv.key), []byte(kv.val))) 125 } 126 127 // Read back values in "apple/pear/cherry" trough a read bucket. 128 cherryReadBucket := pear.NestedReadBucket([]byte("cherry")) 129 for _, kv := range kvs { 130 require.Equal( 131 t, []byte(kv.val), 132 cherryReadBucket.Get([]byte(kv.key)), 133 ) 134 } 135 136 // Try deleting some invalid buckets. 137 require.Error(t, 138 walletdb.ErrBucketNameRequired, apple.DeleteNestedBucket(nil), 139 ) 140 141 // Try deleting a non existing bucket. 142 require.Error( 143 t, 144 walletdb.ErrBucketNotFound, 145 apple.DeleteNestedBucket([]byte("missing")), 146 ) 147 148 // Delete "apple/pear" 149 require.Nil(t, apple.DeleteNestedBucket([]byte("pear"))) 150 151 // "apple/pear" deleted 152 require.Nil(t, apple.NestedReadWriteBucket([]byte("pear"))) 153 154 // "aple/banana" exists 155 require.NotNil(t, apple.NestedReadWriteBucket([]byte("banana"))) 156 return nil 157 }, func() {}) 158 159 require.Nil(t, err) 160 } 161 162 type bucketIterator = func(walletdb.ReadWriteBucket, 163 func(key, val []byte) error) error 164 165 func testBucketForEach(t *testing.T, db walletdb.DB) { 166 testBucketIterator(t, db, func(bucket walletdb.ReadWriteBucket, 167 callback func(key, val []byte) error) error { 168 169 return bucket.ForEach(callback) 170 }) 171 } 172 173 func testBucketIterator(t *testing.T, db walletdb.DB, 174 iterator bucketIterator) { 175 176 err := Update(db, func(tx walletdb.ReadWriteTx) error { 177 // "apple" 178 apple, err := tx.CreateTopLevelBucket([]byte("apple")) 179 require.Nil(t, err) 180 require.NotNil(t, apple) 181 182 // "apple/banana" 183 banana, err := apple.CreateBucket([]byte("banana")) 184 require.Nil(t, err) 185 require.NotNil(t, banana) 186 187 kvs := []KV{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}} 188 189 // put some values into "apple" and "apple/banana" too 190 for _, kv := range kvs { 191 require.Nil(t, apple.Put([]byte(kv.key), []byte(kv.val))) 192 require.Equal(t, []byte(kv.val), apple.Get([]byte(kv.key))) 193 194 require.Nil(t, banana.Put([]byte(kv.key), []byte(kv.val))) 195 require.Equal(t, []byte(kv.val), banana.Get([]byte(kv.key))) 196 } 197 198 got := make(map[string]string) 199 err = apple.ForEach(func(key, val []byte) error { 200 got[string(key)] = string(val) 201 return nil 202 }) 203 204 expected := map[string]string{ 205 "key1": "val1", 206 "key2": "val2", 207 "key3": "val3", 208 "banana": "", 209 } 210 211 require.NoError(t, err) 212 require.Equal(t, expected, got) 213 214 got = make(map[string]string) 215 err = iterator(banana, func(key, val []byte) error { 216 got[string(key)] = string(val) 217 return nil 218 }) 219 220 require.NoError(t, err) 221 // remove the sub-bucket key 222 delete(expected, "banana") 223 require.Equal(t, expected, got) 224 225 return nil 226 }, func() {}) 227 228 require.Nil(t, err) 229 } 230 231 func testBucketForEachWithError(t *testing.T, db walletdb.DB) { 232 err := Update(db, func(tx walletdb.ReadWriteTx) error { 233 // "apple" 234 apple, err := tx.CreateTopLevelBucket([]byte("apple")) 235 require.Nil(t, err) 236 require.NotNil(t, apple) 237 238 // "apple/banana" 239 banana, err := apple.CreateBucket([]byte("banana")) 240 require.Nil(t, err) 241 require.NotNil(t, banana) 242 243 // "apple/pear" 244 pear, err := apple.CreateBucket([]byte("pear")) 245 require.Nil(t, err) 246 require.NotNil(t, pear) 247 248 kvs := []KV{{"key1", "val1"}, {"key2", "val2"}} 249 250 // Put some values into "apple" and "apple/banana" too. 251 for _, kv := range kvs { 252 require.Nil(t, apple.Put([]byte(kv.key), []byte(kv.val))) 253 require.Equal(t, []byte(kv.val), apple.Get([]byte(kv.key))) 254 } 255 256 got := make(map[string]string) 257 i := 0 258 // Error while iterating value keys. 259 err = apple.ForEach(func(key, val []byte) error { 260 if i == 2 { 261 return fmt.Errorf("error") 262 } 263 264 got[string(key)] = string(val) 265 i++ 266 return nil 267 }) 268 269 expected := map[string]string{ 270 "banana": "", 271 "key1": "val1", 272 } 273 274 require.Equal(t, expected, got) 275 require.Error(t, err) 276 277 got = make(map[string]string) 278 i = 0 279 // Erro while iterating buckets. 280 err = apple.ForEach(func(key, val []byte) error { 281 if i == 3 { 282 return fmt.Errorf("error") 283 } 284 285 got[string(key)] = string(val) 286 i++ 287 return nil 288 }) 289 290 expected = map[string]string{ 291 "banana": "", 292 "key1": "val1", 293 "key2": "val2", 294 } 295 296 require.Equal(t, expected, got) 297 require.Error(t, err) 298 return nil 299 }, func() {}) 300 301 require.Nil(t, err) 302 } 303 304 func testBucketSequence(t *testing.T, db walletdb.DB) { 305 err := Update(db, func(tx walletdb.ReadWriteTx) error { 306 apple, err := tx.CreateTopLevelBucket([]byte("apple")) 307 require.Nil(t, err) 308 require.NotNil(t, apple) 309 310 banana, err := apple.CreateBucket([]byte("banana")) 311 require.Nil(t, err) 312 require.NotNil(t, banana) 313 314 require.Equal(t, uint64(0), apple.Sequence()) 315 require.Equal(t, uint64(0), banana.Sequence()) 316 317 require.Nil(t, apple.SetSequence(math.MaxUint64)) 318 require.Equal(t, uint64(math.MaxUint64), apple.Sequence()) 319 320 for i := uint64(0); i < uint64(5); i++ { 321 s, err := apple.NextSequence() 322 require.Nil(t, err) 323 require.Equal(t, i, s) 324 } 325 326 return nil 327 }, func() {}) 328 329 require.Nil(t, err) 330 } 331 332 // TestKeyClash tests that one cannot create a bucket if a value with the same 333 // key exists and the same is true in reverse: that a value cannot be put if 334 // a bucket with the same key exists. 335 func testKeyClash(t *testing.T, db walletdb.DB) { 336 // First: 337 // put: /apple/key -> val 338 // create bucket: /apple/banana 339 err := Update(db, func(tx walletdb.ReadWriteTx) error { 340 apple, err := tx.CreateTopLevelBucket([]byte("apple")) 341 require.Nil(t, err) 342 require.NotNil(t, apple) 343 344 require.NoError(t, apple.Put([]byte("key"), []byte("val"))) 345 346 banana, err := apple.CreateBucket([]byte("banana")) 347 require.Nil(t, err) 348 require.NotNil(t, banana) 349 350 return nil 351 }, func() {}) 352 353 require.Nil(t, err) 354 355 // Next try to: 356 // put: /apple/banana -> val => will fail (as /apple/banana is a bucket) 357 // create bucket: /apple/key => will fail (as /apple/key is a value) 358 err = Update(db, func(tx walletdb.ReadWriteTx) error { 359 apple, err := tx.CreateTopLevelBucket([]byte("apple")) 360 require.Nil(t, err) 361 require.NotNil(t, apple) 362 363 require.Error(t, 364 walletdb.ErrIncompatibleValue, 365 apple.Put([]byte("banana"), []byte("val")), 366 ) 367 368 b, err := apple.CreateBucket([]byte("key")) 369 require.Nil(t, b) 370 require.Error(t, walletdb.ErrIncompatibleValue, err) 371 372 b, err = apple.CreateBucketIfNotExists([]byte("key")) 373 require.Nil(t, b) 374 require.Error(t, walletdb.ErrIncompatibleValue, err) 375 376 return nil 377 }, func() {}) 378 379 require.Nil(t, err) 380 } 381 382 // TestBucketCreateDelete tests that creating then deleting then creating a 383 // bucket suceeds. 384 func testBucketCreateDelete(t *testing.T, db walletdb.DB) { 385 err := Update(db, func(tx walletdb.ReadWriteTx) error { 386 apple, err := tx.CreateTopLevelBucket([]byte("apple")) 387 require.NoError(t, err) 388 require.NotNil(t, apple) 389 390 banana, err := apple.CreateBucket([]byte("banana")) 391 require.NoError(t, err) 392 require.NotNil(t, banana) 393 394 return nil 395 }, func() {}) 396 require.NoError(t, err) 397 398 err = Update(db, func(tx walletdb.ReadWriteTx) error { 399 apple := tx.ReadWriteBucket([]byte("apple")) 400 require.NotNil(t, apple) 401 require.NoError(t, apple.DeleteNestedBucket([]byte("banana"))) 402 403 return nil 404 }, func() {}) 405 require.NoError(t, err) 406 407 err = Update(db, func(tx walletdb.ReadWriteTx) error { 408 apple := tx.ReadWriteBucket([]byte("apple")) 409 require.NotNil(t, apple) 410 require.NoError(t, apple.Put([]byte("banana"), []byte("value"))) 411 412 return nil 413 }, func() {}) 414 require.NoError(t, err) 415 } 416 417 //nolint:unused 418 func testTopLevelBucketCreation(t *testing.T, db walletdb.DB) { 419 require.NoError(t, Update(db, func(tx walletdb.ReadWriteTx) error { 420 // Try to delete all data (there is none). 421 err := tx.DeleteTopLevelBucket([]byte("top")) 422 require.ErrorIs(t, walletdb.ErrBucketNotFound, err) 423 424 // Create top level bucket. 425 top, err := tx.CreateTopLevelBucket([]byte("top")) 426 require.NoError(t, err) 427 require.NotNil(t, top) 428 429 // Create second top level bucket with special characters. 430 top2, err := tx.CreateTopLevelBucket([]byte{1, 2, 3}) 431 require.NoError(t, err) 432 require.NotNil(t, top2) 433 434 top2 = tx.ReadWriteBucket([]byte{1, 2, 3}) 435 require.NotNil(t, top2) 436 437 // List top level buckets. 438 var tlKeys [][]byte 439 require.NoError(t, tx.ForEachBucket(func(k []byte) error { 440 tlKeys = append(tlKeys, k) 441 return nil 442 })) 443 require.Equal(t, [][]byte{{1, 2, 3}, []byte("top")}, tlKeys) 444 445 // Create third top level bucket with special uppercase. 446 top3, err := tx.CreateTopLevelBucket([]byte("UpperBucket")) 447 require.NoError(t, err) 448 require.NotNil(t, top3) 449 450 top3 = tx.ReadWriteBucket([]byte("UpperBucket")) 451 require.NotNil(t, top3) 452 453 require.NoError(t, tx.DeleteTopLevelBucket([]byte("top"))) 454 require.NoError(t, tx.DeleteTopLevelBucket([]byte{1, 2, 3})) 455 require.NoError(t, tx.DeleteTopLevelBucket([]byte("UpperBucket"))) 456 457 tx.ForEachBucket(func(k []byte) error { 458 require.Fail(t, "no top level buckets expected") 459 return nil 460 }) 461 462 return nil 463 }, func() {})) 464 } 465 466 //nolint:unused 467 func testBucketOperations(t *testing.T, db walletdb.DB) { 468 require.NoError(t, Update(db, func(tx walletdb.ReadWriteTx) error { 469 // Create top level bucket. 470 top, err := tx.CreateTopLevelBucket([]byte("top")) 471 require.NoError(t, err) 472 require.NotNil(t, top) 473 474 // Assert that key doesn't exist. 475 require.Nil(t, top.Get([]byte("key"))) 476 477 require.NoError(t, top.ForEach(func(k, v []byte) error { 478 require.Fail(t, "unexpected data") 479 return nil 480 })) 481 482 // Put key. 483 require.NoError(t, top.Put([]byte("key"), []byte("val"))) 484 require.Equal(t, []byte("val"), top.Get([]byte("key"))) 485 486 // Overwrite key. 487 require.NoError(t, top.Put([]byte("key"), []byte("val2"))) 488 require.Equal(t, []byte("val2"), top.Get([]byte("key"))) 489 490 // Put nil value. 491 require.NoError(t, top.Put([]byte("nilkey"), nil)) 492 require.Equal(t, []byte(""), top.Get([]byte("nilkey"))) 493 494 // Put empty value. 495 require.NoError(t, top.Put([]byte("nilkey"), []byte{})) 496 require.Equal(t, []byte(""), top.Get([]byte("nilkey"))) 497 498 // Try to create bucket with same name as previous key. 499 _, err = top.CreateBucket([]byte("key")) 500 require.ErrorIs(t, err, walletdb.ErrIncompatibleValue) 501 502 _, err = top.CreateBucketIfNotExists([]byte("key")) 503 require.ErrorIs(t, err, walletdb.ErrIncompatibleValue) 504 505 // Create sub-bucket. 506 sub2, err := top.CreateBucket([]byte("sub2")) 507 require.NoError(t, err) 508 require.NotNil(t, sub2) 509 510 // Assert that re-creating the bucket fails. 511 _, err = top.CreateBucket([]byte("sub2")) 512 require.ErrorIs(t, err, walletdb.ErrBucketExists) 513 514 // Assert that create-if-not-exists succeeds. 515 _, err = top.CreateBucketIfNotExists([]byte("sub2")) 516 require.NoError(t, err) 517 518 // Assert that fetching the bucket succeeds. 519 sub2 = top.NestedReadWriteBucket([]byte("sub2")) 520 require.NotNil(t, sub2) 521 522 // Try to put key with same name as bucket. 523 require.ErrorIs(t, top.Put([]byte("sub2"), []byte("val")), walletdb.ErrIncompatibleValue) 524 525 // Put key into sub bucket. 526 require.NoError(t, sub2.Put([]byte("subkey"), []byte("subval"))) 527 require.Equal(t, []byte("subval"), sub2.Get([]byte("subkey"))) 528 529 // Overwrite key in sub bucket. 530 require.NoError(t, sub2.Put([]byte("subkey"), []byte("subval2"))) 531 require.Equal(t, []byte("subval2"), sub2.Get([]byte("subkey"))) 532 533 // Check for each result. 534 kvs := make(map[string][]byte) 535 require.NoError(t, top.ForEach(func(k, v []byte) error { 536 kvs[string(k)] = v 537 return nil 538 })) 539 require.Equal(t, map[string][]byte{ 540 "key": []byte("val2"), 541 "nilkey": []byte(""), 542 "sub2": nil, 543 }, kvs) 544 545 // Delete key. 546 require.NoError(t, top.Delete([]byte("key"))) 547 548 // Delete non-existent key. 549 require.NoError(t, top.Delete([]byte("keynonexistent"))) 550 551 // Test cursor. 552 cursor := top.ReadWriteCursor() 553 k, v := cursor.First() 554 require.Equal(t, []byte("nilkey"), k) 555 require.Equal(t, []byte(""), v) 556 557 k, v = cursor.Last() 558 require.Equal(t, []byte("sub2"), k) 559 require.Nil(t, v) 560 561 k, v = cursor.Prev() 562 require.Equal(t, []byte("nilkey"), k) 563 require.Equal(t, []byte(""), v) 564 565 k, v = cursor.Prev() 566 require.Nil(t, k) 567 require.Nil(t, v) 568 569 k, v = cursor.Next() 570 require.Equal(t, []byte("sub2"), k) 571 require.Nil(t, v) 572 573 k, v = cursor.Next() 574 require.Nil(t, k) 575 require.Nil(t, v) 576 577 k, v = cursor.Seek([]byte("nilkey")) 578 require.Equal(t, []byte("nilkey"), k) 579 require.Equal(t, []byte(""), v) 580 581 require.NoError(t, sub2.Put([]byte("k1"), []byte("v1"))) 582 require.NoError(t, sub2.Put([]byte("k2"), []byte("v2"))) 583 require.NoError(t, sub2.Put([]byte("k3"), []byte("v3"))) 584 585 cursor = sub2.ReadWriteCursor() 586 cursor.First() 587 for i := 0; i < 4; i++ { 588 require.NoError(t, cursor.Delete()) 589 } 590 require.NoError(t, sub2.ForEach(func(k, v []byte) error { 591 require.Fail(t, "unexpected data") 592 return nil 593 })) 594 595 _, err = sub2.CreateBucket([]byte("sub3")) 596 require.NoError(t, err) 597 require.ErrorIs(t, cursor.Delete(), walletdb.ErrIncompatibleValue) 598 599 //Try to delete all data. 600 require.NoError(t, tx.DeleteTopLevelBucket([]byte("top"))) 601 require.Nil(t, tx.ReadBucket([]byte("top"))) 602 603 return nil 604 }, func() {})) 605 } 606 607 //nolint:unused 608 func testSubBucketSequence(t *testing.T, db walletdb.DB) { 609 require.NoError(t, Update(db, func(tx walletdb.ReadWriteTx) error { 610 // Create top level bucket. 611 top, err := tx.CreateTopLevelBucket([]byte("top")) 612 require.NoError(t, err) 613 require.NotNil(t, top) 614 615 // Create sub-bucket. 616 sub2, err := top.CreateBucket([]byte("sub2")) 617 require.NoError(t, err) 618 require.NotNil(t, sub2) 619 620 // Test sequence. 621 require.Equal(t, uint64(0), top.Sequence()) 622 623 require.NoError(t, top.SetSequence(100)) 624 require.Equal(t, uint64(100), top.Sequence()) 625 626 require.NoError(t, top.SetSequence(101)) 627 require.Equal(t, uint64(101), top.Sequence()) 628 629 next, err := top.NextSequence() 630 require.NoError(t, err) 631 require.Equal(t, uint64(102), next) 632 633 next, err = sub2.NextSequence() 634 require.NoError(t, err) 635 require.Equal(t, uint64(1), next) 636 637 return nil 638 }, func() {})) 639 }