github.com/insight-chain/inb-go@v1.1.3-0.20191221022159-da049980ae38/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 IndexItem) (key []byte, err error) {
    33  		return fields.Address, nil
    34  	},
    35  	DecodeKey: func(key []byte) (e IndexItem, err error) {
    36  		e.Address = key
    37  		return e, nil
    38  	},
    39  	EncodeValue: func(fields IndexItem) (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(value []byte) (e IndexItem, 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 := IndexItem{
    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(IndexItem{
    74  			Address: want.Address,
    75  		})
    76  		if err != nil {
    77  			t.Fatal(err)
    78  		}
    79  		checkIndexItem(t, got, want)
    80  
    81  		t.Run("overwrite", func(t *testing.T) {
    82  			want := IndexItem{
    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(IndexItem{
    93  				Address: want.Address,
    94  			})
    95  			if err != nil {
    96  				t.Fatal(err)
    97  			}
    98  			checkIndexItem(t, got, want)
    99  		})
   100  	})
   101  
   102  	t.Run("put in batch", func(t *testing.T) {
   103  		want := IndexItem{
   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(IndexItem{
   116  			Address: want.Address,
   117  		})
   118  		if err != nil {
   119  			t.Fatal(err)
   120  		}
   121  		checkIndexItem(t, got, want)
   122  
   123  		t.Run("overwrite", func(t *testing.T) {
   124  			want := IndexItem{
   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(IndexItem{
   137  				Address: want.Address,
   138  			})
   139  			if err != nil {
   140  				t.Fatal(err)
   141  			}
   142  			checkIndexItem(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, IndexItem{
   154  			Address:        address,
   155  			Data:           []byte("DATA"),
   156  			StoreTimestamp: time.Now().UTC().UnixNano(),
   157  		})
   158  
   159  		want := IndexItem{
   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(IndexItem{
   172  			Address: address,
   173  		})
   174  		if err != nil {
   175  			t.Fatal(err)
   176  		}
   177  		checkIndexItem(t, got, want)
   178  	})
   179  
   180  	t.Run("delete", func(t *testing.T) {
   181  		want := IndexItem{
   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(IndexItem{
   192  			Address: want.Address,
   193  		})
   194  		if err != nil {
   195  			t.Fatal(err)
   196  		}
   197  		checkIndexItem(t, got, want)
   198  
   199  		err = index.Delete(IndexItem{
   200  			Address: want.Address,
   201  		})
   202  		if err != nil {
   203  			t.Fatal(err)
   204  		}
   205  
   206  		wantErr := leveldb.ErrNotFound
   207  		got, err = index.Get(IndexItem{
   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 := IndexItem{
   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(IndexItem{
   227  			Address: want.Address,
   228  		})
   229  		if err != nil {
   230  			t.Fatal(err)
   231  		}
   232  		checkIndexItem(t, got, want)
   233  
   234  		batch := new(leveldb.Batch)
   235  		index.DeleteInBatch(batch, IndexItem{
   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(IndexItem{
   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 iterator functions for correctness.
   254  func TestIndex_iterate(t *testing.T) {
   255  	db, cleanupFunc := newTestDB(t)
   256  	defer cleanupFunc()
   257  
   258  	index, err := db.NewIndex("retrieval", retrievalIndexFuncs)
   259  	if err != nil {
   260  		t.Fatal(err)
   261  	}
   262  
   263  	items := []IndexItem{
   264  		{
   265  			Address: []byte("iterate-hash-01"),
   266  			Data:    []byte("data80"),
   267  		},
   268  		{
   269  			Address: []byte("iterate-hash-03"),
   270  			Data:    []byte("data22"),
   271  		},
   272  		{
   273  			Address: []byte("iterate-hash-05"),
   274  			Data:    []byte("data41"),
   275  		},
   276  		{
   277  			Address: []byte("iterate-hash-02"),
   278  			Data:    []byte("data84"),
   279  		},
   280  		{
   281  			Address: []byte("iterate-hash-06"),
   282  			Data:    []byte("data1"),
   283  		},
   284  	}
   285  	batch := new(leveldb.Batch)
   286  	for _, i := range items {
   287  		index.PutInBatch(batch, i)
   288  	}
   289  	err = db.WriteBatch(batch)
   290  	if err != nil {
   291  		t.Fatal(err)
   292  	}
   293  	item04 := IndexItem{
   294  		Address: []byte("iterate-hash-04"),
   295  		Data:    []byte("data0"),
   296  	}
   297  	err = index.Put(item04)
   298  	if err != nil {
   299  		t.Fatal(err)
   300  	}
   301  	items = append(items, item04)
   302  
   303  	sort.SliceStable(items, func(i, j int) bool {
   304  		return bytes.Compare(items[i].Address, items[j].Address) < 0
   305  	})
   306  
   307  	t.Run("all", func(t *testing.T) {
   308  		var i int
   309  		err := index.IterateAll(func(item IndexItem) (stop bool, err error) {
   310  			if i > len(items)-1 {
   311  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   312  			}
   313  			want := items[i]
   314  			checkIndexItem(t, item, want)
   315  			i++
   316  			return false, nil
   317  		})
   318  		if err != nil {
   319  			t.Fatal(err)
   320  		}
   321  	})
   322  
   323  	t.Run("from", func(t *testing.T) {
   324  		startIndex := 2
   325  		i := startIndex
   326  		err := index.IterateFrom(items[startIndex], func(item IndexItem) (stop bool, err error) {
   327  			if i > len(items)-1 {
   328  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   329  			}
   330  			want := items[i]
   331  			checkIndexItem(t, item, want)
   332  			i++
   333  			return false, nil
   334  		})
   335  		if err != nil {
   336  			t.Fatal(err)
   337  		}
   338  	})
   339  
   340  	t.Run("stop", func(t *testing.T) {
   341  		var i int
   342  		stopIndex := 3
   343  		var count int
   344  		err := index.IterateAll(func(item IndexItem) (stop bool, err error) {
   345  			if i > len(items)-1 {
   346  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   347  			}
   348  			want := items[i]
   349  			checkIndexItem(t, item, want)
   350  			count++
   351  			if i == stopIndex {
   352  				return true, nil
   353  			}
   354  			i++
   355  			return false, nil
   356  		})
   357  		if err != nil {
   358  			t.Fatal(err)
   359  		}
   360  		wantItemsCount := stopIndex + 1
   361  		if count != wantItemsCount {
   362  			t.Errorf("got %v items, expected %v", count, wantItemsCount)
   363  		}
   364  	})
   365  
   366  	t.Run("no overflow", func(t *testing.T) {
   367  		secondIndex, err := db.NewIndex("second-index", retrievalIndexFuncs)
   368  		if err != nil {
   369  			t.Fatal(err)
   370  		}
   371  
   372  		secondIndexItem := IndexItem{
   373  			Address: []byte("iterate-hash-10"),
   374  			Data:    []byte("data-second"),
   375  		}
   376  		err = secondIndex.Put(secondIndexItem)
   377  		if err != nil {
   378  			t.Fatal(err)
   379  		}
   380  
   381  		var i int
   382  		err = index.IterateAll(func(item IndexItem) (stop bool, err error) {
   383  			if i > len(items)-1 {
   384  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   385  			}
   386  			want := items[i]
   387  			checkIndexItem(t, item, want)
   388  			i++
   389  			return false, nil
   390  		})
   391  		if err != nil {
   392  			t.Fatal(err)
   393  		}
   394  
   395  		i = 0
   396  		err = secondIndex.IterateAll(func(item IndexItem) (stop bool, err error) {
   397  			if i > 1 {
   398  				return true, fmt.Errorf("got unexpected index item: %#v", item)
   399  			}
   400  			checkIndexItem(t, item, secondIndexItem)
   401  			i++
   402  			return false, nil
   403  		})
   404  		if err != nil {
   405  			t.Fatal(err)
   406  		}
   407  	})
   408  }
   409  
   410  // checkIndexItem is a test helper function that compares if two Index items are the same.
   411  func checkIndexItem(t *testing.T, got, want IndexItem) {
   412  	t.Helper()
   413  
   414  	if !bytes.Equal(got.Address, want.Address) {
   415  		t.Errorf("got hash %q, expected %q", string(got.Address), string(want.Address))
   416  	}
   417  	if !bytes.Equal(got.Data, want.Data) {
   418  		t.Errorf("got data %q, expected %q", string(got.Data), string(want.Data))
   419  	}
   420  	if got.StoreTimestamp != want.StoreTimestamp {
   421  		t.Errorf("got store timestamp %v, expected %v", got.StoreTimestamp, want.StoreTimestamp)
   422  	}
   423  	if got.AccessTimestamp != want.AccessTimestamp {
   424  		t.Errorf("got access timestamp %v, expected %v", got.AccessTimestamp, want.AccessTimestamp)
   425  	}
   426  }