202 lines
5.2 KiB
Go
202 lines
5.2 KiB
Go
package collections
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestIteratorBasic(t *testing.T) {
|
|
sk, ctx := deps()
|
|
// safety check to ensure that iteration does not cross prefix boundaries
|
|
err := sk.OpenKVStore(ctx).Set([]byte{0, 0}, []byte("before prefix"))
|
|
require.NoError(t, err)
|
|
err = sk.OpenKVStore(ctx).Set([]byte{2, 1}, []byte("after prefix"))
|
|
require.NoError(t, err)
|
|
schemaBuilder := NewSchemaBuilder(sk)
|
|
m := NewMap(schemaBuilder, NewPrefix(1), "m", StringKey, Uint64Value)
|
|
_, err = schemaBuilder.Build()
|
|
require.NoError(t, err)
|
|
|
|
for i := uint64(1); i <= 2; i++ {
|
|
require.NoError(t, m.Set(ctx, fmt.Sprintf("%d", i), i))
|
|
}
|
|
|
|
iter, err := m.Iterate(ctx, nil)
|
|
require.NoError(t, err)
|
|
defer iter.Close()
|
|
|
|
// key codec
|
|
key, err := iter.Key()
|
|
require.NoError(t, err)
|
|
require.Equal(t, "1", key)
|
|
|
|
// value codec
|
|
value, err := iter.Value()
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint64(1), value)
|
|
|
|
// assert expected prefixing on iter
|
|
require.Equal(t, m.prefix, iter.iter.Key()[:len(m.prefix)])
|
|
|
|
// advance iter
|
|
iter.Next()
|
|
require.True(t, iter.Valid())
|
|
|
|
// key 2
|
|
key, err = iter.Key()
|
|
require.NoError(t, err)
|
|
require.Equal(t, "2", key)
|
|
|
|
// value 2
|
|
value, err = iter.Value()
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint64(2), value)
|
|
|
|
// call next, invalid
|
|
iter.Next()
|
|
require.False(t, iter.Valid())
|
|
// close no errors
|
|
require.NoError(t, iter.Close())
|
|
}
|
|
|
|
func TestIteratorKeyValues(t *testing.T) {
|
|
sk, ctx := deps()
|
|
schemaBuilder := NewSchemaBuilder(sk)
|
|
m := NewMap(schemaBuilder, NewPrefix("some super amazing prefix"), "m", StringKey, Uint64Value)
|
|
_, err := schemaBuilder.Build()
|
|
require.NoError(t, err)
|
|
|
|
for i := uint64(0); i <= 5; i++ {
|
|
require.NoError(t, m.Set(ctx, fmt.Sprintf("%d", i), i))
|
|
}
|
|
|
|
// test keys
|
|
iter, err := m.Iterate(ctx, nil)
|
|
require.NoError(t, err)
|
|
keys, err := iter.Keys()
|
|
require.NoError(t, err)
|
|
|
|
for i, key := range keys {
|
|
require.Equal(t, fmt.Sprintf("%d", i), key)
|
|
}
|
|
require.NoError(t, iter.Close())
|
|
require.False(t, iter.Valid())
|
|
|
|
// test values
|
|
iter, err = m.Iterate(ctx, nil)
|
|
require.NoError(t, err)
|
|
values, err := iter.Values()
|
|
require.NoError(t, err)
|
|
|
|
for i, value := range values {
|
|
require.Equal(t, uint64(i), value)
|
|
}
|
|
require.NoError(t, iter.Close())
|
|
require.False(t, iter.Valid())
|
|
|
|
// test key value pairings
|
|
iter, err = m.Iterate(ctx, nil)
|
|
require.NoError(t, err)
|
|
kvs, err := iter.KeyValues()
|
|
require.NoError(t, err)
|
|
|
|
for i, kv := range kvs {
|
|
require.Equal(t, fmt.Sprintf("%d", i), kv.Key)
|
|
require.Equal(t, uint64(i), kv.Value)
|
|
}
|
|
require.NoError(t, iter.Close())
|
|
require.False(t, iter.Valid())
|
|
}
|
|
|
|
func TestIteratorPrefixing(t *testing.T) {
|
|
sk, ctx := deps()
|
|
schemaBuilder := NewSchemaBuilder(sk)
|
|
m := NewMap(schemaBuilder, NewPrefix("cool"), "cool", StringKey, Uint64Value)
|
|
_, err := schemaBuilder.Build()
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, m.Set(ctx, "A1", 11))
|
|
require.NoError(t, m.Set(ctx, "A2", 12))
|
|
require.NoError(t, m.Set(ctx, "B1", 21))
|
|
|
|
iter, err := m.Iterate(ctx, new(Range[string]).Prefix("A"))
|
|
require.NoError(t, err)
|
|
keys, err := iter.Keys()
|
|
require.NoError(t, err)
|
|
require.Equal(t, []string{"A1", "A2"}, keys)
|
|
}
|
|
|
|
func TestIteratorRanging(t *testing.T) {
|
|
sk, ctx := deps()
|
|
schemaBuilder := NewSchemaBuilder(sk)
|
|
m := NewMap(schemaBuilder, NewPrefix("cool"), "cool", Uint64Key, Uint64Value)
|
|
_, err := schemaBuilder.Build()
|
|
require.NoError(t, err)
|
|
|
|
for i := uint64(0); i <= 7; i++ {
|
|
require.NoError(t, m.Set(ctx, i, i))
|
|
}
|
|
|
|
// let's range (1-5]; expected: 2..5
|
|
iter, err := m.Iterate(ctx, (&Range[uint64]{}).StartExclusive(1).EndInclusive(5))
|
|
require.NoError(t, err)
|
|
result, err := iter.Keys()
|
|
require.NoError(t, err)
|
|
require.Equal(t, []uint64{2, 3, 4, 5}, result)
|
|
|
|
// let's range [1-5); expected 1..4
|
|
iter, err = m.Iterate(ctx, (&Range[uint64]{}).StartInclusive(1).EndExclusive(5))
|
|
require.NoError(t, err)
|
|
result, err = iter.Keys()
|
|
require.NoError(t, err)
|
|
require.Equal(t, []uint64{1, 2, 3, 4}, result)
|
|
|
|
// let's range [1-5) descending; expected 4..1
|
|
iter, err = m.Iterate(ctx, (&Range[uint64]{}).StartInclusive(1).EndExclusive(5).Descending())
|
|
require.NoError(t, err)
|
|
result, err = iter.Keys()
|
|
require.NoError(t, err)
|
|
require.Equal(t, []uint64{4, 3, 2, 1}, result)
|
|
|
|
// test iterator invalid
|
|
_, err = m.Iterate(ctx, new(Range[uint64]).StartInclusive(10).EndInclusive(1))
|
|
require.ErrorIs(t, err, ErrInvalidIterator)
|
|
}
|
|
|
|
func TestWalk(t *testing.T) {
|
|
sk, ctx := deps()
|
|
schemaBuilder := NewSchemaBuilder(sk)
|
|
m := NewMap(schemaBuilder, NewPrefix("cool"), "cool", Uint64Key, Uint64Value)
|
|
_, err := schemaBuilder.Build()
|
|
require.NoError(t, err)
|
|
|
|
for i := uint64(0); i <= 7; i++ {
|
|
require.NoError(t, m.Set(ctx, i, i))
|
|
}
|
|
|
|
u := uint64(0)
|
|
err = m.Walk(ctx, nil, func(key, value uint64) (bool, error) {
|
|
if key == 5 {
|
|
return true, nil
|
|
}
|
|
require.Equal(t, u, key)
|
|
require.Equal(t, u, value)
|
|
u++
|
|
return false, nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
sentinelErr := errors.New("sentinel error")
|
|
err = m.Walk(ctx, nil, func(key, value uint64) (stop bool, err error) {
|
|
require.LessOrEqual(t, key, uint64(3)) // asserts that after the number three we stop
|
|
if key == 3 {
|
|
return false, sentinelErr
|
|
}
|
|
return false, nil
|
|
})
|
|
require.ErrorIs(t, err, sentinelErr) // asserts correct error propagation
|
|
}
|