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  }