github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/storage/storeandbatch_test.go (about) 1 package storage 2 3 import ( 4 "bytes" 5 "reflect" 6 "runtime" 7 "sort" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 ) 13 14 type dbSetup struct { 15 name string 16 create func(testing.TB) Store 17 } 18 19 type dbTestFunction func(*testing.T, Store) 20 21 func testStoreGetNonExistent(t *testing.T, s Store) { 22 key := []byte("sparse") 23 24 _, err := s.Get(key) 25 assert.Equal(t, err, ErrKeyNotFound) 26 } 27 28 func pushSeekDataSet(t *testing.T, s Store) []KeyValue { 29 // Use the same set of kvs to test Seek with different prefix/start values. 30 kvs := []KeyValue{ 31 {[]byte("10"), []byte("bar")}, 32 {[]byte("11"), []byte("bara")}, 33 {[]byte("20"), []byte("barb")}, 34 {[]byte("21"), []byte("barc")}, 35 {[]byte("22"), []byte("bard")}, 36 {[]byte("30"), []byte("bare")}, 37 {[]byte("31"), []byte("barf")}, 38 } 39 up := NewMemCachedStore(s) 40 for _, v := range kvs { 41 up.Put(v.Key, v.Value) 42 } 43 _, err := up.PersistSync() 44 require.NoError(t, err) 45 return kvs 46 } 47 48 func testStoreSeek(t *testing.T, s Store) { 49 kvs := pushSeekDataSet(t, s) 50 check := func(t *testing.T, goodprefix, start []byte, goodkvs []KeyValue, backwards bool, cont func(k, v []byte) bool) { 51 // Seek result expected to be sorted in an ascending (for forwards seeking) or descending (for backwards seeking) way. 52 cmpFunc := func(i, j int) bool { 53 return bytes.Compare(goodkvs[i].Key, goodkvs[j].Key) < 0 54 } 55 if backwards { 56 cmpFunc = func(i, j int) bool { 57 return bytes.Compare(goodkvs[i].Key, goodkvs[j].Key) > 0 58 } 59 } 60 sort.Slice(goodkvs, cmpFunc) 61 62 rng := SeekRange{ 63 Prefix: goodprefix, 64 Start: start, 65 } 66 if backwards { 67 rng.Backwards = true 68 } 69 actual := make([]KeyValue, 0, len(goodkvs)) 70 s.Seek(rng, func(k, v []byte) bool { 71 actual = append(actual, KeyValue{ 72 Key: bytes.Clone(k), 73 Value: bytes.Clone(v), 74 }) 75 if cont == nil { 76 return true 77 } 78 return cont(k, v) 79 }) 80 assert.Equal(t, goodkvs, actual) 81 } 82 83 t.Run("non-empty prefix, empty start", func(t *testing.T) { 84 t.Run("forwards", func(t *testing.T) { 85 t.Run("good", func(t *testing.T) { 86 // Given this prefix... 87 goodprefix := []byte("2") 88 // and empty start range... 89 start := []byte{} 90 // these pairs should be found. 91 goodkvs := []KeyValue{ 92 kvs[2], // key = "20" 93 kvs[3], // key = "21" 94 kvs[4], // key = "22" 95 } 96 check(t, goodprefix, start, goodkvs, false, nil) 97 }) 98 t.Run("no matching items", func(t *testing.T) { 99 goodprefix := []byte("0") 100 start := []byte{} 101 check(t, goodprefix, start, []KeyValue{}, false, nil) 102 }) 103 t.Run("early stop", func(t *testing.T) { 104 // Given this prefix... 105 goodprefix := []byte("2") 106 // and empty start range... 107 start := []byte{} 108 // these pairs should be found. 109 goodkvs := []KeyValue{ 110 kvs[2], // key = "20" 111 kvs[3], // key = "21" 112 } 113 check(t, goodprefix, start, goodkvs, false, func(k, v []byte) bool { 114 return string(k) < "21" 115 }) 116 }) 117 }) 118 119 t.Run("backwards", func(t *testing.T) { 120 t.Run("good", func(t *testing.T) { 121 goodprefix := []byte("2") 122 start := []byte{} 123 goodkvs := []KeyValue{ 124 kvs[4], // key = "22" 125 kvs[3], // key = "21" 126 kvs[2], // key = "20" 127 } 128 check(t, goodprefix, start, goodkvs, true, nil) 129 }) 130 t.Run("no matching items", func(t *testing.T) { 131 goodprefix := []byte("0") 132 start := []byte{} 133 check(t, goodprefix, start, []KeyValue{}, true, nil) 134 }) 135 t.Run("early stop", func(t *testing.T) { 136 goodprefix := []byte("2") 137 start := []byte{} 138 goodkvs := []KeyValue{ 139 kvs[4], // key = "22" 140 kvs[3], // key = "21" 141 } 142 check(t, goodprefix, start, goodkvs, true, func(k, v []byte) bool { 143 return string(k) > "21" 144 }) 145 }) 146 }) 147 }) 148 149 t.Run("non-empty prefix, non-empty start", func(t *testing.T) { 150 t.Run("forwards", func(t *testing.T) { 151 t.Run("good", func(t *testing.T) { 152 goodprefix := []byte("2") 153 start := []byte("1") // start will be appended to goodprefix to start seek from 154 goodkvs := []KeyValue{ 155 kvs[3], // key = "21" 156 kvs[4], // key = "22" 157 } 158 check(t, goodprefix, start, goodkvs, false, nil) 159 }) 160 t.Run("no matching items", func(t *testing.T) { 161 goodprefix := []byte("2") 162 start := []byte("3") // start is more than all keys prefixed by '2'. 163 check(t, goodprefix, start, []KeyValue{}, false, nil) 164 }) 165 t.Run("early stop", func(t *testing.T) { 166 goodprefix := []byte("2") 167 start := []byte("0") // start will be appended to goodprefix to start seek from 168 goodkvs := []KeyValue{ 169 kvs[2], // key = "20" 170 kvs[3], // key = "21" 171 } 172 check(t, goodprefix, start, goodkvs, false, func(k, v []byte) bool { 173 return string(k) < "21" 174 }) 175 }) 176 }) 177 t.Run("backwards", func(t *testing.T) { 178 t.Run("good", func(t *testing.T) { 179 goodprefix := []byte("2") 180 start := []byte("1") // start will be appended to goodprefix to start seek from 181 goodkvs := []KeyValue{ 182 kvs[3], // key = "21" 183 kvs[2], // key = "20" 184 } 185 check(t, goodprefix, start, goodkvs, true, nil) 186 }) 187 t.Run("no matching items", func(t *testing.T) { 188 goodprefix := []byte("2") 189 start := []byte(".") // start is less than all keys prefixed by '2'. 190 check(t, goodprefix, start, []KeyValue{}, true, nil) 191 }) 192 t.Run("early stop", func(t *testing.T) { 193 goodprefix := []byte("2") 194 start := []byte("2") // start will be appended to goodprefix to start seek from 195 goodkvs := []KeyValue{ 196 kvs[4], // key = "24" 197 kvs[3], // key = "21" 198 } 199 check(t, goodprefix, start, goodkvs, true, func(k, v []byte) bool { 200 return string(k) > "21" 201 }) 202 }) 203 }) 204 }) 205 } 206 207 func testStoreSeekGC(t *testing.T, s Store) { 208 kvs := pushSeekDataSet(t, s) 209 err := s.SeekGC(SeekRange{Prefix: []byte("1")}, func(k, v []byte) bool { 210 return true 211 }) 212 require.NoError(t, err) 213 for i := range kvs { 214 _, err = s.Get(kvs[i].Key) 215 require.NoError(t, err) 216 } 217 err = s.SeekGC(SeekRange{Prefix: []byte("3")}, func(k, v []byte) bool { 218 return false 219 }) 220 require.NoError(t, err) 221 for i := range kvs[:5] { 222 _, err = s.Get(kvs[i].Key) 223 require.NoError(t, err) 224 } 225 for _, kv := range kvs[5:] { 226 _, err = s.Get(kv.Key) 227 require.Error(t, err) 228 } 229 } 230 231 func TestAllDBs(t *testing.T) { 232 var DBs = []dbSetup{ 233 {"BoltDB", newBoltStoreForTesting}, 234 {"LevelDB", newLevelDBForTesting}, 235 {"MemCached", newMemCachedStoreForTesting}, 236 {"Memory", newMemoryStoreForTesting}, 237 } 238 var tests = []dbTestFunction{testStoreGetNonExistent, testStoreSeek, 239 testStoreSeekGC} 240 for _, db := range DBs { 241 for _, test := range tests { 242 s := db.create(t) 243 twrapper := func(t *testing.T) { 244 test(t, s) 245 } 246 fname := runtime.FuncForPC(reflect.ValueOf(test).Pointer()).Name() 247 t.Run(db.name+"/"+fname, twrapper) 248 require.NoError(t, s.Close()) 249 } 250 } 251 }