mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00

* [DEV-81] Overwrite maxOpenFiles for testInterface to force tests to check the LRU-mechanism in openFile * [DEV-81] Added database.UseLogger test * [DEV-81] Completed coverage of reconcileDB() * [DEV-81] Added some tests for dbcache * [DEV-81] Moved init and UseLogger to separate file to make them more easily-testable + added tests * [DEV-81] Added tests for deleteFile * [DEV-81] Added tests to cursor.Delete + made sure it returns error when transaction is not writable * [DEV-81] Moved database/error_test.go from database_test package to database package + added test for IsErrorCode * [DEV-81] Added tests for handleRollback error-cases * [DEV-81] Added tests for cursor.skipPendingUpdates * [DEV-81] Added tests for various cursor edge-cases * [DEV-81] tx.putKey no longer returns error, because there is no case when it does * [DEV-81] Added tests to CreateBucket error cases * [DEV-81] Added tests to bucket.Get and .Delete error cases + .Delete now returns error on empty key * [DEV-81] Added test for ForEachBucket * [DEV-81] Added tests to StoreBlock * [DEV-81] Added test for deleting a double nested bucket * [DEV-81] Removed log_test, as it is no longer necessary with the logging system re-design * [DEV-81] Added test to some of writePendingAndCommit error-cases * [DEV-81] Update references from btcutil to btcd/util * [DEV-81] Add tests for dbCacheIterator{.Next(), .Prev(), .Key, .Value()} in cases when iterator is exhausted * [DEV-81] Added tests for ldbIterator placeholder functions * [DEV-81] Added test name to Error messsages in TestSkipPendingUpdates * [DEV-81] Begin writing TestSkipPendingUpdatesCache * [DEV-81] Added error-cases for DBCache.flush() and DBCache.commitTreaps() * [DEV-81] Use monkey.patch from bou.ke and not from github * [DEV-81] Rewrote IsErrorCode in both database and txscript packages to be more concise * [DEV-81] Rename any database.Tx to dbTx instead of tx - to remove confusion with coin Tx * [DEV-81] Fix typo * [DEV-81] Use os.TempDir() instead of /tmp/ to be cross-platform * [DEV-81] use SimNet for database tests + Error if testDB exists after deleting it * [DEV-81] Removed useLogger - it's redundant * [DEV-81] Added comment on how CRC32 checksums are calculated in reconcile_test.go * [DEV-81] Added comment that explains what setWriteRow does * [DEV-81] Use constant instead of hard-coded value * [DEV-81] Fixed some typo's + better formatting
320 lines
9.7 KiB
Go
320 lines
9.7 KiB
Go
package ffldb
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"testing"
|
|
|
|
"bou.ke/monkey"
|
|
"github.com/btcsuite/goleveldb/leveldb"
|
|
"github.com/btcsuite/goleveldb/leveldb/opt"
|
|
ldbutil "github.com/btcsuite/goleveldb/leveldb/util"
|
|
"github.com/daglabs/btcd/database"
|
|
)
|
|
|
|
// TestDBCacheCloseErrors tests all error-cases in *dbCache.Close().
|
|
// The non-error-cases are tested in the more general tests.
|
|
func TestDBCacheCloseErrors(t *testing.T) {
|
|
cache := newTestDb("TestDBCacheCloseErrors", t).cache
|
|
defer cache.Close()
|
|
|
|
closeCalled := false
|
|
closePatch := monkey.Patch((*leveldb.DB).Close, func(*leveldb.DB) error { closeCalled = true; return nil })
|
|
defer closePatch.Unpatch()
|
|
|
|
expectedErr := errors.New("error on flush")
|
|
|
|
flushPatch := monkey.Patch((*dbCache).flush, func(*dbCache) error { return expectedErr })
|
|
defer flushPatch.Unpatch()
|
|
|
|
err := cache.Close()
|
|
if err != expectedErr {
|
|
t.Errorf("TestDBCacheCloseErrors: Expected error on bad flush is %s but got %s", expectedErr, err)
|
|
}
|
|
if !closeCalled {
|
|
t.Errorf("TestDBCacheCloseErrors: ldb.Close was not called when error flushing")
|
|
}
|
|
}
|
|
|
|
// TestUpdateDBErrors tests all error-cases in *dbCache.UpdateDB().
|
|
// The non-error-cases are tested in the more general tests.
|
|
func TestUpdateDBErrors(t *testing.T) {
|
|
// Test when ldb.OpenTransaction returns error
|
|
func() {
|
|
cache := newTestDb("TestDBCacheCloseErrors", t).cache
|
|
defer cache.Close()
|
|
|
|
patch := monkey.Patch((*leveldb.DB).OpenTransaction,
|
|
func(*leveldb.DB) (*leveldb.Transaction, error) { return nil, errors.New("error in OpenTransaction") })
|
|
defer patch.Unpatch()
|
|
|
|
err := cache.updateDB(func(ldbTx *leveldb.Transaction) error { return nil })
|
|
if err == nil {
|
|
t.Errorf("No error in updateDB when ldb.OpenTransaction returns an error")
|
|
}
|
|
}()
|
|
|
|
// Test when ldbTx.Commit returns an error
|
|
func() {
|
|
cache := newTestDb("TestDBCacheCloseErrors", t).cache
|
|
defer cache.Close()
|
|
|
|
patch := monkey.Patch((*leveldb.Transaction).Commit,
|
|
func(*leveldb.Transaction) error { return errors.New("error in Commit") })
|
|
defer patch.Unpatch()
|
|
|
|
err := cache.updateDB(func(ldbTx *leveldb.Transaction) error { return nil })
|
|
if err == nil {
|
|
t.Errorf("No error in updateDB when ldbTx.Commit returns an error")
|
|
}
|
|
}()
|
|
|
|
cache := newTestDb("TestDBCacheCloseErrors", t).cache
|
|
defer cache.Close()
|
|
|
|
// Test when function passed to updateDB returns an error
|
|
err := cache.updateDB(func(ldbTx *leveldb.Transaction) error { return errors.New("Error in fn") })
|
|
if err == nil {
|
|
t.Errorf("No error in updateDB when passed function returns an error")
|
|
}
|
|
}
|
|
|
|
// TestCommitTxFlushNeeded test the *dbCache.commitTx function when flush is needed,
|
|
// including error-cases.
|
|
// When flush is not needed is tested in the more general tests.
|
|
func TestCommitTxFlushNeeded(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
target interface{}
|
|
replacement interface{}
|
|
expectedError bool
|
|
}{
|
|
{"No errors", nil, nil, false},
|
|
{"Error in flush", (*dbCache).flush, func(*dbCache) error { return errors.New("error") }, true},
|
|
{"Error in commitTreaps", (*dbCache).commitTreaps,
|
|
func(*dbCache, TreapForEacher, TreapForEacher) error { return errors.New("error") }, true},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
func() {
|
|
db := newTestDb("TestDBCacheCloseErrors", t)
|
|
defer db.Close()
|
|
cache := db.cache
|
|
|
|
cache.flushInterval = 0 // set flushInterval to 0 so that flush is always required
|
|
|
|
if test.target != nil && test.replacement != nil {
|
|
patch := monkey.Patch(test.target, test.replacement)
|
|
defer patch.Unpatch()
|
|
}
|
|
|
|
tx, err := db.Begin(true)
|
|
if err != nil {
|
|
t.Fatalf("Error begining transaction: %s", err)
|
|
}
|
|
cache.commitTx(tx.(*transaction))
|
|
db.closeLock.RUnlock()
|
|
}()
|
|
}
|
|
}
|
|
|
|
func TestExhaustedDbCacheIterator(t *testing.T) {
|
|
db := newTestDb("TestExhaustedDbCacheIterator", t)
|
|
defer db.Close()
|
|
|
|
snapshot, err := db.cache.Snapshot()
|
|
if err != nil {
|
|
t.Fatalf("TestExhaustedDbCacheIterator: Error creating cache snapshot: %s", err)
|
|
}
|
|
iterator := snapshot.NewIterator(&ldbutil.Range{})
|
|
|
|
if next := iterator.Next(); next != false {
|
|
t.Errorf("TestExhaustedDbCacheIterator: Expected .Next() = false, but got %v", next)
|
|
}
|
|
|
|
if prev := iterator.Prev(); prev != false {
|
|
t.Errorf("TestExhaustedDbCacheIterator: Expected .Prev() = false, but got %v", prev)
|
|
}
|
|
|
|
if key := iterator.Key(); key != nil {
|
|
t.Errorf("TestExhaustedDbCacheIterator: Expected .Key() = nil, but got %v", key)
|
|
}
|
|
|
|
if value := iterator.Value(); value != nil {
|
|
t.Errorf("TestExhaustedDbCacheIterator: Expected .Value() = nil, but got %v", value)
|
|
}
|
|
}
|
|
|
|
// TestLDBIteratorImplPlaceholders hits functions that are there to implement leveldb iterator.Iterator interface,
|
|
// but surve no other purpose.
|
|
func TestLDBIteratorImplPlaceholders(t *testing.T) {
|
|
db := newTestDb("TestIteratorImplPlaceholders", t)
|
|
defer db.Close()
|
|
|
|
snapshot, err := db.cache.Snapshot()
|
|
if err != nil {
|
|
t.Fatalf("TestLDBIteratorImplPlaceholders: Error creating cache snapshot: %s", err)
|
|
}
|
|
iterator := newLdbCacheIter(snapshot, &ldbutil.Range{})
|
|
|
|
if err = iterator.Error(); err != nil {
|
|
t.Errorf("TestLDBIteratorImplPlaceholders: Expected .Error() = nil, but got %v", err)
|
|
}
|
|
|
|
// Call SetReleaser to achieve coverage of it. Actually does nothing
|
|
iterator.SetReleaser(nil)
|
|
}
|
|
|
|
func TestSkipPendingUpdatesCache(t *testing.T) {
|
|
pdb := newTestDb("TestSkipPendingUpdatesCache", t)
|
|
defer pdb.Close()
|
|
|
|
value := []byte("value")
|
|
// Add numbered prefixes to keys so that they are in expected order, and before any other keys
|
|
firstKey := []byte("1 - first")
|
|
toDeleteKey := []byte("2 - toDelete")
|
|
toUpdateKey := []byte("3 - toUpdate")
|
|
secondKey := []byte("4 - second")
|
|
|
|
// create initial metadata for test
|
|
err := pdb.Update(func(dbTx database.Tx) error {
|
|
metadata := dbTx.Metadata()
|
|
if err := metadata.Put(firstKey, value); err != nil {
|
|
return err
|
|
}
|
|
if err := metadata.Put(toDeleteKey, value); err != nil {
|
|
return err
|
|
}
|
|
if err := metadata.Put(toUpdateKey, value); err != nil {
|
|
return err
|
|
}
|
|
if err := metadata.Put(secondKey, value); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Error adding to metadata: %s", err)
|
|
}
|
|
|
|
err = pdb.cache.flush()
|
|
if err != nil {
|
|
t.Fatalf("Error flushing cache: %s", err)
|
|
}
|
|
|
|
// test skips
|
|
err = pdb.Update(func(dbTx database.Tx) error {
|
|
snapshot, err := pdb.cache.Snapshot()
|
|
if err != nil {
|
|
t.Fatalf("TestSkipPendingUpdatesCache: Error getting snapshot: %s", err)
|
|
}
|
|
|
|
iterator := snapshot.NewIterator(&ldbutil.Range{})
|
|
snapshot.pendingRemove = snapshot.pendingRemove.Put(bucketizedKey(metadataBucketID, toDeleteKey), value)
|
|
snapshot.pendingKeys = snapshot.pendingKeys.Put(bucketizedKey(metadataBucketID, toUpdateKey), value)
|
|
|
|
// Check that first is ok
|
|
iterator.First()
|
|
expectedKey := bucketizedKey(metadataBucketID, firstKey)
|
|
actualKey := iterator.Key()
|
|
if !bytes.Equal(actualKey, expectedKey) {
|
|
t.Errorf("TestSkipPendingUpdatesCache: 1: key expected to be %v but is %v", expectedKey, actualKey)
|
|
}
|
|
|
|
// Go to the next key, which is second, toDelete and toUpdate will be skipped
|
|
iterator.Next()
|
|
expectedKey = bucketizedKey(metadataBucketID, secondKey)
|
|
actualKey = iterator.Key()
|
|
if !bytes.Equal(actualKey, expectedKey) {
|
|
t.Errorf("TestSkipPendingUpdatesCache: 2: key expected to be %s but is %s", expectedKey, actualKey)
|
|
}
|
|
|
|
// now traverse backwards - should get first, toUpdate and toDelete will be skipped
|
|
iterator.Prev()
|
|
expectedKey = bucketizedKey(metadataBucketID, firstKey)
|
|
actualKey = iterator.Key()
|
|
if !bytes.Equal(actualKey, expectedKey) {
|
|
t.Errorf("TestSkipPendingUpdatesCache: 4: key expected to be %s but is %s", expectedKey, actualKey)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("TestSkipPendingUpdatesCache: Error running main part of test: %s", err)
|
|
}
|
|
}
|
|
|
|
// TestFlushCommitTreapsErrors tests error-cases in *dbCache.flush() when commitTreaps returns error.
|
|
// The non-error-cases are tested in the more general tests.
|
|
func TestFlushCommitTreapsErrors(t *testing.T) {
|
|
pdb := newTestDb("TestFlushCommitTreapsErrors", t)
|
|
defer pdb.Close()
|
|
|
|
key := []byte("key")
|
|
value := []byte("value")
|
|
|
|
// Before setting flush interval to zero - put some data so that there's something to flush
|
|
err := pdb.Update(func(dbTx database.Tx) error {
|
|
metadata := dbTx.Metadata()
|
|
metadata.Put(key, value)
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("TestFlushCommitTreapsErrors: Error putting some data to flush: %s", err)
|
|
}
|
|
|
|
cache := pdb.cache
|
|
cache.flushInterval = 0 // set flushInterval to 0 so that flush is always required
|
|
|
|
// Test for correctness when encountered error on Put
|
|
func() {
|
|
patch := monkey.Patch((*leveldb.Transaction).Put,
|
|
func(*leveldb.Transaction, []byte, []byte, *opt.WriteOptions) error { return errors.New("error") })
|
|
defer patch.Unpatch()
|
|
|
|
err := pdb.Update(func(dbTx database.Tx) error {
|
|
metadata := dbTx.Metadata()
|
|
metadata.Put(key, value)
|
|
|
|
return nil
|
|
})
|
|
|
|
if err == nil {
|
|
t.Errorf("TestFlushCommitTreapsErrors: No error from pdb.Update when ldbTx.Put returned error")
|
|
}
|
|
}()
|
|
|
|
// Test for correctness when encountered error on Delete
|
|
|
|
// First put some data we can later "fail" to delete
|
|
err = pdb.Update(func(dbTx database.Tx) error {
|
|
metadata := dbTx.Metadata()
|
|
metadata.Put(key, value)
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("TestFlushCommitTreapsErrors: Error putting some data to delete: %s", err)
|
|
}
|
|
|
|
// Now "fail" to delete it
|
|
func() {
|
|
patch := monkey.Patch((*leveldb.Transaction).Delete,
|
|
func(*leveldb.Transaction, []byte, *opt.WriteOptions) error { return errors.New("error") })
|
|
defer patch.Unpatch()
|
|
|
|
err := pdb.Update(func(dbTx database.Tx) error {
|
|
metadata := dbTx.Metadata()
|
|
metadata.Delete(key)
|
|
|
|
return nil
|
|
})
|
|
|
|
if err == nil {
|
|
t.Errorf("TestFlushCommitTreapsErrors: No error from pdb.Update when ldbTx.Delete returned error")
|
|
}
|
|
}()
|
|
}
|