bolt/transaction_test.go

247 lines
6.2 KiB
Go

package bolt
import (
"fmt"
"os"
"sort"
"testing"
"testing/quick"
"github.com/stretchr/testify/assert"
)
// Ensure that the database can retrieve a list of buckets.
func TestTransactionBuckets(t *testing.T) {
withOpenDB(func(db *DB, path string) {
db.CreateBucket("foo")
db.CreateBucket("bar")
db.CreateBucket("baz")
buckets, err := db.Buckets()
if assert.NoError(t, err) && assert.Equal(t, len(buckets), 3) {
assert.Equal(t, buckets[0].Name(), "bar")
assert.Equal(t, buckets[1].Name(), "baz")
assert.Equal(t, buckets[2].Name(), "foo")
}
})
}
// Ensure that a Transaction can retrieve a bucket.
func TestTransactionBucketMissing(t *testing.T) {
withOpenDB(func(db *DB, path string) {
db.CreateBucket("widgets")
b, err := db.Bucket("widgets")
assert.NoError(t, err)
if assert.NotNil(t, b) {
assert.Equal(t, "widgets", b.Name())
}
})
}
// Ensure that a Transaction retrieving a non-existent key returns nil.
func TestTransactionGetMissing(t *testing.T) {
withOpenDB(func(db *DB, path string) {
db.CreateBucket("widgets")
db.Put("widgets", []byte("foo"), []byte("bar"))
value, err := db.Get("widgets", []byte("no_such_key"))
assert.NoError(t, err)
assert.Nil(t, value)
})
}
// Ensure that a Transaction cursor can iterate over an empty bucket without error.
func TestTransactionCursorEmptyBucket(t *testing.T) {
withOpenDB(func(db *DB, path string) {
db.CreateBucket("widgets")
txn, _ := db.Transaction()
c, err := txn.Cursor("widgets")
assert.NoError(t, err)
k, v := c.First()
assert.Nil(t, k)
assert.Nil(t, v)
txn.Close()
})
}
// Ensure that a Transaction returns a nil when a bucket doesn't exist.
func TestTransactionCursorMissingBucket(t *testing.T) {
withOpenDB(func(db *DB, path string) {
db.CreateBucket("widgets")
txn, _ := db.Transaction()
c, err := txn.Cursor("woojits")
assert.Nil(t, c)
assert.Equal(t, err, ErrBucketNotFound)
txn.Close()
})
}
// Ensure that a Transaction cursor can iterate over a single root with a couple elements.
func TestTransactionCursorLeafRoot(t *testing.T) {
withOpenDB(func(db *DB, path string) {
db.CreateBucket("widgets")
db.Put("widgets", []byte("baz"), []byte{})
db.Put("widgets", []byte("foo"), []byte{0})
db.Put("widgets", []byte("bar"), []byte{1})
txn, _ := db.Transaction()
c, err := txn.Cursor("widgets")
assert.NoError(t, err)
k, v := c.First()
assert.Equal(t, string(k), "bar")
assert.Equal(t, v, []byte{1})
k, v = c.Next()
assert.Equal(t, string(k), "baz")
assert.Equal(t, v, []byte{})
k, v = c.Next()
assert.Equal(t, string(k), "foo")
assert.Equal(t, v, []byte{0})
k, v = c.Next()
assert.Nil(t, k)
assert.Nil(t, v)
k, v = c.Next()
assert.Nil(t, k)
assert.Nil(t, v)
txn.Close()
})
}
// Ensure that a Transaction cursor can iterate in reverse over a single root with a couple elements.
func TestTransactionCursorLeafRootReverse(t *testing.T) {
withOpenDB(func(db *DB, path string) {
db.CreateBucket("widgets")
db.Put("widgets", []byte("baz"), []byte{})
db.Put("widgets", []byte("foo"), []byte{0})
db.Put("widgets", []byte("bar"), []byte{1})
txn, _ := db.Transaction()
c, err := txn.Cursor("widgets")
assert.NoError(t, err)
k, v := c.Last()
assert.Equal(t, string(k), "foo")
assert.Equal(t, v, []byte{0})
k, v = c.Prev()
assert.Equal(t, string(k), "baz")
assert.Equal(t, v, []byte{})
k, v = c.Prev()
assert.Equal(t, string(k), "bar")
assert.Equal(t, v, []byte{1})
k, v = c.Prev()
assert.Nil(t, k)
assert.Nil(t, v)
k, v = c.Prev()
assert.Nil(t, k)
assert.Nil(t, v)
txn.Close()
})
}
// Ensure that a Transaction cursor can restart from the beginning.
func TestTransactionCursorRestart(t *testing.T) {
withOpenDB(func(db *DB, path string) {
db.CreateBucket("widgets")
db.Put("widgets", []byte("bar"), []byte{})
db.Put("widgets", []byte("foo"), []byte{})
txn, _ := db.Transaction()
c, err := txn.Cursor("widgets")
assert.NoError(t, err)
k, _ := c.First()
assert.Equal(t, string(k), "bar")
k, _ = c.Next()
assert.Equal(t, string(k), "foo")
k, _ = c.First()
assert.Equal(t, string(k), "bar")
k, _ = c.Next()
assert.Equal(t, string(k), "foo")
txn.Close()
})
}
// Ensure that a transaction can iterate over all elements in a bucket.
func TestTransactionCursorIterate(t *testing.T) {
f := func(items testdata) bool {
withOpenDB(func(db *DB, path string) {
// Bulk insert all values.
db.CreateBucket("widgets")
rwtxn, _ := db.RWTransaction()
for _, item := range items {
assert.NoError(t, rwtxn.Put("widgets", item.Key, item.Value))
}
assert.NoError(t, rwtxn.Commit())
// Sort test data.
sort.Sort(items)
// Iterate over all items and check consistency.
var index = 0
txn, _ := db.Transaction()
c, err := txn.Cursor("widgets")
assert.NoError(t, err)
for k, v := c.First(); k != nil && index < len(items); k, v = c.Next() {
assert.Equal(t, k, items[index].Key)
assert.Equal(t, v, items[index].Value)
index++
}
assert.Equal(t, len(items), index)
txn.Close()
})
fmt.Fprint(os.Stderr, ".")
return true
}
if err := quick.Check(f, qconfig()); err != nil {
t.Error(err)
}
fmt.Fprint(os.Stderr, "\n")
}
// Ensure that a transaction can iterate over all elements in a bucket in reverse.
func TestTransactionCursorIterateReverse(t *testing.T) {
f := func(items testdata) bool {
withOpenDB(func(db *DB, path string) {
// Bulk insert all values.
db.CreateBucket("widgets")
rwtxn, _ := db.RWTransaction()
for _, item := range items {
assert.NoError(t, rwtxn.Put("widgets", item.Key, item.Value))
}
assert.NoError(t, rwtxn.Commit())
// Sort test data.
sort.Sort(revtestdata(items))
// Iterate over all items and check consistency.
var index = 0
txn, _ := db.Transaction()
c, err := txn.Cursor("widgets")
assert.NoError(t, err)
for k, v := c.Last(); k != nil && index < len(items); k, v = c.Prev() {
assert.Equal(t, k, items[index].Key)
assert.Equal(t, v, items[index].Value)
index++
}
assert.Equal(t, len(items), index)
txn.Close()
})
fmt.Fprint(os.Stderr, ".")
return true
}
if err := quick.Check(f, qconfig()); err != nil {
t.Error(err)
}
fmt.Fprint(os.Stderr, "\n")
}