kvdb/test: generalize etcd tests
This commit is contained in:
parent
bb5c3f3b51
commit
3c6d35ec41
@ -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)))
|
||||
}
|
||||
|
@ -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)))
|
||||
}
|
@ -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())
|
||||
}
|
||||
|
@ -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)
|
@ -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())
|
||||
}
|
||||
|
154
kvdb/etcd_test.go
Normal file
154
kvdb/etcd_test.go
Normal file
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
@ -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())
|
||||
}
|
49
kvdb/readwrite_tx_test.go
Normal file
49
kvdb/readwrite_tx_test.go
Normal file
@ -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())
|
||||
}
|
14
kvdb/test.go
Normal file
14
kvdb/test.go
Normal file
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user