1 // Copyright (c) 2015-2016 The btcsuite developers
2 // Use of this source code is governed by an ISC
3 // license that can be found in the LICENSE file.
15 "github.com/btcsuite/btcd/chaincfg"
16 "github.com/btcsuite/btcd/database"
17 "github.com/btcsuite/btcd/database/ffldb"
18 "github.com/btcsuite/btcutil"
21 // dbType is the database type name for this driver.
22 const dbType = "ffldb"
24 // TestCreateOpenFail ensures that errors related to creating and opening a
25 // database are handled properly.
26 func TestCreateOpenFail(t *testing.T) {
29 // Ensure that attempting to open a database that doesn't exist returns
30 // the expected error.
31 wantErrCode := database.ErrDbDoesNotExist
32 _, err := database.Open(dbType, "noexist", blockDataNet)
33 if !checkDbError(t, "Open", err, wantErrCode) {
37 // Ensure that attempting to open a database with the wrong number of
38 // parameters returns the expected error.
39 wantErr := fmt.Errorf("invalid arguments to %s.Open -- expected "+
40 "database path and block network", dbType)
41 _, err = database.Open(dbType, 1, 2, 3)
42 if err.Error() != wantErr.Error() {
43 t.Errorf("Open: did not receive expected error - got %v, "+
44 "want %v", err, wantErr)
48 // Ensure that attempting to open a database with an invalid type for
49 // the first parameter returns the expected error.
50 wantErr = fmt.Errorf("first argument to %s.Open is invalid -- "+
51 "expected database path string", dbType)
52 _, err = database.Open(dbType, 1, blockDataNet)
53 if err.Error() != wantErr.Error() {
54 t.Errorf("Open: did not receive expected error - got %v, "+
55 "want %v", err, wantErr)
59 // Ensure that attempting to open a database with an invalid type for
60 // the second parameter returns the expected error.
61 wantErr = fmt.Errorf("second argument to %s.Open is invalid -- "+
62 "expected block network", dbType)
63 _, err = database.Open(dbType, "noexist", "invalid")
64 if err.Error() != wantErr.Error() {
65 t.Errorf("Open: did not receive expected error - got %v, "+
66 "want %v", err, wantErr)
70 // Ensure that attempting to create a database with the wrong number of
71 // parameters returns the expected error.
72 wantErr = fmt.Errorf("invalid arguments to %s.Create -- expected "+
73 "database path and block network", dbType)
74 _, err = database.Create(dbType, 1, 2, 3)
75 if err.Error() != wantErr.Error() {
76 t.Errorf("Create: did not receive expected error - got %v, "+
77 "want %v", err, wantErr)
81 // Ensure that attempting to create a database with an invalid type for
82 // the first parameter returns the expected error.
83 wantErr = fmt.Errorf("first argument to %s.Create is invalid -- "+
84 "expected database path string", dbType)
85 _, err = database.Create(dbType, 1, blockDataNet)
86 if err.Error() != wantErr.Error() {
87 t.Errorf("Create: did not receive expected error - got %v, "+
88 "want %v", err, wantErr)
92 // Ensure that attempting to create a database with an invalid type for
93 // the second parameter returns the expected error.
94 wantErr = fmt.Errorf("second argument to %s.Create is invalid -- "+
95 "expected block network", dbType)
96 _, err = database.Create(dbType, "noexist", "invalid")
97 if err.Error() != wantErr.Error() {
98 t.Errorf("Create: did not receive expected error - got %v, "+
99 "want %v", err, wantErr)
103 // Ensure operations against a closed database return the expected
105 dbPath := filepath.Join(os.TempDir(), "ffldb-createfail")
106 _ = os.RemoveAll(dbPath)
107 db, err := database.Create(dbType, dbPath, blockDataNet)
109 t.Errorf("Create: unexpected error: %v", err)
112 defer os.RemoveAll(dbPath)
115 wantErrCode = database.ErrDbNotOpen
116 err = db.View(func(tx database.Tx) error {
119 if !checkDbError(t, "View", err, wantErrCode) {
123 wantErrCode = database.ErrDbNotOpen
124 err = db.Update(func(tx database.Tx) error {
127 if !checkDbError(t, "Update", err, wantErrCode) {
131 wantErrCode = database.ErrDbNotOpen
132 _, err = db.Begin(false)
133 if !checkDbError(t, "Begin(false)", err, wantErrCode) {
137 wantErrCode = database.ErrDbNotOpen
138 _, err = db.Begin(true)
139 if !checkDbError(t, "Begin(true)", err, wantErrCode) {
143 wantErrCode = database.ErrDbNotOpen
145 if !checkDbError(t, "Close", err, wantErrCode) {
150 // TestPersistence ensures that values stored are still valid after closing and
151 // reopening the database.
152 func TestPersistence(t *testing.T) {
155 // Create a new database to run tests against.
156 dbPath := filepath.Join(os.TempDir(), "ffldb-persistencetest")
157 _ = os.RemoveAll(dbPath)
158 db, err := database.Create(dbType, dbPath, blockDataNet)
160 t.Errorf("Failed to create test database (%s) %v", dbType, err)
163 defer os.RemoveAll(dbPath)
166 // Create a bucket, put some values into it, and store a block so they
167 // can be tested for existence on re-open.
168 bucket1Key := []byte("bucket1")
169 storeValues := map[string]string{
174 genesisBlock := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock)
175 genesisHash := chaincfg.MainNetParams.GenesisHash
176 err = db.Update(func(tx database.Tx) error {
177 metadataBucket := tx.Metadata()
178 if metadataBucket == nil {
179 return fmt.Errorf("Metadata: unexpected nil bucket")
182 bucket1, err := metadataBucket.CreateBucket(bucket1Key)
184 return fmt.Errorf("CreateBucket: unexpected error: %v",
188 for k, v := range storeValues {
189 err := bucket1.Put([]byte(k), []byte(v))
191 return fmt.Errorf("Put: unexpected error: %v",
196 if err := tx.StoreBlock(genesisBlock); err != nil {
197 return fmt.Errorf("StoreBlock: unexpected error: %v",
204 t.Errorf("Update: unexpected error: %v", err)
208 // Close and reopen the database to ensure the values persist.
210 db, err = database.Open(dbType, dbPath, blockDataNet)
212 t.Errorf("Failed to open test database (%s) %v", dbType, err)
217 // Ensure the values previously stored in the 3rd namespace still exist
219 err = db.View(func(tx database.Tx) error {
220 metadataBucket := tx.Metadata()
221 if metadataBucket == nil {
222 return fmt.Errorf("Metadata: unexpected nil bucket")
225 bucket1 := metadataBucket.Bucket(bucket1Key)
227 return fmt.Errorf("Bucket1: unexpected nil bucket")
230 for k, v := range storeValues {
231 gotVal := bucket1.Get([]byte(k))
232 if !reflect.DeepEqual(gotVal, []byte(v)) {
233 return fmt.Errorf("Get: key '%s' does not "+
234 "match expected value - got %s, want %s",
239 genesisBlockBytes, _ := genesisBlock.Bytes()
240 gotBytes, err := tx.FetchBlock(genesisHash)
242 return fmt.Errorf("FetchBlock: unexpected error: %v",
245 if !reflect.DeepEqual(gotBytes, genesisBlockBytes) {
246 return fmt.Errorf("FetchBlock: stored block mismatch")
252 t.Errorf("View: unexpected error: %v", err)
257 // TestInterface performs all interfaces tests for this database driver.
258 func TestInterface(t *testing.T) {
261 // Create a new database to run tests against.
262 dbPath := filepath.Join(os.TempDir(), "ffldb-interfacetest")
263 _ = os.RemoveAll(dbPath)
264 db, err := database.Create(dbType, dbPath, blockDataNet)
266 t.Errorf("Failed to create test database (%s) %v", dbType, err)
269 defer os.RemoveAll(dbPath)
272 // Ensure the driver type is the expected value.
273 gotDbType := db.Type()
274 if gotDbType != dbType {
275 t.Errorf("Type: unepxected driver type - got %v, want %v",
280 // Run all of the interface tests against the database.
281 runtime.GOMAXPROCS(runtime.NumCPU())
283 // Change the maximum file size to a small value to force multiple flat
284 // files with the test data set.
285 ffldb.TstRunWithMaxBlockFileSize(db, 2048, func() {