github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/core/ledger/util/couchdb/couchdb_test.go (about)

     1  /*
     2  Copyright IBM Corp. 2016, 2017 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package couchdb
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"os"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  	"unicode/utf8"
    27  
    28  	"github.com/hyperledger/fabric/common/ledger/testutil"
    29  	"github.com/hyperledger/fabric/core/ledger/ledgerconfig"
    30  	ledgertestutil "github.com/hyperledger/fabric/core/ledger/testutil"
    31  	"github.com/spf13/viper"
    32  )
    33  
    34  const badConnectURL = "couchdb:5990"
    35  const updateDocumentConflictError = "conflict"
    36  const updateDocumentConflictReason = "Document update conflict."
    37  
    38  var couchDBDef *CouchDBDef
    39  
    40  func cleanup(database string) error {
    41  	//create a new connection
    42  	couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
    43  		couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
    44  
    45  	if err != nil {
    46  		fmt.Println("Unexpected error", err)
    47  		return err
    48  	}
    49  	db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
    50  	//drop the test database
    51  	db.DropDatabase()
    52  	return nil
    53  }
    54  
    55  type Asset struct {
    56  	ID        string `json:"_id"`
    57  	Rev       string `json:"_rev"`
    58  	AssetName string `json:"asset_name"`
    59  	Color     string `json:"color"`
    60  	Size      string `json:"size"`
    61  	Owner     string `json:"owner"`
    62  }
    63  
    64  var assetJSON = []byte(`{"asset_name":"marble1","color":"blue","size":"35","owner":"jerry"}`)
    65  
    66  func TestMain(m *testing.M) {
    67  	// Read the core.yaml file for default config.
    68  	ledgertestutil.SetupCoreYAMLConfig()
    69  
    70  	// Switch to CouchDB
    71  	viper.Set("ledger.state.stateDatabase", "CouchDB")
    72  
    73  	// both vagrant and CI have couchdb configured at host "couchdb"
    74  	viper.Set("ledger.state.couchDBConfig.couchDBAddress", "couchdb:5984")
    75  	// Replace with correct username/password such as
    76  	// admin/admin if user security is enabled on couchdb.
    77  	viper.Set("ledger.state.couchDBConfig.username", "")
    78  	viper.Set("ledger.state.couchDBConfig.password", "")
    79  	viper.Set("ledger.state.couchDBConfig.maxRetries", 3)
    80  	viper.Set("ledger.state.couchDBConfig.maxRetriesOnStartup", 10)
    81  	viper.Set("ledger.state.couchDBConfig.requestTimeout", time.Second*35)
    82  
    83  	// Create CouchDB definition from config parameters
    84  	couchDBDef = GetCouchDBDefinition()
    85  
    86  	//run the tests
    87  	result := m.Run()
    88  
    89  	//revert to default goleveldb
    90  	viper.Set("ledger.state.stateDatabase", "goleveldb")
    91  	os.Exit(result)
    92  }
    93  
    94  func TestDBConnectionDef(t *testing.T) {
    95  
    96  	//create a new connection
    97  	_, err := CreateConnectionDefinition(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
    98  		couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
    99  	testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create database connection definition"))
   100  
   101  }
   102  
   103  func TestDBBadConnectionDef(t *testing.T) {
   104  
   105  	//create a new connection
   106  	_, err := CreateConnectionDefinition("^^^localhost:5984", couchDBDef.Username, couchDBDef.Password,
   107  		couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   108  	testutil.AssertError(t, err, fmt.Sprintf("Did not receive error when trying to create database connection definition with a bad hostname"))
   109  
   110  }
   111  
   112  func TestDBCreateSaveWithoutRevision(t *testing.T) {
   113  
   114  	if ledgerconfig.IsCouchDBEnabled() {
   115  
   116  		database := "testdbcreatesavewithoutrevision"
   117  		err := cleanup(database)
   118  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   119  		defer cleanup(database)
   120  
   121  		if err == nil {
   122  			//create a new instance and database object
   123  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   124  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   125  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   126  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   127  
   128  			//create a new database
   129  			_, errdb := db.CreateDatabaseIfNotExist()
   130  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   131  
   132  			//Save the test document
   133  			_, saveerr := db.SaveDoc("2", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   134  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   135  		}
   136  	}
   137  }
   138  
   139  func TestDBCreateEnsureFullCommit(t *testing.T) {
   140  
   141  	if ledgerconfig.IsCouchDBEnabled() {
   142  
   143  		database := "testdbensurefullcommit"
   144  		err := cleanup(database)
   145  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   146  		defer cleanup(database)
   147  
   148  		if err == nil {
   149  			//create a new instance and database object
   150  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   151  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   152  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   153  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   154  
   155  			//create a new database
   156  			_, errdb := db.CreateDatabaseIfNotExist()
   157  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   158  
   159  			//Save the test document
   160  			_, saveerr := db.SaveDoc("2", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   161  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   162  
   163  			//Ensure a full commit
   164  			_, commiterr := db.EnsureFullCommit()
   165  			testutil.AssertNoError(t, commiterr, fmt.Sprintf("Error when trying to ensure a full commit"))
   166  
   167  		}
   168  	}
   169  }
   170  
   171  func TestDBBadDatabaseName(t *testing.T) {
   172  
   173  	if ledgerconfig.IsCouchDBEnabled() {
   174  
   175  		//create a new instance and database object using a valid database name mixed case
   176  		couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   177  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   178  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   179  		_, dberr := CreateCouchDatabase(*couchInstance, "testDB")
   180  		testutil.AssertNoError(t, dberr, fmt.Sprintf("Error when testing a valid database name"))
   181  
   182  		//create a new instance and database object using a valid database name letters and numbers
   183  		couchInstance, err = CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   184  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   185  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   186  		_, dberr = CreateCouchDatabase(*couchInstance, "test132")
   187  		testutil.AssertNoError(t, dberr, fmt.Sprintf("Error when testing a valid database name"))
   188  
   189  		//create a new instance and database object using a valid database name - special characters
   190  		couchInstance, err = CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   191  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   192  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   193  		_, dberr = CreateCouchDatabase(*couchInstance, "test1234~!@#$%^&*()[]{}.")
   194  		testutil.AssertNoError(t, dberr, fmt.Sprintf("Error when testing a valid database name"))
   195  
   196  		//create a new instance and database object using a invalid database name - too long	/*
   197  		couchInstance, err = CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   198  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   199  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   200  		_, dberr = CreateCouchDatabase(*couchInstance, "A12345678901234567890123456789012345678901234"+
   201  			"56789012345678901234567890123456789012345678901234567890123456789012345678901234567890"+
   202  			"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456"+
   203  			"78901234567890123456789012345678901234567890")
   204  		testutil.AssertError(t, dberr, fmt.Sprintf("Error should have been thrown for invalid database name"))
   205  
   206  	}
   207  
   208  }
   209  
   210  func TestDBBadConnection(t *testing.T) {
   211  
   212  	if ledgerconfig.IsCouchDBEnabled() {
   213  
   214  		//create a new instance and database object
   215  		//Limit the maxRetriesOnStartup to 3 in order to reduce time for the failure
   216  		_, err := CreateCouchInstance(badConnectURL, couchDBDef.Username, couchDBDef.Password,
   217  			couchDBDef.MaxRetries, 3, couchDBDef.RequestTimeout)
   218  		testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for a bad connection"))
   219  	}
   220  }
   221  
   222  func TestDBCreateDatabaseAndPersist(t *testing.T) {
   223  
   224  	if ledgerconfig.IsCouchDBEnabled() {
   225  
   226  		database := "testdbcreatedatabaseandpersist"
   227  		err := cleanup(database)
   228  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   229  		defer cleanup(database)
   230  
   231  		if err == nil {
   232  			//create a new instance and database object
   233  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   234  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   235  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   236  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   237  
   238  			//create a new database
   239  			_, errdb := db.CreateDatabaseIfNotExist()
   240  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   241  
   242  			//Retrieve the info for the new database and make sure the name matches
   243  			dbResp, _, errdb := db.GetDatabaseInfo()
   244  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to retrieve database information"))
   245  			testutil.AssertEquals(t, dbResp.DbName, database)
   246  
   247  			//Save the test document
   248  			_, saveerr := db.SaveDoc("idWith/slash", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   249  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   250  
   251  			//Retrieve the test document
   252  			dbGetResp, _, geterr := db.ReadDoc("idWith/slash")
   253  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   254  
   255  			//Unmarshal the document to Asset structure
   256  			assetResp := &Asset{}
   257  			geterr = json.Unmarshal(dbGetResp.JSONValue, &assetResp)
   258  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   259  
   260  			//Verify the owner retrieved matches
   261  			testutil.AssertEquals(t, assetResp.Owner, "jerry")
   262  
   263  			//Save the test document
   264  			_, saveerr = db.SaveDoc("1", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   265  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   266  
   267  			//Retrieve the test document
   268  			dbGetResp, _, geterr = db.ReadDoc("1")
   269  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   270  
   271  			//Unmarshal the document to Asset structure
   272  			assetResp = &Asset{}
   273  			geterr = json.Unmarshal(dbGetResp.JSONValue, &assetResp)
   274  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   275  
   276  			//Verify the owner retrieved matches
   277  			testutil.AssertEquals(t, assetResp.Owner, "jerry")
   278  
   279  			//Change owner to bob
   280  			assetResp.Owner = "bob"
   281  
   282  			//create a byte array of the JSON
   283  			assetDocUpdated, _ := json.Marshal(assetResp)
   284  
   285  			//Save the updated test document
   286  			_, saveerr = db.SaveDoc("1", "", &CouchDoc{JSONValue: assetDocUpdated, Attachments: nil})
   287  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save the updated document"))
   288  
   289  			//Retrieve the updated test document
   290  			dbGetResp, _, geterr = db.ReadDoc("1")
   291  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   292  
   293  			//Unmarshal the document to Asset structure
   294  			assetResp = &Asset{}
   295  			json.Unmarshal(dbGetResp.JSONValue, &assetResp)
   296  
   297  			//Assert that the update was saved and retrieved
   298  			testutil.AssertEquals(t, assetResp.Owner, "bob")
   299  
   300  			//Drop the database
   301  			_, errdbdrop := db.DropDatabase()
   302  			testutil.AssertNoError(t, errdbdrop, fmt.Sprintf("Error dropping database"))
   303  
   304  			//Retrieve the info for the new database and make sure the name matches
   305  			_, _, errdbinfo := db.GetDatabaseInfo()
   306  			testutil.AssertError(t, errdbinfo, fmt.Sprintf("Error should have been thrown for missing database"))
   307  		}
   308  	}
   309  
   310  }
   311  
   312  func TestDBRequestTimeout(t *testing.T) {
   313  
   314  	if ledgerconfig.IsCouchDBEnabled() {
   315  
   316  		database := "testdbrequesttimeout"
   317  		err := cleanup(database)
   318  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   319  		defer cleanup(database)
   320  
   321  		if err == nil {
   322  
   323  			//create an impossibly short timeout
   324  			impossibleTimeout := time.Microsecond * 1
   325  
   326  			//create a new instance and database object with a timeout that will fail
   327  			//Also use a maxRetriesOnStartup=3 to reduce the number of retries
   328  			_, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   329  				couchDBDef.MaxRetries, 3, impossibleTimeout)
   330  			testutil.AssertError(t, err, fmt.Sprintf("Error should have been thown while trying to create a couchdb instance with a connection timeout"))
   331  
   332  			//see if the error message contains the timeout error
   333  			testutil.AssertEquals(t, strings.Count(err.Error(), "Client.Timeout exceeded while awaiting headers"), 1)
   334  
   335  		}
   336  	}
   337  }
   338  
   339  func TestDBBadJSON(t *testing.T) {
   340  
   341  	if ledgerconfig.IsCouchDBEnabled() {
   342  
   343  		database := "testdbbadjson"
   344  		err := cleanup(database)
   345  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   346  		defer cleanup(database)
   347  
   348  		if err == nil {
   349  
   350  			//create a new instance and database object
   351  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   352  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   353  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   354  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   355  
   356  			//create a new database
   357  			_, errdb := db.CreateDatabaseIfNotExist()
   358  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   359  
   360  			//Retrieve the info for the new database and make sure the name matches
   361  			dbResp, _, errdb := db.GetDatabaseInfo()
   362  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to retrieve database information"))
   363  			testutil.AssertEquals(t, dbResp.DbName, database)
   364  
   365  			badJSON := []byte(`{"asset_name"}`)
   366  
   367  			//Save the test document
   368  			_, saveerr := db.SaveDoc("1", "", &CouchDoc{JSONValue: badJSON, Attachments: nil})
   369  			testutil.AssertError(t, saveerr, fmt.Sprintf("Error should have been thrown for a bad JSON"))
   370  
   371  		}
   372  
   373  	}
   374  
   375  }
   376  
   377  func TestPrefixScan(t *testing.T) {
   378  	if !ledgerconfig.IsCouchDBEnabled() {
   379  		return
   380  	}
   381  	database := "testprefixscan"
   382  	err := cleanup(database)
   383  	testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   384  	defer cleanup(database)
   385  
   386  	if err == nil {
   387  		//create a new instance and database object
   388  		couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   389  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   390  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   391  		db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   392  
   393  		//create a new database
   394  		_, errdb := db.CreateDatabaseIfNotExist()
   395  		testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   396  
   397  		//Retrieve the info for the new database and make sure the name matches
   398  		dbResp, _, errdb := db.GetDatabaseInfo()
   399  		testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to retrieve database information"))
   400  		testutil.AssertEquals(t, dbResp.DbName, database)
   401  
   402  		//Save documents
   403  		for i := 0; i < 20; i++ {
   404  			id1 := string(0) + string(i) + string(0)
   405  			id2 := string(0) + string(i) + string(1)
   406  			id3 := string(0) + string(i) + string(utf8.MaxRune-1)
   407  			_, saveerr := db.SaveDoc(id1, "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   408  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   409  			_, saveerr = db.SaveDoc(id2, "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   410  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   411  			_, saveerr = db.SaveDoc(id3, "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   412  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   413  
   414  		}
   415  		startKey := string(0) + string(10)
   416  		endKey := startKey + string(utf8.MaxRune)
   417  		_, _, geterr := db.ReadDoc(endKey)
   418  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to get lastkey"))
   419  
   420  		resultsPtr, geterr := db.ReadDocRange(startKey, endKey, 1000, 0)
   421  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to perform a range scan"))
   422  		testutil.AssertNotNil(t, resultsPtr)
   423  		results := *resultsPtr
   424  		testutil.AssertEquals(t, len(results), 3)
   425  		testutil.AssertEquals(t, results[0].ID, string(0)+string(10)+string(0))
   426  		testutil.AssertEquals(t, results[1].ID, string(0)+string(10)+string(1))
   427  		testutil.AssertEquals(t, results[2].ID, string(0)+string(10)+string(utf8.MaxRune-1))
   428  
   429  		//Drop the database
   430  		_, errdbdrop := db.DropDatabase()
   431  		testutil.AssertNoError(t, errdbdrop, fmt.Sprintf("Error dropping database"))
   432  
   433  		//Retrieve the info for the new database and make sure the name matches
   434  		_, _, errdbinfo := db.GetDatabaseInfo()
   435  		testutil.AssertError(t, errdbinfo, fmt.Sprintf("Error should have been thrown for missing database"))
   436  	}
   437  }
   438  
   439  func TestDBSaveAttachment(t *testing.T) {
   440  
   441  	if ledgerconfig.IsCouchDBEnabled() {
   442  
   443  		database := "testdbsaveattachment"
   444  		err := cleanup(database)
   445  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   446  		defer cleanup(database)
   447  
   448  		if err == nil {
   449  
   450  			byteText := []byte(`This is a test document.  This is only a test`)
   451  
   452  			attachment := &Attachment{}
   453  			attachment.AttachmentBytes = byteText
   454  			attachment.ContentType = "text/plain"
   455  			attachment.Name = "valueBytes"
   456  
   457  			attachments := []*Attachment{}
   458  			attachments = append(attachments, attachment)
   459  
   460  			//create a new instance and database object
   461  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   462  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   463  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   464  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   465  
   466  			//create a new database
   467  			_, errdb := db.CreateDatabaseIfNotExist()
   468  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   469  
   470  			//Save the test document
   471  			_, saveerr := db.SaveDoc("10", "", &CouchDoc{JSONValue: nil, Attachments: attachments})
   472  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   473  
   474  			//Attempt to retrieve the updated test document with attachments
   475  			couchDoc, _, geterr2 := db.ReadDoc("10")
   476  			testutil.AssertNoError(t, geterr2, fmt.Sprintf("Error when trying to retrieve a document with attachment"))
   477  			testutil.AssertNotNil(t, couchDoc.Attachments)
   478  			testutil.AssertEquals(t, couchDoc.Attachments[0].AttachmentBytes, byteText)
   479  		}
   480  
   481  	}
   482  }
   483  
   484  func TestDBDeleteDocument(t *testing.T) {
   485  
   486  	if ledgerconfig.IsCouchDBEnabled() {
   487  
   488  		database := "testdbdeletedocument"
   489  		err := cleanup(database)
   490  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   491  		defer cleanup(database)
   492  
   493  		if err == nil {
   494  			//create a new instance and database object
   495  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   496  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   497  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   498  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   499  
   500  			//create a new database
   501  			_, errdb := db.CreateDatabaseIfNotExist()
   502  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   503  
   504  			//Save the test document
   505  			_, saveerr := db.SaveDoc("2", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   506  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   507  
   508  			//Attempt to retrieve the test document
   509  			_, _, readErr := db.ReadDoc("2")
   510  			testutil.AssertNoError(t, readErr, fmt.Sprintf("Error when trying to retrieve a document with attachment"))
   511  
   512  			//Delete the test document
   513  			deleteErr := db.DeleteDoc("2", "")
   514  			testutil.AssertNoError(t, deleteErr, fmt.Sprintf("Error when trying to delete a document"))
   515  
   516  			//Attempt to retrieve the test document
   517  			readValue, _, _ := db.ReadDoc("2")
   518  			testutil.AssertNil(t, readValue)
   519  		}
   520  	}
   521  }
   522  
   523  func TestDBDeleteNonExistingDocument(t *testing.T) {
   524  
   525  	if ledgerconfig.IsCouchDBEnabled() {
   526  
   527  		database := "testdbdeletenonexistingdocument"
   528  		err := cleanup(database)
   529  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   530  		defer cleanup(database)
   531  
   532  		if err == nil {
   533  			//create a new instance and database object
   534  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   535  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   536  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   537  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   538  
   539  			//create a new database
   540  			_, errdb := db.CreateDatabaseIfNotExist()
   541  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   542  
   543  			//Save the test document
   544  			deleteErr := db.DeleteDoc("2", "")
   545  			testutil.AssertNoError(t, deleteErr, fmt.Sprintf("Error when trying to delete a non existing document"))
   546  		}
   547  	}
   548  }
   549  
   550  func TestCouchDBVersion(t *testing.T) {
   551  
   552  	err := checkCouchDBVersion("2.0.0")
   553  	testutil.AssertNoError(t, err, fmt.Sprintf("Error should not have been thrown for valid version"))
   554  
   555  	err = checkCouchDBVersion("4.5.0")
   556  	testutil.AssertNoError(t, err, fmt.Sprintf("Error should not have been thrown for valid version"))
   557  
   558  	err = checkCouchDBVersion("1.6.5.4")
   559  	testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for invalid version"))
   560  
   561  	err = checkCouchDBVersion("0.0.0.0")
   562  	testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for invalid version"))
   563  
   564  }
   565  
   566  func TestRichQuery(t *testing.T) {
   567  
   568  	if ledgerconfig.IsCouchDBEnabled() {
   569  
   570  		byteJSON01 := []byte(`{"asset_name":"marble01","color":"blue","size":1,"owner":"jerry"}`)
   571  		byteJSON02 := []byte(`{"asset_name":"marble02","color":"red","size":2,"owner":"tom"}`)
   572  		byteJSON03 := []byte(`{"asset_name":"marble03","color":"green","size":3,"owner":"jerry"}`)
   573  		byteJSON04 := []byte(`{"asset_name":"marble04","color":"purple","size":4,"owner":"tom"}`)
   574  		byteJSON05 := []byte(`{"asset_name":"marble05","color":"blue","size":5,"owner":"jerry"}`)
   575  		byteJSON06 := []byte(`{"asset_name":"marble06","color":"white","size":6,"owner":"tom"}`)
   576  		byteJSON07 := []byte(`{"asset_name":"marble07","color":"white","size":7,"owner":"tom"}`)
   577  		byteJSON08 := []byte(`{"asset_name":"marble08","color":"white","size":8,"owner":"tom"}`)
   578  		byteJSON09 := []byte(`{"asset_name":"marble09","color":"white","size":9,"owner":"tom"}`)
   579  		byteJSON10 := []byte(`{"asset_name":"marble10","color":"white","size":10,"owner":"tom"}`)
   580  		byteJSON11 := []byte(`{"asset_name":"marble11","color":"green","size":11,"owner":"tom"}`)
   581  		byteJSON12 := []byte(`{"asset_name":"marble12","color":"green","size":12,"owner":"frank"}`)
   582  
   583  		attachment1 := &Attachment{}
   584  		attachment1.AttachmentBytes = []byte(`marble01 - test attachment`)
   585  		attachment1.ContentType = "application/octet-stream"
   586  		attachment1.Name = "data"
   587  		attachments1 := []*Attachment{}
   588  		attachments1 = append(attachments1, attachment1)
   589  
   590  		attachment2 := &Attachment{}
   591  		attachment2.AttachmentBytes = []byte(`marble02 - test attachment`)
   592  		attachment2.ContentType = "application/octet-stream"
   593  		attachment2.Name = "data"
   594  		attachments2 := []*Attachment{}
   595  		attachments2 = append(attachments2, attachment2)
   596  
   597  		attachment3 := &Attachment{}
   598  		attachment3.AttachmentBytes = []byte(`marble03 - test attachment`)
   599  		attachment3.ContentType = "application/octet-stream"
   600  		attachment3.Name = "data"
   601  		attachments3 := []*Attachment{}
   602  		attachments3 = append(attachments3, attachment3)
   603  
   604  		attachment4 := &Attachment{}
   605  		attachment4.AttachmentBytes = []byte(`marble04 - test attachment`)
   606  		attachment4.ContentType = "application/octet-stream"
   607  		attachment4.Name = "data"
   608  		attachments4 := []*Attachment{}
   609  		attachments4 = append(attachments4, attachment4)
   610  
   611  		attachment5 := &Attachment{}
   612  		attachment5.AttachmentBytes = []byte(`marble05 - test attachment`)
   613  		attachment5.ContentType = "application/octet-stream"
   614  		attachment5.Name = "data"
   615  		attachments5 := []*Attachment{}
   616  		attachments5 = append(attachments5, attachment5)
   617  
   618  		attachment6 := &Attachment{}
   619  		attachment6.AttachmentBytes = []byte(`marble06 - test attachment`)
   620  		attachment6.ContentType = "application/octet-stream"
   621  		attachment6.Name = "data"
   622  		attachments6 := []*Attachment{}
   623  		attachments6 = append(attachments6, attachment6)
   624  
   625  		attachment7 := &Attachment{}
   626  		attachment7.AttachmentBytes = []byte(`marble07 - test attachment`)
   627  		attachment7.ContentType = "application/octet-stream"
   628  		attachment7.Name = "data"
   629  		attachments7 := []*Attachment{}
   630  		attachments7 = append(attachments7, attachment7)
   631  
   632  		attachment8 := &Attachment{}
   633  		attachment8.AttachmentBytes = []byte(`marble08 - test attachment`)
   634  		attachment8.ContentType = "application/octet-stream"
   635  		attachment7.Name = "data"
   636  		attachments8 := []*Attachment{}
   637  		attachments8 = append(attachments8, attachment8)
   638  
   639  		attachment9 := &Attachment{}
   640  		attachment9.AttachmentBytes = []byte(`marble09 - test attachment`)
   641  		attachment9.ContentType = "application/octet-stream"
   642  		attachment9.Name = "data"
   643  		attachments9 := []*Attachment{}
   644  		attachments9 = append(attachments9, attachment9)
   645  
   646  		attachment10 := &Attachment{}
   647  		attachment10.AttachmentBytes = []byte(`marble10 - test attachment`)
   648  		attachment10.ContentType = "application/octet-stream"
   649  		attachment10.Name = "data"
   650  		attachments10 := []*Attachment{}
   651  		attachments10 = append(attachments10, attachment10)
   652  
   653  		attachment11 := &Attachment{}
   654  		attachment11.AttachmentBytes = []byte(`marble11 - test attachment`)
   655  		attachment11.ContentType = "application/octet-stream"
   656  		attachment11.Name = "data"
   657  		attachments11 := []*Attachment{}
   658  		attachments11 = append(attachments11, attachment11)
   659  
   660  		attachment12 := &Attachment{}
   661  		attachment12.AttachmentBytes = []byte(`marble12 - test attachment`)
   662  		attachment12.ContentType = "application/octet-stream"
   663  		attachment12.Name = "data"
   664  		attachments12 := []*Attachment{}
   665  		attachments12 = append(attachments12, attachment12)
   666  
   667  		database := "testrichquery"
   668  		err := cleanup(database)
   669  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   670  		defer cleanup(database)
   671  
   672  		if err == nil {
   673  			//create a new instance and database object   --------------------------------------------------------
   674  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   675  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   676  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   677  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   678  
   679  			//create a new database
   680  			_, errdb := db.CreateDatabaseIfNotExist()
   681  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   682  
   683  			//Save the test document
   684  			_, saveerr := db.SaveDoc("marble01", "", &CouchDoc{JSONValue: byteJSON01, Attachments: attachments1})
   685  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   686  
   687  			//Save the test document
   688  			_, saveerr = db.SaveDoc("marble02", "", &CouchDoc{JSONValue: byteJSON02, Attachments: attachments2})
   689  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   690  
   691  			//Save the test document
   692  			_, saveerr = db.SaveDoc("marble03", "", &CouchDoc{JSONValue: byteJSON03, Attachments: attachments3})
   693  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   694  
   695  			//Save the test document
   696  			_, saveerr = db.SaveDoc("marble04", "", &CouchDoc{JSONValue: byteJSON04, Attachments: attachments4})
   697  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   698  
   699  			//Save the test document
   700  			_, saveerr = db.SaveDoc("marble05", "", &CouchDoc{JSONValue: byteJSON05, Attachments: attachments5})
   701  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   702  
   703  			//Save the test document
   704  			_, saveerr = db.SaveDoc("marble06", "", &CouchDoc{JSONValue: byteJSON06, Attachments: attachments6})
   705  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   706  
   707  			//Save the test document
   708  			_, saveerr = db.SaveDoc("marble07", "", &CouchDoc{JSONValue: byteJSON07, Attachments: attachments7})
   709  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   710  
   711  			//Save the test document
   712  			_, saveerr = db.SaveDoc("marble08", "", &CouchDoc{JSONValue: byteJSON08, Attachments: attachments8})
   713  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   714  
   715  			//Save the test document
   716  			_, saveerr = db.SaveDoc("marble09", "", &CouchDoc{JSONValue: byteJSON09, Attachments: attachments9})
   717  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   718  
   719  			//Save the test document
   720  			_, saveerr = db.SaveDoc("marble10", "", &CouchDoc{JSONValue: byteJSON10, Attachments: attachments10})
   721  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   722  
   723  			//Save the test document
   724  			_, saveerr = db.SaveDoc("marble11", "", &CouchDoc{JSONValue: byteJSON11, Attachments: attachments11})
   725  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   726  
   727  			//Save the test document
   728  			_, saveerr = db.SaveDoc("marble12", "", &CouchDoc{JSONValue: byteJSON12, Attachments: attachments12})
   729  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   730  
   731  			//Test query with invalid JSON -------------------------------------------------------------------
   732  			queryString := "{\"selector\":{\"owner\":}}"
   733  
   734  			_, err = db.QueryDocuments(queryString)
   735  			testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for bad json"))
   736  
   737  			//Test query with object  -------------------------------------------------------------------
   738  			queryString = "{\"selector\":{\"owner\":{\"$eq\":\"jerry\"}}}"
   739  
   740  			queryResult, err := db.QueryDocuments(queryString)
   741  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
   742  
   743  			//There should be 3 results for owner="jerry"
   744  			testutil.AssertEquals(t, len(*queryResult), 3)
   745  
   746  			//Test query with implicit operator   --------------------------------------------------------------
   747  			queryString = "{\"selector\":{\"owner\":\"jerry\"}}"
   748  
   749  			queryResult, err = db.QueryDocuments(queryString)
   750  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
   751  
   752  			//There should be 3 results for owner="jerry"
   753  			testutil.AssertEquals(t, len(*queryResult), 3)
   754  
   755  			//Test query with specified fields   -------------------------------------------------------------------
   756  			queryString = "{\"selector\":{\"owner\":{\"$eq\":\"jerry\"}},\"fields\": [\"owner\",\"asset_name\",\"color\",\"size\"]}"
   757  
   758  			queryResult, err = db.QueryDocuments(queryString)
   759  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
   760  
   761  			//There should be 3 results for owner="jerry"
   762  			testutil.AssertEquals(t, len(*queryResult), 3)
   763  
   764  			//Test query with a leading operator   -------------------------------------------------------------------
   765  			queryString = "{\"selector\":{\"$or\":[{\"owner\":{\"$eq\":\"jerry\"}},{\"owner\": {\"$eq\": \"frank\"}}]}}"
   766  
   767  			queryResult, err = db.QueryDocuments(queryString)
   768  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
   769  
   770  			//There should be 4 results for owner="jerry" or owner="frank"
   771  			testutil.AssertEquals(t, len(*queryResult), 4)
   772  
   773  			//Test query implicit and explicit operator   ------------------------------------------------------------------
   774  			queryString = "{\"selector\":{\"color\":\"green\",\"$or\":[{\"owner\":\"tom\"},{\"owner\":\"frank\"}]}}"
   775  
   776  			queryResult, err = db.QueryDocuments(queryString)
   777  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
   778  
   779  			//There should be 2 results for color="green" and (owner="jerry" or owner="frank")
   780  			testutil.AssertEquals(t, len(*queryResult), 2)
   781  
   782  			//Test query with a leading operator  -------------------------------------------------------------------------
   783  			queryString = "{\"selector\":{\"$and\":[{\"size\":{\"$gte\":2}},{\"size\":{\"$lte\":5}}]}}"
   784  
   785  			queryResult, err = db.QueryDocuments(queryString)
   786  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
   787  
   788  			//There should be 4 results for size >= 2 and size <= 5
   789  			testutil.AssertEquals(t, len(*queryResult), 4)
   790  
   791  			//Test query with leading and embedded operator  -------------------------------------------------------------
   792  			queryString = "{\"selector\":{\"$and\":[{\"size\":{\"$gte\":3}},{\"size\":{\"$lte\":10}},{\"$not\":{\"size\":7}}]}}"
   793  
   794  			queryResult, err = db.QueryDocuments(queryString)
   795  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
   796  
   797  			//There should be 7 results for size >= 3 and size <= 10 and not 7
   798  			testutil.AssertEquals(t, len(*queryResult), 7)
   799  
   800  			//Test query with leading operator and array of objects ----------------------------------------------------------
   801  			queryString = "{\"selector\":{\"$and\":[{\"size\":{\"$gte\":2}},{\"size\":{\"$lte\":10}},{\"$nor\":[{\"size\":3},{\"size\":5},{\"size\":7}]}]}}"
   802  
   803  			queryResult, err = db.QueryDocuments(queryString)
   804  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
   805  
   806  			//There should be 6 results for size >= 2 and size <= 10 and not 3,5 or 7
   807  			testutil.AssertEquals(t, len(*queryResult), 6)
   808  
   809  			//Test a range query ---------------------------------------------------------------------------------------------
   810  			queryResult, err = db.ReadDocRange("marble02", "marble06", 10000, 0)
   811  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a range query"))
   812  
   813  			//There should be 4 results
   814  			testutil.AssertEquals(t, len(*queryResult), 4)
   815  
   816  			//Test query with for tom  -------------------------------------------------------------------
   817  			queryString = "{\"selector\":{\"owner\":{\"$eq\":\"tom\"}}}"
   818  
   819  			queryResult, err = db.QueryDocuments(queryString)
   820  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
   821  
   822  			//There should be 8 results for owner="tom"
   823  			testutil.AssertEquals(t, len(*queryResult), 8)
   824  
   825  			//Test query with for tom with limit  -------------------------------------------------------------------
   826  			queryString = "{\"selector\":{\"owner\":{\"$eq\":\"tom\"}},\"limit\":2}"
   827  
   828  			queryResult, err = db.QueryDocuments(queryString)
   829  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
   830  
   831  			//There should be 2 results for owner="tom" with a limit of 2
   832  			testutil.AssertEquals(t, len(*queryResult), 2)
   833  
   834  		}
   835  	}
   836  }
   837  
   838  func TestBatchBatchOperations(t *testing.T) {
   839  
   840  	if ledgerconfig.IsCouchDBEnabled() {
   841  
   842  		byteJSON01 := []byte(`{"_id":"marble01","asset_name":"marble01","color":"blue","size":"1","owner":"jerry"}`)
   843  		byteJSON02 := []byte(`{"_id":"marble02","asset_name":"marble02","color":"red","size":"2","owner":"tom"}`)
   844  		byteJSON03 := []byte(`{"_id":"marble03","asset_name":"marble03","color":"green","size":"3","owner":"jerry"}`)
   845  		byteJSON04 := []byte(`{"_id":"marble04","asset_name":"marble04","color":"purple","size":"4","owner":"tom"}`)
   846  		byteJSON05 := []byte(`{"_id":"marble05","asset_name":"marble05","color":"blue","size":"5","owner":"jerry"}`)
   847  
   848  		attachment1 := &Attachment{}
   849  		attachment1.AttachmentBytes = []byte(`marble01 - test attachment`)
   850  		attachment1.ContentType = "application/octet-stream"
   851  		attachment1.Name = "data"
   852  		attachments1 := []*Attachment{}
   853  		attachments1 = append(attachments1, attachment1)
   854  
   855  		attachment2 := &Attachment{}
   856  		attachment2.AttachmentBytes = []byte(`marble02 - test attachment`)
   857  		attachment2.ContentType = "application/octet-stream"
   858  		attachment2.Name = "data"
   859  		attachments2 := []*Attachment{}
   860  		attachments2 = append(attachments2, attachment2)
   861  
   862  		attachment3 := &Attachment{}
   863  		attachment3.AttachmentBytes = []byte(`marble03 - test attachment`)
   864  		attachment3.ContentType = "application/octet-stream"
   865  		attachment3.Name = "data"
   866  		attachments3 := []*Attachment{}
   867  		attachments3 = append(attachments3, attachment3)
   868  
   869  		attachment4 := &Attachment{}
   870  		attachment4.AttachmentBytes = []byte(`marble04 - test attachment`)
   871  		attachment4.ContentType = "application/octet-stream"
   872  		attachment4.Name = "data"
   873  		attachments4 := []*Attachment{}
   874  		attachments4 = append(attachments4, attachment4)
   875  
   876  		attachment5 := &Attachment{}
   877  		attachment5.AttachmentBytes = []byte(`marble05 - test attachment`)
   878  		attachment5.ContentType = "application/octet-stream"
   879  		attachment5.Name = "data"
   880  		attachments5 := []*Attachment{}
   881  		attachments5 = append(attachments5, attachment5)
   882  
   883  		database := "testbatch"
   884  		err := cleanup(database)
   885  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   886  		defer cleanup(database)
   887  
   888  		//create a new instance and database object   --------------------------------------------------------
   889  		couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   890  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   891  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   892  		db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   893  
   894  		//create a new database
   895  		_, errdb := db.CreateDatabaseIfNotExist()
   896  		testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   897  
   898  		batchUpdateDocs := []*CouchDoc{}
   899  
   900  		value1 := &CouchDoc{JSONValue: byteJSON01, Attachments: attachments1}
   901  		value2 := &CouchDoc{JSONValue: byteJSON02, Attachments: attachments2}
   902  		value3 := &CouchDoc{JSONValue: byteJSON03, Attachments: attachments3}
   903  		value4 := &CouchDoc{JSONValue: byteJSON04, Attachments: attachments4}
   904  		value5 := &CouchDoc{JSONValue: byteJSON05, Attachments: attachments5}
   905  
   906  		batchUpdateDocs = append(batchUpdateDocs, value1)
   907  		batchUpdateDocs = append(batchUpdateDocs, value2)
   908  		batchUpdateDocs = append(batchUpdateDocs, value3)
   909  		batchUpdateDocs = append(batchUpdateDocs, value4)
   910  		batchUpdateDocs = append(batchUpdateDocs, value5)
   911  
   912  		batchUpdateResp, err := db.BatchUpdateDocuments(batchUpdateDocs)
   913  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to update a batch of documents"))
   914  
   915  		//check to make sure each batch update response was successful
   916  		for _, updateDoc := range batchUpdateResp {
   917  			testutil.AssertEquals(t, updateDoc.Ok, true)
   918  		}
   919  
   920  		//----------------------------------------------
   921  		//Test Retrieve JSON
   922  		dbGetResp, _, geterr := db.ReadDoc("marble01")
   923  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when attempting read a document"))
   924  
   925  		assetResp := &Asset{}
   926  		geterr = json.Unmarshal(dbGetResp.JSONValue, &assetResp)
   927  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   928  		//Verify the owner retrieved matches
   929  		testutil.AssertEquals(t, assetResp.Owner, "jerry")
   930  
   931  		//----------------------------------------------
   932  		//Test retrieve binary
   933  		dbGetResp, _, geterr = db.ReadDoc("marble03")
   934  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when attempting read a document"))
   935  		//Retrieve the attachments
   936  		attachments := dbGetResp.Attachments
   937  		//Only one was saved, so take the first
   938  		retrievedAttachment := attachments[0]
   939  		//Verify the text matches
   940  		testutil.AssertEquals(t, attachment3.AttachmentBytes, retrievedAttachment.AttachmentBytes)
   941  		//----------------------------------------------
   942  		//Test Bad Updates
   943  		batchUpdateDocs = []*CouchDoc{}
   944  		batchUpdateDocs = append(batchUpdateDocs, value1)
   945  		batchUpdateDocs = append(batchUpdateDocs, value2)
   946  		batchUpdateResp, err = db.BatchUpdateDocuments(batchUpdateDocs)
   947  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to update a batch of documents"))
   948  		//No revision was provided, so these two updates should fail
   949  		//Verify that the "Ok" field is returned as false
   950  		for _, updateDoc := range batchUpdateResp {
   951  			testutil.AssertEquals(t, updateDoc.Ok, false)
   952  			testutil.AssertEquals(t, updateDoc.Error, updateDocumentConflictError)
   953  			testutil.AssertEquals(t, updateDoc.Reason, updateDocumentConflictReason)
   954  		}
   955  
   956  		//----------------------------------------------
   957  		//Test Batch Retrieve Keys and Update
   958  
   959  		var keys []string
   960  
   961  		keys = append(keys, "marble01")
   962  		keys = append(keys, "marble03")
   963  
   964  		batchRevs, err := db.BatchRetrieveIDRevision(keys)
   965  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting retrieve revisions"))
   966  
   967  		batchUpdateDocs = []*CouchDoc{}
   968  
   969  		//iterate through the revision docs
   970  		for _, revdoc := range batchRevs {
   971  			if revdoc.ID == "marble01" {
   972  				//update the json with the rev and add to the batch
   973  				marble01Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON01, false)
   974  				batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble01Doc, Attachments: attachments1})
   975  			}
   976  
   977  			if revdoc.ID == "marble03" {
   978  				//update the json with the rev and add to the batch
   979  				marble03Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON03, false)
   980  				batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble03Doc, Attachments: attachments3})
   981  			}
   982  		}
   983  
   984  		//Update couchdb with the batch
   985  		batchUpdateResp, err = db.BatchUpdateDocuments(batchUpdateDocs)
   986  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to update a batch of documents"))
   987  		//check to make sure each batch update response was successful
   988  		for _, updateDoc := range batchUpdateResp {
   989  			testutil.AssertEquals(t, updateDoc.Ok, true)
   990  		}
   991  
   992  		//----------------------------------------------
   993  		//Test Batch Delete
   994  
   995  		keys = []string{}
   996  
   997  		keys = append(keys, "marble02")
   998  		keys = append(keys, "marble04")
   999  
  1000  		batchRevs, err = db.BatchRetrieveIDRevision(keys)
  1001  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting retrieve revisions"))
  1002  
  1003  		batchUpdateDocs = []*CouchDoc{}
  1004  
  1005  		//iterate through the revision docs
  1006  		for _, revdoc := range batchRevs {
  1007  			if revdoc.ID == "marble02" {
  1008  				//update the json with the rev and add to the batch
  1009  				marble02Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON02, true)
  1010  				batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble02Doc, Attachments: attachments1})
  1011  			}
  1012  			if revdoc.ID == "marble04" {
  1013  				//update the json with the rev and add to the batch
  1014  				marble04Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON04, true)
  1015  				batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble04Doc, Attachments: attachments3})
  1016  			}
  1017  		}
  1018  
  1019  		//Update couchdb with the batch
  1020  		batchUpdateResp, err = db.BatchUpdateDocuments(batchUpdateDocs)
  1021  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to update a batch of documents"))
  1022  
  1023  		//check to make sure each batch update response was successful
  1024  		for _, updateDoc := range batchUpdateResp {
  1025  			testutil.AssertEquals(t, updateDoc.Ok, true)
  1026  		}
  1027  
  1028  		//Retrieve the test document
  1029  		dbGetResp, _, geterr = db.ReadDoc("marble02")
  1030  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
  1031  
  1032  		//assert the value was deleted
  1033  		testutil.AssertNil(t, dbGetResp)
  1034  
  1035  		//Retrieve the test document
  1036  		dbGetResp, _, geterr = db.ReadDoc("marble04")
  1037  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
  1038  
  1039  		//assert the value was deleted
  1040  		testutil.AssertNil(t, dbGetResp)
  1041  	}
  1042  }
  1043  
  1044  //addRevisionAndDeleteStatus adds keys for version and chaincodeID to the JSON value
  1045  func addRevisionAndDeleteStatus(revision string, value []byte, deleted bool) []byte {
  1046  
  1047  	//create a version mapping
  1048  	jsonMap := make(map[string]interface{})
  1049  
  1050  	json.Unmarshal(value, &jsonMap)
  1051  
  1052  	//add the revision
  1053  	if revision != "" {
  1054  		jsonMap["_rev"] = revision
  1055  	}
  1056  
  1057  	//If this record is to be deleted, set the "_deleted" property to true
  1058  	if deleted {
  1059  		jsonMap["_deleted"] = true
  1060  	}
  1061  	//marshal the data to a byte array
  1062  	returnJSON, _ := json.Marshal(jsonMap)
  1063  
  1064  	return returnJSON
  1065  
  1066  }