|
|
@ -29,20 +29,20 @@ import ( |
|
|
|
|
|
|
|
|
|
|
|
// Index functions for the index that is used in tests in this file.
|
|
|
|
// Index functions for the index that is used in tests in this file.
|
|
|
|
var retrievalIndexFuncs = IndexFuncs{ |
|
|
|
var retrievalIndexFuncs = IndexFuncs{ |
|
|
|
EncodeKey: func(fields IndexItem) (key []byte, err error) { |
|
|
|
EncodeKey: func(fields Item) (key []byte, err error) { |
|
|
|
return fields.Address, nil |
|
|
|
return fields.Address, nil |
|
|
|
}, |
|
|
|
}, |
|
|
|
DecodeKey: func(key []byte) (e IndexItem, err error) { |
|
|
|
DecodeKey: func(key []byte) (e Item, err error) { |
|
|
|
e.Address = key |
|
|
|
e.Address = key |
|
|
|
return e, nil |
|
|
|
return e, nil |
|
|
|
}, |
|
|
|
}, |
|
|
|
EncodeValue: func(fields IndexItem) (value []byte, err error) { |
|
|
|
EncodeValue: func(fields Item) (value []byte, err error) { |
|
|
|
b := make([]byte, 8) |
|
|
|
b := make([]byte, 8) |
|
|
|
binary.BigEndian.PutUint64(b, uint64(fields.StoreTimestamp)) |
|
|
|
binary.BigEndian.PutUint64(b, uint64(fields.StoreTimestamp)) |
|
|
|
value = append(b, fields.Data...) |
|
|
|
value = append(b, fields.Data...) |
|
|
|
return value, nil |
|
|
|
return value, nil |
|
|
|
}, |
|
|
|
}, |
|
|
|
DecodeValue: func(value []byte) (e IndexItem, err error) { |
|
|
|
DecodeValue: func(keyItem Item, value []byte) (e Item, err error) { |
|
|
|
e.StoreTimestamp = int64(binary.BigEndian.Uint64(value[:8])) |
|
|
|
e.StoreTimestamp = int64(binary.BigEndian.Uint64(value[:8])) |
|
|
|
e.Data = value[8:] |
|
|
|
e.Data = value[8:] |
|
|
|
return e, nil |
|
|
|
return e, nil |
|
|
@ -60,7 +60,7 @@ func TestIndex(t *testing.T) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
t.Run("put", func(t *testing.T) { |
|
|
|
t.Run("put", func(t *testing.T) { |
|
|
|
want := IndexItem{ |
|
|
|
want := Item{ |
|
|
|
Address: []byte("put-hash"), |
|
|
|
Address: []byte("put-hash"), |
|
|
|
Data: []byte("DATA"), |
|
|
|
Data: []byte("DATA"), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
@ -70,16 +70,16 @@ func TestIndex(t *testing.T) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
got, err := index.Get(IndexItem{ |
|
|
|
got, err := index.Get(Item{ |
|
|
|
Address: want.Address, |
|
|
|
Address: want.Address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
checkIndexItem(t, got, want) |
|
|
|
checkItem(t, got, want) |
|
|
|
|
|
|
|
|
|
|
|
t.Run("overwrite", func(t *testing.T) { |
|
|
|
t.Run("overwrite", func(t *testing.T) { |
|
|
|
want := IndexItem{ |
|
|
|
want := Item{ |
|
|
|
Address: []byte("put-hash"), |
|
|
|
Address: []byte("put-hash"), |
|
|
|
Data: []byte("New DATA"), |
|
|
|
Data: []byte("New DATA"), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
@ -89,18 +89,18 @@ func TestIndex(t *testing.T) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
got, err := index.Get(IndexItem{ |
|
|
|
got, err := index.Get(Item{ |
|
|
|
Address: want.Address, |
|
|
|
Address: want.Address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
checkIndexItem(t, got, want) |
|
|
|
checkItem(t, got, want) |
|
|
|
}) |
|
|
|
}) |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
t.Run("put in batch", func(t *testing.T) { |
|
|
|
t.Run("put in batch", func(t *testing.T) { |
|
|
|
want := IndexItem{ |
|
|
|
want := Item{ |
|
|
|
Address: []byte("put-in-batch-hash"), |
|
|
|
Address: []byte("put-in-batch-hash"), |
|
|
|
Data: []byte("DATA"), |
|
|
|
Data: []byte("DATA"), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
@ -112,16 +112,16 @@ func TestIndex(t *testing.T) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
got, err := index.Get(IndexItem{ |
|
|
|
got, err := index.Get(Item{ |
|
|
|
Address: want.Address, |
|
|
|
Address: want.Address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
checkIndexItem(t, got, want) |
|
|
|
checkItem(t, got, want) |
|
|
|
|
|
|
|
|
|
|
|
t.Run("overwrite", func(t *testing.T) { |
|
|
|
t.Run("overwrite", func(t *testing.T) { |
|
|
|
want := IndexItem{ |
|
|
|
want := Item{ |
|
|
|
Address: []byte("put-in-batch-hash"), |
|
|
|
Address: []byte("put-in-batch-hash"), |
|
|
|
Data: []byte("New DATA"), |
|
|
|
Data: []byte("New DATA"), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
@ -133,13 +133,13 @@ func TestIndex(t *testing.T) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
got, err := index.Get(IndexItem{ |
|
|
|
got, err := index.Get(Item{ |
|
|
|
Address: want.Address, |
|
|
|
Address: want.Address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
checkIndexItem(t, got, want) |
|
|
|
checkItem(t, got, want) |
|
|
|
}) |
|
|
|
}) |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
@ -150,13 +150,13 @@ func TestIndex(t *testing.T) { |
|
|
|
address := []byte("put-in-batch-twice-hash") |
|
|
|
address := []byte("put-in-batch-twice-hash") |
|
|
|
|
|
|
|
|
|
|
|
// put the first item
|
|
|
|
// put the first item
|
|
|
|
index.PutInBatch(batch, IndexItem{ |
|
|
|
index.PutInBatch(batch, Item{ |
|
|
|
Address: address, |
|
|
|
Address: address, |
|
|
|
Data: []byte("DATA"), |
|
|
|
Data: []byte("DATA"), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
want := IndexItem{ |
|
|
|
want := Item{ |
|
|
|
Address: address, |
|
|
|
Address: address, |
|
|
|
Data: []byte("New DATA"), |
|
|
|
Data: []byte("New DATA"), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
@ -168,17 +168,17 @@ func TestIndex(t *testing.T) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
got, err := index.Get(IndexItem{ |
|
|
|
got, err := index.Get(Item{ |
|
|
|
Address: address, |
|
|
|
Address: address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
checkIndexItem(t, got, want) |
|
|
|
checkItem(t, got, want) |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
t.Run("delete", func(t *testing.T) { |
|
|
|
t.Run("delete", func(t *testing.T) { |
|
|
|
want := IndexItem{ |
|
|
|
want := Item{ |
|
|
|
Address: []byte("delete-hash"), |
|
|
|
Address: []byte("delete-hash"), |
|
|
|
Data: []byte("DATA"), |
|
|
|
Data: []byte("DATA"), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
@ -188,15 +188,15 @@ func TestIndex(t *testing.T) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
got, err := index.Get(IndexItem{ |
|
|
|
got, err := index.Get(Item{ |
|
|
|
Address: want.Address, |
|
|
|
Address: want.Address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
checkIndexItem(t, got, want) |
|
|
|
checkItem(t, got, want) |
|
|
|
|
|
|
|
|
|
|
|
err = index.Delete(IndexItem{ |
|
|
|
err = index.Delete(Item{ |
|
|
|
Address: want.Address, |
|
|
|
Address: want.Address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
@ -204,7 +204,7 @@ func TestIndex(t *testing.T) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
wantErr := leveldb.ErrNotFound |
|
|
|
wantErr := leveldb.ErrNotFound |
|
|
|
got, err = index.Get(IndexItem{ |
|
|
|
got, err = index.Get(Item{ |
|
|
|
Address: want.Address, |
|
|
|
Address: want.Address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != wantErr { |
|
|
|
if err != wantErr { |
|
|
@ -213,7 +213,7 @@ func TestIndex(t *testing.T) { |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
t.Run("delete in batch", func(t *testing.T) { |
|
|
|
t.Run("delete in batch", func(t *testing.T) { |
|
|
|
want := IndexItem{ |
|
|
|
want := Item{ |
|
|
|
Address: []byte("delete-in-batch-hash"), |
|
|
|
Address: []byte("delete-in-batch-hash"), |
|
|
|
Data: []byte("DATA"), |
|
|
|
Data: []byte("DATA"), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
|
StoreTimestamp: time.Now().UTC().UnixNano(), |
|
|
@ -223,16 +223,16 @@ func TestIndex(t *testing.T) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
got, err := index.Get(IndexItem{ |
|
|
|
got, err := index.Get(Item{ |
|
|
|
Address: want.Address, |
|
|
|
Address: want.Address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
checkIndexItem(t, got, want) |
|
|
|
checkItem(t, got, want) |
|
|
|
|
|
|
|
|
|
|
|
batch := new(leveldb.Batch) |
|
|
|
batch := new(leveldb.Batch) |
|
|
|
index.DeleteInBatch(batch, IndexItem{ |
|
|
|
index.DeleteInBatch(batch, Item{ |
|
|
|
Address: want.Address, |
|
|
|
Address: want.Address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
err = db.WriteBatch(batch) |
|
|
|
err = db.WriteBatch(batch) |
|
|
@ -241,7 +241,7 @@ func TestIndex(t *testing.T) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
wantErr := leveldb.ErrNotFound |
|
|
|
wantErr := leveldb.ErrNotFound |
|
|
|
got, err = index.Get(IndexItem{ |
|
|
|
got, err = index.Get(Item{ |
|
|
|
Address: want.Address, |
|
|
|
Address: want.Address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != wantErr { |
|
|
|
if err != wantErr { |
|
|
@ -250,8 +250,9 @@ func TestIndex(t *testing.T) { |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// TestIndex_iterate validates index iterator functions for correctness.
|
|
|
|
// TestIndex_Iterate validates index Iterate
|
|
|
|
func TestIndex_iterate(t *testing.T) { |
|
|
|
// functions for correctness.
|
|
|
|
|
|
|
|
func TestIndex_Iterate(t *testing.T) { |
|
|
|
db, cleanupFunc := newTestDB(t) |
|
|
|
db, cleanupFunc := newTestDB(t) |
|
|
|
defer cleanupFunc() |
|
|
|
defer cleanupFunc() |
|
|
|
|
|
|
|
|
|
|
@ -260,7 +261,7 @@ func TestIndex_iterate(t *testing.T) { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
items := []IndexItem{ |
|
|
|
items := []Item{ |
|
|
|
{ |
|
|
|
{ |
|
|
|
Address: []byte("iterate-hash-01"), |
|
|
|
Address: []byte("iterate-hash-01"), |
|
|
|
Data: []byte("data80"), |
|
|
|
Data: []byte("data80"), |
|
|
@ -290,7 +291,7 @@ func TestIndex_iterate(t *testing.T) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
item04 := IndexItem{ |
|
|
|
item04 := Item{ |
|
|
|
Address: []byte("iterate-hash-04"), |
|
|
|
Address: []byte("iterate-hash-04"), |
|
|
|
Data: []byte("data0"), |
|
|
|
Data: []byte("data0"), |
|
|
|
} |
|
|
|
} |
|
|
@ -306,31 +307,53 @@ func TestIndex_iterate(t *testing.T) { |
|
|
|
|
|
|
|
|
|
|
|
t.Run("all", func(t *testing.T) { |
|
|
|
t.Run("all", func(t *testing.T) { |
|
|
|
var i int |
|
|
|
var i int |
|
|
|
err := index.IterateAll(func(item IndexItem) (stop bool, err error) { |
|
|
|
err := index.Iterate(func(item Item) (stop bool, err error) { |
|
|
|
if i > len(items)-1 { |
|
|
|
if i > len(items)-1 { |
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
} |
|
|
|
} |
|
|
|
want := items[i] |
|
|
|
want := items[i] |
|
|
|
checkIndexItem(t, item, want) |
|
|
|
checkItem(t, item, want) |
|
|
|
i++ |
|
|
|
i++ |
|
|
|
return false, nil |
|
|
|
return false, nil |
|
|
|
}) |
|
|
|
}, nil) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
t.Run("from", func(t *testing.T) { |
|
|
|
t.Run("start from", func(t *testing.T) { |
|
|
|
startIndex := 2 |
|
|
|
startIndex := 2 |
|
|
|
i := startIndex |
|
|
|
i := startIndex |
|
|
|
err := index.IterateFrom(items[startIndex], func(item IndexItem) (stop bool, err error) { |
|
|
|
err := index.Iterate(func(item Item) (stop bool, err error) { |
|
|
|
if i > len(items)-1 { |
|
|
|
if i > len(items)-1 { |
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
} |
|
|
|
} |
|
|
|
want := items[i] |
|
|
|
want := items[i] |
|
|
|
checkIndexItem(t, item, want) |
|
|
|
checkItem(t, item, want) |
|
|
|
i++ |
|
|
|
i++ |
|
|
|
return false, nil |
|
|
|
return false, nil |
|
|
|
|
|
|
|
}, &IterateOptions{ |
|
|
|
|
|
|
|
StartFrom: &items[startIndex], |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("skip start from", func(t *testing.T) { |
|
|
|
|
|
|
|
startIndex := 2 |
|
|
|
|
|
|
|
i := startIndex + 1 |
|
|
|
|
|
|
|
err := index.Iterate(func(item Item) (stop bool, err error) { |
|
|
|
|
|
|
|
if i > len(items)-1 { |
|
|
|
|
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
want := items[i] |
|
|
|
|
|
|
|
checkItem(t, item, want) |
|
|
|
|
|
|
|
i++ |
|
|
|
|
|
|
|
return false, nil |
|
|
|
|
|
|
|
}, &IterateOptions{ |
|
|
|
|
|
|
|
StartFrom: &items[startIndex], |
|
|
|
|
|
|
|
SkipStartFromItem: true, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
@ -341,19 +364,19 @@ func TestIndex_iterate(t *testing.T) { |
|
|
|
var i int |
|
|
|
var i int |
|
|
|
stopIndex := 3 |
|
|
|
stopIndex := 3 |
|
|
|
var count int |
|
|
|
var count int |
|
|
|
err := index.IterateAll(func(item IndexItem) (stop bool, err error) { |
|
|
|
err := index.Iterate(func(item Item) (stop bool, err error) { |
|
|
|
if i > len(items)-1 { |
|
|
|
if i > len(items)-1 { |
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
} |
|
|
|
} |
|
|
|
want := items[i] |
|
|
|
want := items[i] |
|
|
|
checkIndexItem(t, item, want) |
|
|
|
checkItem(t, item, want) |
|
|
|
count++ |
|
|
|
count++ |
|
|
|
if i == stopIndex { |
|
|
|
if i == stopIndex { |
|
|
|
return true, nil |
|
|
|
return true, nil |
|
|
|
} |
|
|
|
} |
|
|
|
i++ |
|
|
|
i++ |
|
|
|
return false, nil |
|
|
|
return false, nil |
|
|
|
}) |
|
|
|
}, nil) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
@ -369,46 +392,378 @@ func TestIndex_iterate(t *testing.T) { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
secondIndexItem := IndexItem{ |
|
|
|
secondItem := Item{ |
|
|
|
Address: []byte("iterate-hash-10"), |
|
|
|
Address: []byte("iterate-hash-10"), |
|
|
|
Data: []byte("data-second"), |
|
|
|
Data: []byte("data-second"), |
|
|
|
} |
|
|
|
} |
|
|
|
err = secondIndex.Put(secondIndexItem) |
|
|
|
err = secondIndex.Put(secondItem) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var i int |
|
|
|
var i int |
|
|
|
err = index.IterateAll(func(item IndexItem) (stop bool, err error) { |
|
|
|
err = index.Iterate(func(item Item) (stop bool, err error) { |
|
|
|
if i > len(items)-1 { |
|
|
|
if i > len(items)-1 { |
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
} |
|
|
|
} |
|
|
|
want := items[i] |
|
|
|
want := items[i] |
|
|
|
checkIndexItem(t, item, want) |
|
|
|
checkItem(t, item, want) |
|
|
|
i++ |
|
|
|
i++ |
|
|
|
return false, nil |
|
|
|
return false, nil |
|
|
|
}) |
|
|
|
}, nil) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
i = 0 |
|
|
|
i = 0 |
|
|
|
err = secondIndex.IterateAll(func(item IndexItem) (stop bool, err error) { |
|
|
|
err = secondIndex.Iterate(func(item Item) (stop bool, err error) { |
|
|
|
if i > 1 { |
|
|
|
if i > 1 { |
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
} |
|
|
|
} |
|
|
|
checkIndexItem(t, item, secondIndexItem) |
|
|
|
checkItem(t, item, secondItem) |
|
|
|
|
|
|
|
i++ |
|
|
|
|
|
|
|
return false, nil |
|
|
|
|
|
|
|
}, nil) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TestIndex_Iterate_withPrefix validates index Iterate
|
|
|
|
|
|
|
|
// function for correctness.
|
|
|
|
|
|
|
|
func TestIndex_Iterate_withPrefix(t *testing.T) { |
|
|
|
|
|
|
|
db, cleanupFunc := newTestDB(t) |
|
|
|
|
|
|
|
defer cleanupFunc() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
index, err := db.NewIndex("retrieval", retrievalIndexFuncs) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
allItems := []Item{ |
|
|
|
|
|
|
|
{Address: []byte("want-hash-00"), Data: []byte("data80")}, |
|
|
|
|
|
|
|
{Address: []byte("skip-hash-01"), Data: []byte("data81")}, |
|
|
|
|
|
|
|
{Address: []byte("skip-hash-02"), Data: []byte("data82")}, |
|
|
|
|
|
|
|
{Address: []byte("skip-hash-03"), Data: []byte("data83")}, |
|
|
|
|
|
|
|
{Address: []byte("want-hash-04"), Data: []byte("data84")}, |
|
|
|
|
|
|
|
{Address: []byte("want-hash-05"), Data: []byte("data85")}, |
|
|
|
|
|
|
|
{Address: []byte("want-hash-06"), Data: []byte("data86")}, |
|
|
|
|
|
|
|
{Address: []byte("want-hash-07"), Data: []byte("data87")}, |
|
|
|
|
|
|
|
{Address: []byte("want-hash-08"), Data: []byte("data88")}, |
|
|
|
|
|
|
|
{Address: []byte("want-hash-09"), Data: []byte("data89")}, |
|
|
|
|
|
|
|
{Address: []byte("skip-hash-10"), Data: []byte("data90")}, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
batch := new(leveldb.Batch) |
|
|
|
|
|
|
|
for _, i := range allItems { |
|
|
|
|
|
|
|
index.PutInBatch(batch, i) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
err = db.WriteBatch(batch) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
prefix := []byte("want") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
items := make([]Item, 0) |
|
|
|
|
|
|
|
for _, item := range allItems { |
|
|
|
|
|
|
|
if bytes.HasPrefix(item.Address, prefix) { |
|
|
|
|
|
|
|
items = append(items, item) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
sort.SliceStable(items, func(i, j int) bool { |
|
|
|
|
|
|
|
return bytes.Compare(items[i].Address, items[j].Address) < 0 |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("with prefix", func(t *testing.T) { |
|
|
|
|
|
|
|
var i int |
|
|
|
|
|
|
|
err := index.Iterate(func(item Item) (stop bool, err error) { |
|
|
|
|
|
|
|
if i > len(items)-1 { |
|
|
|
|
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
want := items[i] |
|
|
|
|
|
|
|
checkItem(t, item, want) |
|
|
|
|
|
|
|
i++ |
|
|
|
|
|
|
|
return false, nil |
|
|
|
|
|
|
|
}, &IterateOptions{ |
|
|
|
|
|
|
|
Prefix: prefix, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if i != len(items) { |
|
|
|
|
|
|
|
t.Errorf("got %v items, want %v", i, len(items)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("with prefix and start from", func(t *testing.T) { |
|
|
|
|
|
|
|
startIndex := 2 |
|
|
|
|
|
|
|
var count int |
|
|
|
|
|
|
|
i := startIndex |
|
|
|
|
|
|
|
err := index.Iterate(func(item Item) (stop bool, err error) { |
|
|
|
|
|
|
|
if i > len(items)-1 { |
|
|
|
|
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
want := items[i] |
|
|
|
|
|
|
|
checkItem(t, item, want) |
|
|
|
|
|
|
|
i++ |
|
|
|
|
|
|
|
count++ |
|
|
|
|
|
|
|
return false, nil |
|
|
|
|
|
|
|
}, &IterateOptions{ |
|
|
|
|
|
|
|
StartFrom: &items[startIndex], |
|
|
|
|
|
|
|
Prefix: prefix, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
wantCount := len(items) - startIndex |
|
|
|
|
|
|
|
if count != wantCount { |
|
|
|
|
|
|
|
t.Errorf("got %v items, want %v", count, wantCount) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("with prefix and skip start from", func(t *testing.T) { |
|
|
|
|
|
|
|
startIndex := 2 |
|
|
|
|
|
|
|
var count int |
|
|
|
|
|
|
|
i := startIndex + 1 |
|
|
|
|
|
|
|
err := index.Iterate(func(item Item) (stop bool, err error) { |
|
|
|
|
|
|
|
if i > len(items)-1 { |
|
|
|
|
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
want := items[i] |
|
|
|
|
|
|
|
checkItem(t, item, want) |
|
|
|
|
|
|
|
i++ |
|
|
|
|
|
|
|
count++ |
|
|
|
|
|
|
|
return false, nil |
|
|
|
|
|
|
|
}, &IterateOptions{ |
|
|
|
|
|
|
|
StartFrom: &items[startIndex], |
|
|
|
|
|
|
|
SkipStartFromItem: true, |
|
|
|
|
|
|
|
Prefix: prefix, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
wantCount := len(items) - startIndex - 1 |
|
|
|
|
|
|
|
if count != wantCount { |
|
|
|
|
|
|
|
t.Errorf("got %v items, want %v", count, wantCount) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("stop", func(t *testing.T) { |
|
|
|
|
|
|
|
var i int |
|
|
|
|
|
|
|
stopIndex := 3 |
|
|
|
|
|
|
|
var count int |
|
|
|
|
|
|
|
err := index.Iterate(func(item Item) (stop bool, err error) { |
|
|
|
|
|
|
|
if i > len(items)-1 { |
|
|
|
|
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
want := items[i] |
|
|
|
|
|
|
|
checkItem(t, item, want) |
|
|
|
|
|
|
|
count++ |
|
|
|
|
|
|
|
if i == stopIndex { |
|
|
|
|
|
|
|
return true, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
i++ |
|
|
|
|
|
|
|
return false, nil |
|
|
|
|
|
|
|
}, &IterateOptions{ |
|
|
|
|
|
|
|
Prefix: prefix, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
wantItemsCount := stopIndex + 1 |
|
|
|
|
|
|
|
if count != wantItemsCount { |
|
|
|
|
|
|
|
t.Errorf("got %v items, expected %v", count, wantItemsCount) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("no overflow", func(t *testing.T) { |
|
|
|
|
|
|
|
secondIndex, err := db.NewIndex("second-index", retrievalIndexFuncs) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
secondItem := Item{ |
|
|
|
|
|
|
|
Address: []byte("iterate-hash-10"), |
|
|
|
|
|
|
|
Data: []byte("data-second"), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
err = secondIndex.Put(secondItem) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var i int |
|
|
|
|
|
|
|
err = index.Iterate(func(item Item) (stop bool, err error) { |
|
|
|
|
|
|
|
if i > len(items)-1 { |
|
|
|
|
|
|
|
return true, fmt.Errorf("got unexpected index item: %#v", item) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
want := items[i] |
|
|
|
|
|
|
|
checkItem(t, item, want) |
|
|
|
i++ |
|
|
|
i++ |
|
|
|
return false, nil |
|
|
|
return false, nil |
|
|
|
|
|
|
|
}, &IterateOptions{ |
|
|
|
|
|
|
|
Prefix: prefix, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if i != len(items) { |
|
|
|
|
|
|
|
t.Errorf("got %v items, want %v", i, len(items)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TestIndex_count tests if Index.Count and Index.CountFrom
|
|
|
|
|
|
|
|
// returns the correct number of items.
|
|
|
|
|
|
|
|
func TestIndex_count(t *testing.T) { |
|
|
|
|
|
|
|
db, cleanupFunc := newTestDB(t) |
|
|
|
|
|
|
|
defer cleanupFunc() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
index, err := db.NewIndex("retrieval", retrievalIndexFuncs) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
items := []Item{ |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Address: []byte("iterate-hash-01"), |
|
|
|
|
|
|
|
Data: []byte("data80"), |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Address: []byte("iterate-hash-02"), |
|
|
|
|
|
|
|
Data: []byte("data84"), |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Address: []byte("iterate-hash-03"), |
|
|
|
|
|
|
|
Data: []byte("data22"), |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Address: []byte("iterate-hash-04"), |
|
|
|
|
|
|
|
Data: []byte("data41"), |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Address: []byte("iterate-hash-05"), |
|
|
|
|
|
|
|
Data: []byte("data1"), |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
batch := new(leveldb.Batch) |
|
|
|
|
|
|
|
for _, i := range items { |
|
|
|
|
|
|
|
index.PutInBatch(batch, i) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
err = db.WriteBatch(batch) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("Count", func(t *testing.T) { |
|
|
|
|
|
|
|
got, err := index.Count() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
want := len(items) |
|
|
|
|
|
|
|
if got != want { |
|
|
|
|
|
|
|
t.Errorf("got %v items count, want %v", got, want) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("CountFrom", func(t *testing.T) { |
|
|
|
|
|
|
|
got, err := index.CountFrom(Item{ |
|
|
|
|
|
|
|
Address: items[1].Address, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
want := len(items) - 1 |
|
|
|
|
|
|
|
if got != want { |
|
|
|
|
|
|
|
t.Errorf("got %v items count, want %v", got, want) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// update the index with another item
|
|
|
|
|
|
|
|
t.Run("add item", func(t *testing.T) { |
|
|
|
|
|
|
|
item04 := Item{ |
|
|
|
|
|
|
|
Address: []byte("iterate-hash-06"), |
|
|
|
|
|
|
|
Data: []byte("data0"), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
err = index.Put(item04) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
count := len(items) + 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("Count", func(t *testing.T) { |
|
|
|
|
|
|
|
got, err := index.Count() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
want := count |
|
|
|
|
|
|
|
if got != want { |
|
|
|
|
|
|
|
t.Errorf("got %v items count, want %v", got, want) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("CountFrom", func(t *testing.T) { |
|
|
|
|
|
|
|
got, err := index.CountFrom(Item{ |
|
|
|
|
|
|
|
Address: items[1].Address, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
want := count - 1 |
|
|
|
|
|
|
|
if got != want { |
|
|
|
|
|
|
|
t.Errorf("got %v items count, want %v", got, want) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// delete some items
|
|
|
|
|
|
|
|
t.Run("delete items", func(t *testing.T) { |
|
|
|
|
|
|
|
deleteCount := 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _, item := range items[:deleteCount] { |
|
|
|
|
|
|
|
err := index.Delete(item) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
count := len(items) + 1 - deleteCount |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("Count", func(t *testing.T) { |
|
|
|
|
|
|
|
got, err := index.Count() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
want := count |
|
|
|
|
|
|
|
if got != want { |
|
|
|
|
|
|
|
t.Errorf("got %v items count, want %v", got, want) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("CountFrom", func(t *testing.T) { |
|
|
|
|
|
|
|
got, err := index.CountFrom(Item{ |
|
|
|
|
|
|
|
Address: items[deleteCount+1].Address, |
|
|
|
}) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
t.Fatal(err) |
|
|
|
t.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
want := count - 1 |
|
|
|
|
|
|
|
if got != want { |
|
|
|
|
|
|
|
t.Errorf("got %v items count, want %v", got, want) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// checkIndexItem is a test helper function that compares if two Index items are the same.
|
|
|
|
// checkItem is a test helper function that compares if two Index items are the same.
|
|
|
|
func checkIndexItem(t *testing.T, got, want IndexItem) { |
|
|
|
func checkItem(t *testing.T, got, want Item) { |
|
|
|
t.Helper() |
|
|
|
t.Helper() |
|
|
|
|
|
|
|
|
|
|
|
if !bytes.Equal(got.Address, want.Address) { |
|
|
|
if !bytes.Equal(got.Address, want.Address) { |
|
|
|