github.com/ava-labs/avalanchego@v1.11.11/database/corruptabledb/db_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package corruptabledb
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/require"
    12  	"go.uber.org/mock/gomock"
    13  
    14  	"github.com/ava-labs/avalanchego/database"
    15  	"github.com/ava-labs/avalanchego/database/databasemock"
    16  	"github.com/ava-labs/avalanchego/database/dbtest"
    17  	"github.com/ava-labs/avalanchego/database/memdb"
    18  )
    19  
    20  var errTest = errors.New("non-nil error")
    21  
    22  func newDB() *Database {
    23  	baseDB := memdb.New()
    24  	return New(baseDB)
    25  }
    26  
    27  func TestInterface(t *testing.T) {
    28  	for name, test := range dbtest.Tests {
    29  		t.Run(name, func(t *testing.T) {
    30  			test(t, newDB())
    31  		})
    32  	}
    33  }
    34  
    35  func FuzzKeyValue(f *testing.F) {
    36  	dbtest.FuzzKeyValue(f, newDB())
    37  }
    38  
    39  func FuzzNewIteratorWithPrefix(f *testing.F) {
    40  	dbtest.FuzzNewIteratorWithPrefix(f, newDB())
    41  }
    42  
    43  func FuzzNewIteratorWithStartAndPrefix(f *testing.F) {
    44  	dbtest.FuzzNewIteratorWithStartAndPrefix(f, newDB())
    45  }
    46  
    47  // TestCorruption tests to make sure corruptabledb wrapper works as expected.
    48  func TestCorruption(t *testing.T) {
    49  	key := []byte("hello")
    50  	value := []byte("world")
    51  	tests := map[string]func(db database.Database) error{
    52  		"corrupted has": func(db database.Database) error {
    53  			_, err := db.Has(key)
    54  			return err
    55  		},
    56  		"corrupted get": func(db database.Database) error {
    57  			_, err := db.Get(key)
    58  			return err
    59  		},
    60  		"corrupted put": func(db database.Database) error {
    61  			return db.Put(key, value)
    62  		},
    63  		"corrupted delete": func(db database.Database) error {
    64  			return db.Delete(key)
    65  		},
    66  		"corrupted batch": func(db database.Database) error {
    67  			corruptableBatch := db.NewBatch()
    68  			require.NotNil(t, corruptableBatch)
    69  
    70  			require.NoError(t, corruptableBatch.Put(key, value))
    71  
    72  			return corruptableBatch.Write()
    73  		},
    74  		"corrupted healthcheck": func(db database.Database) error {
    75  			_, err := db.HealthCheck(context.Background())
    76  			return err
    77  		},
    78  	}
    79  	corruptableDB := newDB()
    80  	_ = corruptableDB.handleError(errTest)
    81  	for name, testFn := range tests {
    82  		t.Run(name, func(tt *testing.T) {
    83  			err := testFn(corruptableDB)
    84  			require.ErrorIs(tt, err, errTest)
    85  		})
    86  	}
    87  }
    88  
    89  func TestIterator(t *testing.T) {
    90  	errIter := errors.New("iterator error")
    91  
    92  	type test struct {
    93  		name              string
    94  		databaseErrBefore error
    95  		modifyIter        func(*gomock.Controller, *iterator)
    96  		op                func(*require.Assertions, *iterator)
    97  		expectedErr       error
    98  	}
    99  
   100  	tests := []test{
   101  		{
   102  			name:              "corrupted database; Next",
   103  			databaseErrBefore: errTest,
   104  			expectedErr:       errTest,
   105  			modifyIter:        func(*gomock.Controller, *iterator) {},
   106  			op: func(require *require.Assertions, iter *iterator) {
   107  				require.False(iter.Next())
   108  			},
   109  		},
   110  		{
   111  			name:              "Next corrupts database",
   112  			databaseErrBefore: nil,
   113  			expectedErr:       errIter,
   114  			modifyIter: func(ctrl *gomock.Controller, iter *iterator) {
   115  				mockInnerIter := databasemock.NewIterator(ctrl)
   116  				mockInnerIter.EXPECT().Next().Return(false)
   117  				mockInnerIter.EXPECT().Error().Return(errIter)
   118  				iter.Iterator = mockInnerIter
   119  			},
   120  			op: func(require *require.Assertions, iter *iterator) {
   121  				require.False(iter.Next())
   122  			},
   123  		},
   124  		{
   125  			name:              "corrupted database; Error",
   126  			databaseErrBefore: errTest,
   127  			expectedErr:       errTest,
   128  			modifyIter:        func(*gomock.Controller, *iterator) {},
   129  			op: func(require *require.Assertions, iter *iterator) {
   130  				err := iter.Error()
   131  				require.ErrorIs(err, errTest)
   132  			},
   133  		},
   134  		{
   135  			name:              "Error corrupts database",
   136  			databaseErrBefore: nil,
   137  			expectedErr:       errIter,
   138  			modifyIter: func(ctrl *gomock.Controller, iter *iterator) {
   139  				mockInnerIter := databasemock.NewIterator(ctrl)
   140  				mockInnerIter.EXPECT().Error().Return(errIter)
   141  				iter.Iterator = mockInnerIter
   142  			},
   143  			op: func(require *require.Assertions, iter *iterator) {
   144  				err := iter.Error()
   145  				require.ErrorIs(err, errIter)
   146  			},
   147  		},
   148  		{
   149  			name:              "corrupted database; Key",
   150  			databaseErrBefore: errTest,
   151  			expectedErr:       errTest,
   152  			modifyIter:        func(*gomock.Controller, *iterator) {},
   153  			op: func(_ *require.Assertions, iter *iterator) {
   154  				_ = iter.Key()
   155  			},
   156  		},
   157  		{
   158  			name:              "corrupted database; Value",
   159  			databaseErrBefore: errTest,
   160  			expectedErr:       errTest,
   161  			modifyIter:        func(*gomock.Controller, *iterator) {},
   162  			op: func(_ *require.Assertions, iter *iterator) {
   163  				_ = iter.Value()
   164  			},
   165  		},
   166  		{
   167  			name:              "corrupted database; Release",
   168  			databaseErrBefore: errTest,
   169  			expectedErr:       errTest,
   170  			modifyIter:        func(*gomock.Controller, *iterator) {},
   171  			op: func(_ *require.Assertions, iter *iterator) {
   172  				iter.Release()
   173  			},
   174  		},
   175  	}
   176  
   177  	for _, tt := range tests {
   178  		t.Run(tt.name, func(t *testing.T) {
   179  			require := require.New(t)
   180  			ctrl := gomock.NewController(t)
   181  
   182  			// Make a database
   183  			corruptableDB := newDB()
   184  			// Put a key-value pair in the database.
   185  			require.NoError(corruptableDB.Put([]byte{0}, []byte{1}))
   186  
   187  			// Mark database as corupted, if applicable
   188  			_ = corruptableDB.handleError(tt.databaseErrBefore)
   189  
   190  			// Make an iterator
   191  			iter := &iterator{
   192  				Iterator: corruptableDB.NewIterator(),
   193  				db:       corruptableDB,
   194  			}
   195  
   196  			// Modify the iterator (optional)
   197  			tt.modifyIter(ctrl, iter)
   198  
   199  			// Do an iterator operation
   200  			tt.op(require, iter)
   201  
   202  			err := corruptableDB.corrupted()
   203  			require.ErrorIs(err, tt.expectedErr)
   204  		})
   205  	}
   206  }