github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/db/backend_test.go (about) 1 package db_test 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/gnolang/gno/tm2/pkg/db" 8 _ "github.com/gnolang/gno/tm2/pkg/db/_all" 9 "github.com/gnolang/gno/tm2/pkg/db/internal" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 ) 13 14 func testBackendGetSetDelete(t *testing.T, backend db.BackendType) { 15 t.Helper() 16 17 // Default 18 db, err := db.NewDB("testdb", backend, t.TempDir()) 19 require.NoError(t, err) 20 21 // A nonexistent key should return nil, even if the key is empty 22 require.Nil(t, db.Get([]byte(""))) 23 24 // A nonexistent key should return nil, even if the key is nil 25 require.Nil(t, db.Get(nil)) 26 27 // A nonexistent key should return nil. 28 key := []byte("abc") 29 require.Nil(t, db.Get(key)) 30 31 // Set empty value. 32 db.SetSync(key, []byte("")) 33 require.NotNil(t, db.Get(key)) 34 require.Empty(t, db.Get(key)) 35 36 // Set nil value. 37 db.SetSync(key, nil) 38 require.NotNil(t, db.Get(key)) 39 require.Empty(t, db.Get(key)) 40 41 // Delete. 42 db.DeleteSync(key) 43 require.Nil(t, db.Get(key)) 44 } 45 46 func TestBackendsGetSetDelete(t *testing.T) { 47 t.Parallel() 48 49 for _, dbType := range db.BackendList() { 50 t.Run(string(dbType), func(t *testing.T) { 51 t.Parallel() 52 53 testBackendGetSetDelete(t, dbType) 54 }) 55 } 56 } 57 58 func withDB(t *testing.T, dbType db.BackendType, fn func(db.DB)) { 59 t.Helper() 60 61 name := fmt.Sprintf("test_%x", internal.RandStr(12)) 62 db, err := db.NewDB(name, dbType, t.TempDir()) 63 require.Nil(t, err) 64 fn(db) 65 db.Close() 66 } 67 68 func TestBackendsNilKeys(t *testing.T) { 69 t.Parallel() 70 71 // Test all backends. 72 for _, dbType := range db.BackendList() { 73 withDB(t, dbType, func(db db.DB) { 74 t.Run(fmt.Sprintf("Testing %s", dbType), func(t *testing.T) { 75 // Nil keys are treated as the empty key for most operations. 76 expect := func(key, value []byte) { 77 if len(key) == 0 { // nil or empty 78 assert.Equal(t, db.Get(nil), db.Get([]byte(""))) 79 assert.Equal(t, db.Has(nil), db.Has([]byte(""))) 80 } 81 assert.Equal(t, db.Get(key), value) 82 assert.Equal(t, db.Has(key), value != nil) 83 } 84 85 // Not set 86 expect(nil, nil) 87 88 // Set nil value 89 db.SetSync(nil, nil) 90 expect(nil, []byte("")) 91 92 // Set empty value 93 db.SetSync(nil, []byte("")) 94 expect(nil, []byte("")) 95 96 // Set nil, Delete nil 97 db.SetSync(nil, []byte("abc")) 98 expect(nil, []byte("abc")) 99 db.DeleteSync(nil) 100 expect(nil, nil) 101 102 // Set nil, Delete empty 103 db.SetSync(nil, []byte("abc")) 104 expect(nil, []byte("abc")) 105 db.DeleteSync([]byte("")) 106 expect(nil, nil) 107 108 // Set empty, Delete nil 109 db.SetSync([]byte(""), []byte("abc")) 110 expect(nil, []byte("abc")) 111 db.DeleteSync(nil) 112 expect(nil, nil) 113 114 // Set empty, Delete empty 115 db.SetSync([]byte(""), []byte("abc")) 116 expect(nil, []byte("abc")) 117 db.DeleteSync([]byte("")) 118 expect(nil, nil) 119 120 // Set nil, Delete nil 121 db.SetSync(nil, []byte("abc")) 122 expect(nil, []byte("abc")) 123 db.DeleteSync(nil) 124 expect(nil, nil) 125 126 // Set nil, Delete empty 127 db.SetSync(nil, []byte("abc")) 128 expect(nil, []byte("abc")) 129 db.DeleteSync([]byte("")) 130 expect(nil, nil) 131 132 // Set empty, Delete nil 133 db.SetSync([]byte(""), []byte("abc")) 134 expect(nil, []byte("abc")) 135 db.DeleteSync(nil) 136 expect(nil, nil) 137 138 // Set empty, Delete empty 139 db.SetSync([]byte(""), []byte("abc")) 140 expect(nil, []byte("abc")) 141 db.DeleteSync([]byte("")) 142 expect(nil, nil) 143 }) 144 }) 145 } 146 } 147 148 func TestDBIterator(t *testing.T) { 149 t.Parallel() 150 151 for _, dbType := range db.BackendList() { 152 t.Run(fmt.Sprintf("%v", dbType), func(t *testing.T) { 153 t.Parallel() 154 155 testDBIterator(t, dbType) 156 }) 157 } 158 } 159 160 func testDBIterator(t *testing.T, backend db.BackendType) { 161 t.Helper() 162 163 name := fmt.Sprintf("test_%x", internal.RandStr(12)) 164 db, err := db.NewDB(name, backend, t.TempDir()) 165 require.NoError(t, err) 166 167 for i := 0; i < 10; i++ { 168 if i != 6 { // but skip 6. 169 db.Set(int642Bytes(int64(i)), nil) 170 } 171 } 172 173 verifyIterator(t, db.Iterator(nil, nil), []int64{0, 1, 2, 3, 4, 5, 7, 8, 9}, "forward iterator") 174 verifyIterator(t, db.ReverseIterator(nil, nil), []int64{9, 8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator") 175 176 verifyIterator(t, db.Iterator(nil, int642Bytes(0)), []int64(nil), "forward iterator to 0") 177 verifyIterator(t, db.ReverseIterator(int642Bytes(10), nil), []int64(nil), "reverse iterator from 10 (ex)") 178 179 verifyIterator(t, db.Iterator(int642Bytes(0), nil), []int64{0, 1, 2, 3, 4, 5, 7, 8, 9}, "forward iterator from 0") 180 verifyIterator(t, db.Iterator(int642Bytes(1), nil), []int64{1, 2, 3, 4, 5, 7, 8, 9}, "forward iterator from 1") 181 verifyIterator(t, db.ReverseIterator(nil, int642Bytes(10)), 182 []int64{9, 8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 10 (ex)") 183 verifyIterator(t, db.ReverseIterator(nil, int642Bytes(9)), 184 []int64{8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 9 (ex)") 185 verifyIterator(t, db.ReverseIterator(nil, int642Bytes(8)), 186 []int64{7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 8 (ex)") 187 188 verifyIterator(t, db.Iterator(int642Bytes(5), int642Bytes(6)), []int64{5}, "forward iterator from 5 to 6") 189 verifyIterator(t, db.Iterator(int642Bytes(5), int642Bytes(7)), []int64{5}, "forward iterator from 5 to 7") 190 verifyIterator(t, db.Iterator(int642Bytes(5), int642Bytes(8)), []int64{5, 7}, "forward iterator from 5 to 8") 191 verifyIterator(t, db.Iterator(int642Bytes(6), int642Bytes(7)), []int64(nil), "forward iterator from 6 to 7") 192 verifyIterator(t, db.Iterator(int642Bytes(6), int642Bytes(8)), []int64{7}, "forward iterator from 6 to 8") 193 verifyIterator(t, db.Iterator(int642Bytes(7), int642Bytes(8)), []int64{7}, "forward iterator from 7 to 8") 194 195 verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(5)), []int64{4}, "reverse iterator from 5 (ex) to 4") 196 verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(6)), 197 []int64{5, 4}, "reverse iterator from 6 (ex) to 4") 198 verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(7)), 199 []int64{5, 4}, "reverse iterator from 7 (ex) to 4") 200 verifyIterator(t, db.ReverseIterator(int642Bytes(5), int642Bytes(6)), []int64{5}, "reverse iterator from 6 (ex) to 5") 201 verifyIterator(t, db.ReverseIterator(int642Bytes(5), int642Bytes(7)), []int64{5}, "reverse iterator from 7 (ex) to 5") 202 verifyIterator(t, db.ReverseIterator(int642Bytes(6), int642Bytes(7)), 203 []int64(nil), "reverse iterator from 7 (ex) to 6") 204 205 verifyIterator(t, db.Iterator(int642Bytes(0), int642Bytes(1)), []int64{0}, "forward iterator from 0 to 1") 206 verifyIterator(t, db.ReverseIterator(int642Bytes(8), int642Bytes(9)), []int64{8}, "reverse iterator from 9 (ex) to 8") 207 208 verifyIterator(t, db.Iterator(int642Bytes(2), int642Bytes(4)), []int64{2, 3}, "forward iterator from 2 to 4") 209 verifyIterator(t, db.Iterator(int642Bytes(4), int642Bytes(2)), []int64(nil), "forward iterator from 4 to 2") 210 verifyIterator(t, db.ReverseIterator(int642Bytes(2), int642Bytes(4)), 211 []int64{3, 2}, "reverse iterator from 4 (ex) to 2") 212 verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(2)), 213 []int64(nil), "reverse iterator from 2 (ex) to 4") 214 } 215 216 func verifyIterator(t *testing.T, itr db.Iterator, expected []int64, msg string) { 217 t.Helper() 218 219 var list []int64 220 for itr.Valid() { 221 list = append(list, bytes2Int64(itr.Key())) 222 itr.Next() 223 } 224 assert.Equal(t, expected, list, msg) 225 } 226 227 func TestDBIteratorSingleKey(t *testing.T) { 228 t.Parallel() 229 230 for _, backend := range db.BackendList() { 231 t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { 232 t.Parallel() 233 234 db := newTempDB(t, backend) 235 236 db.SetSync(bz("1"), bz("value_1")) 237 itr := db.Iterator(nil, nil) 238 239 checkValid(t, itr, true) 240 checkNext(t, itr, false) 241 checkValid(t, itr, false) 242 checkNextPanics(t, itr) 243 244 // Once invalid... 245 checkInvalid(t, itr) 246 }) 247 } 248 } 249 250 func TestDBIteratorTwoKeys(t *testing.T) { 251 t.Parallel() 252 253 for _, backend := range db.BackendList() { 254 t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { 255 t.Parallel() 256 257 db := newTempDB(t, backend) 258 259 db.SetSync(bz("1"), bz("value_1")) 260 db.SetSync(bz("2"), bz("value_1")) 261 262 { // Fail by calling Next too much 263 itr := db.Iterator(nil, nil) 264 checkValid(t, itr, true) 265 266 checkNext(t, itr, true) 267 checkValid(t, itr, true) 268 269 checkNext(t, itr, false) 270 checkValid(t, itr, false) 271 272 checkNextPanics(t, itr) 273 274 // Once invalid... 275 checkInvalid(t, itr) 276 } 277 }) 278 } 279 } 280 281 func TestDBIteratorMany(t *testing.T) { 282 t.Parallel() 283 284 for _, backend := range db.BackendList() { 285 t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { 286 t.Parallel() 287 288 db := newTempDB(t, backend) 289 290 keys := make([][]byte, 100) 291 for i := 0; i < 100; i++ { 292 keys[i] = []byte{byte(i)} 293 } 294 295 value := []byte{5} 296 for _, k := range keys { 297 db.Set(k, value) 298 } 299 300 itr := db.Iterator(nil, nil) 301 defer itr.Close() 302 for ; itr.Valid(); itr.Next() { 303 assert.Equal(t, db.Get(itr.Key()), itr.Value()) 304 } 305 }) 306 } 307 } 308 309 func TestDBIteratorEmpty(t *testing.T) { 310 t.Parallel() 311 312 for _, backend := range db.BackendList() { 313 t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { 314 t.Parallel() 315 316 db := newTempDB(t, backend) 317 318 itr := db.Iterator(nil, nil) 319 320 checkInvalid(t, itr) 321 }) 322 } 323 } 324 325 func TestDBIteratorEmptyBeginAfter(t *testing.T) { 326 t.Parallel() 327 328 for _, backend := range db.BackendList() { 329 t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { 330 t.Parallel() 331 332 db := newTempDB(t, backend) 333 334 itr := db.Iterator(bz("1"), nil) 335 336 checkInvalid(t, itr) 337 }) 338 } 339 } 340 341 func TestDBIteratorNonemptyBeginAfter(t *testing.T) { 342 t.Parallel() 343 344 for _, backend := range db.BackendList() { 345 t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { 346 t.Parallel() 347 348 db := newTempDB(t, backend) 349 350 db.SetSync(bz("1"), bz("value_1")) 351 itr := db.Iterator(bz("2"), nil) 352 353 checkInvalid(t, itr) 354 }) 355 } 356 } 357 358 func TestDBBatchWrite(t *testing.T) { 359 t.Parallel() 360 361 testCases := []struct { 362 modify func(batch db.Batch) 363 calls map[string]int 364 }{ 365 0: { 366 func(batch db.Batch) { 367 batch.Set(bz("1"), bz("1")) 368 batch.Set(bz("2"), bz("2")) 369 batch.Delete(bz("3")) 370 batch.Set(bz("4"), bz("4")) 371 batch.Write() 372 }, 373 map[string]int{ 374 "Set": 0, "SetSync": 0, "SetNoLock": 3, "SetNoLockSync": 0, 375 "Delete": 0, "DeleteSync": 0, "DeleteNoLock": 1, "DeleteNoLockSync": 0, 376 }, 377 }, 378 1: { 379 func(batch db.Batch) { 380 batch.Set(bz("1"), bz("1")) 381 batch.Set(bz("2"), bz("2")) 382 batch.Set(bz("4"), bz("4")) 383 batch.Delete(bz("3")) 384 batch.Write() 385 }, 386 map[string]int{ 387 "Set": 0, "SetSync": 0, "SetNoLock": 3, "SetNoLockSync": 0, 388 "Delete": 0, "DeleteSync": 0, "DeleteNoLock": 1, "DeleteNoLockSync": 0, 389 }, 390 }, 391 2: { 392 func(batch db.Batch) { 393 batch.Set(bz("1"), bz("1")) 394 batch.Set(bz("2"), bz("2")) 395 batch.Delete(bz("3")) 396 batch.Set(bz("4"), bz("4")) 397 batch.WriteSync() 398 }, 399 map[string]int{ 400 "Set": 0, "SetSync": 0, "SetNoLock": 2, "SetNoLockSync": 1, 401 "Delete": 0, "DeleteSync": 0, "DeleteNoLock": 1, "DeleteNoLockSync": 0, 402 }, 403 }, 404 3: { 405 func(batch db.Batch) { 406 batch.Set(bz("1"), bz("1")) 407 batch.Set(bz("2"), bz("2")) 408 batch.Set(bz("4"), bz("4")) 409 batch.Delete(bz("3")) 410 batch.WriteSync() 411 }, 412 map[string]int{ 413 "Set": 0, "SetSync": 0, "SetNoLock": 3, "SetNoLockSync": 0, 414 "Delete": 0, "DeleteSync": 0, "DeleteNoLock": 0, "DeleteNoLockSync": 1, 415 }, 416 }, 417 } 418 419 for i, tc := range testCases { 420 mdb := newMockDB() 421 batch := mdb.NewBatch() 422 423 tc.modify(batch) 424 425 for call, exp := range tc.calls { 426 got := mdb.calls[call] 427 assert.Equal(t, exp, got, "#%v - key: %s", i, call) 428 } 429 } 430 } 431 432 func newTempDB(t *testing.T, backend db.BackendType) db.DB { 433 t.Helper() 434 435 tmpdb, err := db.NewDB("testdb", backend, t.TempDir()) 436 require.NoError(t, err) 437 438 return tmpdb 439 }