bbolt/rwtransaction_test.go

182 lines
4.9 KiB
Go

package bolt
import (
"math/rand"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
// Ensure that a RWTx can be retrieved.
func TestRWTx(t *testing.T) {
withOpenDB(func(db *DB, path string) {
txn, err := db.RWTx()
assert.NotNil(t, txn)
assert.NoError(t, err)
assert.Equal(t, txn.DB(), db)
})
}
// Ensure that opening a RWTx while the DB is closed returns an error.
func TestRWTxOpenWithClosedDB(t *testing.T) {
withDB(func(db *DB, path string) {
txn, err := db.RWTx()
assert.Equal(t, err, ErrDatabaseNotOpen)
assert.Nil(t, txn)
})
}
// Ensure that retrieving all buckets returns writable buckets.
func TestRWTxBuckets(t *testing.T) {
withOpenDB(func(db *DB, path string) {
db.CreateBucket("widgets")
db.CreateBucket("woojits")
db.Do(func(txn *RWTx) error {
buckets := txn.Buckets()
assert.Equal(t, len(buckets), 2)
assert.Equal(t, buckets[0].Name(), "widgets")
assert.Equal(t, buckets[1].Name(), "woojits")
buckets[0].Put([]byte("foo"), []byte("0000"))
buckets[1].Put([]byte("bar"), []byte("0001"))
return nil
})
v, _ := db.Get("widgets", []byte("foo"))
assert.Equal(t, v, []byte("0000"))
v, _ = db.Get("woojits", []byte("bar"))
assert.Equal(t, v, []byte("0001"))
})
}
// Ensure that a bucket can be created and retrieved.
func TestRWTxCreateBucket(t *testing.T) {
withOpenDB(func(db *DB, path string) {
// Create a bucket.
err := db.CreateBucket("widgets")
assert.NoError(t, err)
// Read the bucket through a separate transaction.
b, err := db.Bucket("widgets")
assert.NotNil(t, b)
assert.NoError(t, err)
})
}
// Ensure that a bucket can be created if it doesn't already exist.
func TestRWTxCreateBucketIfNotExists(t *testing.T) {
withOpenDB(func(db *DB, path string) {
assert.NoError(t, db.CreateBucketIfNotExists("widgets"))
assert.NoError(t, db.CreateBucketIfNotExists("widgets"))
assert.Equal(t, db.CreateBucketIfNotExists(""), ErrBucketNameRequired)
// Read the bucket through a separate transaction.
b, err := db.Bucket("widgets")
assert.NotNil(t, b)
assert.NoError(t, err)
})
}
// Ensure that a bucket cannot be created twice.
func TestRWTxRecreateBucket(t *testing.T) {
withOpenDB(func(db *DB, path string) {
// Create a bucket.
err := db.CreateBucket("widgets")
assert.NoError(t, err)
// Create the same bucket again.
err = db.CreateBucket("widgets")
assert.Equal(t, err, ErrBucketExists)
})
}
// Ensure that a bucket is created with a non-blank name.
func TestRWTxCreateBucketWithoutName(t *testing.T) {
withOpenDB(func(db *DB, path string) {
err := db.CreateBucket("")
assert.Equal(t, err, ErrBucketNameRequired)
})
}
// Ensure that a bucket name is not too long.
func TestRWTxCreateBucketWithLongName(t *testing.T) {
withOpenDB(func(db *DB, path string) {
err := db.CreateBucket(strings.Repeat("X", 255))
assert.NoError(t, err)
err = db.CreateBucket(strings.Repeat("X", 256))
assert.Equal(t, err, ErrBucketNameTooLarge)
})
}
// Ensure that a bucket can be deleted.
func TestRWTxDeleteBucket(t *testing.T) {
withOpenDB(func(db *DB, path string) {
// Create a bucket and add a value.
db.CreateBucket("widgets")
db.Put("widgets", []byte("foo"), []byte("bar"))
b, _ := db.Bucket("widgets")
// Delete the bucket and make sure we can't get the value.
assert.NoError(t, db.DeleteBucket("widgets"))
value, err := db.Get("widgets", []byte("foo"))
assert.Equal(t, err, ErrBucketNotFound)
assert.Nil(t, value)
// Verify that the bucket's page is free.
assert.Equal(t, db.freelist.all(), []pgid{b.root})
// Create the bucket again and make sure there's not a phantom value.
assert.NoError(t, db.CreateBucket("widgets"))
value, err = db.Get("widgets", []byte("foo"))
assert.NoError(t, err)
assert.Nil(t, value)
})
}
// Ensure that an error is returned when deleting from a bucket that doesn't exist.
func TestRWTxDeleteBucketNotFound(t *testing.T) {
withOpenDB(func(db *DB, path string) {
err := db.DeleteBucket("widgets")
assert.Equal(t, err, ErrBucketNotFound)
})
}
// Benchmark the performance of bulk put transactions in random order.
func BenchmarkRWTxPutRandom(b *testing.B) {
indexes := rand.Perm(b.N)
value := []byte(strings.Repeat("0", 64))
withOpenDB(func(db *DB, path string) {
db.CreateBucket("widgets")
var txn *RWTx
var bucket *Bucket
for i := 0; i < b.N; i++ {
if i%1000 == 0 {
if txn != nil {
txn.Commit()
}
txn, _ = db.RWTx()
bucket = txn.Bucket("widgets")
}
bucket.Put([]byte(strconv.Itoa(indexes[i])), value)
}
txn.Commit()
})
}
// Benchmark the performance of bulk put transactions in sequential order.
func BenchmarkRWTxPutSequential(b *testing.B) {
value := []byte(strings.Repeat("0", 64))
withOpenDB(func(db *DB, path string) {
db.CreateBucket("widgets")
db.Do(func(txn *RWTx) error {
bucket := txn.Bucket("widgets")
for i := 0; i < b.N; i++ {
bucket.Put([]byte(strconv.Itoa(i)), value)
}
return nil
})
})
}