github.com/maynardminer/ethereumprogpow@v1.8.23/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 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("delete", func(t *testing.T) {
   181  		want := Item{
   182  			Address:        []byte("delete-hash"),
   183  			Data:           []byte("DATA"),
   184  			StoreTimestamp: time.Now().UTC().UnixNano(),
   185  		}
   186  
   187  		err := index.Put(want)
   188  		if err != nil {
   189  			t.Fatal(err)
   190  		}
   191  		got, err := index.Get(Item{
   192  			Address: want.Address,
   193  		})
   194  		if err != nil {
   195  			t.Fatal(err)
   196  		}
   197  		checkItem(t, got, want)
   198  
   199  		err = index.Delete(Item{
   200  			Address: want.Address,
   201  		})
   202  		if err != nil {
   203  			t.Fatal(err)
   204  		}
   205  
   206  		wantErr := leveldb.ErrNotFound
   207  		got, err = index.Get(Item{
   208  			Address: want.Address,
   209  		})
   210  		if err != wantErr {
   211  			t.Fatalf("got error %v, want %v", err, wantErr)
   212  		}
   213  	})
   214  
   215  	t.Run("delete in batch", func(t *testing.T) {
   216  		want := Item{
   217  			Address:        []byte("delete-in-batch-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  		batch := new(leveldb.Batch)
   235  		index.DeleteInBatch(batch, Item{
   236  			Address: want.Address,
   237  		})
   238  		err = db.WriteBatch(batch)
   239  		if err != nil {
   240  			t.Fatal(err)
   241  		}
   242  
   243  		wantErr := leveldb.ErrNotFound
   244  		got, err = index.Get(Item{
   245  			Address: want.Address,
   246  		})
   247  		if err != wantErr {
   248  			t.Fatalf("got error %v, want %v", err, wantErr)
   249  		}
   250  	})
   251  }
   252  
   253  // TestIndex_Iterate validates index Iterate
   254  // functions for correctness.
   255  func TestIndex_Iterate(t *testing.T) {
   256  	db, cleanupFunc := newTestDB(t)
   257  	defer cleanupFunc()
   258  
   259  	index, err := db.NewIndex("retrieval", retrievalIndexFuncs)
   260  	if err != nil {
   261  		t.Fatal(err)
   262  	}
   263  
   264  	items := []Item{
   265  		{
   266  			Address: []byte("iterate-hash-01"),
   267  			Data:    []byte("data80"),
   268  		},
   269  		{
   270  			Address: []byte("iterate-hash-03"),
   271  			Data:    []byte("data22"),
   272  		},
   273  		{
   274  			Address: []byte("iterate-hash-05"),
   275  			Data:    []byte("data41"),
   276  		},
   277  		{
   278  			Address: []byte("iterate-hash-02"),
   279  			Data:    []byte("data84"),
   280  		},
   281  		{
   282  			Address: []byte("iterate-hash-06"),
   283  			Data:    []byte("data1"),
   284  		},
   285  	}
   286  	batch := new(leveldb.Batch)
   287  	for _, i := range items {
   288  		index.PutInBatch(batch, i)
   289  	}
   290  	err = db.WriteBatch(batch)
   291  	if err != nil {
   292  		t.Fatal(err)
   293  	}
   294  	item04 := Item{
   295  		Address: []byte("iterate-hash-04"),
   296  		Data:    []byte("data0"),
   297  	}
   298  	err = index.Put(item04)
   299  	if err != nil {
   300  		t.Fatal(err)
   301  	}
   302  	items = append(items, item04)
   303  
   304  	sort.SliceStable(items, func(i, j int) bool {
   305  		return bytes.Compare(items[i].Address, items[j].Address) < 0
   306  	})
   307  
   308  	t.Run("all", func(t *testing.T) {
   309  		var i int
   310  		err := index.Iterate(func(item Item) (stop bool, err error) {
   311  			if i > len(items)-1 {
   312  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   313  			}
   314  			want := items[i]
   315  			checkItem(t, item, want)
   316  			i++
   317  			return false, nil
   318  		}, nil)
   319  		if err != nil {
   320  			t.Fatal(err)
   321  		}
   322  	})
   323  
   324  	t.Run("start from", func(t *testing.T) {
   325  		startIndex := 2
   326  		i := startIndex
   327  		err := index.Iterate(func(item Item) (stop bool, err error) {
   328  			if i > len(items)-1 {
   329  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   330  			}
   331  			want := items[i]
   332  			checkItem(t, item, want)
   333  			i++
   334  			return false, nil
   335  		}, &IterateOptions{
   336  			StartFrom: &items[startIndex],
   337  		})
   338  		if err != nil {
   339  			t.Fatal(err)
   340  		}
   341  	})
   342  
   343  	t.Run("skip start from", func(t *testing.T) {
   344  		startIndex := 2
   345  		i := startIndex + 1
   346  		err := index.Iterate(func(item Item) (stop bool, err error) {
   347  			if i > len(items)-1 {
   348  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   349  			}
   350  			want := items[i]
   351  			checkItem(t, item, want)
   352  			i++
   353  			return false, nil
   354  		}, &IterateOptions{
   355  			StartFrom:         &items[startIndex],
   356  			SkipStartFromItem: true,
   357  		})
   358  		if err != nil {
   359  			t.Fatal(err)
   360  		}
   361  	})
   362  
   363  	t.Run("stop", func(t *testing.T) {
   364  		var i int
   365  		stopIndex := 3
   366  		var count int
   367  		err := index.Iterate(func(item Item) (stop bool, err error) {
   368  			if i > len(items)-1 {
   369  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   370  			}
   371  			want := items[i]
   372  			checkItem(t, item, want)
   373  			count++
   374  			if i == stopIndex {
   375  				return true, nil
   376  			}
   377  			i++
   378  			return false, nil
   379  		}, nil)
   380  		if err != nil {
   381  			t.Fatal(err)
   382  		}
   383  		wantItemsCount := stopIndex + 1
   384  		if count != wantItemsCount {
   385  			t.Errorf("got %v items, expected %v", count, wantItemsCount)
   386  		}
   387  	})
   388  
   389  	t.Run("no overflow", func(t *testing.T) {
   390  		secondIndex, err := db.NewIndex("second-index", retrievalIndexFuncs)
   391  		if err != nil {
   392  			t.Fatal(err)
   393  		}
   394  
   395  		secondItem := Item{
   396  			Address: []byte("iterate-hash-10"),
   397  			Data:    []byte("data-second"),
   398  		}
   399  		err = secondIndex.Put(secondItem)
   400  		if err != nil {
   401  			t.Fatal(err)
   402  		}
   403  
   404  		var i int
   405  		err = index.Iterate(func(item Item) (stop bool, err error) {
   406  			if i > len(items)-1 {
   407  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   408  			}
   409  			want := items[i]
   410  			checkItem(t, item, want)
   411  			i++
   412  			return false, nil
   413  		}, nil)
   414  		if err != nil {
   415  			t.Fatal(err)
   416  		}
   417  
   418  		i = 0
   419  		err = secondIndex.Iterate(func(item Item) (stop bool, err error) {
   420  			if i > 1 {
   421  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   422  			}
   423  			checkItem(t, item, secondItem)
   424  			i++
   425  			return false, nil
   426  		}, nil)
   427  		if err != nil {
   428  			t.Fatal(err)
   429  		}
   430  	})
   431  }
   432  
   433  // TestIndex_Iterate_withPrefix validates index Iterate
   434  // function for correctness.
   435  func TestIndex_Iterate_withPrefix(t *testing.T) {
   436  	db, cleanupFunc := newTestDB(t)
   437  	defer cleanupFunc()
   438  
   439  	index, err := db.NewIndex("retrieval", retrievalIndexFuncs)
   440  	if err != nil {
   441  		t.Fatal(err)
   442  	}
   443  
   444  	allItems := []Item{
   445  		{Address: []byte("want-hash-00"), Data: []byte("data80")},
   446  		{Address: []byte("skip-hash-01"), Data: []byte("data81")},
   447  		{Address: []byte("skip-hash-02"), Data: []byte("data82")},
   448  		{Address: []byte("skip-hash-03"), Data: []byte("data83")},
   449  		{Address: []byte("want-hash-04"), Data: []byte("data84")},
   450  		{Address: []byte("want-hash-05"), Data: []byte("data85")},
   451  		{Address: []byte("want-hash-06"), Data: []byte("data86")},
   452  		{Address: []byte("want-hash-07"), Data: []byte("data87")},
   453  		{Address: []byte("want-hash-08"), Data: []byte("data88")},
   454  		{Address: []byte("want-hash-09"), Data: []byte("data89")},
   455  		{Address: []byte("skip-hash-10"), Data: []byte("data90")},
   456  	}
   457  	batch := new(leveldb.Batch)
   458  	for _, i := range allItems {
   459  		index.PutInBatch(batch, i)
   460  	}
   461  	err = db.WriteBatch(batch)
   462  	if err != nil {
   463  		t.Fatal(err)
   464  	}
   465  
   466  	prefix := []byte("want")
   467  
   468  	items := make([]Item, 0)
   469  	for _, item := range allItems {
   470  		if bytes.HasPrefix(item.Address, prefix) {
   471  			items = append(items, item)
   472  		}
   473  	}
   474  	sort.SliceStable(items, func(i, j int) bool {
   475  		return bytes.Compare(items[i].Address, items[j].Address) < 0
   476  	})
   477  
   478  	t.Run("with prefix", func(t *testing.T) {
   479  		var i int
   480  		err := index.Iterate(func(item Item) (stop bool, err error) {
   481  			if i > len(items)-1 {
   482  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   483  			}
   484  			want := items[i]
   485  			checkItem(t, item, want)
   486  			i++
   487  			return false, nil
   488  		}, &IterateOptions{
   489  			Prefix: prefix,
   490  		})
   491  		if err != nil {
   492  			t.Fatal(err)
   493  		}
   494  		if i != len(items) {
   495  			t.Errorf("got %v items, want %v", i, len(items))
   496  		}
   497  	})
   498  
   499  	t.Run("with prefix and start from", func(t *testing.T) {
   500  		startIndex := 2
   501  		var count int
   502  		i := startIndex
   503  		err := index.Iterate(func(item Item) (stop bool, err error) {
   504  			if i > len(items)-1 {
   505  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   506  			}
   507  			want := items[i]
   508  			checkItem(t, item, want)
   509  			i++
   510  			count++
   511  			return false, nil
   512  		}, &IterateOptions{
   513  			StartFrom: &items[startIndex],
   514  			Prefix:    prefix,
   515  		})
   516  		if err != nil {
   517  			t.Fatal(err)
   518  		}
   519  		wantCount := len(items) - startIndex
   520  		if count != wantCount {
   521  			t.Errorf("got %v items, want %v", count, wantCount)
   522  		}
   523  	})
   524  
   525  	t.Run("with prefix and skip start from", func(t *testing.T) {
   526  		startIndex := 2
   527  		var count int
   528  		i := startIndex + 1
   529  		err := index.Iterate(func(item Item) (stop bool, err error) {
   530  			if i > len(items)-1 {
   531  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   532  			}
   533  			want := items[i]
   534  			checkItem(t, item, want)
   535  			i++
   536  			count++
   537  			return false, nil
   538  		}, &IterateOptions{
   539  			StartFrom:         &items[startIndex],
   540  			SkipStartFromItem: true,
   541  			Prefix:            prefix,
   542  		})
   543  		if err != nil {
   544  			t.Fatal(err)
   545  		}
   546  		wantCount := len(items) - startIndex - 1
   547  		if count != wantCount {
   548  			t.Errorf("got %v items, want %v", count, wantCount)
   549  		}
   550  	})
   551  
   552  	t.Run("stop", func(t *testing.T) {
   553  		var i int
   554  		stopIndex := 3
   555  		var count int
   556  		err := index.Iterate(func(item Item) (stop bool, err error) {
   557  			if i > len(items)-1 {
   558  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   559  			}
   560  			want := items[i]
   561  			checkItem(t, item, want)
   562  			count++
   563  			if i == stopIndex {
   564  				return true, nil
   565  			}
   566  			i++
   567  			return false, nil
   568  		}, &IterateOptions{
   569  			Prefix: prefix,
   570  		})
   571  		if err != nil {
   572  			t.Fatal(err)
   573  		}
   574  		wantItemsCount := stopIndex + 1
   575  		if count != wantItemsCount {
   576  			t.Errorf("got %v items, expected %v", count, wantItemsCount)
   577  		}
   578  	})
   579  
   580  	t.Run("no overflow", func(t *testing.T) {
   581  		secondIndex, err := db.NewIndex("second-index", retrievalIndexFuncs)
   582  		if err != nil {
   583  			t.Fatal(err)
   584  		}
   585  
   586  		secondItem := Item{
   587  			Address: []byte("iterate-hash-10"),
   588  			Data:    []byte("data-second"),
   589  		}
   590  		err = secondIndex.Put(secondItem)
   591  		if err != nil {
   592  			t.Fatal(err)
   593  		}
   594  
   595  		var i int
   596  		err = index.Iterate(func(item Item) (stop bool, err error) {
   597  			if i > len(items)-1 {
   598  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   599  			}
   600  			want := items[i]
   601  			checkItem(t, item, want)
   602  			i++
   603  			return false, nil
   604  		}, &IterateOptions{
   605  			Prefix: prefix,
   606  		})
   607  		if err != nil {
   608  			t.Fatal(err)
   609  		}
   610  		if i != len(items) {
   611  			t.Errorf("got %v items, want %v", i, len(items))
   612  		}
   613  	})
   614  }
   615  
   616  // TestIndex_count tests if Index.Count and Index.CountFrom
   617  // returns the correct number of items.
   618  func TestIndex_count(t *testing.T) {
   619  	db, cleanupFunc := newTestDB(t)
   620  	defer cleanupFunc()
   621  
   622  	index, err := db.NewIndex("retrieval", retrievalIndexFuncs)
   623  	if err != nil {
   624  		t.Fatal(err)
   625  	}
   626  
   627  	items := []Item{
   628  		{
   629  			Address: []byte("iterate-hash-01"),
   630  			Data:    []byte("data80"),
   631  		},
   632  		{
   633  			Address: []byte("iterate-hash-02"),
   634  			Data:    []byte("data84"),
   635  		},
   636  		{
   637  			Address: []byte("iterate-hash-03"),
   638  			Data:    []byte("data22"),
   639  		},
   640  		{
   641  			Address: []byte("iterate-hash-04"),
   642  			Data:    []byte("data41"),
   643  		},
   644  		{
   645  			Address: []byte("iterate-hash-05"),
   646  			Data:    []byte("data1"),
   647  		},
   648  	}
   649  	batch := new(leveldb.Batch)
   650  	for _, i := range items {
   651  		index.PutInBatch(batch, i)
   652  	}
   653  	err = db.WriteBatch(batch)
   654  	if err != nil {
   655  		t.Fatal(err)
   656  	}
   657  
   658  	t.Run("Count", func(t *testing.T) {
   659  		got, err := index.Count()
   660  		if err != nil {
   661  			t.Fatal(err)
   662  		}
   663  
   664  		want := len(items)
   665  		if got != want {
   666  			t.Errorf("got %v items count, want %v", got, want)
   667  		}
   668  	})
   669  
   670  	t.Run("CountFrom", func(t *testing.T) {
   671  		got, err := index.CountFrom(Item{
   672  			Address: items[1].Address,
   673  		})
   674  		if err != nil {
   675  			t.Fatal(err)
   676  		}
   677  
   678  		want := len(items) - 1
   679  		if got != want {
   680  			t.Errorf("got %v items count, want %v", got, want)
   681  		}
   682  	})
   683  
   684  	// update the index with another item
   685  	t.Run("add item", func(t *testing.T) {
   686  		item04 := Item{
   687  			Address: []byte("iterate-hash-06"),
   688  			Data:    []byte("data0"),
   689  		}
   690  		err = index.Put(item04)
   691  		if err != nil {
   692  			t.Fatal(err)
   693  		}
   694  
   695  		count := len(items) + 1
   696  
   697  		t.Run("Count", func(t *testing.T) {
   698  			got, err := index.Count()
   699  			if err != nil {
   700  				t.Fatal(err)
   701  			}
   702  
   703  			want := count
   704  			if got != want {
   705  				t.Errorf("got %v items count, want %v", got, want)
   706  			}
   707  		})
   708  
   709  		t.Run("CountFrom", func(t *testing.T) {
   710  			got, err := index.CountFrom(Item{
   711  				Address: items[1].Address,
   712  			})
   713  			if err != nil {
   714  				t.Fatal(err)
   715  			}
   716  
   717  			want := count - 1
   718  			if got != want {
   719  				t.Errorf("got %v items count, want %v", got, want)
   720  			}
   721  		})
   722  	})
   723  
   724  	// delete some items
   725  	t.Run("delete items", func(t *testing.T) {
   726  		deleteCount := 3
   727  
   728  		for _, item := range items[:deleteCount] {
   729  			err := index.Delete(item)
   730  			if err != nil {
   731  				t.Fatal(err)
   732  			}
   733  		}
   734  
   735  		count := len(items) + 1 - deleteCount
   736  
   737  		t.Run("Count", func(t *testing.T) {
   738  			got, err := index.Count()
   739  			if err != nil {
   740  				t.Fatal(err)
   741  			}
   742  
   743  			want := count
   744  			if got != want {
   745  				t.Errorf("got %v items count, want %v", got, want)
   746  			}
   747  		})
   748  
   749  		t.Run("CountFrom", func(t *testing.T) {
   750  			got, err := index.CountFrom(Item{
   751  				Address: items[deleteCount+1].Address,
   752  			})
   753  			if err != nil {
   754  				t.Fatal(err)
   755  			}
   756  
   757  			want := count - 1
   758  			if got != want {
   759  				t.Errorf("got %v items count, want %v", got, want)
   760  			}
   761  		})
   762  	})
   763  }
   764  
   765  // checkItem is a test helper function that compares if two Index items are the same.
   766  func checkItem(t *testing.T, got, want Item) {
   767  	t.Helper()
   768  
   769  	if !bytes.Equal(got.Address, want.Address) {
   770  		t.Errorf("got hash %q, expected %q", string(got.Address), string(want.Address))
   771  	}
   772  	if !bytes.Equal(got.Data, want.Data) {
   773  		t.Errorf("got data %q, expected %q", string(got.Data), string(want.Data))
   774  	}
   775  	if got.StoreTimestamp != want.StoreTimestamp {
   776  		t.Errorf("got store timestamp %v, expected %v", got.StoreTimestamp, want.StoreTimestamp)
   777  	}
   778  	if got.AccessTimestamp != want.AccessTimestamp {
   779  		t.Errorf("got access timestamp %v, expected %v", got.AccessTimestamp, want.AccessTimestamp)
   780  	}
   781  }