/** * @file * @copyright defined in aergo/LICENSE.txt */ package db import ( "fmt" "io/ioutil" "log" "os" "strconv" "testing" "github.com/stretchr/testify/assert" ) const ( tmpDbTestKey1 = "tempkey1" tmpDbTestKey2 = "tempkey2" tmpDbTestStrVal1 = "val1" tmpDbTestStrVal2 = "val2" tmpDbTestIntVal1 = 1 tmpDbTestIntVal2 = 2 ) func createTmpDB(key ImplType) (dir string, db DB) { dir, err := ioutil.TempDir("", string(key)) if err != nil { log.Fatal(err) } db = NewDB(key, dir) return } func setInitData(db DB) { tx := db.NewTx() tx.Set([]byte("1"), []byte("1")) tx.Set([]byte("2"), []byte("2")) tx.Set([]byte("3"), []byte("3")) tx.Set([]byte("4"), []byte("4")) tx.Set([]byte("5"), []byte("5")) tx.Set([]byte("6"), []byte("6")) tx.Set([]byte("7"), []byte("7")) tx.Commit() } func TestGetSetDeleteExist(t *testing.T) { // for each db implementation for key := range dbImpls { dir, db := createTmpDB(key) // initial value of empty key must be empty byte assert.Empty(t, db.Get([]byte(tmpDbTestKey1)), db.Type()) assert.False(t, db.Exist([]byte(tmpDbTestKey1)), db.Type()) // set value db.Set([]byte(tmpDbTestKey1), []byte(tmpDbTestStrVal1)) // check value set assert.Equal(t, tmpDbTestStrVal1, string(db.Get([]byte(tmpDbTestKey1))), db.Type()) assert.True(t, db.Exist([]byte(tmpDbTestKey1)), db.Type()) // delete value db.Delete([]byte(tmpDbTestKey1)) // value must be erased assert.Empty(t, db.Get([]byte(tmpDbTestKey1)), db.Type()) assert.False(t, db.Exist([]byte(tmpDbTestKey1)), db.Type()) db.Close() os.RemoveAll(dir) } } func TestTransactionSet(t *testing.T) { for key := range dbImpls { dir, db := createTmpDB(key) // create a new writable tx tx := db.NewTx() // set the value in the tx tx.Set([]byte(tmpDbTestKey1), []byte(tmpDbTestStrVal1)) // the value will not visible at a db assert.Empty(t, db.Get([]byte(tmpDbTestKey1)), db.Type()) tx.Commit() // after commit, the value visible from the db assert.Equal(t, tmpDbTestStrVal1, string(db.Get([]byte(tmpDbTestKey1))), db.Type()) db.Close() os.RemoveAll(dir) } } func TestTransactionDiscard(t *testing.T) { for key := range dbImpls { dir, db := createTmpDB(key) // create a new writable tx tx := db.NewTx() // discard test tx = db.NewTx() // set the value in the tx tx.Set([]byte(tmpDbTestKey1), []byte(tmpDbTestStrVal2)) // discard tx tx.Discard() assert.Panics(t, func() { tx.Commit() }, "commit after discard is not allowed") // after discard, the value must be reset at the db assert.False(t, db.Exist([]byte(tmpDbTestKey1)), db.Type()) db.Close() os.RemoveAll(dir) } } func TestTransactionDelete(t *testing.T) { for key := range dbImpls { dir, db := createTmpDB(key) // create a new writable tx tx := db.NewTx() // set the value in the tx tx.Set([]byte(tmpDbTestKey1), []byte(tmpDbTestStrVal1)) // delete the value in the tx tx.Delete([]byte(tmpDbTestKey1)) tx.Commit() // after commit, chekc the value from the db assert.Equal(t, "", string(db.Get([]byte(tmpDbTestKey1))), db.Type()) db.Close() os.RemoveAll(dir) } } func TestTransactionCommitTwice(t *testing.T) { for key := range dbImpls { dir, db := createTmpDB(key) // create a new writable tx tx := db.NewTx() // a first commit will success tx.Commit() // a second commit will cause panic assert.Panics(t, func() { tx.Commit() }) db.Close() os.RemoveAll(dir) } } func TestBulk(t *testing.T) { for key := range dbImpls { dir, db := createTmpDB(key) // create a new Bulk instance bulk := db.NewBulk() // set the huge number of value in the bulk for i := 0; i < 1000000; i++ { bulk.Set([]byte(fmt.Sprintf("key%d", i)), []byte(tmpDbTestStrVal1)) } bulk.Flush() // after commit, the value visible from the db for i := 0; i < 1000000; i++ { assert.Equal(t, tmpDbTestStrVal1, string(db.Get([]byte(fmt.Sprintf("key%d", i)))), db.Type()) } db.Close() os.RemoveAll(dir) } } func TestIter(t *testing.T) { for key := range dbImpls { dir, db := createTmpDB(key) setInitData(db) i := 1 for iter := db.Iterator(nil, nil); iter.Valid(); iter.Next() { assert.EqualValues(t, strconv.Itoa(i), string(iter.Key())) i++ } db.Close() os.RemoveAll(dir) } } func TestRangeIter(t *testing.T) { for key := range dbImpls { dir, db := createTmpDB(key) setInitData(db) // test iteration 2 -> 5 i := 2 for iter := db.Iterator([]byte("2"), []byte("5")); iter.Valid(); iter.Next() { assert.EqualValues(t, strconv.Itoa(i), string(iter.Key())) assert.EqualValues(t, strconv.Itoa(i), string(iter.Value())) i++ } assert.EqualValues(t, i, 5) // nil sames with []byte("0") // test iteration 0 -> 5 i = 1 for iter := db.Iterator(nil, []byte("5")); iter.Valid(); iter.Next() { assert.EqualValues(t, strconv.Itoa(i), string(iter.Key())) assert.EqualValues(t, strconv.Itoa(i), string(iter.Value())) i++ } assert.EqualValues(t, i, 5) db.Close() os.RemoveAll(dir) } } func TestReverseIter(t *testing.T) { for key := range dbImpls { dir, db := createTmpDB(key) setInitData(db) // test reverse iteration 5 <- 2 i := 5 for iter := db.Iterator([]byte("5"), []byte("2")); iter.Valid(); iter.Next() { assert.EqualValues(t, strconv.Itoa(i), string(iter.Key())) assert.EqualValues(t, strconv.Itoa(i), string(iter.Value())) i-- } assert.EqualValues(t, i, 2) // nil sames with []byte("0") // test reverse iteration 5 -> 0 i = 5 for iter := db.Iterator([]byte("5"), nil); iter.Valid(); iter.Next() { assert.EqualValues(t, strconv.Itoa(i), string(iter.Key())) assert.EqualValues(t, strconv.Itoa(i), string(iter.Value())) i-- } assert.EqualValues(t, i, 0) db.Close() os.RemoveAll(dir) } }