cosmos-sdk/server/v2/stf/branch/mergeiter_test.go
testinginprod 99b4858a95
feat(stf/branch): simplify merged iterator (#22131)
Co-authored-by: marbar3778 <marbar3778@yahoo.com>
2024-11-07 14:50:26 +00:00

177 lines
4.7 KiB
Go

package branch
import (
"reflect"
"testing"
corestore "cosmossdk.io/core/store"
)
func TestMergedIterator_Validity(t *testing.T) {
panics := func(f func()) {
defer func() {
r := recover()
if r == nil {
t.Error("panic expected")
}
}()
f()
}
t.Run("panics when calling key on invalid iter", func(t *testing.T) {
parent, err := newMemState().Iterator(nil, nil)
if err != nil {
t.Fatal(err)
}
cache, err := newMemState().Iterator(nil, nil)
if err != nil {
t.Fatal(err)
}
it := mergeIterators(parent, cache, true)
panics(func() {
it.Key()
})
})
t.Run("panics when calling value on invalid iter", func(t *testing.T) {
parent, err := newMemState().Iterator(nil, nil)
if err != nil {
t.Fatal(err)
}
cache, err := newMemState().Iterator(nil, nil)
if err != nil {
t.Fatal(err)
}
it := mergeIterators(parent, cache, true)
panics(func() {
it.Value()
})
})
}
func TestMergedIterator_Next(t *testing.T) {
specs := map[string]struct {
setup func() corestore.Iterator
exp [][2]string
}{
"both iterators are empty": {
setup: func() corestore.Iterator {
parent := newMemState()
cache := newMemState()
return mergeIterators(must(parent.Iterator(nil, nil)), must(cache.Iterator(nil, nil)), true)
},
},
"parent iterator has one item, child is empty": {
setup: func() corestore.Iterator {
parent := newMemState()
if err := parent.Set([]byte("k1"), []byte("1")); err != nil {
t.Fatal(err)
}
cache := newMemState()
return mergeIterators(must(parent.Iterator(nil, nil)), must(cache.Iterator(nil, nil)), true)
},
exp: [][2]string{{"k1", "1"}},
},
"child has one item, parent is empty": {
setup: func() corestore.Iterator {
parent := newMemState()
cache := newMemState()
if err := cache.Set([]byte("k1"), []byte("1")); err != nil {
t.Fatal(err)
}
return mergeIterators(must(parent.Iterator(nil, nil)), must(cache.Iterator(nil, nil)), true)
},
exp: [][2]string{{"k1", "1"}},
},
"both iterators have same key, child preferred": {
setup: func() corestore.Iterator {
parent := newMemState()
if err := parent.Set([]byte("k1"), []byte("parent-val")); err != nil {
t.Fatal(err)
}
cache := newMemState()
if err := cache.Set([]byte("k1"), []byte("child-val")); err != nil {
t.Fatal(err)
}
return mergeIterators(must(parent.Iterator(nil, nil)), must(cache.Iterator(nil, nil)), true)
},
exp: [][2]string{{"k1", "child-val"}},
},
"both iterators have same key, but child value is nil": {
setup: func() corestore.Iterator {
parent := newMemState()
if err := parent.Set([]byte("k1"), []byte("1")); err != nil {
t.Fatal(err)
}
cache := newMemState()
if err := cache.Set([]byte("k1"), nil); err != nil {
t.Fatal(err)
}
return mergeIterators(must(parent.Iterator(nil, nil)), must(cache.Iterator(nil, nil)), true)
},
},
"parent and child are ascending": {
setup: func() corestore.Iterator {
parent := newMemState()
if err := parent.Set([]byte("k2"), []byte("v2")); err != nil {
t.Fatal(err)
}
if err := parent.Set([]byte("k3"), []byte("v3")); err != nil {
t.Fatal(err)
}
cache := newMemState()
if err := cache.Set([]byte("k1"), []byte("v1")); err != nil {
t.Fatal(err)
}
if err := cache.Set([]byte("k4"), []byte("v4")); err != nil {
t.Fatal(err)
}
return mergeIterators(must(parent.Iterator(nil, nil)), must(cache.Iterator(nil, nil)), true)
},
exp: [][2]string{{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}, {"k4", "v4"}},
},
"parent and child are descending": {
setup: func() corestore.Iterator {
parent := newMemState()
if err := parent.Set([]byte("k3"), []byte("v3")); err != nil {
t.Fatal(err)
}
if err := parent.Set([]byte("k2"), []byte("v2")); err != nil {
t.Fatal(err)
}
cache := newMemState()
if err := cache.Set([]byte("k4"), []byte("v4")); err != nil {
t.Fatal(err)
}
if err := cache.Set([]byte("k1"), []byte("v1")); err != nil {
t.Fatal(err)
}
return mergeIterators(must(parent.ReverseIterator(nil, nil)), must(cache.ReverseIterator(nil, nil)), false)
},
exp: [][2]string{{"k4", "v4"}, {"k3", "v3"}, {"k2", "v2"}, {"k1", "v1"}},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
var got [][2]string
for iter := spec.setup(); iter.Valid(); iter.Next() {
got = append(got, [2]string{string(iter.Key()), string(iter.Value())})
}
if !reflect.DeepEqual(spec.exp, got) {
t.Errorf("expected: %#v, got: %#v", spec.exp, got)
}
})
}
}
func must[T any](r T, err error) T {
if err != nil {
panic(err)
}
return r
}