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 }