@@ -2,9 +2,13 @@ package bbolt_test
2
2
3
3
import (
4
4
"bytes"
5
+ crand "crypto/rand"
5
6
"errors"
6
7
"fmt"
8
+ "go.etcd.io/bbolt/internal/common"
9
+ "go.etcd.io/bbolt/internal/guts_cli"
7
10
"log"
11
+ "math/rand"
8
12
"os"
9
13
"runtime"
10
14
"testing"
@@ -18,6 +22,100 @@ import (
18
22
"go.etcd.io/bbolt/internal/btesting"
19
23
)
20
24
25
+ func TestTx_Check_Page (t * testing.T ) {
26
+ bucketKey := "testBucket"
27
+ db := btesting .MustCreateDBWithOption (t , & bolt.Options {PageSize : pageSize })
28
+ defer db .MustDeleteFile ()
29
+
30
+ // arrange
31
+ if err := db .Update (func (tx * bolt.Tx ) error {
32
+ b , err := tx .CreateBucketIfNotExists ([]byte (bucketKey ))
33
+ if err != nil {
34
+ t .Fatal (err )
35
+ }
36
+ // insert random data
37
+ for i := 0 ; i < 10 ; i ++ {
38
+ insertRandKeysValuesBucket (t , b , 100 )
39
+ }
40
+ return nil
41
+ }); err != nil {
42
+ t .Fatal (err )
43
+ }
44
+ db .MustCheck ()
45
+
46
+ // act : corrupt a leaf page
47
+ corruptLeafPageFromDB (t , db .Path ())
48
+
49
+ // assert: expected an error after check
50
+ if err := db .View (func (tx * bolt.Tx ) error {
51
+ for err := range tx .Check () {
52
+ require .ErrorContains (t , err , "has unexpected type/flags: 20" )
53
+ }
54
+ return nil
55
+ }); err != nil {
56
+ t .Fatal (err )
57
+ }
58
+ db .MustClose ()
59
+ }
60
+
61
+ func corruptLeafPageFromDB (t testing.TB , dbPath string ) {
62
+ _ , hwm , err := guts_cli .ReadPageAndHWMSize (dbPath )
63
+ if err != nil {
64
+ t .Fatalf ("error reading HWM from dbPath %v: %v" , dbPath , err )
65
+ }
66
+
67
+ var victimPage * common.Page
68
+ var victimBuf []byte
69
+
70
+ // find first leaf page starting page after root bucket's page
71
+ for pageID := uint64 (4 ); pageID < uint64 (hwm ); {
72
+ p , buf , err := guts_cli .ReadPage (dbPath , pageID )
73
+ if err != nil {
74
+ t .Fatalf ("error reading page %v from dbPath %v: %v" , pageID , dbPath , err )
75
+ }
76
+
77
+ if p .IsLeafPage () {
78
+ victimPage = p
79
+ victimBuf = buf
80
+ break
81
+ }
82
+ }
83
+
84
+ if victimPage == nil || victimBuf == nil {
85
+ t .Fatalf ("no leaf page is found in dbPath %v" , dbPath )
86
+ }
87
+
88
+ // corrupt page's type
89
+ victimPage .SetFlags (0x20 )
90
+ // write page back to file
91
+ err = guts_cli .WritePage (dbPath , victimBuf )
92
+ if err != nil {
93
+ t .Errorf ("error writing victim page %v back to dbpath %v: %v" , victimPage .Id (), dbPath , err )
94
+ }
95
+ }
96
+
97
+ func insertRandKeysValuesBucket (t testing.TB , bk * bolt.Bucket , n int ) {
98
+ var min , max = 1 , 1024
99
+
100
+ for i := 0 ; i < n ; i ++ {
101
+ // generate rand key/value length
102
+ keyLength := rand .Intn (max - min ) + min
103
+ valLength := rand .Intn (max - min ) + min
104
+
105
+ keyData := make ([]byte , keyLength )
106
+ valData := make ([]byte , valLength )
107
+
108
+ _ , err := crand .Read (keyData )
109
+ require .NoError (t , err )
110
+
111
+ _ , err = crand .Read (valData )
112
+ require .NoError (t , err )
113
+
114
+ err = bk .Put (keyData , valData )
115
+ require .NoError (t , err )
116
+ }
117
+ }
118
+
21
119
// TestTx_Check_ReadOnly tests consistency checking on a ReadOnly database.
22
120
func TestTx_Check_ReadOnly (t * testing.T ) {
23
121
db := btesting .MustCreateDB (t )
0 commit comments