mirror of
https://github.com/luxfi/database.git
synced 2026-10-30 15:01:18 +00:00
208 lines
5.3 KiB
Go
208 lines
5.3 KiB
Go
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package corruptabledb
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/mock/gomock"
|
|
|
|
"github.com/luxfi/database"
|
|
"github.com/luxfi/database/databasemock"
|
|
"github.com/luxfi/database/dbtest"
|
|
"github.com/luxfi/database/memdb"
|
|
log "github.com/luxfi/log"
|
|
)
|
|
|
|
var errTest = errors.New("non-nil error")
|
|
|
|
func newDB() *Database {
|
|
baseDB := memdb.New()
|
|
return New(baseDB, log.NewNoOpLogger())
|
|
}
|
|
|
|
func TestInterface(t *testing.T) {
|
|
for name, test := range dbtest.Tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
test(t, newDB())
|
|
})
|
|
}
|
|
}
|
|
|
|
func FuzzKeyValue(f *testing.F) {
|
|
dbtest.FuzzKeyValue(f, newDB())
|
|
}
|
|
|
|
func FuzzNewIteratorWithPrefix(f *testing.F) {
|
|
dbtest.FuzzNewIteratorWithPrefix(f, newDB())
|
|
}
|
|
|
|
func FuzzNewIteratorWithStartAndPrefix(f *testing.F) {
|
|
dbtest.FuzzNewIteratorWithStartAndPrefix(f, newDB())
|
|
}
|
|
|
|
// TestCorruption tests to make sure corruptabledb wrapper works as expected.
|
|
func TestCorruption(t *testing.T) {
|
|
key := []byte("hello")
|
|
value := []byte("world")
|
|
tests := map[string]func(db database.Database) error{
|
|
"corrupted has": func(db database.Database) error {
|
|
_, err := db.Has(key)
|
|
return err
|
|
},
|
|
"corrupted get": func(db database.Database) error {
|
|
_, err := db.Get(key)
|
|
return err
|
|
},
|
|
"corrupted put": func(db database.Database) error {
|
|
return db.Put(key, value)
|
|
},
|
|
"corrupted delete": func(db database.Database) error {
|
|
return db.Delete(key)
|
|
},
|
|
"corrupted batch": func(db database.Database) error {
|
|
corruptableBatch := db.NewBatch()
|
|
require.NotNil(t, corruptableBatch)
|
|
|
|
require.NoError(t, corruptableBatch.Put(key, value))
|
|
|
|
return corruptableBatch.Write()
|
|
},
|
|
"corrupted healthcheck": func(db database.Database) error {
|
|
_, err := db.HealthCheck(context.Background())
|
|
return err
|
|
},
|
|
}
|
|
corruptableDB := newDB()
|
|
_ = corruptableDB.handleError(errTest)
|
|
for name, testFn := range tests {
|
|
t.Run(name, func(tt *testing.T) {
|
|
err := testFn(corruptableDB)
|
|
require.ErrorIs(tt, err, errTest)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIterator(t *testing.T) {
|
|
errIter := errors.New("iterator error")
|
|
|
|
type test struct {
|
|
name string
|
|
databaseErrBefore error
|
|
modifyIter func(*gomock.Controller, *iterator)
|
|
op func(*require.Assertions, *iterator)
|
|
expectedErr error
|
|
}
|
|
|
|
tests := []test{
|
|
{
|
|
name: "corrupted database; Next",
|
|
databaseErrBefore: errTest,
|
|
expectedErr: errTest,
|
|
modifyIter: func(*gomock.Controller, *iterator) {},
|
|
op: func(require *require.Assertions, iter *iterator) {
|
|
require.False(iter.Next())
|
|
},
|
|
},
|
|
{
|
|
name: "Next corrupts database",
|
|
databaseErrBefore: nil,
|
|
expectedErr: errIter,
|
|
modifyIter: func(ctrl *gomock.Controller, iter *iterator) {
|
|
mockInnerIter := databasemock.NewIterator(ctrl)
|
|
mockInnerIter.EXPECT().Next().Return(false)
|
|
mockInnerIter.EXPECT().Error().Return(errIter)
|
|
iter.Iterator = mockInnerIter
|
|
},
|
|
op: func(require *require.Assertions, iter *iterator) {
|
|
require.False(iter.Next())
|
|
},
|
|
},
|
|
{
|
|
name: "corrupted database; Error",
|
|
databaseErrBefore: errTest,
|
|
expectedErr: errTest,
|
|
modifyIter: func(*gomock.Controller, *iterator) {},
|
|
op: func(require *require.Assertions, iter *iterator) {
|
|
err := iter.Error()
|
|
require.ErrorIs(err, errTest)
|
|
},
|
|
},
|
|
{
|
|
name: "Error corrupts database",
|
|
databaseErrBefore: nil,
|
|
expectedErr: errIter,
|
|
modifyIter: func(ctrl *gomock.Controller, iter *iterator) {
|
|
mockInnerIter := databasemock.NewIterator(ctrl)
|
|
mockInnerIter.EXPECT().Error().Return(errIter)
|
|
iter.Iterator = mockInnerIter
|
|
},
|
|
op: func(require *require.Assertions, iter *iterator) {
|
|
err := iter.Error()
|
|
require.ErrorIs(err, errIter)
|
|
},
|
|
},
|
|
{
|
|
name: "corrupted database; Key",
|
|
databaseErrBefore: errTest,
|
|
expectedErr: errTest,
|
|
modifyIter: func(*gomock.Controller, *iterator) {},
|
|
op: func(_ *require.Assertions, iter *iterator) {
|
|
_ = iter.Key()
|
|
},
|
|
},
|
|
{
|
|
name: "corrupted database; Value",
|
|
databaseErrBefore: errTest,
|
|
expectedErr: errTest,
|
|
modifyIter: func(*gomock.Controller, *iterator) {},
|
|
op: func(_ *require.Assertions, iter *iterator) {
|
|
_ = iter.Value()
|
|
},
|
|
},
|
|
{
|
|
name: "corrupted database; Release",
|
|
databaseErrBefore: errTest,
|
|
expectedErr: errTest,
|
|
modifyIter: func(*gomock.Controller, *iterator) {},
|
|
op: func(_ *require.Assertions, iter *iterator) {
|
|
iter.Release()
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
ctrl := gomock.NewController(t)
|
|
|
|
// Make a database
|
|
corruptableDB := newDB()
|
|
// Put a key-value pair in the database.
|
|
require.NoError(corruptableDB.Put([]byte{0}, []byte{1}))
|
|
|
|
// Mark database as corrupted, if applicable
|
|
_ = corruptableDB.handleError(tt.databaseErrBefore)
|
|
|
|
// Make an iterator
|
|
iter := &iterator{
|
|
Iterator: corruptableDB.NewIterator(),
|
|
db: corruptableDB,
|
|
}
|
|
|
|
// Modify the iterator (optional)
|
|
tt.modifyIter(ctrl, iter)
|
|
|
|
// Do an iterator operation
|
|
tt.op(require, iter)
|
|
|
|
err := corruptableDB.corrupted()
|
|
require.ErrorIs(err, tt.expectedErr)
|
|
})
|
|
}
|
|
}
|