interface.go raw

   1  package ci
   2  
   3  import (
   4  	"fmt"
   5  	"os"
   6  	"reflect"
   7  	
   8  	"github.com/p9c/p9/pkg/walletdb"
   9  )
  10  
  11  // errSubTestFail is used to signal that a sub test returned false.
  12  var errSubTestFail = fmt.Errorf("sub test failure")
  13  
  14  // testContext is used to store context information about a running test which is passed into helper functions.
  15  type testContext struct {
  16  	t           Tester
  17  	db          walletdb.DB
  18  	bucketDepth int
  19  	isWritable  bool
  20  }
  21  
  22  // rollbackValues returns a copy of the provided map with all values set to an empty string. This is used to test that
  23  // values are properly rolled back.
  24  func rollbackValues(
  25  	values map[string]string,
  26  ) map[string]string {
  27  	retMap := make(map[string]string, len(values))
  28  	for k := range values {
  29  		retMap[k] = ""
  30  	}
  31  	return retMap
  32  }
  33  
  34  // testGetValues checks that all of the provided key/value pairs can be retrieved from the database and the retrieved
  35  // values match the provided values.
  36  func testGetValues(
  37  	tc *testContext, bucket walletdb.ReadBucket, values map[string]string,
  38  ) bool {
  39  	for k, v := range values {
  40  		var vBytes []byte
  41  		if v != "" {
  42  			vBytes = []byte(v)
  43  		}
  44  		gotValue := bucket.Get([]byte(k))
  45  		if !reflect.DeepEqual(gotValue, vBytes) {
  46  			tc.t.Errorf("Get: unexpected value - got %s, want %s",
  47  				gotValue, vBytes,
  48  			)
  49  			return false
  50  		}
  51  	}
  52  	return true
  53  }
  54  
  55  // testPutValues stores all of the provided key/value pairs in the provided bucket while checking for errors.
  56  func testPutValues(
  57  	tc *testContext, bucket walletdb.ReadWriteBucket, values map[string]string,
  58  ) bool {
  59  	for k, v := range values {
  60  		var vBytes []byte
  61  		if v != "" {
  62  			vBytes = []byte(v)
  63  		}
  64  		if e := bucket.Put([]byte(k), vBytes); E.Chk(e) {
  65  			tc.t.Errorf("Put: unexpected error: %v", e)
  66  			return false
  67  		}
  68  	}
  69  	return true
  70  }
  71  
  72  // testDeleteValues removes all of the provided key/value pairs from the provided bucket.
  73  func testDeleteValues(
  74  	tc *testContext, bucket walletdb.ReadWriteBucket, values map[string]string,
  75  ) bool {
  76  	for k := range values {
  77  		if e := bucket.Delete([]byte(k)); E.Chk(e) {
  78  			tc.t.Errorf("Delete: unexpected error: %v", e)
  79  			return false
  80  		}
  81  	}
  82  	return true
  83  }
  84  
  85  // testNestedReadWriteBucket reruns the testBucketInterface against a nested bucket along with a counter to only test a
  86  // couple of level deep.
  87  func testNestedReadWriteBucket(
  88  	tc *testContext, testBucket walletdb.ReadWriteBucket,
  89  ) bool {
  90  	// Don't go more than 2 nested level deep.
  91  	if tc.bucketDepth > 1 {
  92  		return true
  93  	}
  94  	tc.bucketDepth++
  95  	defer func() {
  96  		tc.bucketDepth--
  97  	}()
  98  	return testReadWriteBucketInterface(tc, testBucket)
  99  }
 100  
 101  // testReadWriteBucketInterface ensures the bucket interface is working properly by exercising all of its functions.
 102  func testReadWriteBucketInterface(
 103  	tc *testContext, bucket walletdb.ReadWriteBucket,
 104  ) bool {
 105  	// keyValues holds the keys and values to use when putting values into the bucket.
 106  	var keyValues = map[string]string{
 107  		"bucketkey1": "foo1",
 108  		"bucketkey2": "foo2",
 109  		"bucketkey3": "foo3",
 110  	}
 111  	if !testPutValues(tc, bucket, keyValues) {
 112  		return false
 113  	}
 114  	if !testGetValues(tc, bucket, keyValues) {
 115  		return false
 116  	}
 117  	// Iterate all of the keys using ForEach while making sure the stored values are the expected values.
 118  	keysFound := make(map[string]struct{}, len(keyValues))
 119  	e := bucket.ForEach(func(k, v []byte) (e error) {
 120  		ks := string(k)
 121  		wantV, ok := keyValues[ks]
 122  		if !ok {
 123  			return fmt.Errorf("ForEach: key '%s' should "+
 124  				"exist", ks,
 125  			)
 126  		}
 127  		if !reflect.DeepEqual(v, []byte(wantV)) {
 128  			return fmt.Errorf("ForEach: value for key '%s' "+
 129  				"does not match - got %s, want %s",
 130  				ks, v, wantV,
 131  			)
 132  		}
 133  		keysFound[ks] = struct{}{}
 134  		return nil
 135  	},
 136  	)
 137  	if e != nil {
 138  		tc.t.Errorf("%v", e)
 139  		return false
 140  	}
 141  	// Ensure all keys were iterated.
 142  	for k := range keyValues {
 143  		if _, ok := keysFound[k]; !ok {
 144  			tc.t.Errorf("ForEach: key '%s' was not iterated "+
 145  				"when it should have been", k,
 146  			)
 147  			return false
 148  		}
 149  	}
 150  	// Delete the keys and ensure they were deleted.
 151  	if !testDeleteValues(tc, bucket, keyValues) {
 152  		return false
 153  	}
 154  	if !testGetValues(tc, bucket, rollbackValues(keyValues)) {
 155  		return false
 156  	}
 157  	// Ensure creating a new bucket works as expected.
 158  	testBucketName := []byte("testbucket")
 159  	testBucket, e := bucket.CreateBucket(testBucketName)
 160  	if e != nil {
 161  		tc.t.Errorf("CreateBucket: unexpected error: %v", e)
 162  		return false
 163  	}
 164  	if !testNestedReadWriteBucket(tc, testBucket) {
 165  		return false
 166  	}
 167  	// Ensure creating a bucket that already exists fails with the expected error.
 168  	wantErr := walletdb.ErrBucketExists
 169  	if _, e = bucket.CreateBucket(testBucketName); e != wantErr {
 170  		tc.t.Errorf("CreateBucket: unexpected error - got %v, "+
 171  			"want %v", e, wantErr,
 172  		)
 173  		return false
 174  	}
 175  	// Ensure CreateBucketIfNotExists returns an existing bucket.
 176  	testBucket, e = bucket.CreateBucketIfNotExists(testBucketName)
 177  	if e != nil {
 178  		tc.t.Errorf("CreateBucketIfNotExists: unexpected "+
 179  			"error: %v", e,
 180  		)
 181  		return false
 182  	}
 183  	if !testNestedReadWriteBucket(tc, testBucket) {
 184  		return false
 185  	}
 186  	// Ensure retrieving and existing bucket works as expected.
 187  	testBucket = bucket.NestedReadWriteBucket(testBucketName)
 188  	if !testNestedReadWriteBucket(tc, testBucket) {
 189  		return false
 190  	}
 191  	// Ensure deleting a bucket works as intended.
 192  	if e = bucket.DeleteNestedBucket(testBucketName); E.Chk(e) {
 193  		tc.t.Errorf("DeleteNestedBucket: unexpected error: %v", e)
 194  		return false
 195  	}
 196  	if b := bucket.NestedReadWriteBucket(testBucketName); b != nil {
 197  		tc.t.Errorf("DeleteNestedBucket: bucket '%s' still exists",
 198  			testBucketName,
 199  		)
 200  		return false
 201  	}
 202  	// Ensure deleting a bucket that doesn't exist returns the expected error.
 203  	wantErr = walletdb.ErrBucketNotFound
 204  	if e = bucket.DeleteNestedBucket(testBucketName); e != wantErr {
 205  		tc.t.Errorf("DeleteNestedBucket: unexpected error - got %v, "+
 206  			"want %v", e, wantErr,
 207  		)
 208  		return false
 209  	}
 210  	// Ensure CreateBucketIfNotExists creates a new bucket when it doesn't already exist.
 211  	testBucket, e = bucket.CreateBucketIfNotExists(testBucketName)
 212  	if e != nil {
 213  		tc.t.Errorf("CreateBucketIfNotExists: unexpected "+
 214  			"error: %v", e,
 215  		)
 216  		return false
 217  	}
 218  	if !testNestedReadWriteBucket(tc, testBucket) {
 219  		return false
 220  	}
 221  	// Delete the test bucket to avoid leaving it around for future calls.
 222  	if e := bucket.DeleteNestedBucket(testBucketName); E.Chk(e) {
 223  		tc.t.Errorf("DeleteNestedBucket: unexpected error: %v", e)
 224  		return false
 225  	}
 226  	if b := bucket.NestedReadWriteBucket(testBucketName); b != nil {
 227  		tc.t.Errorf("DeleteNestedBucket: bucket '%s' still exists",
 228  			testBucketName,
 229  		)
 230  		return false
 231  	}
 232  	return true
 233  }
 234  
 235  // testManualTxInterface ensures that manual transactions work as expected.
 236  func testManualTxInterface(
 237  	tc *testContext, bucketKey []byte,
 238  ) bool {
 239  	db := tc.db
 240  	// populateValues tests that populating values works as expected.
 241  	//
 242  	// When the writable flag is false, a read-only tranasction is created, standard bucket tests for read-only
 243  	// transactions are performed, and the Commit function is checked to ensure it fails as expected.
 244  	//
 245  	// Otherwise, a read-write transaction is created, the values are written, standard bucket tests for read-write
 246  	// transactions are performed, and then the transaction is either commited or rolled back depending on the flag.
 247  	populateValues := func(writable, rollback bool, putValues map[string]string) bool {
 248  		var dbtx walletdb.ReadTx
 249  		var rootBucket walletdb.ReadBucket
 250  		var e error
 251  		if writable {
 252  			dbtx, e = db.BeginReadWriteTx()
 253  			if e != nil {
 254  				tc.t.Errorf("BeginReadWriteTx: unexpected error %v", e)
 255  				return false
 256  			}
 257  			rootBucket = dbtx.(walletdb.ReadWriteTx).ReadWriteBucket(bucketKey)
 258  		} else {
 259  			dbtx, e = db.BeginReadTx()
 260  			if e != nil {
 261  				tc.t.Errorf("BeginReadTx: unexpected error %v", e)
 262  				return false
 263  			}
 264  			rootBucket = dbtx.ReadBucket(bucketKey)
 265  		}
 266  		if rootBucket == nil {
 267  			tc.t.Errorf("ReadWriteBucket/ReadBucket: unexpected nil root bucket")
 268  			_ = dbtx.Rollback()
 269  			return false
 270  		}
 271  		if writable {
 272  			tc.isWritable = writable
 273  			if !testReadWriteBucketInterface(tc, rootBucket.(walletdb.ReadWriteBucket)) {
 274  				_ = dbtx.Rollback()
 275  				return false
 276  			}
 277  		}
 278  		if !writable {
 279  			// Rollback the transaction.
 280  			if e := dbtx.Rollback(); E.Chk(e) {
 281  				tc.t.Errorf("Commit: unexpected error %v", e)
 282  				return false
 283  			}
 284  		} else {
 285  			rootBucket := rootBucket.(walletdb.ReadWriteBucket)
 286  			if !testPutValues(tc, rootBucket, putValues) {
 287  				return false
 288  			}
 289  			if rollback {
 290  				// Rollback the transaction.
 291  				if e := dbtx.Rollback(); E.Chk(e) {
 292  					tc.t.Errorf("Rollback: unexpected "+
 293  						"error %v", e,
 294  					)
 295  					return false
 296  				}
 297  			} else {
 298  				// The commit should succeed.
 299  				if e := dbtx.(walletdb.ReadWriteTx).Commit(); E.Chk(e) {
 300  					tc.t.Errorf("Commit: unexpected error "+
 301  						"%v", e,
 302  					)
 303  					return false
 304  				}
 305  			}
 306  		}
 307  		return true
 308  	}
 309  	// checkValues starts a read-only transaction and checks that all of the key/value pairs specified in the
 310  	// expectedValues parameter match what's in the database.
 311  	checkValues := func(expectedValues map[string]string) bool {
 312  		// Begin another read-only transaction to ensure...
 313  		dbtx, e := db.BeginReadTx()
 314  		if e != nil {
 315  			tc.t.Errorf("BeginReadTx: unexpected error %v", e)
 316  			return false
 317  		}
 318  		rootBucket := dbtx.ReadBucket(bucketKey)
 319  		if rootBucket == nil {
 320  			tc.t.Errorf("ReadBucket: unexpected nil root bucket")
 321  			_ = dbtx.Rollback()
 322  			return false
 323  		}
 324  		if !testGetValues(tc, rootBucket, expectedValues) {
 325  			_ = dbtx.Rollback()
 326  			return false
 327  		}
 328  		// Rollback the read-only transaction.
 329  		if e := dbtx.Rollback(); E.Chk(e) {
 330  			tc.t.Errorf("Commit: unexpected error %v", e)
 331  			return false
 332  		}
 333  		return true
 334  	}
 335  	// deleteValues starts a read-write transaction and deletes the keys in the passed key/value pairs.
 336  	deleteValues := func(values map[string]string) bool {
 337  		dbtx, e := db.BeginReadWriteTx()
 338  		if e != nil {
 339  			tc.t.Errorf("BeginReadWriteTx: unexpected error %v", e)
 340  			_ = dbtx.Rollback()
 341  			return false
 342  		}
 343  		rootBucket := dbtx.ReadWriteBucket(bucketKey)
 344  		if rootBucket == nil {
 345  			tc.t.Errorf("RootBucket: unexpected nil root bucket")
 346  			_ = dbtx.Rollback()
 347  			return false
 348  		}
 349  		// Delete the keys and ensure they were deleted.
 350  		if !testDeleteValues(tc, rootBucket, values) {
 351  			_ = dbtx.Rollback()
 352  			return false
 353  		}
 354  		if !testGetValues(tc, rootBucket, rollbackValues(values)) {
 355  			_ = dbtx.Rollback()
 356  			return false
 357  		}
 358  		// Commit the changes and ensure it was successful.
 359  		if e := dbtx.Commit(); E.Chk(e) {
 360  			tc.t.Errorf("Commit: unexpected error %v", e)
 361  			return false
 362  		}
 363  		return true
 364  	}
 365  	// keyValues holds the keys and values to use when putting values into a bucket.
 366  	var keyValues = map[string]string{
 367  		"umtxkey1": "foo1",
 368  		"umtxkey2": "foo2",
 369  		"umtxkey3": "foo3",
 370  	}
 371  	// Ensure that attempting populating the values using a read-only transaction fails as expected.
 372  	if !populateValues(false, true, keyValues) {
 373  		return false
 374  	}
 375  	if !checkValues(rollbackValues(keyValues)) {
 376  		return false
 377  	}
 378  	// Ensure that attempting populating the values using a read-write transaction and then rolling it back yields the
 379  	// expected values.
 380  	if !populateValues(true, true, keyValues) {
 381  		return false
 382  	}
 383  	if !checkValues(rollbackValues(keyValues)) {
 384  		return false
 385  	}
 386  	// Ensure that attempting populating the values using a read-write transaction and then committing it stores the
 387  	// expected values.
 388  	if !populateValues(true, false, keyValues) {
 389  		return false
 390  	}
 391  	if !checkValues(keyValues) {
 392  		return false
 393  	}
 394  	// Clean up the keys.
 395  	if !deleteValues(keyValues) {
 396  		return false
 397  	}
 398  	return true
 399  }
 400  
 401  // testNamespaceAndTxInterfaces creates a namespace using the provided key and tests all facets of it interface as well
 402  // as transaction and bucket interfaces under it.
 403  func testNamespaceAndTxInterfaces(
 404  	tc *testContext, namespaceKey string,
 405  ) bool {
 406  	namespaceKeyBytes := []byte(namespaceKey)
 407  	e := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) (e error) {
 408  		_, e = tx.CreateTopLevelBucket(namespaceKeyBytes)
 409  		return e
 410  	},
 411  	)
 412  	if e != nil {
 413  		tc.t.Errorf("CreateTopLevelBucket: unexpected error: %v", e)
 414  		return false
 415  	}
 416  	defer func() {
 417  		// Remove the namespace now that the tests are done for it.
 418  		e = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) (e error) {
 419  			return tx.DeleteTopLevelBucket(namespaceKeyBytes)
 420  		},
 421  		)
 422  		if e != nil {
 423  			tc.t.Errorf("DeleteTopLevelBucket: unexpected error: %v", e)
 424  			return
 425  		}
 426  	}()
 427  	if !testManualTxInterface(tc, namespaceKeyBytes) {
 428  		return false
 429  	}
 430  	// keyValues holds the keys and values to use when putting values into a bucket.
 431  	var keyValues = map[string]string{
 432  		"mtxkey1": "foo1",
 433  		"mtxkey2": "foo2",
 434  		"mtxkey3": "foo3",
 435  	}
 436  	// Test the bucket interface via a managed read-only transaction.
 437  	e = walletdb.View(tc.db, func(tx walletdb.ReadTx) (e error) {
 438  		rootBucket := tx.ReadBucket(namespaceKeyBytes)
 439  		if rootBucket == nil {
 440  			return fmt.Errorf("ReadBucket: unexpected nil root bucket")
 441  		}
 442  		return nil
 443  	},
 444  	)
 445  	if e != nil {
 446  		if e != errSubTestFail {
 447  			tc.t.Errorf("%v", e)
 448  		}
 449  		return false
 450  	}
 451  	// Test the bucket interface via a managed read-write transaction. Also, put a series of values and force a rollback
 452  	// so the following code can ensure the values were not stored.
 453  	forceRollbackError := fmt.Errorf("force rollback")
 454  	e = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) (e error) {
 455  		rootBucket := tx.ReadWriteBucket(namespaceKeyBytes)
 456  		if rootBucket == nil {
 457  			return fmt.Errorf("ReadWriteBucket: unexpected nil root bucket")
 458  		}
 459  		tc.isWritable = true
 460  		if !testReadWriteBucketInterface(tc, rootBucket) {
 461  			return errSubTestFail
 462  		}
 463  		if !testPutValues(tc, rootBucket, keyValues) {
 464  			return errSubTestFail
 465  		}
 466  		// Return an error to force a rollback.
 467  		return forceRollbackError
 468  	},
 469  	)
 470  	if e != forceRollbackError {
 471  		if e == errSubTestFail {
 472  			return false
 473  		}
 474  		tc.t.Errorf("Update: inner function error not returned - got "+
 475  			"%v, want %v", e, forceRollbackError,
 476  		)
 477  		return false
 478  	}
 479  	// Ensure the values that should have not been stored due to the forced rollback above were not actually stored.
 480  	e = walletdb.View(tc.db, func(tx walletdb.ReadTx) (e error) {
 481  		rootBucket := tx.ReadBucket(namespaceKeyBytes)
 482  		if rootBucket == nil {
 483  			return fmt.Errorf("ReadBucket: unexpected nil root bucket")
 484  		}
 485  		if !testGetValues(tc, rootBucket, rollbackValues(keyValues)) {
 486  			return errSubTestFail
 487  		}
 488  		return nil
 489  	},
 490  	)
 491  	if e != nil {
 492  		if e != errSubTestFail {
 493  			tc.t.Errorf("%v", e)
 494  		}
 495  		return false
 496  	}
 497  	// Store a series of values via a managed read-write transaction.
 498  	e = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) (e error) {
 499  		rootBucket := tx.ReadWriteBucket(namespaceKeyBytes)
 500  		if rootBucket == nil {
 501  			return fmt.Errorf("ReadWriteBucket: unexpected nil root bucket")
 502  		}
 503  		if !testPutValues(tc, rootBucket, keyValues) {
 504  			return errSubTestFail
 505  		}
 506  		return nil
 507  	},
 508  	)
 509  	if e != nil {
 510  		if e != errSubTestFail {
 511  			tc.t.Errorf("%v", e)
 512  		}
 513  		return false
 514  	}
 515  	// Ensure the values stored above were committed as expected.
 516  	e = walletdb.View(tc.db, func(tx walletdb.ReadTx) (e error) {
 517  		rootBucket := tx.ReadBucket(namespaceKeyBytes)
 518  		if rootBucket == nil {
 519  			return fmt.Errorf("ReadBucket: unexpected nil root bucket")
 520  		}
 521  		if !testGetValues(tc, rootBucket, keyValues) {
 522  			return errSubTestFail
 523  		}
 524  		return nil
 525  	},
 526  	)
 527  	if e != nil {
 528  		E.Ln(e)
 529  		if e != errSubTestFail {
 530  			tc.t.Errorf("%v", e)
 531  		}
 532  		return false
 533  	}
 534  	// Clean up the values stored above in a managed read-write transaction.
 535  	e = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) (e error) {
 536  		rootBucket := tx.ReadWriteBucket(namespaceKeyBytes)
 537  		if rootBucket == nil {
 538  			return fmt.Errorf("ReadWriteBucket: unexpected nil root bucket")
 539  		}
 540  		if !testDeleteValues(tc, rootBucket, keyValues) {
 541  			return errSubTestFail
 542  		}
 543  		return nil
 544  	},
 545  	)
 546  	if e != nil {
 547  		E.Ln(e)
 548  		if e != errSubTestFail {
 549  			tc.t.Errorf("%v", e)
 550  		}
 551  		return false
 552  	}
 553  	return true
 554  }
 555  
 556  // testAdditionalErrors performs some tests for error cases not covered elsewhere in the tests and therefore improves
 557  // negative test coverage.
 558  func testAdditionalErrors(
 559  	tc *testContext,
 560  ) bool {
 561  	ns3Key := []byte("ns3")
 562  	e := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) (e error) {
 563  		// Create a new namespace
 564  		rootBucket, e := tx.CreateTopLevelBucket(ns3Key)
 565  		if e != nil {
 566  			return fmt.Errorf("CreateTopLevelBucket: unexpected error: %v", e)
 567  		}
 568  		// Ensure CreateBucket returns the expected error when no bucket key is specified.
 569  		wantErr := walletdb.ErrBucketNameRequired
 570  		if _, e = rootBucket.CreateBucket(nil); e != wantErr {
 571  			return fmt.Errorf("CreateBucket: unexpected error - "+
 572  				"got %v, want %v", e, wantErr,
 573  			)
 574  		}
 575  		// Ensure DeleteNestedBucket returns the expected error when no bucket key is specified.
 576  		wantErr = walletdb.ErrIncompatibleValue
 577  		if e := rootBucket.DeleteNestedBucket(nil); e != wantErr {
 578  			return fmt.Errorf("DeleteNestedBucket: unexpected error - "+
 579  				"got %v, want %v", e, wantErr,
 580  			)
 581  		}
 582  		// Ensure Put returns the expected error when no key is specified.
 583  		wantErr = walletdb.ErrKeyRequired
 584  		if e := rootBucket.Put(nil, nil); e != wantErr {
 585  			return fmt.Errorf("put: unexpected error - got %v, want %v", e, wantErr)
 586  		}
 587  		return nil
 588  	},
 589  	)
 590  	if e != nil {
 591  		if e != errSubTestFail {
 592  			tc.t.Errorf("%v", e)
 593  		}
 594  		return false
 595  	}
 596  	// Ensure that attempting to rollback or commit a transaction that is already closed returns the expected error.
 597  	tx, e := tc.db.BeginReadWriteTx()
 598  	if e != nil {
 599  		tc.t.Errorf("Begin: unexpected error: %v", e)
 600  		return false
 601  	}
 602  	if e := tx.Rollback(); E.Chk(e) {
 603  		tc.t.Errorf("Rollback: unexpected error: %v", e)
 604  		return false
 605  	}
 606  	wantErr := walletdb.ErrTxClosed
 607  	if e := tx.Rollback(); e != wantErr {
 608  		tc.t.Errorf("Rollback: unexpected error - got %v, want %v", e,
 609  			wantErr,
 610  		)
 611  		return false
 612  	}
 613  	if e := tx.Commit(); e != wantErr {
 614  		tc.t.Errorf("Commit: unexpected error - got %v, want %v", e,
 615  			wantErr,
 616  		)
 617  		return false
 618  	}
 619  	return true
 620  }
 621  
 622  // TestInterface performs all interfaces tests for this database driver.
 623  func TestInterface(
 624  	t Tester, dbType, dbPath string,
 625  ) {
 626  	db, e := walletdb.Create(dbType, dbPath)
 627  	if e != nil {
 628  		t.Errorf("Failed to create test database (%s) %v", dbType, e)
 629  		return
 630  	}
 631  	defer func() {
 632  		if e := os.Remove(dbPath); E.Chk(e) {
 633  		}
 634  	}()
 635  	defer func() {
 636  		if e := db.Close(); E.Chk(e) {
 637  		}
 638  	}()
 639  	// Run all of the interface tests against the database. Create a test context to pass around.
 640  	context := testContext{t: t, db: db}
 641  	// Create a namespace and test the interface for it.
 642  	if !testNamespaceAndTxInterfaces(&context, "ns1") {
 643  		return
 644  	}
 645  	// Create a second namespace and test the interface for it.
 646  	if !testNamespaceAndTxInterfaces(&context, "ns2") {
 647  		return
 648  	}
 649  	// Chk a few more error conditions not covered elsewhere.
 650  	if !testAdditionalErrors(&context) {
 651  		return
 652  	}
 653  }
 654