diff --git a/kvdb/etcd/bucket.go b/kvdb/etcd/bucket.go index 8a1ff071..514eab63 100644 --- a/kvdb/etcd/bucket.go +++ b/kvdb/etcd/bucket.go @@ -90,3 +90,42 @@ func getKeyVal(kv *KV) ([]byte, []byte) { return getKey(kv.key), val } + +// BucketKey is a helper functon used in tests to create a bucket key from +// passed bucket list. +func BucketKey(buckets ...string) string { + var bucketKey []byte + + rootID := makeBucketID([]byte(etcdDefaultRootBucketId)) + parent := rootID[:] + + for _, bucketName := range buckets { + bucketKey = makeBucketKey(parent, []byte(bucketName)) + id := makeBucketID(bucketKey) + parent = id[:] + } + + return string(bucketKey) +} + +// BucketVal is a helper function used in tests to create a bucket value (the +// value for a bucket key) from the passed bucket list. +func BucketVal(buckets ...string) string { + id := makeBucketID([]byte(BucketKey(buckets...))) + return string(id[:]) +} + +// ValueKey is a helper function used in tests to create a value key from the +// passed key and bucket list. +func ValueKey(key string, buckets ...string) string { + rootID := makeBucketID([]byte(etcdDefaultRootBucketId)) + bucket := rootID[:] + + for _, bucketName := range buckets { + bucketKey := makeBucketKey(bucket, []byte(bucketName)) + id := makeBucketID(bucketKey) + bucket = id[:] + } + + return string(makeValueKey(bucket, []byte(key))) +} diff --git a/kvdb/etcd/bucket_test.go b/kvdb/etcd/bucket_test.go deleted file mode 100644 index 6a97a9b8..00000000 --- a/kvdb/etcd/bucket_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// +build kvdb_etcd - -package etcd - -// bkey is a helper functon used in tests to create a bucket key from passed -// bucket list. -func bkey(buckets ...string) string { - var bucketKey []byte - - rootID := makeBucketID([]byte(etcdDefaultRootBucketId)) - parent := rootID[:] - - for _, bucketName := range buckets { - bucketKey = makeBucketKey(parent, []byte(bucketName)) - id := makeBucketID(bucketKey) - parent = id[:] - } - - return string(bucketKey) -} - -// bval is a helper function used in tests to create a bucket value (the value -// for a bucket key) from the passed bucket list. -func bval(buckets ...string) string { - id := makeBucketID([]byte(bkey(buckets...))) - return string(id[:]) -} - -// vkey is a helper function used in tests to create a value key from the -// passed key and bucket list. -func vkey(key string, buckets ...string) string { - rootID := makeBucketID([]byte(etcdDefaultRootBucketId)) - bucket := rootID[:] - - for _, bucketName := range buckets { - bucketKey := makeBucketKey(bucket, []byte(bucketName)) - id := makeBucketID(bucketKey) - bucket = id[:] - } - - return string(makeValueKey(bucket, []byte(key))) -} diff --git a/kvdb/etcd/db_test.go b/kvdb/etcd/db_test.go index 7d0c10be..357c4ed5 100644 --- a/kvdb/etcd/db_test.go +++ b/kvdb/etcd/db_test.go @@ -38,8 +38,8 @@ func TestCopy(t *testing.T) { require.Nil(t, err) expected := map[string]string{ - bkey("apple"): bval("apple"), - vkey("key", "apple"): "val", + BucketKey("apple"): BucketVal("apple"), + ValueKey("key", "apple"): "val", } require.Equal(t, expected, f.Dump()) } diff --git a/kvdb/etcd/fixture_test.go b/kvdb/etcd/fixture.go similarity index 93% rename from kvdb/etcd/fixture_test.go rename to kvdb/etcd/fixture.go index 377e8473..01781b7b 100644 --- a/kvdb/etcd/fixture_test.go +++ b/kvdb/etcd/fixture.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "github.com/btcsuite/btcwallet/walletdb" + "github.com/stretchr/testify/require" "go.etcd.io/etcd/clientv3" "go.etcd.io/etcd/clientv3/namespace" ) @@ -76,6 +78,13 @@ func NewEtcdTestFixture(t *testing.T) *EtcdTestFixture { } } +func (f *EtcdTestFixture) NewBackend() walletdb.DB { + db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) + require.NoError(f.t, err) + + return db +} + // Put puts a string key/value into the test etcd database. func (f *EtcdTestFixture) Put(key, value string) { ctx, cancel := context.WithTimeout(context.TODO(), testEtcdTimeout) diff --git a/kvdb/etcd/readwrite_tx_test.go b/kvdb/etcd/readwrite_tx_test.go index 7e8c92c6..c640493e 100644 --- a/kvdb/etcd/readwrite_tx_test.go +++ b/kvdb/etcd/readwrite_tx_test.go @@ -10,70 +10,6 @@ import ( "github.com/stretchr/testify/require" ) -func TestTxManualCommit(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) - - tx, err := db.BeginReadWriteTx() - require.NoError(t, err) - require.NotNil(t, tx) - - committed := false - - tx.OnCommit(func() { - committed = true - }) - - apple, err := tx.CreateTopLevelBucket([]byte("apple")) - require.NoError(t, err) - require.NotNil(t, apple) - require.NoError(t, apple.Put([]byte("testKey"), []byte("testVal"))) - - banana, err := tx.CreateTopLevelBucket([]byte("banana")) - require.NoError(t, err) - require.NotNil(t, banana) - require.NoError(t, banana.Put([]byte("testKey"), []byte("testVal"))) - require.NoError(t, tx.DeleteTopLevelBucket([]byte("banana"))) - - require.NoError(t, tx.Commit()) - require.True(t, committed) - - expected := map[string]string{ - bkey("apple"): bval("apple"), - vkey("testKey", "apple"): "testVal", - } - require.Equal(t, expected, f.Dump()) -} - -func TestTxRollback(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) - - tx, err := db.BeginReadWriteTx() - require.Nil(t, err) - require.NotNil(t, tx) - - apple, err := tx.CreateTopLevelBucket([]byte("apple")) - require.Nil(t, err) - require.NotNil(t, apple) - - require.NoError(t, apple.Put([]byte("testKey"), []byte("testVal"))) - - require.NoError(t, tx.Rollback()) - require.Error(t, walletdb.ErrTxClosed, tx.Commit()) - require.Equal(t, map[string]string{}, f.Dump()) -} - func TestChangeDuringManualTx(t *testing.T) { t.Parallel() @@ -94,12 +30,12 @@ func TestChangeDuringManualTx(t *testing.T) { require.NoError(t, apple.Put([]byte("testKey"), []byte("testVal"))) // Try overwriting the bucket key. - f.Put(bkey("apple"), "banana") + f.Put(BucketKey("apple"), "banana") // TODO: translate error require.NotNil(t, tx.Commit()) require.Equal(t, map[string]string{ - bkey("apple"): "banana", + BucketKey("apple"): "banana", }, f.Dump()) } @@ -122,8 +58,8 @@ func TestChangeDuringUpdate(t *testing.T) { require.NoError(t, apple.Put([]byte("key"), []byte("value"))) if count == 0 { - f.Put(vkey("key", "apple"), "new_value") - f.Put(vkey("key2", "apple"), "value2") + f.Put(ValueKey("key", "apple"), "new_value") + f.Put(ValueKey("key2", "apple"), "value2") } cursor := apple.ReadCursor() @@ -149,9 +85,9 @@ func TestChangeDuringUpdate(t *testing.T) { require.Equal(t, count, 2) expected := map[string]string{ - bkey("apple"): bval("apple"), - vkey("key", "apple"): "value", - vkey("key2", "apple"): "value2", + BucketKey("apple"): BucketVal("apple"), + ValueKey("key", "apple"): "value", + ValueKey("key2", "apple"): "value2", } require.Equal(t, expected, f.Dump()) } diff --git a/kvdb/etcd_test.go b/kvdb/etcd_test.go new file mode 100644 index 00000000..fc81bb93 --- /dev/null +++ b/kvdb/etcd_test.go @@ -0,0 +1,154 @@ +// +build kvdb_etcd + +package kvdb + +import ( + "testing" + + "github.com/btcsuite/btcwallet/walletdb" + "github.com/lightningnetwork/lnd/kvdb/etcd" + "github.com/stretchr/testify/require" +) + +var ( + bkey = etcd.BucketKey + bval = etcd.BucketVal + vkey = etcd.ValueKey +) + +func TestEtcd(t *testing.T) { + tests := []struct { + name string + test func(*testing.T, walletdb.DB) + expectedDb map[string]string + }{ + { + name: "read cursor empty interval", + test: testReadCursorEmptyInterval, + }, + { + name: "read cursor non empty interval", + test: testReadCursorNonEmptyInterval, + }, + { + name: "read write cursor", + test: testReadWriteCursor, + expectedDb: map[string]string{ + bkey("apple"): bval("apple"), + vkey("a", "apple"): "0", + vkey("c", "apple"): "3", + vkey("cx", "apple"): "x", + vkey("cy", "apple"): "y", + vkey("da", "apple"): "3", + vkey("f", "apple"): "5", + }, + }, + { + name: "read write cursor with bucket and value", + test: testReadWriteCursorWithBucketAndValue, + expectedDb: map[string]string{ + bkey("apple"): bval("apple"), + bkey("apple", "banana"): bval("apple", "banana"), + bkey("apple", "pear"): bval("apple", "pear"), + vkey("key", "apple"): "val", + }, + }, + { + name: "bucket creation", + test: testBucketCreation, + expectedDb: map[string]string{ + bkey("apple"): bval("apple"), + bkey("apple", "banana"): bval("apple", "banana"), + bkey("apple", "mango"): bval("apple", "mango"), + bkey("apple", "banana", "pear"): bval("apple", "banana", "pear"), + }, + }, + { + name: "bucket deletion", + test: testBucketDeletion, + expectedDb: map[string]string{ + bkey("apple"): bval("apple"), + bkey("apple", "banana"): bval("apple", "banana"), + vkey("key1", "apple", "banana"): "val1", + vkey("key3", "apple", "banana"): "val3", + }, + }, + { + name: "bucket for each", + test: testBucketForEach, + expectedDb: map[string]string{ + bkey("apple"): bval("apple"), + bkey("apple", "banana"): bval("apple", "banana"), + vkey("key1", "apple"): "val1", + vkey("key2", "apple"): "val2", + vkey("key3", "apple"): "val3", + vkey("key1", "apple", "banana"): "val1", + vkey("key2", "apple", "banana"): "val2", + vkey("key3", "apple", "banana"): "val3", + }, + }, + { + name: "bucket for each with error", + test: testBucketForEachWithError, + expectedDb: map[string]string{ + bkey("apple"): bval("apple"), + bkey("apple", "banana"): bval("apple", "banana"), + bkey("apple", "pear"): bval("apple", "pear"), + vkey("key1", "apple"): "val1", + vkey("key2", "apple"): "val2", + }, + }, + { + name: "bucket sequence", + test: testBucketSequence, + }, + { + name: "key clash", + test: testKeyClash, + expectedDb: map[string]string{ + bkey("apple"): bval("apple"), + bkey("apple", "banana"): bval("apple", "banana"), + vkey("key", "apple"): "val", + }, + }, + { + name: "bucket create delete", + test: testBucketCreateDelete, + expectedDb: map[string]string{ + vkey("banana", "apple"): "value", + bkey("apple"): bval("apple"), + }, + }, + { + name: "tx manual commit", + test: testTxManualCommit, + expectedDb: map[string]string{ + bkey("apple"): bval("apple"), + vkey("testKey", "apple"): "testVal", + }, + }, + { + name: "tx rollback", + test: testTxRollback, + expectedDb: map[string]string{}, + }, + } + + for _, test := range tests { + test := test + + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + f := etcd.NewEtcdTestFixture(t) + defer f.Cleanup() + + test.test(t, f.NewBackend()) + + if test.expectedDb != nil { + dump := f.Dump() + require.Equal(t, test.expectedDb, dump) + } + }) + } +} diff --git a/kvdb/etcd/readwrite_bucket_test.go b/kvdb/readwrite_bucket_test.go similarity index 72% rename from kvdb/etcd/readwrite_bucket_test.go rename to kvdb/readwrite_bucket_test.go index b630e5ee..1927f8dc 100644 --- a/kvdb/etcd/readwrite_bucket_test.go +++ b/kvdb/readwrite_bucket_test.go @@ -1,9 +1,6 @@ -// +build kvdb_etcd - -package etcd +package kvdb import ( - "context" "fmt" "math" "testing" @@ -12,16 +9,8 @@ import ( "github.com/stretchr/testify/require" ) -func TestBucketCreation(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) - - err = db.Update(func(tx walletdb.ReadWriteTx) error { +func testBucketCreation(t *testing.T, db walletdb.DB) { + err := Update(db, func(tx walletdb.ReadWriteTx) error { // empty bucket name b, err := tx.CreateTopLevelBucket(nil) require.Error(t, walletdb.ErrBucketNameRequired, err) @@ -83,26 +72,10 @@ func TestBucketCreation(t *testing.T) { }, func() {}) require.Nil(t, err) - - expected := map[string]string{ - bkey("apple"): bval("apple"), - bkey("apple", "banana"): bval("apple", "banana"), - bkey("apple", "mango"): bval("apple", "mango"), - bkey("apple", "banana", "pear"): bval("apple", "banana", "pear"), - } - require.Equal(t, expected, f.Dump()) } -func TestBucketDeletion(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) - - err = db.Update(func(tx walletdb.ReadWriteTx) error { +func testBucketDeletion(t *testing.T, db walletdb.DB) { + err := Update(db, func(tx walletdb.ReadWriteTx) error { // "apple" apple, err := tx.CreateTopLevelBucket([]byte("apple")) require.Nil(t, err) @@ -193,26 +166,10 @@ func TestBucketDeletion(t *testing.T) { }, func() {}) require.Nil(t, err) - - expected := map[string]string{ - bkey("apple"): bval("apple"), - bkey("apple", "banana"): bval("apple", "banana"), - vkey("key1", "apple", "banana"): "val1", - vkey("key3", "apple", "banana"): "val3", - } - require.Equal(t, expected, f.Dump()) } -func TestBucketForEach(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) - - err = db.Update(func(tx walletdb.ReadWriteTx) error { +func testBucketForEach(t *testing.T, db walletdb.DB) { + err := Update(db, func(tx walletdb.ReadWriteTx) error { // "apple" apple, err := tx.CreateTopLevelBucket([]byte("apple")) require.Nil(t, err) @@ -265,30 +222,10 @@ func TestBucketForEach(t *testing.T) { }, func() {}) require.Nil(t, err) - - expected := map[string]string{ - bkey("apple"): bval("apple"), - bkey("apple", "banana"): bval("apple", "banana"), - vkey("key1", "apple"): "val1", - vkey("key2", "apple"): "val2", - vkey("key3", "apple"): "val3", - vkey("key1", "apple", "banana"): "val1", - vkey("key2", "apple", "banana"): "val2", - vkey("key3", "apple", "banana"): "val3", - } - require.Equal(t, expected, f.Dump()) } -func TestBucketForEachWithError(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) - - err = db.Update(func(tx walletdb.ReadWriteTx) error { +func testBucketForEachWithError(t *testing.T, db walletdb.DB) { + err := Update(db, func(tx walletdb.ReadWriteTx) error { // "apple" apple, err := tx.CreateTopLevelBucket([]byte("apple")) require.Nil(t, err) @@ -358,27 +295,10 @@ func TestBucketForEachWithError(t *testing.T) { }, func() {}) require.Nil(t, err) - - expected := map[string]string{ - bkey("apple"): bval("apple"), - bkey("apple", "banana"): bval("apple", "banana"), - bkey("apple", "pear"): bval("apple", "pear"), - vkey("key1", "apple"): "val1", - vkey("key2", "apple"): "val2", - } - require.Equal(t, expected, f.Dump()) } -func TestBucketSequence(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) - - err = db.Update(func(tx walletdb.ReadWriteTx) error { +func testBucketSequence(t *testing.T, db walletdb.DB) { + err := Update(db, func(tx walletdb.ReadWriteTx) error { apple, err := tx.CreateTopLevelBucket([]byte("apple")) require.Nil(t, err) require.NotNil(t, apple) @@ -408,19 +328,11 @@ func TestBucketSequence(t *testing.T) { // TestKeyClash tests that one cannot create a bucket if a value with the same // key exists and the same is true in reverse: that a value cannot be put if // a bucket with the same key exists. -func TestKeyClash(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) - +func testKeyClash(t *testing.T, db walletdb.DB) { // First: // put: /apple/key -> val // create bucket: /apple/banana - err = db.Update(func(tx walletdb.ReadWriteTx) error { + err := Update(db, func(tx walletdb.ReadWriteTx) error { apple, err := tx.CreateTopLevelBucket([]byte("apple")) require.Nil(t, err) require.NotNil(t, apple) @@ -439,7 +351,7 @@ func TestKeyClash(t *testing.T) { // Next try to: // put: /apple/banana -> val => will fail (as /apple/banana is a bucket) // create bucket: /apple/key => will fail (as /apple/key is a value) - err = db.Update(func(tx walletdb.ReadWriteTx) error { + err = Update(db, func(tx walletdb.ReadWriteTx) error { apple, err := tx.CreateTopLevelBucket([]byte("apple")) require.Nil(t, err) require.NotNil(t, apple) @@ -461,31 +373,12 @@ func TestKeyClash(t *testing.T) { }, func() {}) require.Nil(t, err) - - // Except that the only existing items in the db are: - // bucket: /apple - // bucket: /apple/banana - // value: /apple/key -> val - expected := map[string]string{ - bkey("apple"): bval("apple"), - bkey("apple", "banana"): bval("apple", "banana"), - vkey("key", "apple"): "val", - } - require.Equal(t, expected, f.Dump()) - } // TestBucketCreateDelete tests that creating then deleting then creating a // bucket suceeds. -func TestBucketCreateDelete(t *testing.T) { - t.Parallel() - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) - - err = db.Update(func(tx walletdb.ReadWriteTx) error { +func testBucketCreateDelete(t *testing.T, db walletdb.DB) { + err := Update(db, func(tx walletdb.ReadWriteTx) error { apple, err := tx.CreateTopLevelBucket([]byte("apple")) require.NoError(t, err) require.NotNil(t, apple) @@ -498,7 +391,7 @@ func TestBucketCreateDelete(t *testing.T) { }, func() {}) require.NoError(t, err) - err = db.Update(func(tx walletdb.ReadWriteTx) error { + err = Update(db, func(tx walletdb.ReadWriteTx) error { apple := tx.ReadWriteBucket([]byte("apple")) require.NotNil(t, apple) require.NoError(t, apple.DeleteNestedBucket([]byte("banana"))) @@ -507,7 +400,7 @@ func TestBucketCreateDelete(t *testing.T) { }, func() {}) require.NoError(t, err) - err = db.Update(func(tx walletdb.ReadWriteTx) error { + err = Update(db, func(tx walletdb.ReadWriteTx) error { apple := tx.ReadWriteBucket([]byte("apple")) require.NotNil(t, apple) require.NoError(t, apple.Put([]byte("banana"), []byte("value"))) @@ -515,10 +408,4 @@ func TestBucketCreateDelete(t *testing.T) { return nil }, func() {}) require.NoError(t, err) - - expected := map[string]string{ - vkey("banana", "apple"): "value", - bkey("apple"): bval("apple"), - } - require.Equal(t, expected, f.Dump()) } diff --git a/kvdb/etcd/readwrite_cursor_test.go b/kvdb/readwrite_cursor_test.go similarity index 77% rename from kvdb/etcd/readwrite_cursor_test.go rename to kvdb/readwrite_cursor_test.go index ce524f80..3ac83ba3 100644 --- a/kvdb/etcd/readwrite_cursor_test.go +++ b/kvdb/readwrite_cursor_test.go @@ -1,25 +1,14 @@ -// +build kvdb_etcd - -package etcd +package kvdb import ( - "context" "testing" "github.com/btcsuite/btcwallet/walletdb" "github.com/stretchr/testify/require" ) -func TestReadCursorEmptyInterval(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) - - err = db.Update(func(tx walletdb.ReadWriteTx) error { +func testReadCursorEmptyInterval(t *testing.T, db walletdb.DB) { + err := Update(db, func(tx walletdb.ReadWriteTx) error { b, err := tx.CreateTopLevelBucket([]byte("apple")) require.NoError(t, err) require.NotNil(t, b) @@ -28,7 +17,7 @@ func TestReadCursorEmptyInterval(t *testing.T) { }, func() {}) require.NoError(t, err) - err = db.View(func(tx walletdb.ReadTx) error { + err = View(db, func(tx walletdb.ReadTx) error { b := tx.ReadBucket([]byte("apple")) require.NotNil(t, b) @@ -54,15 +43,7 @@ func TestReadCursorEmptyInterval(t *testing.T) { require.NoError(t, err) } -func TestReadCursorNonEmptyInterval(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) - +func testReadCursorNonEmptyInterval(t *testing.T, db walletdb.DB) { testKeyValues := []KV{ {"b", "1"}, {"c", "2"}, @@ -70,7 +51,7 @@ func TestReadCursorNonEmptyInterval(t *testing.T) { {"e", "4"}, } - err = db.Update(func(tx walletdb.ReadWriteTx) error { + err := Update(db, func(tx walletdb.ReadWriteTx) error { b, err := tx.CreateTopLevelBucket([]byte("apple")) require.NoError(t, err) require.NotNil(t, b) @@ -83,7 +64,7 @@ func TestReadCursorNonEmptyInterval(t *testing.T) { require.NoError(t, err) - err = db.View(func(tx walletdb.ReadTx) error { + err = View(db, func(tx walletdb.ReadTx) error { b := tx.ReadBucket([]byte("apple")) require.NotNil(t, b) @@ -131,14 +112,7 @@ func TestReadCursorNonEmptyInterval(t *testing.T) { require.NoError(t, err) } -func TestReadWriteCursor(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) +func testReadWriteCursor(t *testing.T, db walletdb.DB) { testKeyValues := []KV{ {"b", "1"}, @@ -150,7 +124,7 @@ func TestReadWriteCursor(t *testing.T) { count := len(testKeyValues) // Pre-store the first half of the interval. - require.NoError(t, db.Update(func(tx walletdb.ReadWriteTx) error { + require.NoError(t, Update(db, func(tx walletdb.ReadWriteTx) error { b, err := tx.CreateTopLevelBucket([]byte("apple")) require.NoError(t, err) require.NotNil(t, b) @@ -165,13 +139,13 @@ func TestReadWriteCursor(t *testing.T) { return nil }, func() {})) - err = db.Update(func(tx walletdb.ReadWriteTx) error { + err := Update(db, func(tx walletdb.ReadWriteTx) error { b := tx.ReadWriteBucket([]byte("apple")) require.NotNil(t, b) // Store the second half of the interval. for i := count / 2; i < count; i++ { - err = b.Put( + err := b.Put( []byte(testKeyValues[i].key), []byte(testKeyValues[i].val), ) @@ -280,32 +254,14 @@ func TestReadWriteCursor(t *testing.T) { }, func() {}) require.NoError(t, err) - - expected := map[string]string{ - bkey("apple"): bval("apple"), - vkey("a", "apple"): "0", - vkey("c", "apple"): "3", - vkey("cx", "apple"): "x", - vkey("cy", "apple"): "y", - vkey("da", "apple"): "3", - vkey("f", "apple"): "5", - } - require.Equal(t, expected, f.Dump()) } -// TestReadWriteCursorWithBucketAndValue tests that cursors are able to iterate +// testReadWriteCursorWithBucketAndValue tests that cursors are able to iterate // over both bucket and value keys if both are present in the iterated bucket. -func TestReadWriteCursorWithBucketAndValue(t *testing.T) { - t.Parallel() - - f := NewEtcdTestFixture(t) - defer f.Cleanup() - - db, err := newEtcdBackend(context.TODO(), f.BackendConfig()) - require.NoError(t, err) +func testReadWriteCursorWithBucketAndValue(t *testing.T, db walletdb.DB) { // Pre-store the first half of the interval. - require.NoError(t, db.Update(func(tx walletdb.ReadWriteTx) error { + require.NoError(t, Update(db, func(tx walletdb.ReadWriteTx) error { b, err := tx.CreateTopLevelBucket([]byte("apple")) require.NoError(t, err) require.NotNil(t, b) @@ -323,7 +279,7 @@ func TestReadWriteCursorWithBucketAndValue(t *testing.T) { return nil }, func() {})) - err = db.View(func(tx walletdb.ReadTx) error { + err := View(db, func(tx walletdb.ReadTx) error { b := tx.ReadBucket([]byte("apple")) require.NotNil(t, b) @@ -358,12 +314,4 @@ func TestReadWriteCursorWithBucketAndValue(t *testing.T) { }, func() {}) require.NoError(t, err) - - expected := map[string]string{ - bkey("apple"): bval("apple"), - bkey("apple", "banana"): bval("apple", "banana"), - bkey("apple", "pear"): bval("apple", "pear"), - vkey("key", "apple"): "val", - } - require.Equal(t, expected, f.Dump()) } diff --git a/kvdb/readwrite_tx_test.go b/kvdb/readwrite_tx_test.go new file mode 100644 index 00000000..b07e2efb --- /dev/null +++ b/kvdb/readwrite_tx_test.go @@ -0,0 +1,49 @@ +package kvdb + +import ( + "testing" + + "github.com/btcsuite/btcwallet/walletdb" + "github.com/stretchr/testify/require" +) + +func testTxManualCommit(t *testing.T, db walletdb.DB) { + tx, err := db.BeginReadWriteTx() + require.NoError(t, err) + require.NotNil(t, tx) + + committed := false + + tx.OnCommit(func() { + committed = true + }) + + apple, err := tx.CreateTopLevelBucket([]byte("apple")) + require.NoError(t, err) + require.NotNil(t, apple) + require.NoError(t, apple.Put([]byte("testKey"), []byte("testVal"))) + + banana, err := tx.CreateTopLevelBucket([]byte("banana")) + require.NoError(t, err) + require.NotNil(t, banana) + require.NoError(t, banana.Put([]byte("testKey"), []byte("testVal"))) + require.NoError(t, tx.DeleteTopLevelBucket([]byte("banana"))) + + require.NoError(t, tx.Commit()) + require.True(t, committed) +} + +func testTxRollback(t *testing.T, db walletdb.DB) { + tx, err := db.BeginReadWriteTx() + require.Nil(t, err) + require.NotNil(t, tx) + + apple, err := tx.CreateTopLevelBucket([]byte("apple")) + require.Nil(t, err) + require.NotNil(t, apple) + + require.NoError(t, apple.Put([]byte("testKey"), []byte("testVal"))) + + require.NoError(t, tx.Rollback()) + require.Error(t, walletdb.ErrTxClosed, tx.Commit()) +} diff --git a/kvdb/test.go b/kvdb/test.go new file mode 100644 index 00000000..483862e9 --- /dev/null +++ b/kvdb/test.go @@ -0,0 +1,14 @@ +package kvdb + +type KV struct { + key string + val string +} + +func reverseKVs(a []KV) []KV { + for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 { + a[i], a[j] = a[j], a[i] + } + + return a +}