github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/swarm/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  	"fmt"
    23  	"sort"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/syndtr/goleveldb/leveldb"
    28  )
    29  
    30  // Index functions for the index that is used in tests in this file.
    31  var retrievalIndexFuncs = IndexFuncs{
    32  	EncodeKey: func(fields Item) (key []byte, err error) {
    33  		return fields.Address, nil
    34  	},
    35  	DecodeKey: func(key []byte) (e Item, err error) {
    36  		e.Address = key
    37  		return e, nil
    38  	},
    39  	EncodeValue: func(fields Item) (value []byte, err error) {
    40  		b := make([]byte, 8)
    41  		binary.BigEndian.PutUint64(b, uint64(fields.StoreTimestamp))
    42  		value = append(b, fields.Data...)
    43  		return value, nil
    44  	},
    45  	DecodeValue: func(keyItem Item, value []byte) (e Item, err error) {
    46  		e.StoreTimestamp = int64(binary.BigEndian.Uint64(value[:8]))
    47  		e.Data = value[8:]
    48  		return e, nil
    49  	},
    50  }
    51  
    52  // TestIndex validates put, get, has and delete functions of the Index implementation.
    53  func TestIndex(t *testing.T) {
    54  	db, cleanupFunc := newTestDB(t)
    55  	defer cleanupFunc()
    56  
    57  	index, err := db.NewIndex("retrieval", retrievalIndexFuncs)
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  
    62  	t.Run("put", func(t *testing.T) {
    63  		want := Item{
    64  			Address:        []byte("put-hash"),
    65  			Data:           []byte("DATA"),
    66  			StoreTimestamp: time.Now().UTC().UnixNano(),
    67  		}
    68  
    69  		err := index.Put(want)
    70  		if err != nil {
    71  			t.Fatal(err)
    72  		}
    73  		got, err := index.Get(Item{
    74  			Address: want.Address,
    75  		})
    76  		if err != nil {
    77  			t.Fatal(err)
    78  		}
    79  		checkItem(t, got, want)
    80  
    81  		t.Run("overwrite", func(t *testing.T) {
    82  			want := Item{
    83  				Address:        []byte("put-hash"),
    84  				Data:           []byte("New DATA"),
    85  				StoreTimestamp: time.Now().UTC().UnixNano(),
    86  			}
    87  
    88  			err = index.Put(want)
    89  			if err != nil {
    90  				t.Fatal(err)
    91  			}
    92  			got, err := index.Get(Item{
    93  				Address: want.Address,
    94  			})
    95  			if err != nil {
    96  				t.Fatal(err)
    97  			}
    98  			checkItem(t, got, want)
    99  		})
   100  	})
   101  
   102  	t.Run("put in batch", func(t *testing.T) {
   103  		want := Item{
   104  			Address:        []byte("put-in-batch-hash"),
   105  			Data:           []byte("DATA"),
   106  			StoreTimestamp: time.Now().UTC().UnixNano(),
   107  		}
   108  
   109  		batch := new(leveldb.Batch)
   110  		index.PutInBatch(batch, want)
   111  		err := db.WriteBatch(batch)
   112  		if err != nil {
   113  			t.Fatal(err)
   114  		}
   115  		got, err := index.Get(Item{
   116  			Address: want.Address,
   117  		})
   118  		if err != nil {
   119  			t.Fatal(err)
   120  		}
   121  		checkItem(t, got, want)
   122  
   123  		t.Run("overwrite", func(t *testing.T) {
   124  			want := Item{
   125  				Address:        []byte("put-in-batch-hash"),
   126  				Data:           []byte("New DATA"),
   127  				StoreTimestamp: time.Now().UTC().UnixNano(),
   128  			}
   129  
   130  			batch := new(leveldb.Batch)
   131  			index.PutInBatch(batch, want)
   132  			db.WriteBatch(batch)
   133  			if err != nil {
   134  				t.Fatal(err)
   135  			}
   136  			got, err := index.Get(Item{
   137  				Address: want.Address,
   138  			})
   139  			if err != nil {
   140  				t.Fatal(err)
   141  			}
   142  			checkItem(t, got, want)
   143  		})
   144  	})
   145  
   146  	t.Run("put in batch twice", func(t *testing.T) {
   147  		// ensure that the last item of items with the same db keys
   148  		// is actually saved
   149  		batch := new(leveldb.Batch)
   150  		address := []byte("put-in-batch-twice-hash")
   151  
   152  		// put the first item
   153  		index.PutInBatch(batch, Item{
   154  			Address:        address,
   155  			Data:           []byte("DATA"),
   156  			StoreTimestamp: time.Now().UTC().UnixNano(),
   157  		})
   158  
   159  		want := Item{
   160  			Address:        address,
   161  			Data:           []byte("New DATA"),
   162  			StoreTimestamp: time.Now().UTC().UnixNano(),
   163  		}
   164  		// then put the item that will produce the same key
   165  		// but different value in the database
   166  		index.PutInBatch(batch, want)
   167  		db.WriteBatch(batch)
   168  		if err != nil {
   169  			t.Fatal(err)
   170  		}
   171  		got, err := index.Get(Item{
   172  			Address: address,
   173  		})
   174  		if err != nil {
   175  			t.Fatal(err)
   176  		}
   177  		checkItem(t, got, want)
   178  	})
   179  
   180  	t.Run("has", func(t *testing.T) {
   181  		want := Item{
   182  			Address:        []byte("has-hash"),
   183  			Data:           []byte("DATA"),
   184  			StoreTimestamp: time.Now().UTC().UnixNano(),
   185  		}
   186  
   187  		dontWant := Item{
   188  			Address:        []byte("do-not-has-hash"),
   189  			Data:           []byte("DATA"),
   190  			StoreTimestamp: time.Now().UTC().UnixNano(),
   191  		}
   192  
   193  		err := index.Put(want)
   194  		if err != nil {
   195  			t.Fatal(err)
   196  		}
   197  
   198  		has, err := index.Has(want)
   199  		if err != nil {
   200  			t.Fatal(err)
   201  		}
   202  		if !has {
   203  			t.Error("item is not found")
   204  		}
   205  
   206  		has, err = index.Has(dontWant)
   207  		if err != nil {
   208  			t.Fatal(err)
   209  		}
   210  		if has {
   211  			t.Error("unwanted item is found")
   212  		}
   213  	})
   214  
   215  	t.Run("delete", func(t *testing.T) {
   216  		want := Item{
   217  			Address:        []byte("delete-hash"),
   218  			Data:           []byte("DATA"),
   219  			StoreTimestamp: time.Now().UTC().UnixNano(),
   220  		}
   221  
   222  		err := index.Put(want)
   223  		if err != nil {
   224  			t.Fatal(err)
   225  		}
   226  		got, err := index.Get(Item{
   227  			Address: want.Address,
   228  		})
   229  		if err != nil {
   230  			t.Fatal(err)
   231  		}
   232  		checkItem(t, got, want)
   233  
   234  		err = index.Delete(Item{
   235  			Address: want.Address,
   236  		})
   237  		if err != nil {
   238  			t.Fatal(err)
   239  		}
   240  
   241  		wantErr := leveldb.ErrNotFound
   242  		got, err = index.Get(Item{
   243  			Address: want.Address,
   244  		})
   245  		if err != wantErr {
   246  			t.Fatalf("got error %v, want %v", err, wantErr)
   247  		}
   248  	})
   249  
   250  	t.Run("delete in batch", func(t *testing.T) {
   251  		want := Item{
   252  			Address:        []byte("delete-in-batch-hash"),
   253  			Data:           []byte("DATA"),
   254  			StoreTimestamp: time.Now().UTC().UnixNano(),
   255  		}
   256  
   257  		err := index.Put(want)
   258  		if err != nil {
   259  			t.Fatal(err)
   260  		}
   261  		got, err := index.Get(Item{
   262  			Address: want.Address,
   263  		})
   264  		if err != nil {
   265  			t.Fatal(err)
   266  		}
   267  		checkItem(t, got, want)
   268  
   269  		batch := new(leveldb.Batch)
   270  		index.DeleteInBatch(batch, Item{
   271  			Address: want.Address,
   272  		})
   273  		err = db.WriteBatch(batch)
   274  		if err != nil {
   275  			t.Fatal(err)
   276  		}
   277  
   278  		wantErr := leveldb.ErrNotFound
   279  		got, err = index.Get(Item{
   280  			Address: want.Address,
   281  		})
   282  		if err != wantErr {
   283  			t.Fatalf("got error %v, want %v", err, wantErr)
   284  		}
   285  	})
   286  }
   287  
   288  // TestIndex_Iterate validates index Iterate
   289  // functions for correctness.
   290  func TestIndex_Iterate(t *testing.T) {
   291  	db, cleanupFunc := newTestDB(t)
   292  	defer cleanupFunc()
   293  
   294  	index, err := db.NewIndex("retrieval", retrievalIndexFuncs)
   295  	if err != nil {
   296  		t.Fatal(err)
   297  	}
   298  
   299  	items := []Item{
   300  		{
   301  			Address: []byte("iterate-hash-01"),
   302  			Data:    []byte("data80"),
   303  		},
   304  		{
   305  			Address: []byte("iterate-hash-03"),
   306  			Data:    []byte("data22"),
   307  		},
   308  		{
   309  			Address: []byte("iterate-hash-05"),
   310  			Data:    []byte("data41"),
   311  		},
   312  		{
   313  			Address: []byte("iterate-hash-02"),
   314  			Data:    []byte("data84"),
   315  		},
   316  		{
   317  			Address: []byte("iterate-hash-06"),
   318  			Data:    []byte("data1"),
   319  		},
   320  	}
   321  	batch := new(leveldb.Batch)
   322  	for _, i := range items {
   323  		index.PutInBatch(batch, i)
   324  	}
   325  	err = db.WriteBatch(batch)
   326  	if err != nil {
   327  		t.Fatal(err)
   328  	}
   329  	item04 := Item{
   330  		Address: []byte("iterate-hash-04"),
   331  		Data:    []byte("data0"),
   332  	}
   333  	err = index.Put(item04)
   334  	if err != nil {
   335  		t.Fatal(err)
   336  	}
   337  	items = append(items, item04)
   338  
   339  	sort.SliceStable(items, func(i, j int) bool {
   340  		return bytes.Compare(items[i].Address, items[j].Address) < 0
   341  	})
   342  
   343  	t.Run("all", func(t *testing.T) {
   344  		var i int
   345  		err := index.Iterate(func(item Item) (stop bool, err error) {
   346  			if i > len(items)-1 {
   347  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   348  			}
   349  			want := items[i]
   350  			checkItem(t, item, want)
   351  			i++
   352  			return false, nil
   353  		}, nil)
   354  		if err != nil {
   355  			t.Fatal(err)
   356  		}
   357  	})
   358  
   359  	t.Run("start from", func(t *testing.T) {
   360  		startIndex := 2
   361  		i := startIndex
   362  		err := index.Iterate(func(item Item) (stop bool, err error) {
   363  			if i > len(items)-1 {
   364  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   365  			}
   366  			want := items[i]
   367  			checkItem(t, item, want)
   368  			i++
   369  			return false, nil
   370  		}, &IterateOptions{
   371  			StartFrom: &items[startIndex],
   372  		})
   373  		if err != nil {
   374  			t.Fatal(err)
   375  		}
   376  	})
   377  
   378  	t.Run("skip start from", func(t *testing.T) {
   379  		startIndex := 2
   380  		i := startIndex + 1
   381  		err := index.Iterate(func(item Item) (stop bool, err error) {
   382  			if i > len(items)-1 {
   383  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   384  			}
   385  			want := items[i]
   386  			checkItem(t, item, want)
   387  			i++
   388  			return false, nil
   389  		}, &IterateOptions{
   390  			StartFrom:         &items[startIndex],
   391  			SkipStartFromItem: true,
   392  		})
   393  		if err != nil {
   394  			t.Fatal(err)
   395  		}
   396  	})
   397  
   398  	t.Run("stop", func(t *testing.T) {
   399  		var i int
   400  		stopIndex := 3
   401  		var count int
   402  		err := index.Iterate(func(item Item) (stop bool, err error) {
   403  			if i > len(items)-1 {
   404  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   405  			}
   406  			want := items[i]
   407  			checkItem(t, item, want)
   408  			count++
   409  			if i == stopIndex {
   410  				return true, nil
   411  			}
   412  			i++
   413  			return false, nil
   414  		}, nil)
   415  		if err != nil {
   416  			t.Fatal(err)
   417  		}
   418  		wantItemsCount := stopIndex + 1
   419  		if count != wantItemsCount {
   420  			t.Errorf("got %v items, expected %v", count, wantItemsCount)
   421  		}
   422  	})
   423  
   424  	t.Run("no overflow", func(t *testing.T) {
   425  		secondIndex, err := db.NewIndex("second-index", retrievalIndexFuncs)
   426  		if err != nil {
   427  			t.Fatal(err)
   428  		}
   429  
   430  		secondItem := Item{
   431  			Address: []byte("iterate-hash-10"),
   432  			Data:    []byte("data-second"),
   433  		}
   434  		err = secondIndex.Put(secondItem)
   435  		if err != nil {
   436  			t.Fatal(err)
   437  		}
   438  
   439  		var i int
   440  		err = index.Iterate(func(item Item) (stop bool, err error) {
   441  			if i > len(items)-1 {
   442  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   443  			}
   444  			want := items[i]
   445  			checkItem(t, item, want)
   446  			i++
   447  			return false, nil
   448  		}, nil)
   449  		if err != nil {
   450  			t.Fatal(err)
   451  		}
   452  
   453  		i = 0
   454  		err = secondIndex.Iterate(func(item Item) (stop bool, err error) {
   455  			if i > 1 {
   456  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   457  			}
   458  			checkItem(t, item, secondItem)
   459  			i++
   460  			return false, nil
   461  		}, nil)
   462  		if err != nil {
   463  			t.Fatal(err)
   464  		}
   465  	})
   466  }
   467  
   468  // TestIndex_Iterate_withPrefix validates index Iterate
   469  // function for correctness.
   470  func TestIndex_Iterate_withPrefix(t *testing.T) {
   471  	db, cleanupFunc := newTestDB(t)
   472  	defer cleanupFunc()
   473  
   474  	index, err := db.NewIndex("retrieval", retrievalIndexFuncs)
   475  	if err != nil {
   476  		t.Fatal(err)
   477  	}
   478  
   479  	allItems := []Item{
   480  		{Address: []byte("want-hash-00"), Data: []byte("data80")},
   481  		{Address: []byte("skip-hash-01"), Data: []byte("data81")},
   482  		{Address: []byte("skip-hash-02"), Data: []byte("data82")},
   483  		{Address: []byte("skip-hash-03"), Data: []byte("data83")},
   484  		{Address: []byte("want-hash-04"), Data: []byte("data84")},
   485  		{Address: []byte("want-hash-05"), Data: []byte("data85")},
   486  		{Address: []byte("want-hash-06"), Data: []byte("data86")},
   487  		{Address: []byte("want-hash-07"), Data: []byte("data87")},
   488  		{Address: []byte("want-hash-08"), Data: []byte("data88")},
   489  		{Address: []byte("want-hash-09"), Data: []byte("data89")},
   490  		{Address: []byte("skip-hash-10"), Data: []byte("data90")},
   491  	}
   492  	batch := new(leveldb.Batch)
   493  	for _, i := range allItems {
   494  		index.PutInBatch(batch, i)
   495  	}
   496  	err = db.WriteBatch(batch)
   497  	if err != nil {
   498  		t.Fatal(err)
   499  	}
   500  
   501  	prefix := []byte("want")
   502  
   503  	items := make([]Item, 0)
   504  	for _, item := range allItems {
   505  		if bytes.HasPrefix(item.Address, prefix) {
   506  			items = append(items, item)
   507  		}
   508  	}
   509  	sort.SliceStable(items, func(i, j int) bool {
   510  		return bytes.Compare(items[i].Address, items[j].Address) < 0
   511  	})
   512  
   513  	t.Run("with prefix", func(t *testing.T) {
   514  		var i int
   515  		err := index.Iterate(func(item Item) (stop bool, err error) {
   516  			if i > len(items)-1 {
   517  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   518  			}
   519  			want := items[i]
   520  			checkItem(t, item, want)
   521  			i++
   522  			return false, nil
   523  		}, &IterateOptions{
   524  			Prefix: prefix,
   525  		})
   526  		if err != nil {
   527  			t.Fatal(err)
   528  		}
   529  		if i != len(items) {
   530  			t.Errorf("got %v items, want %v", i, len(items))
   531  		}
   532  	})
   533  
   534  	t.Run("with prefix and start from", func(t *testing.T) {
   535  		startIndex := 2
   536  		var count int
   537  		i := startIndex
   538  		err := index.Iterate(func(item Item) (stop bool, err error) {
   539  			if i > len(items)-1 {
   540  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   541  			}
   542  			want := items[i]
   543  			checkItem(t, item, want)
   544  			i++
   545  			count++
   546  			return false, nil
   547  		}, &IterateOptions{
   548  			StartFrom: &items[startIndex],
   549  			Prefix:    prefix,
   550  		})
   551  		if err != nil {
   552  			t.Fatal(err)
   553  		}
   554  		wantCount := len(items) - startIndex
   555  		if count != wantCount {
   556  			t.Errorf("got %v items, want %v", count, wantCount)
   557  		}
   558  	})
   559  
   560  	t.Run("with prefix and skip start from", func(t *testing.T) {
   561  		startIndex := 2
   562  		var count int
   563  		i := startIndex + 1
   564  		err := index.Iterate(func(item Item) (stop bool, err error) {
   565  			if i > len(items)-1 {
   566  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   567  			}
   568  			want := items[i]
   569  			checkItem(t, item, want)
   570  			i++
   571  			count++
   572  			return false, nil
   573  		}, &IterateOptions{
   574  			StartFrom:         &items[startIndex],
   575  			SkipStartFromItem: true,
   576  			Prefix:            prefix,
   577  		})
   578  		if err != nil {
   579  			t.Fatal(err)
   580  		}
   581  		wantCount := len(items) - startIndex - 1
   582  		if count != wantCount {
   583  			t.Errorf("got %v items, want %v", count, wantCount)
   584  		}
   585  	})
   586  
   587  	t.Run("stop", func(t *testing.T) {
   588  		var i int
   589  		stopIndex := 3
   590  		var count int
   591  		err := index.Iterate(func(item Item) (stop bool, err error) {
   592  			if i > len(items)-1 {
   593  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   594  			}
   595  			want := items[i]
   596  			checkItem(t, item, want)
   597  			count++
   598  			if i == stopIndex {
   599  				return true, nil
   600  			}
   601  			i++
   602  			return false, nil
   603  		}, &IterateOptions{
   604  			Prefix: prefix,
   605  		})
   606  		if err != nil {
   607  			t.Fatal(err)
   608  		}
   609  		wantItemsCount := stopIndex + 1
   610  		if count != wantItemsCount {
   611  			t.Errorf("got %v items, expected %v", count, wantItemsCount)
   612  		}
   613  	})
   614  
   615  	t.Run("no overflow", func(t *testing.T) {
   616  		secondIndex, err := db.NewIndex("second-index", retrievalIndexFuncs)
   617  		if err != nil {
   618  			t.Fatal(err)
   619  		}
   620  
   621  		secondItem := Item{
   622  			Address: []byte("iterate-hash-10"),
   623  			Data:    []byte("data-second"),
   624  		}
   625  		err = secondIndex.Put(secondItem)
   626  		if err != nil {
   627  			t.Fatal(err)
   628  		}
   629  
   630  		var i int
   631  		err = index.Iterate(func(item Item) (stop bool, err error) {
   632  			if i > len(items)-1 {
   633  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   634  			}
   635  			want := items[i]
   636  			checkItem(t, item, want)
   637  			i++
   638  			return false, nil
   639  		}, &IterateOptions{
   640  			Prefix: prefix,
   641  		})
   642  		if err != nil {
   643  			t.Fatal(err)
   644  		}
   645  		if i != len(items) {
   646  			t.Errorf("got %v items, want %v", i, len(items))
   647  		}
   648  	})
   649  }
   650  
   651  // TestIndex_count tests if Index.Count and Index.CountFrom
   652  // returns the correct number of items.
   653  func TestIndex_count(t *testing.T) {
   654  	db, cleanupFunc := newTestDB(t)
   655  	defer cleanupFunc()
   656  
   657  	index, err := db.NewIndex("retrieval", retrievalIndexFuncs)
   658  	if err != nil {
   659  		t.Fatal(err)
   660  	}
   661  
   662  	items := []Item{
   663  		{
   664  			Address: []byte("iterate-hash-01"),
   665  			Data:    []byte("data80"),
   666  		},
   667  		{
   668  			Address: []byte("iterate-hash-02"),
   669  			Data:    []byte("data84"),
   670  		},
   671  		{
   672  			Address: []byte("iterate-hash-03"),
   673  			Data:    []byte("data22"),
   674  		},
   675  		{
   676  			Address: []byte("iterate-hash-04"),
   677  			Data:    []byte("data41"),
   678  		},
   679  		{
   680  			Address: []byte("iterate-hash-05"),
   681  			Data:    []byte("data1"),
   682  		},
   683  	}
   684  	batch := new(leveldb.Batch)
   685  	for _, i := range items {
   686  		index.PutInBatch(batch, i)
   687  	}
   688  	err = db.WriteBatch(batch)
   689  	if err != nil {
   690  		t.Fatal(err)
   691  	}
   692  
   693  	t.Run("Count", func(t *testing.T) {
   694  		got, err := index.Count()
   695  		if err != nil {
   696  			t.Fatal(err)
   697  		}
   698  
   699  		want := len(items)
   700  		if got != want {
   701  			t.Errorf("got %v items count, want %v", got, want)
   702  		}
   703  	})
   704  
   705  	t.Run("CountFrom", func(t *testing.T) {
   706  		got, err := index.CountFrom(Item{
   707  			Address: items[1].Address,
   708  		})
   709  		if err != nil {
   710  			t.Fatal(err)
   711  		}
   712  
   713  		want := len(items) - 1
   714  		if got != want {
   715  			t.Errorf("got %v items count, want %v", got, want)
   716  		}
   717  	})
   718  
   719  	// update the index with another item
   720  	t.Run("add item", func(t *testing.T) {
   721  		item04 := Item{
   722  			Address: []byte("iterate-hash-06"),
   723  			Data:    []byte("data0"),
   724  		}
   725  		err = index.Put(item04)
   726  		if err != nil {
   727  			t.Fatal(err)
   728  		}
   729  
   730  		count := len(items) + 1
   731  
   732  		t.Run("Count", func(t *testing.T) {
   733  			got, err := index.Count()
   734  			if err != nil {
   735  				t.Fatal(err)
   736  			}
   737  
   738  			want := count
   739  			if got != want {
   740  				t.Errorf("got %v items count, want %v", got, want)
   741  			}
   742  		})
   743  
   744  		t.Run("CountFrom", func(t *testing.T) {
   745  			got, err := index.CountFrom(Item{
   746  				Address: items[1].Address,
   747  			})
   748  			if err != nil {
   749  				t.Fatal(err)
   750  			}
   751  
   752  			want := count - 1
   753  			if got != want {
   754  				t.Errorf("got %v items count, want %v", got, want)
   755  			}
   756  		})
   757  	})
   758  
   759  	// delete some items
   760  	t.Run("delete items", func(t *testing.T) {
   761  		deleteCount := 3
   762  
   763  		for _, item := range items[:deleteCount] {
   764  			err := index.Delete(item)
   765  			if err != nil {
   766  				t.Fatal(err)
   767  			}
   768  		}
   769  
   770  		count := len(items) + 1 - deleteCount
   771  
   772  		t.Run("Count", func(t *testing.T) {
   773  			got, err := index.Count()
   774  			if err != nil {
   775  				t.Fatal(err)
   776  			}
   777  
   778  			want := count
   779  			if got != want {
   780  				t.Errorf("got %v items count, want %v", got, want)
   781  			}
   782  		})
   783  
   784  		t.Run("CountFrom", func(t *testing.T) {
   785  			got, err := index.CountFrom(Item{
   786  				Address: items[deleteCount+1].Address,
   787  			})
   788  			if err != nil {
   789  				t.Fatal(err)
   790  			}
   791  
   792  			want := count - 1
   793  			if got != want {
   794  				t.Errorf("got %v items count, want %v", got, want)
   795  			}
   796  		})
   797  	})
   798  }
   799  
   800  // checkItem is a test helper function that compares if two Index items are the same.
   801  func checkItem(t *testing.T, got, want Item) {
   802  	t.Helper()
   803  
   804  	if !bytes.Equal(got.Address, want.Address) {
   805  		t.Errorf("got hash %q, expected %q", string(got.Address), string(want.Address))
   806  	}
   807  	if !bytes.Equal(got.Data, want.Data) {
   808  		t.Errorf("got data %q, expected %q", string(got.Data), string(want.Data))
   809  	}
   810  	if got.StoreTimestamp != want.StoreTimestamp {
   811  		t.Errorf("got store timestamp %v, expected %v", got.StoreTimestamp, want.StoreTimestamp)
   812  	}
   813  	if got.AccessTimestamp != want.AccessTimestamp {
   814  		t.Errorf("got access timestamp %v, expected %v", got.AccessTimestamp, want.AccessTimestamp)
   815  	}
   816  }
   817  
   818  // TestIndex_firstAndLast validates that index First and Last methods
   819  // are returning expected results based on the provided prefix.
   820  func TestIndex_firstAndLast(t *testing.T) {
   821  	db, cleanupFunc := newTestDB(t)
   822  	defer cleanupFunc()
   823  
   824  	index, err := db.NewIndex("retrieval", retrievalIndexFuncs)
   825  	if err != nil {
   826  		t.Fatal(err)
   827  	}
   828  
   829  	addrs := [][]byte{
   830  		{0, 0, 0, 0, 0},
   831  		{0, 1},
   832  		{0, 1, 0, 0, 0},
   833  		{0, 1, 0, 0, 1},
   834  		{0, 1, 0, 0, 2},
   835  		{0, 2, 0, 0, 1},
   836  		{0, 4, 0, 0, 0},
   837  		{0, 10, 0, 0, 10},
   838  		{0, 10, 0, 0, 11},
   839  		{0, 10, 0, 0, 20},
   840  		{1, 32, 255, 0, 1},
   841  		{1, 32, 255, 0, 2},
   842  		{1, 32, 255, 0, 3},
   843  		{255, 255, 255, 255, 32},
   844  		{255, 255, 255, 255, 64},
   845  		{255, 255, 255, 255, 255},
   846  	}
   847  
   848  	// ensure that the addresses are sorted for
   849  	// validation of nil prefix
   850  	sort.Slice(addrs, func(i, j int) (less bool) {
   851  		return bytes.Compare(addrs[i], addrs[j]) == -1
   852  	})
   853  
   854  	batch := new(leveldb.Batch)
   855  	for _, addr := range addrs {
   856  		index.PutInBatch(batch, Item{
   857  			Address: addr,
   858  		})
   859  	}
   860  	err = db.WriteBatch(batch)
   861  	if err != nil {
   862  		t.Fatal(err)
   863  	}
   864  
   865  	for _, tc := range []struct {
   866  		prefix []byte
   867  		first  []byte
   868  		last   []byte
   869  		err    error
   870  	}{
   871  		{
   872  			prefix: nil,
   873  			first:  addrs[0],
   874  			last:   addrs[len(addrs)-1],
   875  		},
   876  		{
   877  			prefix: []byte{0, 0},
   878  			first:  []byte{0, 0, 0, 0, 0},
   879  			last:   []byte{0, 0, 0, 0, 0},
   880  		},
   881  		{
   882  			prefix: []byte{0},
   883  			first:  []byte{0, 0, 0, 0, 0},
   884  			last:   []byte{0, 10, 0, 0, 20},
   885  		},
   886  		{
   887  			prefix: []byte{0, 1},
   888  			first:  []byte{0, 1},
   889  			last:   []byte{0, 1, 0, 0, 2},
   890  		},
   891  		{
   892  			prefix: []byte{0, 10},
   893  			first:  []byte{0, 10, 0, 0, 10},
   894  			last:   []byte{0, 10, 0, 0, 20},
   895  		},
   896  		{
   897  			prefix: []byte{1, 32, 255},
   898  			first:  []byte{1, 32, 255, 0, 1},
   899  			last:   []byte{1, 32, 255, 0, 3},
   900  		},
   901  		{
   902  			prefix: []byte{255},
   903  			first:  []byte{255, 255, 255, 255, 32},
   904  			last:   []byte{255, 255, 255, 255, 255},
   905  		},
   906  		{
   907  			prefix: []byte{255, 255, 255, 255, 255},
   908  			first:  []byte{255, 255, 255, 255, 255},
   909  			last:   []byte{255, 255, 255, 255, 255},
   910  		},
   911  		{
   912  			prefix: []byte{0, 3},
   913  			err:    leveldb.ErrNotFound,
   914  		},
   915  		{
   916  			prefix: []byte{222},
   917  			err:    leveldb.ErrNotFound,
   918  		},
   919  	} {
   920  		got, err := index.Last(tc.prefix)
   921  		if tc.err != err {
   922  			t.Errorf("got error %v for Last with prefix %v, want %v", err, tc.prefix, tc.err)
   923  		} else {
   924  			if !bytes.Equal(got.Address, tc.last) {
   925  				t.Errorf("got %v for Last with prefix %v, want %v", got.Address, tc.prefix, tc.last)
   926  			}
   927  		}
   928  
   929  		got, err = index.First(tc.prefix)
   930  		if tc.err != err {
   931  			t.Errorf("got error %v for First with prefix %v, want %v", err, tc.prefix, tc.err)
   932  		} else {
   933  			if !bytes.Equal(got.Address, tc.first) {
   934  				t.Errorf("got %v for First with prefix %v, want %v", got.Address, tc.prefix, tc.first)
   935  			}
   936  		}
   937  	}
   938  }
   939  
   940  // TestIncByteSlice validates returned values of incByteSlice function.
   941  func TestIncByteSlice(t *testing.T) {
   942  	for _, tc := range []struct {
   943  		b    []byte
   944  		want []byte
   945  	}{
   946  		{b: nil, want: nil},
   947  		{b: []byte{}, want: nil},
   948  		{b: []byte{0}, want: []byte{1}},
   949  		{b: []byte{42}, want: []byte{43}},
   950  		{b: []byte{255}, want: nil},
   951  		{b: []byte{0, 0}, want: []byte{0, 1}},
   952  		{b: []byte{1, 0}, want: []byte{1, 1}},
   953  		{b: []byte{1, 255}, want: []byte{2, 0}},
   954  		{b: []byte{255, 255}, want: nil},
   955  		{b: []byte{32, 0, 255}, want: []byte{32, 1, 0}},
   956  	} {
   957  		got := incByteSlice(tc.b)
   958  		if !bytes.Equal(got, tc.want) {
   959  			t.Errorf("got %v, want %v", got, tc.want)
   960  		}
   961  	}
   962  }