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 }