github.com/ethersphere/bee/v2@v2.2.0/pkg/shed/index_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package shed 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "errors" 23 "fmt" 24 "sort" 25 "testing" 26 "time" 27 28 "github.com/syndtr/goleveldb/leveldb" 29 ) 30 31 // Index functions for the index that is used in tests in this file. 32 var retrievalIndexFuncs = IndexFuncs{ 33 EncodeKey: func(fields Item) (key []byte, err error) { 34 return fields.Address, nil 35 }, 36 DecodeKey: func(key []byte) (e Item, err error) { 37 e.Address = key 38 return e, nil 39 }, 40 EncodeValue: func(fields Item) (value []byte, err error) { 41 b := make([]byte, 8) 42 binary.BigEndian.PutUint64(b, uint64(fields.StoreTimestamp)) 43 value = append(b, fields.Data...) 44 return value, nil 45 }, 46 DecodeValue: func(keyItem Item, value []byte) (e Item, err error) { 47 e.StoreTimestamp = int64(binary.BigEndian.Uint64(value[:8])) 48 e.Data = value[8:] 49 return e, nil 50 }, 51 } 52 53 // nolint:paralleltest 54 // TestIndex validates put, get, fill, has and delete functions of the Index implementation. 55 func TestIndex(t *testing.T) { 56 db := newTestDB(t) 57 58 index, err := db.NewIndex("retrieval", retrievalIndexFuncs) 59 if err != nil { 60 t.Fatal(err) 61 } 62 63 t.Run("put", func(t *testing.T) { 64 want := Item{ 65 Address: []byte("put-hash"), 66 Data: []byte("DATA"), 67 StoreTimestamp: time.Now().UTC().UnixNano(), 68 } 69 70 err := index.Put(want) 71 if err != nil { 72 t.Fatal(err) 73 } 74 got, err := index.Get(Item{ 75 Address: want.Address, 76 }) 77 if err != nil { 78 t.Fatal(err) 79 } 80 checkItem(t, got, want) 81 82 t.Run("overwrite", func(t *testing.T) { 83 want := Item{ 84 Address: []byte("put-hash"), 85 Data: []byte("New DATA"), 86 StoreTimestamp: time.Now().UTC().UnixNano(), 87 } 88 89 err = index.Put(want) 90 if err != nil { 91 t.Fatal(err) 92 } 93 got, err := index.Get(Item{ 94 Address: want.Address, 95 }) 96 if err != nil { 97 t.Fatal(err) 98 } 99 checkItem(t, got, want) 100 }) 101 }) 102 103 t.Run("put in batch", func(t *testing.T) { 104 want := Item{ 105 Address: []byte("put-in-batch-hash"), 106 Data: []byte("DATA"), 107 StoreTimestamp: time.Now().UTC().UnixNano(), 108 } 109 110 batch := new(leveldb.Batch) 111 err = index.PutInBatch(batch, want) 112 if err != nil { 113 t.Fatal(err) 114 } 115 err := db.WriteBatch(batch) 116 if err != nil { 117 t.Fatal(err) 118 } 119 got, err := index.Get(Item{ 120 Address: want.Address, 121 }) 122 if err != nil { 123 t.Fatal(err) 124 } 125 checkItem(t, got, want) 126 127 t.Run("overwrite", func(t *testing.T) { 128 want := Item{ 129 Address: []byte("put-in-batch-hash"), 130 Data: []byte("New DATA"), 131 StoreTimestamp: time.Now().UTC().UnixNano(), 132 } 133 134 batch := new(leveldb.Batch) 135 err = index.PutInBatch(batch, want) 136 if err != nil { 137 t.Fatal(err) 138 } 139 err = db.WriteBatch(batch) 140 if err != nil { 141 t.Fatal(err) 142 } 143 got, err := index.Get(Item{ 144 Address: want.Address, 145 }) 146 if err != nil { 147 t.Fatal(err) 148 } 149 checkItem(t, got, want) 150 }) 151 }) 152 153 t.Run("put in batch twice", func(t *testing.T) { 154 // ensure that the last item of items with the same db keys 155 // is actually saved 156 batch := new(leveldb.Batch) 157 address := []byte("put-in-batch-twice-hash") 158 159 // put the first item 160 err = index.PutInBatch(batch, Item{ 161 Address: address, 162 Data: []byte("DATA"), 163 StoreTimestamp: time.Now().UTC().UnixNano(), 164 }) 165 if err != nil { 166 t.Fatal(err) 167 } 168 169 want := Item{ 170 Address: address, 171 Data: []byte("New DATA"), 172 StoreTimestamp: time.Now().UTC().UnixNano(), 173 } 174 // then put the item that will produce the same key 175 // but different value in the database 176 err = index.PutInBatch(batch, want) 177 if err != nil { 178 t.Fatal(err) 179 } 180 err = db.WriteBatch(batch) 181 if err != nil { 182 t.Fatal(err) 183 } 184 got, err := index.Get(Item{ 185 Address: address, 186 }) 187 if err != nil { 188 t.Fatal(err) 189 } 190 checkItem(t, got, want) 191 }) 192 193 t.Run("has", func(t *testing.T) { 194 want := Item{ 195 Address: []byte("has-hash"), 196 Data: []byte("DATA"), 197 StoreTimestamp: time.Now().UTC().UnixNano(), 198 } 199 200 dontWant := Item{ 201 Address: []byte("do-not-has-hash"), 202 Data: []byte("DATA"), 203 StoreTimestamp: time.Now().UTC().UnixNano(), 204 } 205 206 err := index.Put(want) 207 if err != nil { 208 t.Fatal(err) 209 } 210 211 has, err := index.Has(want) 212 if err != nil { 213 t.Fatal(err) 214 } 215 if !has { 216 t.Error("item is not found") 217 } 218 219 has, err = index.Has(dontWant) 220 if err != nil { 221 t.Fatal(err) 222 } 223 if has { 224 t.Error("unwanted item is found") 225 } 226 }) 227 228 t.Run("delete", func(t *testing.T) { 229 want := Item{ 230 Address: []byte("delete-hash"), 231 Data: []byte("DATA"), 232 StoreTimestamp: time.Now().UTC().UnixNano(), 233 } 234 235 err := index.Put(want) 236 if err != nil { 237 t.Fatal(err) 238 } 239 got, err := index.Get(Item{ 240 Address: want.Address, 241 }) 242 if err != nil { 243 t.Fatal(err) 244 } 245 checkItem(t, got, want) 246 247 err = index.Delete(Item{ 248 Address: want.Address, 249 }) 250 if err != nil { 251 t.Fatal(err) 252 } 253 254 wantErr := leveldb.ErrNotFound 255 _, err = index.Get(Item{ 256 Address: want.Address, 257 }) 258 if !errors.Is(err, wantErr) { 259 t.Fatalf("got error %v, want %v", err, wantErr) 260 } 261 }) 262 263 t.Run("delete in batch", func(t *testing.T) { 264 want := Item{ 265 Address: []byte("delete-in-batch-hash"), 266 Data: []byte("DATA"), 267 StoreTimestamp: time.Now().UTC().UnixNano(), 268 } 269 270 err := index.Put(want) 271 if err != nil { 272 t.Fatal(err) 273 } 274 got, err := index.Get(Item{ 275 Address: want.Address, 276 }) 277 if err != nil { 278 t.Fatal(err) 279 } 280 checkItem(t, got, want) 281 282 batch := new(leveldb.Batch) 283 err = index.DeleteInBatch(batch, Item{ 284 Address: want.Address, 285 }) 286 if err != nil { 287 t.Fatal(err) 288 } 289 err = db.WriteBatch(batch) 290 if err != nil { 291 t.Fatal(err) 292 } 293 294 wantErr := leveldb.ErrNotFound 295 _, err = index.Get(Item{ 296 Address: want.Address, 297 }) 298 if !errors.Is(err, wantErr) { 299 t.Fatalf("got error %v, want %v", err, wantErr) 300 } 301 }) 302 303 t.Run("fill", func(t *testing.T) { 304 want := []Item{ 305 { 306 Address: []byte("put-hash-1"), 307 Data: []byte("DATA123"), 308 StoreTimestamp: time.Now().UTC().UnixNano(), 309 }, 310 { 311 Address: []byte("put-hash-32"), 312 Data: []byte("DATA124"), 313 StoreTimestamp: time.Now().UTC().UnixNano(), 314 }, 315 { 316 Address: []byte("put-hash-42"), 317 Data: []byte("DATA125"), 318 StoreTimestamp: time.Now().UTC().UnixNano(), 319 }, 320 { 321 Address: []byte("put-hash-71"), 322 Data: []byte("DATA126"), 323 StoreTimestamp: time.Now().UTC().UnixNano(), 324 }, 325 } 326 327 for _, item := range want { 328 err := index.Put(item) 329 if err != nil { 330 t.Fatal(err) 331 } 332 } 333 items := make([]Item, len(want)) 334 for i, w := range want { 335 items[i] = Item{ 336 Address: w.Address, 337 } 338 } 339 err = index.Fill(items) 340 if err != nil { 341 t.Fatal(err) 342 } 343 for i := range items { 344 checkItem(t, items[i], want[i]) 345 } 346 347 t.Run("not found", func(t *testing.T) { 348 items := make([]Item, len(want)) 349 for i, w := range want { 350 items[i] = Item{ 351 Address: w.Address, 352 } 353 } 354 items = append(items, Item{ 355 Address: []byte("put-hash-missing"), 356 }) 357 want := leveldb.ErrNotFound 358 err := index.Fill(items) 359 if !errors.Is(err, want) { 360 t.Errorf("got error %v, want %v", err, want) 361 } 362 }) 363 }) 364 } 365 366 // nolint:paralleltest 367 // TestIndex_Iterate validates index Iterate functions for correctness. 368 func TestIndex_Iterate(t *testing.T) { 369 db := newTestDB(t) 370 371 index, err := db.NewIndex("retrieval", retrievalIndexFuncs) 372 if err != nil { 373 t.Fatal(err) 374 } 375 376 items := []Item{ 377 { 378 Address: []byte("iterate-hash-01"), 379 Data: []byte("data80"), 380 }, 381 { 382 Address: []byte("iterate-hash-03"), 383 Data: []byte("data22"), 384 }, 385 { 386 Address: []byte("iterate-hash-05"), 387 Data: []byte("data41"), 388 }, 389 { 390 Address: []byte("iterate-hash-02"), 391 Data: []byte("data84"), 392 }, 393 { 394 Address: []byte("iterate-hash-06"), 395 Data: []byte("data1"), 396 }, 397 } 398 batch := new(leveldb.Batch) 399 for _, i := range items { 400 err = index.PutInBatch(batch, i) 401 if err != nil { 402 t.Fatal(err) 403 } 404 } 405 err = db.WriteBatch(batch) 406 if err != nil { 407 t.Fatal(err) 408 } 409 item04 := Item{ 410 Address: []byte("iterate-hash-04"), 411 Data: []byte("data0"), 412 } 413 err = index.Put(item04) 414 if err != nil { 415 t.Fatal(err) 416 } 417 items = append(items, item04) 418 419 sort.SliceStable(items, func(i, j int) bool { 420 return bytes.Compare(items[i].Address, items[j].Address) < 0 421 }) 422 423 t.Run("all", func(t *testing.T) { 424 var i int 425 err := index.Iterate(func(item Item) (stop bool, err error) { 426 if i > len(items)-1 { 427 return true, fmt.Errorf("got unexpected index item: %#v", item) 428 } 429 want := items[i] 430 checkItem(t, item, want) 431 i++ 432 return false, nil 433 }, nil) 434 if err != nil { 435 t.Fatal(err) 436 } 437 }) 438 439 t.Run("start from", func(t *testing.T) { 440 startIndex := 2 441 i := startIndex 442 err := index.Iterate(func(item Item) (stop bool, err error) { 443 if i > len(items)-1 { 444 return true, fmt.Errorf("got unexpected index item: %#v", item) 445 } 446 want := items[i] 447 checkItem(t, item, want) 448 i++ 449 return false, nil 450 }, &IterateOptions{ 451 StartFrom: &items[startIndex], 452 }) 453 if err != nil { 454 t.Fatal(err) 455 } 456 }) 457 458 t.Run("skip start from", func(t *testing.T) { 459 startIndex := 2 460 i := startIndex + 1 461 err := index.Iterate(func(item Item) (stop bool, err error) { 462 if i > len(items)-1 { 463 return true, fmt.Errorf("got unexpected index item: %#v", item) 464 } 465 want := items[i] 466 checkItem(t, item, want) 467 i++ 468 return false, nil 469 }, &IterateOptions{ 470 StartFrom: &items[startIndex], 471 SkipStartFromItem: true, 472 }) 473 if err != nil { 474 t.Fatal(err) 475 } 476 }) 477 478 t.Run("stop", func(t *testing.T) { 479 var i int 480 stopIndex := 3 481 var count int 482 err := index.Iterate(func(item Item) (stop bool, err error) { 483 if i > len(items)-1 { 484 return true, fmt.Errorf("got unexpected index item: %#v", item) 485 } 486 want := items[i] 487 checkItem(t, item, want) 488 count++ 489 if i == stopIndex { 490 return true, nil 491 } 492 i++ 493 return false, nil 494 }, nil) 495 if err != nil { 496 t.Fatal(err) 497 } 498 wantItemsCount := stopIndex + 1 499 if count != wantItemsCount { 500 t.Errorf("got %v items, expected %v", count, wantItemsCount) 501 } 502 }) 503 504 t.Run("no overflow", func(t *testing.T) { 505 secondIndex, err := db.NewIndex("second-index", retrievalIndexFuncs) 506 if err != nil { 507 t.Fatal(err) 508 } 509 510 secondItem := Item{ 511 Address: []byte("iterate-hash-10"), 512 Data: []byte("data-second"), 513 } 514 err = secondIndex.Put(secondItem) 515 if err != nil { 516 t.Fatal(err) 517 } 518 519 var i int 520 err = index.Iterate(func(item Item) (stop bool, err error) { 521 if i > len(items)-1 { 522 return true, fmt.Errorf("got unexpected index item: %#v", item) 523 } 524 want := items[i] 525 checkItem(t, item, want) 526 i++ 527 return false, nil 528 }, nil) 529 if err != nil { 530 t.Fatal(err) 531 } 532 533 i = 0 534 err = secondIndex.Iterate(func(item Item) (stop bool, err error) { 535 if i > 1 { 536 return true, fmt.Errorf("got unexpected index item: %#v", item) 537 } 538 checkItem(t, item, secondItem) 539 i++ 540 return false, nil 541 }, nil) 542 if err != nil { 543 t.Fatal(err) 544 } 545 }) 546 } 547 548 // nolint:paralleltest 549 // TestIndex_IterateReverse validates index Iterate functions for correctness in reversed order. 550 func TestIndex_IterateReverse(t *testing.T) { 551 db := newTestDB(t) 552 553 index, err := db.NewIndex("retrieval", retrievalIndexFuncs) 554 if err != nil { 555 t.Fatal(err) 556 } 557 558 items := []Item{ 559 { 560 Address: []byte("iterate-hash-01"), 561 Data: []byte("data80"), 562 }, 563 { 564 Address: []byte("iterate-hash-03"), 565 Data: []byte("data22"), 566 }, 567 { 568 Address: []byte("iterate-hash-05"), 569 Data: []byte("data41"), 570 }, 571 { 572 Address: []byte("iterate-hash-02"), 573 Data: []byte("data84"), 574 }, 575 { 576 Address: []byte("iterate-hash-06"), 577 Data: []byte("data1"), 578 }, 579 } 580 batch := new(leveldb.Batch) 581 for _, i := range items { 582 err = index.PutInBatch(batch, i) 583 if err != nil { 584 t.Fatal(err) 585 } 586 } 587 err = db.WriteBatch(batch) 588 if err != nil { 589 t.Fatal(err) 590 } 591 item04 := Item{ 592 Address: []byte("iterate-hash-04"), 593 Data: []byte("data0"), 594 } 595 err = index.Put(item04) 596 if err != nil { 597 t.Fatal(err) 598 } 599 items = append(items, item04) 600 601 sort.SliceStable(items, func(i, j int) bool { 602 return bytes.Compare(items[i].Address, items[j].Address) < 0 603 }) 604 605 t.Run("all", func(t *testing.T) { 606 i := len(items) - 1 607 var count int 608 err := index.Iterate(func(item Item) (stop bool, err error) { 609 if i < 0 { 610 return true, fmt.Errorf("got unexpected index item: %#v", item) 611 } 612 want := items[i] 613 checkItem(t, item, want) 614 i-- 615 count++ 616 return false, nil 617 }, &IterateOptions{ 618 Reverse: true, 619 }) 620 if err != nil { 621 t.Fatal(err) 622 } 623 wantItemsCount := len(items) 624 if count != wantItemsCount { 625 t.Errorf("got %v items, expected %v", count, wantItemsCount) 626 } 627 }) 628 629 t.Run("start from", func(t *testing.T) { 630 startIndex := 3 631 i := startIndex 632 var count int 633 err := index.Iterate(func(item Item) (stop bool, err error) { 634 if i < 0 { 635 return true, fmt.Errorf("got unexpected index item: %#v", item) 636 } 637 want := items[i] 638 checkItem(t, item, want) 639 i-- 640 count++ 641 return false, nil 642 }, &IterateOptions{ 643 StartFrom: &items[startIndex], 644 Reverse: true, 645 }) 646 if err != nil { 647 t.Fatal(err) 648 } 649 wantItemsCount := len(items) - startIndex + 1 650 if count != wantItemsCount { 651 t.Errorf("got %v items, expected %v", count, wantItemsCount) 652 } 653 }) 654 655 t.Run("skip start from", func(t *testing.T) { 656 startIndex := 3 657 i := startIndex - 1 658 var count int 659 err := index.Iterate(func(item Item) (stop bool, err error) { 660 if i < 0 { 661 return true, fmt.Errorf("got unexpected index item: %#v", item) 662 } 663 want := items[i] 664 checkItem(t, item, want) 665 i-- 666 count++ 667 return false, nil 668 }, &IterateOptions{ 669 StartFrom: &items[startIndex], 670 SkipStartFromItem: true, 671 Reverse: true, 672 }) 673 if err != nil { 674 t.Fatal(err) 675 } 676 wantItemsCount := len(items) - startIndex 677 if count != wantItemsCount { 678 t.Errorf("got %v items, expected %v", count, wantItemsCount) 679 } 680 }) 681 682 t.Run("stop", func(t *testing.T) { 683 i := len(items) - 1 684 stopIndex := 3 685 var count int 686 err := index.Iterate(func(item Item) (stop bool, err error) { 687 if i < 0 { 688 return true, fmt.Errorf("got unexpected index item: %#v", item) 689 } 690 want := items[i] 691 checkItem(t, item, want) 692 count++ 693 if i == stopIndex { 694 return true, nil 695 } 696 i-- 697 return false, nil 698 }, &IterateOptions{ 699 Reverse: true, 700 }) 701 if err != nil { 702 t.Fatal(err) 703 } 704 wantItemsCount := stopIndex 705 if count != wantItemsCount { 706 t.Errorf("got %v items, expected %v", count, wantItemsCount) 707 } 708 }) 709 710 t.Run("no overflow", func(t *testing.T) { 711 secondIndex, err := db.NewIndex("second-index", retrievalIndexFuncs) 712 if err != nil { 713 t.Fatal(err) 714 } 715 716 secondItem := Item{ 717 Address: []byte("iterate-hash-10"), 718 Data: []byte("data-second"), 719 } 720 err = secondIndex.Put(secondItem) 721 if err != nil { 722 t.Fatal(err) 723 } 724 725 i := len(items) - 1 726 count := 0 727 err = index.Iterate(func(item Item) (stop bool, err error) { 728 if i < 0 { 729 return true, fmt.Errorf("got unexpected index item: %#v", item) 730 } 731 want := items[i] 732 checkItem(t, item, want) 733 i-- 734 count++ 735 return false, nil 736 }, &IterateOptions{ 737 Reverse: true, 738 }) 739 if err != nil { 740 t.Fatal(err) 741 } 742 wantItemsCount := len(items) 743 if count != wantItemsCount { 744 t.Errorf("got %v items, expected %v", count, wantItemsCount) 745 } 746 747 i = 1 748 count = 0 749 err = secondIndex.Iterate(func(item Item) (stop bool, err error) { 750 if i < 0 { 751 return true, fmt.Errorf("got unexpected index item: %#v", item) 752 } 753 checkItem(t, item, secondItem) 754 i-- 755 count++ 756 return false, nil 757 }, &IterateOptions{ 758 Reverse: true, 759 }) 760 if err != nil { 761 t.Fatal(err) 762 } 763 wantItemsCount = 1 764 if count != wantItemsCount { 765 t.Errorf("got %v items, expected %v", count, wantItemsCount) 766 } 767 }) 768 } 769 770 // nolint:paralleltest 771 // TestIndex_Iterate_withPrefix validates index Iterate 772 // function for correctness. 773 func TestIndex_Iterate_withPrefix(t *testing.T) { 774 db := newTestDB(t) 775 776 index, err := db.NewIndex("retrieval", retrievalIndexFuncs) 777 if err != nil { 778 t.Fatal(err) 779 } 780 781 allItems := []Item{ 782 {Address: []byte("want-hash-00"), Data: []byte("data80")}, 783 {Address: []byte("skip-hash-01"), Data: []byte("data81")}, 784 {Address: []byte("skip-hash-02"), Data: []byte("data82")}, 785 {Address: []byte("skip-hash-03"), Data: []byte("data83")}, 786 {Address: []byte("want-hash-04"), Data: []byte("data84")}, 787 {Address: []byte("want-hash-05"), Data: []byte("data85")}, 788 {Address: []byte("want-hash-06"), Data: []byte("data86")}, 789 {Address: []byte("want-hash-07"), Data: []byte("data87")}, 790 {Address: []byte("want-hash-08"), Data: []byte("data88")}, 791 {Address: []byte("want-hash-09"), Data: []byte("data89")}, 792 {Address: []byte("skip-hash-10"), Data: []byte("data90")}, 793 } 794 batch := new(leveldb.Batch) 795 for _, i := range allItems { 796 err = index.PutInBatch(batch, i) 797 if err != nil { 798 t.Fatal(err) 799 } 800 801 } 802 err = db.WriteBatch(batch) 803 if err != nil { 804 t.Fatal(err) 805 } 806 807 prefix := []byte("want") 808 809 items := make([]Item, 0) 810 for _, item := range allItems { 811 if bytes.HasPrefix(item.Address, prefix) { 812 items = append(items, item) 813 } 814 } 815 sort.SliceStable(items, func(i, j int) bool { 816 return bytes.Compare(items[i].Address, items[j].Address) < 0 817 }) 818 819 t.Run("with prefix", func(t *testing.T) { 820 var i int 821 err := index.Iterate(func(item Item) (stop bool, err error) { 822 if i > len(items)-1 { 823 return true, fmt.Errorf("got unexpected index item: %#v", item) 824 } 825 want := items[i] 826 checkItem(t, item, want) 827 i++ 828 return false, nil 829 }, &IterateOptions{ 830 Prefix: prefix, 831 }) 832 if err != nil { 833 t.Fatal(err) 834 } 835 if i != len(items) { 836 t.Errorf("got %v items, want %v", i, len(items)) 837 } 838 }) 839 840 t.Run("with prefix and start from", func(t *testing.T) { 841 startIndex := 2 842 var count int 843 i := startIndex 844 err := index.Iterate(func(item Item) (stop bool, err error) { 845 if i > len(items)-1 { 846 return true, fmt.Errorf("got unexpected index item: %#v", item) 847 } 848 want := items[i] 849 checkItem(t, item, want) 850 i++ 851 count++ 852 return false, nil 853 }, &IterateOptions{ 854 StartFrom: &items[startIndex], 855 Prefix: prefix, 856 }) 857 if err != nil { 858 t.Fatal(err) 859 } 860 wantCount := len(items) - startIndex 861 if count != wantCount { 862 t.Errorf("got %v items, want %v", count, wantCount) 863 } 864 }) 865 866 t.Run("with prefix and skip start from", func(t *testing.T) { 867 startIndex := 2 868 var count int 869 i := startIndex + 1 870 err := index.Iterate(func(item Item) (stop bool, err error) { 871 if i > len(items)-1 { 872 return true, fmt.Errorf("got unexpected index item: %#v", item) 873 } 874 want := items[i] 875 checkItem(t, item, want) 876 i++ 877 count++ 878 return false, nil 879 }, &IterateOptions{ 880 StartFrom: &items[startIndex], 881 SkipStartFromItem: true, 882 Prefix: prefix, 883 }) 884 if err != nil { 885 t.Fatal(err) 886 } 887 wantCount := len(items) - startIndex - 1 888 if count != wantCount { 889 t.Errorf("got %v items, want %v", count, wantCount) 890 } 891 }) 892 893 t.Run("stop", func(t *testing.T) { 894 var i int 895 stopIndex := 3 896 var count int 897 err := index.Iterate(func(item Item) (stop bool, err error) { 898 if i > len(items)-1 { 899 return true, fmt.Errorf("got unexpected index item: %#v", item) 900 } 901 want := items[i] 902 checkItem(t, item, want) 903 count++ 904 if i == stopIndex { 905 return true, nil 906 } 907 i++ 908 return false, nil 909 }, &IterateOptions{ 910 Prefix: prefix, 911 }) 912 if err != nil { 913 t.Fatal(err) 914 } 915 wantItemsCount := stopIndex + 1 916 if count != wantItemsCount { 917 t.Errorf("got %v items, expected %v", count, wantItemsCount) 918 } 919 }) 920 921 t.Run("no overflow", func(t *testing.T) { 922 secondIndex, err := db.NewIndex("second-index", retrievalIndexFuncs) 923 if err != nil { 924 t.Fatal(err) 925 } 926 927 secondItem := Item{ 928 Address: []byte("iterate-hash-10"), 929 Data: []byte("data-second"), 930 } 931 err = secondIndex.Put(secondItem) 932 if err != nil { 933 t.Fatal(err) 934 } 935 936 var i int 937 err = index.Iterate(func(item Item) (stop bool, err error) { 938 if i > len(items)-1 { 939 return true, fmt.Errorf("got unexpected index item: %#v", item) 940 } 941 want := items[i] 942 checkItem(t, item, want) 943 i++ 944 return false, nil 945 }, &IterateOptions{ 946 Prefix: prefix, 947 }) 948 if err != nil { 949 t.Fatal(err) 950 } 951 if i != len(items) { 952 t.Errorf("got %v items, want %v", i, len(items)) 953 } 954 }) 955 } 956 957 // nolint:paralleltest 958 // TestIndex_IterateReverse_withPrefix validates index Iterate 959 // functions for correctness in reversed order. 960 func TestIndex_IterateReverse_withPrefix(t *testing.T) { 961 db := newTestDB(t) 962 963 index, err := db.NewIndex("retrieval", retrievalIndexFuncs) 964 if err != nil { 965 t.Fatal(err) 966 } 967 968 allItems := []Item{ 969 {Address: []byte("want-hash-00"), Data: []byte("data80")}, 970 {Address: []byte("skip-hash-01"), Data: []byte("data81")}, 971 {Address: []byte("skip-hash-02"), Data: []byte("data82")}, 972 {Address: []byte("skip-hash-03"), Data: []byte("data83")}, 973 {Address: []byte("want-hash-04"), Data: []byte("data84")}, 974 {Address: []byte("want-hash-05"), Data: []byte("data85")}, 975 {Address: []byte("want-hash-06"), Data: []byte("data86")}, 976 {Address: []byte("want-hash-07"), Data: []byte("data87")}, 977 {Address: []byte("want-hash-08"), Data: []byte("data88")}, 978 {Address: []byte("want-hash-09"), Data: []byte("data89")}, 979 {Address: []byte("z-skip-hash-10"), Data: []byte("data90")}, 980 } 981 batch := new(leveldb.Batch) 982 for _, i := range allItems { 983 err = index.PutInBatch(batch, i) 984 if err != nil { 985 t.Fatal(err) 986 } 987 } 988 err = db.WriteBatch(batch) 989 if err != nil { 990 t.Fatal(err) 991 } 992 993 prefix := []byte("want") 994 995 items := make([]Item, 0) 996 for _, item := range allItems { 997 if bytes.HasPrefix(item.Address, prefix) { 998 items = append(items, item) 999 } 1000 } 1001 sort.SliceStable(items, func(i, j int) bool { 1002 return bytes.Compare(items[i].Address, items[j].Address) < 0 1003 }) 1004 1005 t.Run("with prefix", func(t *testing.T) { 1006 i := len(items) - 1 1007 var count int 1008 err := index.Iterate(func(item Item) (stop bool, err error) { 1009 if i < 0 { 1010 return true, fmt.Errorf("got unexpected index item: %#v", item) 1011 } 1012 want := items[i] 1013 checkItem(t, item, want) 1014 i-- 1015 count++ 1016 return false, nil 1017 }, &IterateOptions{ 1018 Prefix: prefix, 1019 Reverse: true, 1020 }) 1021 if err != nil { 1022 t.Fatal(err) 1023 } 1024 wantItemsCount := len(items) 1025 if count != wantItemsCount { 1026 t.Errorf("got %v items, expected %v", count, wantItemsCount) 1027 } 1028 }) 1029 1030 t.Run("with prefix and start from", func(t *testing.T) { 1031 startIndex := 2 1032 i := startIndex 1033 var count int 1034 err := index.Iterate(func(item Item) (stop bool, err error) { 1035 if i < 0 { 1036 return true, fmt.Errorf("got unexpected index item: %#v", item) 1037 } 1038 want := items[i] 1039 checkItem(t, item, want) 1040 i-- 1041 count++ 1042 return false, nil 1043 }, &IterateOptions{ 1044 StartFrom: &items[startIndex], 1045 Prefix: prefix, 1046 Reverse: true, 1047 }) 1048 if err != nil { 1049 t.Fatal(err) 1050 } 1051 wantItemsCount := startIndex + 1 1052 if count != wantItemsCount { 1053 t.Errorf("got %v items, expected %v", count, wantItemsCount) 1054 } 1055 }) 1056 1057 t.Run("with prefix and skip start from", func(t *testing.T) { 1058 startIndex := 3 1059 i := startIndex - 1 1060 var count int 1061 err := index.Iterate(func(item Item) (stop bool, err error) { 1062 if i < 0 { 1063 return true, fmt.Errorf("got unexpected index item: %#v", item) 1064 } 1065 want := items[i] 1066 checkItem(t, item, want) 1067 i-- 1068 count++ 1069 return false, nil 1070 }, &IterateOptions{ 1071 StartFrom: &items[startIndex], 1072 SkipStartFromItem: true, 1073 Prefix: prefix, 1074 Reverse: true, 1075 }) 1076 if err != nil { 1077 t.Fatal(err) 1078 } 1079 wantItemsCount := len(items) - startIndex - 1 1080 if count != wantItemsCount { 1081 t.Errorf("got %v items, expected %v", count, wantItemsCount) 1082 } 1083 }) 1084 1085 t.Run("stop", func(t *testing.T) { 1086 i := len(items) - 1 1087 stopIndex := 3 1088 var count int 1089 err := index.Iterate(func(item Item) (stop bool, err error) { 1090 if i < 0 { 1091 return true, fmt.Errorf("got unexpected index item: %#v", item) 1092 } 1093 want := items[i] 1094 checkItem(t, item, want) 1095 count++ 1096 if i == stopIndex { 1097 return true, nil 1098 } 1099 i-- 1100 return false, nil 1101 }, &IterateOptions{ 1102 Prefix: prefix, 1103 Reverse: true, 1104 }) 1105 if err != nil { 1106 t.Fatal(err) 1107 } 1108 wantItemsCount := stopIndex + 1 1109 if count != wantItemsCount { 1110 t.Errorf("got %v items, expected %v", count, wantItemsCount) 1111 } 1112 }) 1113 1114 t.Run("no overflow", func(t *testing.T) { 1115 secondIndex, err := db.NewIndex("second-index", retrievalIndexFuncs) 1116 if err != nil { 1117 t.Fatal(err) 1118 } 1119 1120 secondItem := Item{ 1121 Address: []byte("iterate-hash-10"), 1122 Data: []byte("data-second"), 1123 } 1124 err = secondIndex.Put(secondItem) 1125 if err != nil { 1126 t.Fatal(err) 1127 } 1128 1129 i := len(items) - 1 1130 var count int 1131 err = index.Iterate(func(item Item) (stop bool, err error) { 1132 if i < 0 { 1133 return true, fmt.Errorf("got unexpected index item: %#v", item) 1134 } 1135 want := items[i] 1136 checkItem(t, item, want) 1137 i-- 1138 count++ 1139 return false, nil 1140 }, &IterateOptions{ 1141 Prefix: prefix, 1142 Reverse: true, 1143 }) 1144 if err != nil { 1145 t.Fatal(err) 1146 } 1147 wantItemsCount := len(items) 1148 if count != wantItemsCount { 1149 t.Errorf("got %v items, expected %v", count, wantItemsCount) 1150 } 1151 }) 1152 } 1153 1154 // nolint:paralleltest 1155 // TestIndex_count tests if Index.Count and Index.CountFrom 1156 // returns the correct number of items. 1157 func TestIndex_count(t *testing.T) { 1158 db := newTestDB(t) 1159 1160 index, err := db.NewIndex("retrieval", retrievalIndexFuncs) 1161 if err != nil { 1162 t.Fatal(err) 1163 } 1164 1165 items := []Item{ 1166 { 1167 Address: []byte("iterate-hash-01"), 1168 Data: []byte("data80"), 1169 }, 1170 { 1171 Address: []byte("iterate-hash-02"), 1172 Data: []byte("data84"), 1173 }, 1174 { 1175 Address: []byte("iterate-hash-03"), 1176 Data: []byte("data22"), 1177 }, 1178 { 1179 Address: []byte("iterate-hash-04"), 1180 Data: []byte("data41"), 1181 }, 1182 { 1183 Address: []byte("iterate-hash-05"), 1184 Data: []byte("data1"), 1185 }, 1186 } 1187 batch := new(leveldb.Batch) 1188 for _, i := range items { 1189 err = index.PutInBatch(batch, i) 1190 if err != nil { 1191 t.Fatal(err) 1192 } 1193 } 1194 err = db.WriteBatch(batch) 1195 if err != nil { 1196 t.Fatal(err) 1197 } 1198 1199 t.Run("Count", func(t *testing.T) { 1200 got, err := index.Count() 1201 if err != nil { 1202 t.Fatal(err) 1203 } 1204 1205 want := len(items) 1206 if got != want { 1207 t.Errorf("got %v items count, want %v", got, want) 1208 } 1209 }) 1210 1211 t.Run("CountFrom", func(t *testing.T) { 1212 got, err := index.CountFrom(Item{ 1213 Address: items[1].Address, 1214 }) 1215 if err != nil { 1216 t.Fatal(err) 1217 } 1218 1219 want := len(items) - 1 1220 if got != want { 1221 t.Errorf("got %v items count, want %v", got, want) 1222 } 1223 }) 1224 1225 // update the index with another item 1226 t.Run("add item", func(t *testing.T) { 1227 item04 := Item{ 1228 Address: []byte("iterate-hash-06"), 1229 Data: []byte("data0"), 1230 } 1231 err = index.Put(item04) 1232 if err != nil { 1233 t.Fatal(err) 1234 } 1235 1236 count := len(items) + 1 1237 1238 t.Run("Count", func(t *testing.T) { 1239 got, err := index.Count() 1240 if err != nil { 1241 t.Fatal(err) 1242 } 1243 1244 want := count 1245 if got != want { 1246 t.Errorf("got %v items count, want %v", got, want) 1247 } 1248 }) 1249 1250 t.Run("CountFrom", func(t *testing.T) { 1251 got, err := index.CountFrom(Item{ 1252 Address: items[1].Address, 1253 }) 1254 if err != nil { 1255 t.Fatal(err) 1256 } 1257 1258 want := count - 1 1259 if got != want { 1260 t.Errorf("got %v items count, want %v", got, want) 1261 } 1262 }) 1263 }) 1264 1265 // delete some items 1266 t.Run("delete items", func(t *testing.T) { 1267 deleteCount := 3 1268 1269 for _, item := range items[:deleteCount] { 1270 err := index.Delete(item) 1271 if err != nil { 1272 t.Fatal(err) 1273 } 1274 } 1275 1276 count := len(items) + 1 - deleteCount 1277 1278 t.Run("Count", func(t *testing.T) { 1279 got, err := index.Count() 1280 if err != nil { 1281 t.Fatal(err) 1282 } 1283 1284 want := count 1285 if got != want { 1286 t.Errorf("got %v items count, want %v", got, want) 1287 } 1288 }) 1289 1290 t.Run("CountFrom", func(t *testing.T) { 1291 got, err := index.CountFrom(Item{ 1292 Address: items[deleteCount+1].Address, 1293 }) 1294 if err != nil { 1295 t.Fatal(err) 1296 } 1297 1298 want := count - 1 1299 if got != want { 1300 t.Errorf("got %v items count, want %v", got, want) 1301 } 1302 }) 1303 }) 1304 } 1305 1306 // checkItem is a test helper function that compares if two Index items are the same. 1307 func checkItem(t *testing.T, got, want Item) { 1308 t.Helper() 1309 1310 if !bytes.Equal(got.Address, want.Address) { 1311 t.Errorf("got hash %q, expected %q", string(got.Address), string(want.Address)) 1312 } 1313 if !bytes.Equal(got.Data, want.Data) { 1314 t.Errorf("got data %q, expected %q", string(got.Data), string(want.Data)) 1315 } 1316 if got.StoreTimestamp != want.StoreTimestamp { 1317 t.Errorf("got store timestamp %v, expected %v", got.StoreTimestamp, want.StoreTimestamp) 1318 } 1319 if got.AccessTimestamp != want.AccessTimestamp { 1320 t.Errorf("got access timestamp %v, expected %v", got.AccessTimestamp, want.AccessTimestamp) 1321 } 1322 } 1323 1324 // TestIndex_firstAndLast validates that index First and Last methods 1325 // are returning expected results based on the provided prefix. 1326 func TestIndex_firstAndLast(t *testing.T) { 1327 t.Parallel() 1328 1329 db := newTestDB(t) 1330 1331 index, err := db.NewIndex("retrieval", retrievalIndexFuncs) 1332 if err != nil { 1333 t.Fatal(err) 1334 } 1335 1336 addrs := [][]byte{ 1337 {0, 0, 0, 0, 0}, 1338 {0, 1}, 1339 {0, 1, 0, 0, 0}, 1340 {0, 1, 0, 0, 1}, 1341 {0, 1, 0, 0, 2}, 1342 {0, 2, 0, 0, 1}, 1343 {0, 4, 0, 0, 0}, 1344 {0, 10, 0, 0, 10}, 1345 {0, 10, 0, 0, 11}, 1346 {0, 10, 0, 0, 20}, 1347 {1, 32, 255, 0, 1}, 1348 {1, 32, 255, 0, 2}, 1349 {1, 32, 255, 0, 3}, 1350 {255, 255, 255, 255, 32}, 1351 {255, 255, 255, 255, 64}, 1352 {255, 255, 255, 255, 255}, 1353 } 1354 1355 // ensure that the addresses are sorted for 1356 // validation of nil prefix 1357 sort.Slice(addrs, func(i, j int) (less bool) { 1358 return bytes.Compare(addrs[i], addrs[j]) == -1 1359 }) 1360 1361 batch := new(leveldb.Batch) 1362 for _, addr := range addrs { 1363 err = index.PutInBatch(batch, Item{ 1364 Address: addr, 1365 }) 1366 if err != nil { 1367 t.Fatal(err) 1368 } 1369 } 1370 err = db.WriteBatch(batch) 1371 if err != nil { 1372 t.Fatal(err) 1373 } 1374 1375 for _, tc := range []struct { 1376 prefix []byte 1377 first []byte 1378 last []byte 1379 err error 1380 }{ 1381 { 1382 prefix: nil, 1383 first: addrs[0], 1384 last: addrs[len(addrs)-1], 1385 }, 1386 { 1387 prefix: []byte{0, 0}, 1388 first: []byte{0, 0, 0, 0, 0}, 1389 last: []byte{0, 0, 0, 0, 0}, 1390 }, 1391 { 1392 prefix: []byte{0}, 1393 first: []byte{0, 0, 0, 0, 0}, 1394 last: []byte{0, 10, 0, 0, 20}, 1395 }, 1396 { 1397 prefix: []byte{0, 1}, 1398 first: []byte{0, 1}, 1399 last: []byte{0, 1, 0, 0, 2}, 1400 }, 1401 { 1402 prefix: []byte{0, 10}, 1403 first: []byte{0, 10, 0, 0, 10}, 1404 last: []byte{0, 10, 0, 0, 20}, 1405 }, 1406 { 1407 prefix: []byte{1, 32, 255}, 1408 first: []byte{1, 32, 255, 0, 1}, 1409 last: []byte{1, 32, 255, 0, 3}, 1410 }, 1411 { 1412 prefix: []byte{255}, 1413 first: []byte{255, 255, 255, 255, 32}, 1414 last: []byte{255, 255, 255, 255, 255}, 1415 }, 1416 { 1417 prefix: []byte{255, 255, 255, 255, 255}, 1418 first: []byte{255, 255, 255, 255, 255}, 1419 last: []byte{255, 255, 255, 255, 255}, 1420 }, 1421 { 1422 prefix: []byte{0, 3}, 1423 err: leveldb.ErrNotFound, 1424 }, 1425 { 1426 prefix: []byte{222}, 1427 err: leveldb.ErrNotFound, 1428 }, 1429 } { 1430 got, err := index.Last(tc.prefix) 1431 if !errors.Is(err, tc.err) { 1432 t.Errorf("got error %v for Last with prefix %v, want %v", err, tc.prefix, tc.err) 1433 } else if !bytes.Equal(got.Address, tc.last) { 1434 t.Errorf("got %v for Last with prefix %v, want %v", got.Address, tc.prefix, tc.last) 1435 } 1436 1437 got, err = index.First(tc.prefix) 1438 if !errors.Is(err, tc.err) { 1439 t.Errorf("got error %v for First with prefix %v, want %v", err, tc.prefix, tc.err) 1440 } else if !bytes.Equal(got.Address, tc.first) { 1441 t.Errorf("got %v for First with prefix %v, want %v", got.Address, tc.prefix, tc.first) 1442 } 1443 } 1444 } 1445 1446 // TestIncByteSlice validates returned values of incByteSlice function. 1447 func TestIncByteSlice(t *testing.T) { 1448 t.Parallel() 1449 1450 for _, tc := range []struct { 1451 b []byte 1452 want []byte 1453 }{ 1454 {b: nil, want: nil}, 1455 {b: []byte{}, want: nil}, 1456 {b: []byte{0}, want: []byte{1}}, 1457 {b: []byte{42}, want: []byte{43}}, 1458 {b: []byte{255}, want: nil}, 1459 {b: []byte{0, 0}, want: []byte{0, 1}}, 1460 {b: []byte{1, 0}, want: []byte{1, 1}}, 1461 {b: []byte{1, 255}, want: []byte{2, 0}}, 1462 {b: []byte{255, 255}, want: nil}, 1463 {b: []byte{32, 0, 255}, want: []byte{32, 1, 0}}, 1464 } { 1465 got := incByteSlice(tc.b) 1466 if !bytes.Equal(got, tc.want) { 1467 t.Errorf("got %v, want %v", got, tc.want) 1468 } 1469 } 1470 } 1471 1472 // TestIndex_HasMulti validates that HasMulti returns a correct 1473 // slice of booleans for provided Items. 1474 func TestIndex_HasMulti(t *testing.T) { 1475 t.Parallel() 1476 1477 db := newTestDB(t) 1478 1479 index, err := db.NewIndex("retrieval", retrievalIndexFuncs) 1480 if err != nil { 1481 t.Fatal(err) 1482 } 1483 1484 items := []Item{ 1485 { 1486 Address: []byte("hash-01"), 1487 Data: []byte("data94"), 1488 }, 1489 { 1490 Address: []byte("hash-03"), 1491 Data: []byte("data33"), 1492 }, 1493 { 1494 Address: []byte("hash-05"), 1495 Data: []byte("data55"), 1496 }, 1497 { 1498 Address: []byte("hash-02"), 1499 Data: []byte("data21"), 1500 }, 1501 { 1502 Address: []byte("hash-06"), 1503 Data: []byte("data8"), 1504 }, 1505 } 1506 missingItem := Item{ 1507 Address: []byte("hash-10"), 1508 Data: []byte("data0"), 1509 } 1510 1511 batch := new(leveldb.Batch) 1512 for _, i := range items { 1513 err = index.PutInBatch(batch, i) 1514 if err != nil { 1515 t.Fatal(err) 1516 } 1517 } 1518 err = db.WriteBatch(batch) 1519 if err != nil { 1520 t.Fatal(err) 1521 } 1522 1523 got, err := index.HasMulti(items[0]) 1524 if err != nil { 1525 t.Fatal(err) 1526 } 1527 if !got[0] { 1528 t.Error("first item not found") 1529 } 1530 1531 got, err = index.HasMulti(missingItem) 1532 if err != nil { 1533 t.Fatal(err) 1534 } 1535 if got[0] { 1536 t.Error("missing item found") 1537 } 1538 1539 got, err = index.HasMulti(items...) 1540 if err != nil { 1541 t.Fatal(err) 1542 } 1543 want := []bool{true, true, true, true, true} 1544 if fmt.Sprint(got) != fmt.Sprint(want) { 1545 t.Errorf("got %v, want %v", got, want) 1546 } 1547 1548 got, err = index.HasMulti(append(items, missingItem)...) 1549 if err != nil { 1550 t.Fatal(err) 1551 } 1552 want = []bool{true, true, true, true, true, false} 1553 if fmt.Sprint(got) != fmt.Sprint(want) { 1554 t.Errorf("got %v, want %v", got, want) 1555 } 1556 }