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  }