github.com/decred/dcrlnd@v0.7.6/kvdb/readwrite_cursor_test.go (about)

     1  package kvdb
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/btcsuite/btcwallet/walletdb"
     7  	"github.com/stretchr/testify/require"
     8  )
     9  
    10  func testReadCursorEmptyInterval(t *testing.T, db walletdb.DB) {
    11  	err := Update(db, func(tx walletdb.ReadWriteTx) error {
    12  		b, err := tx.CreateTopLevelBucket([]byte("apple"))
    13  		require.NoError(t, err)
    14  		require.NotNil(t, b)
    15  
    16  		return nil
    17  	}, func() {})
    18  	require.NoError(t, err)
    19  
    20  	err = View(db, func(tx walletdb.ReadTx) error {
    21  		b := tx.ReadBucket([]byte("apple"))
    22  		require.NotNil(t, b)
    23  
    24  		cursor := b.ReadCursor()
    25  		k, v := cursor.First()
    26  		require.Nil(t, k)
    27  		require.Nil(t, v)
    28  
    29  		k, v = cursor.Next()
    30  		require.Nil(t, k)
    31  		require.Nil(t, v)
    32  
    33  		k, v = cursor.Last()
    34  		require.Nil(t, k)
    35  		require.Nil(t, v)
    36  
    37  		k, v = cursor.Prev()
    38  		require.Nil(t, k)
    39  		require.Nil(t, v)
    40  
    41  		return nil
    42  	}, func() {})
    43  	require.NoError(t, err)
    44  }
    45  
    46  func testReadCursorNonEmptyInterval(t *testing.T, db walletdb.DB) {
    47  	testKeyValues := []KV{
    48  		{"b", "1"},
    49  		{"c", "2"},
    50  		{"da", "3"},
    51  		{"e", "4"},
    52  	}
    53  
    54  	err := Update(db, func(tx walletdb.ReadWriteTx) error {
    55  		b, err := tx.CreateTopLevelBucket([]byte("apple"))
    56  		require.NoError(t, err)
    57  		require.NotNil(t, b)
    58  
    59  		for _, kv := range testKeyValues {
    60  			require.NoError(t, b.Put([]byte(kv.key), []byte(kv.val)))
    61  		}
    62  		return nil
    63  	}, func() {})
    64  
    65  	require.NoError(t, err)
    66  
    67  	err = View(db, func(tx walletdb.ReadTx) error {
    68  		b := tx.ReadBucket([]byte("apple"))
    69  		require.NotNil(t, b)
    70  
    71  		// Iterate from the front.
    72  		var kvs []KV
    73  		cursor := b.ReadCursor()
    74  		k, v := cursor.First()
    75  
    76  		for k != nil && v != nil {
    77  			kvs = append(kvs, KV{string(k), string(v)})
    78  			k, v = cursor.Next()
    79  		}
    80  		require.Equal(t, testKeyValues, kvs)
    81  
    82  		// Iterate from the back.
    83  		kvs = []KV{}
    84  		k, v = cursor.Last()
    85  
    86  		for k != nil && v != nil {
    87  			kvs = append(kvs, KV{string(k), string(v)})
    88  			k, v = cursor.Prev()
    89  		}
    90  		require.Equal(t, reverseKVs(testKeyValues), kvs)
    91  
    92  		// Random access
    93  		perm := []int{3, 0, 2, 1}
    94  		for _, i := range perm {
    95  			k, v := cursor.Seek([]byte(testKeyValues[i].key))
    96  			require.Equal(t, []byte(testKeyValues[i].key), k)
    97  			require.Equal(t, []byte(testKeyValues[i].val), v)
    98  		}
    99  
   100  		// Seek to nonexisting key.
   101  		k, v = cursor.Seek(nil)
   102  		require.Equal(t, "b", string(k))
   103  		require.Equal(t, "1", string(v))
   104  
   105  		k, v = cursor.Seek([]byte("x"))
   106  		require.Nil(t, k)
   107  		require.Nil(t, v)
   108  
   109  		return nil
   110  	}, func() {})
   111  
   112  	require.NoError(t, err)
   113  }
   114  
   115  func testReadWriteCursor(t *testing.T, db walletdb.DB) {
   116  	testKeyValues := []KV{
   117  		{"b", "1"},
   118  		{"c", "2"},
   119  		{"da", "3"},
   120  		{"e", "4"},
   121  	}
   122  
   123  	count := len(testKeyValues)
   124  
   125  	// Pre-store the first half of the interval.
   126  	require.NoError(t, Update(db, func(tx walletdb.ReadWriteTx) error {
   127  		b, err := tx.CreateTopLevelBucket([]byte("apple"))
   128  		require.NoError(t, err)
   129  		require.NotNil(t, b)
   130  
   131  		for i := 0; i < count/2; i++ {
   132  			err = b.Put(
   133  				[]byte(testKeyValues[i].key),
   134  				[]byte(testKeyValues[i].val),
   135  			)
   136  			require.NoError(t, err)
   137  		}
   138  		return nil
   139  	}, func() {}))
   140  
   141  	err := Update(db, func(tx walletdb.ReadWriteTx) error {
   142  		b := tx.ReadWriteBucket([]byte("apple"))
   143  		require.NotNil(t, b)
   144  
   145  		// Store the second half of the interval.
   146  		for i := count / 2; i < count; i++ {
   147  			err := b.Put(
   148  				[]byte(testKeyValues[i].key),
   149  				[]byte(testKeyValues[i].val),
   150  			)
   151  			require.NoError(t, err)
   152  		}
   153  
   154  		cursor := b.ReadWriteCursor()
   155  
   156  		// First on valid interval.
   157  		fk, fv := cursor.First()
   158  		require.Equal(t, []byte("b"), fk)
   159  		require.Equal(t, []byte("1"), fv)
   160  
   161  		// Prev(First()) = nil
   162  		k, v := cursor.Prev()
   163  		require.Nil(t, k)
   164  		require.Nil(t, v)
   165  
   166  		// Last on valid interval.
   167  		lk, lv := cursor.Last()
   168  		require.Equal(t, []byte("e"), lk)
   169  		require.Equal(t, []byte("4"), lv)
   170  
   171  		// Next(Last()) = nil
   172  		k, v = cursor.Next()
   173  		require.Nil(t, k)
   174  		require.Nil(t, v)
   175  
   176  		// Delete first item, then add an item before the
   177  		// deleted one. Check that First/Next will "jump"
   178  		// over the deleted item and return the new first.
   179  		_, _ = cursor.First()
   180  		require.NoError(t, cursor.Delete())
   181  		require.NoError(t, b.Put([]byte("a"), []byte("0")))
   182  		fk, fv = cursor.First()
   183  
   184  		require.Equal(t, []byte("a"), fk)
   185  		require.Equal(t, []byte("0"), fv)
   186  
   187  		k, v = cursor.Next()
   188  		require.Equal(t, []byte("c"), k)
   189  		require.Equal(t, []byte("2"), v)
   190  
   191  		// Similarly test that a new end is returned if
   192  		// the old end is deleted first.
   193  		_, _ = cursor.Last()
   194  		require.NoError(t, cursor.Delete())
   195  		require.NoError(t, b.Put([]byte("f"), []byte("5")))
   196  
   197  		lk, lv = cursor.Last()
   198  		require.Equal(t, []byte("f"), lk)
   199  		require.Equal(t, []byte("5"), lv)
   200  
   201  		k, v = cursor.Prev()
   202  		require.Equal(t, []byte("da"), k)
   203  		require.Equal(t, []byte("3"), v)
   204  
   205  		// Overwrite k/v in the middle of the interval.
   206  		require.NoError(t, b.Put([]byte("c"), []byte("3")))
   207  		k, v = cursor.Prev()
   208  		require.Equal(t, []byte("c"), k)
   209  		require.Equal(t, []byte("3"), v)
   210  
   211  		// Insert new key/values.
   212  		require.NoError(t, b.Put([]byte("cx"), []byte("x")))
   213  		require.NoError(t, b.Put([]byte("cy"), []byte("y")))
   214  
   215  		k, v = cursor.Next()
   216  		require.Equal(t, []byte("cx"), k)
   217  		require.Equal(t, []byte("x"), v)
   218  
   219  		k, v = cursor.Next()
   220  		require.Equal(t, []byte("cy"), k)
   221  		require.Equal(t, []byte("y"), v)
   222  
   223  		expected := []KV{
   224  			{"a", "0"},
   225  			{"c", "3"},
   226  			{"cx", "x"},
   227  			{"cy", "y"},
   228  			{"da", "3"},
   229  			{"f", "5"},
   230  		}
   231  
   232  		// Iterate from the front.
   233  		var kvs []KV
   234  		k, v = cursor.First()
   235  
   236  		for k != nil && v != nil {
   237  			kvs = append(kvs, KV{string(k), string(v)})
   238  			k, v = cursor.Next()
   239  		}
   240  		require.Equal(t, expected, kvs)
   241  
   242  		// Iterate from the back.
   243  		kvs = []KV{}
   244  		k, v = cursor.Last()
   245  
   246  		for k != nil && v != nil {
   247  			kvs = append(kvs, KV{string(k), string(v)})
   248  			k, v = cursor.Prev()
   249  		}
   250  		require.Equal(t, reverseKVs(expected), kvs)
   251  
   252  		return nil
   253  	}, func() {})
   254  
   255  	require.NoError(t, err)
   256  }
   257  
   258  // testReadWriteCursorWithBucketAndValue tests that cursors are able to iterate
   259  // over both bucket and value keys if both are present in the iterated bucket.
   260  func testReadWriteCursorWithBucketAndValue(t *testing.T, db walletdb.DB) {
   261  
   262  	// Pre-store the first half of the interval.
   263  	require.NoError(t, Update(db, func(tx walletdb.ReadWriteTx) error {
   264  		b, err := tx.CreateTopLevelBucket([]byte("apple"))
   265  		require.NoError(t, err)
   266  		require.NotNil(t, b)
   267  
   268  		require.NoError(t, b.Put([]byte("key"), []byte("val")))
   269  
   270  		b1, err := b.CreateBucket([]byte("banana"))
   271  		require.NoError(t, err)
   272  		require.NotNil(t, b1)
   273  
   274  		b2, err := b.CreateBucket([]byte("pear"))
   275  		require.NoError(t, err)
   276  		require.NotNil(t, b2)
   277  
   278  		return nil
   279  	}, func() {}))
   280  
   281  	err := View(db, func(tx walletdb.ReadTx) error {
   282  		b := tx.ReadBucket([]byte("apple"))
   283  		require.NotNil(t, b)
   284  
   285  		cursor := b.ReadCursor()
   286  
   287  		// First on valid interval.
   288  		k, v := cursor.First()
   289  		require.Equal(t, []byte("banana"), k)
   290  		require.Nil(t, v)
   291  
   292  		k, v = cursor.Next()
   293  		require.Equal(t, []byte("key"), k)
   294  		require.Equal(t, []byte("val"), v)
   295  
   296  		k, v = cursor.Last()
   297  		require.Equal(t, []byte("pear"), k)
   298  		require.Nil(t, v)
   299  
   300  		k, v = cursor.Seek([]byte("k"))
   301  		require.Equal(t, []byte("key"), k)
   302  		require.Equal(t, []byte("val"), v)
   303  
   304  		k, v = cursor.Seek([]byte("banana"))
   305  		require.Equal(t, []byte("banana"), k)
   306  		require.Nil(t, v)
   307  
   308  		k, v = cursor.Next()
   309  		require.Equal(t, []byte("key"), k)
   310  		require.Equal(t, []byte("val"), v)
   311  
   312  		return nil
   313  	}, func() {})
   314  
   315  	require.NoError(t, err)
   316  }