github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/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  	"net/http"
    23  	"os"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  	"unicode/utf8"
    28  
    29  	"github.com/hyperledger/fabric/common/ledger/testutil"
    30  	"github.com/hyperledger/fabric/core/ledger/ledgerconfig"
    31  	ledgertestutil "github.com/hyperledger/fabric/core/ledger/testutil"
    32  	"github.com/spf13/viper"
    33  )
    34  
    35  const badConnectURL = "couchdb:5990"
    36  const badParseConnectURL = "http://host.com|5432"
    37  const updateDocumentConflictError = "conflict"
    38  const updateDocumentConflictReason = "Document update conflict."
    39  
    40  var couchDBDef *CouchDBDef
    41  
    42  func cleanup(database string) error {
    43  	//create a new connection
    44  	couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
    45  		couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
    46  
    47  	if err != nil {
    48  		fmt.Println("Unexpected error", err)
    49  		return err
    50  	}
    51  	db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
    52  	//drop the test database
    53  	db.DropDatabase()
    54  	return nil
    55  }
    56  
    57  type Asset struct {
    58  	ID        string `json:"_id"`
    59  	Rev       string `json:"_rev"`
    60  	AssetName string `json:"asset_name"`
    61  	Color     string `json:"color"`
    62  	Size      string `json:"size"`
    63  	Owner     string `json:"owner"`
    64  }
    65  
    66  var assetJSON = []byte(`{"asset_name":"marble1","color":"blue","size":"35","owner":"jerry"}`)
    67  
    68  func TestMain(m *testing.M) {
    69  	// Read the core.yaml file for default config.
    70  	ledgertestutil.SetupCoreYAMLConfig()
    71  
    72  	// Switch to CouchDB
    73  	viper.Set("ledger.state.stateDatabase", "CouchDB")
    74  
    75  	// both vagrant and CI have couchdb configured at host "couchdb"
    76  	viper.Set("ledger.state.couchDBConfig.couchDBAddress", "couchdb:5984")
    77  	// Replace with correct username/password such as
    78  	// admin/admin if user security is enabled on couchdb.
    79  	viper.Set("ledger.state.couchDBConfig.username", "")
    80  	viper.Set("ledger.state.couchDBConfig.password", "")
    81  	viper.Set("ledger.state.couchDBConfig.maxRetries", 3)
    82  	viper.Set("ledger.state.couchDBConfig.maxRetriesOnStartup", 10)
    83  	viper.Set("ledger.state.couchDBConfig.requestTimeout", time.Second*35)
    84  
    85  	// Create CouchDB definition from config parameters
    86  	couchDBDef = GetCouchDBDefinition()
    87  
    88  	//run the tests
    89  	result := m.Run()
    90  
    91  	//revert to default goleveldb
    92  	viper.Set("ledger.state.stateDatabase", "goleveldb")
    93  	os.Exit(result)
    94  }
    95  
    96  func TestDBConnectionDef(t *testing.T) {
    97  
    98  	//create a new connection
    99  	_, err := CreateConnectionDefinition(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   100  		couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   101  	testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create database connection definition"))
   102  
   103  }
   104  
   105  func TestDBBadConnectionDef(t *testing.T) {
   106  
   107  	//create a new connection
   108  	_, err := CreateConnectionDefinition(badParseConnectURL, couchDBDef.Username, couchDBDef.Password,
   109  		couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   110  	testutil.AssertError(t, err, fmt.Sprintf("Did not receive error when trying to create database connection definition with a bad hostname"))
   111  
   112  }
   113  
   114  func TestEncodePathElement(t *testing.T) {
   115  
   116  	encodedString := encodePathElement("testelement")
   117  	testutil.AssertEquals(t, encodedString, "testelement")
   118  
   119  	encodedString = encodePathElement("test element")
   120  	testutil.AssertEquals(t, encodedString, "test%20element")
   121  
   122  	encodedString = encodePathElement("/test element")
   123  	testutil.AssertEquals(t, encodedString, "%2Ftest%20element")
   124  
   125  	encodedString = encodePathElement("/test element:")
   126  	testutil.AssertEquals(t, encodedString, "%2Ftest%20element:")
   127  
   128  	encodedString = encodePathElement("/test+ element:")
   129  	testutil.AssertEquals(t, encodedString, "%2Ftest%2B%20element:")
   130  
   131  }
   132  
   133  func TestBadCouchDBInstance(t *testing.T) {
   134  
   135  	//TODO continue changes to return and removal of sprintf in followon changes
   136  	if !ledgerconfig.IsCouchDBEnabled() {
   137  		t.Skip("CouchDB is not enabled")
   138  		return
   139  	}
   140  	//Create a bad connection definition
   141  	badConnectDef := CouchConnectionDef{URL: badParseConnectURL, Username: "", Password: "",
   142  		MaxRetries: 3, MaxRetriesOnStartup: 10, RequestTimeout: time.Second * 30}
   143  
   144  	client := &http.Client{}
   145  
   146  	//Create a bad couchdb instance
   147  	badCouchDBInstance := CouchInstance{badConnectDef, client}
   148  
   149  	//Create a bad CouchDatabase
   150  	badDB := CouchDatabase{badCouchDBInstance, "baddb"}
   151  
   152  	//Test CreateCouchDatabase with bad connection
   153  	_, err := CreateCouchDatabase(badCouchDBInstance, "baddbtest")
   154  	testutil.AssertError(t, err, "Error should have been thrown with CreateCouchDatabase and invalid connection")
   155  
   156  	//Test CreateSystemDatabasesIfNotExist with bad connection
   157  	err = CreateSystemDatabasesIfNotExist(badCouchDBInstance)
   158  	testutil.AssertError(t, err, "Error should have been thrown with CreateSystemDatabasesIfNotExist and invalid connection")
   159  
   160  	//Test CreateDatabaseIfNotExist with bad connection
   161  	_, err = badDB.CreateDatabaseIfNotExist()
   162  	testutil.AssertError(t, err, "Error should have been thrown with CreateDatabaseIfNotExist and invalid connection")
   163  
   164  	//Test GetDatabaseInfo with bad connection
   165  	_, _, err = badDB.GetDatabaseInfo()
   166  	testutil.AssertError(t, err, "Error should have been thrown with GetDatabaseInfo and invalid connection")
   167  
   168  	//Test VerifyCouchConfig with bad connection
   169  	_, _, err = badCouchDBInstance.VerifyCouchConfig()
   170  	testutil.AssertError(t, err, "Error should have been thrown with VerifyCouchConfig and invalid connection")
   171  
   172  	//Test EnsureFullCommit with bad connection
   173  	_, err = badDB.EnsureFullCommit()
   174  	testutil.AssertError(t, err, "Error should have been thrown with EnsureFullCommit and invalid connection")
   175  
   176  	//Test DropDatabase with bad connection
   177  	_, err = badDB.DropDatabase()
   178  	testutil.AssertError(t, err, "Error should have been thrown with DropDatabase and invalid connection")
   179  
   180  	//Test ReadDoc with bad connection
   181  	_, _, err = badDB.ReadDoc("1")
   182  	testutil.AssertError(t, err, "Error should have been thrown with ReadDoc and invalid connection")
   183  
   184  	//Test SaveDoc with bad connection
   185  	_, err = badDB.SaveDoc("1", "1", nil)
   186  	testutil.AssertError(t, err, "Error should have been thrown with SaveDoc and invalid connection")
   187  
   188  	//Test DeleteDoc with bad connection
   189  	err = badDB.DeleteDoc("1", "1")
   190  	testutil.AssertError(t, err, "Error should have been thrown with DeleteDoc and invalid connection")
   191  
   192  	//Test ReadDocRange with bad connection
   193  	_, err = badDB.ReadDocRange("1", "2", 1000, 0)
   194  	testutil.AssertError(t, err, "Error should have been thrown with ReadDocRange and invalid connection")
   195  
   196  	//Test QueryDocuments with bad connection
   197  	_, err = badDB.QueryDocuments("1")
   198  	testutil.AssertError(t, err, "Error should have been thrown with QueryDocuments and invalid connection")
   199  
   200  	//Test BatchRetrieveIDRevision with bad connection
   201  	_, err = badDB.BatchRetrieveIDRevision(nil)
   202  	testutil.AssertError(t, err, "Error should have been thrown with BatchRetrieveIDRevision and invalid connection")
   203  
   204  	//Test BatchUpdateDocuments with bad connection
   205  	_, err = badDB.BatchUpdateDocuments(nil)
   206  	testutil.AssertError(t, err, "Error should have been thrown with BatchUpdateDocuments and invalid connection")
   207  
   208  }
   209  
   210  func TestDBCreateSaveWithoutRevision(t *testing.T) {
   211  
   212  	if ledgerconfig.IsCouchDBEnabled() {
   213  
   214  		database := "testdbcreatesavewithoutrevision"
   215  		err := cleanup(database)
   216  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   217  		defer cleanup(database)
   218  
   219  		if err == nil {
   220  			//create a new instance and database object
   221  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   222  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   223  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   224  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   225  
   226  			//create a new database
   227  			_, errdb := db.CreateDatabaseIfNotExist()
   228  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   229  
   230  			//Save the test document
   231  			_, saveerr := db.SaveDoc("2", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   232  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   233  		}
   234  	}
   235  }
   236  
   237  func TestDBCreateEnsureFullCommit(t *testing.T) {
   238  
   239  	if ledgerconfig.IsCouchDBEnabled() {
   240  
   241  		database := "testdbensurefullcommit"
   242  		err := cleanup(database)
   243  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   244  		defer cleanup(database)
   245  
   246  		if err == nil {
   247  			//create a new instance and database object
   248  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   249  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   250  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   251  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   252  
   253  			//create a new database
   254  			_, errdb := db.CreateDatabaseIfNotExist()
   255  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   256  
   257  			//Save the test document
   258  			_, saveerr := db.SaveDoc("2", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   259  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   260  
   261  			//Ensure a full commit
   262  			_, commiterr := db.EnsureFullCommit()
   263  			testutil.AssertNoError(t, commiterr, fmt.Sprintf("Error when trying to ensure a full commit"))
   264  
   265  		}
   266  	}
   267  }
   268  
   269  func TestDBBadDatabaseName(t *testing.T) {
   270  
   271  	if ledgerconfig.IsCouchDBEnabled() {
   272  
   273  		//create a new instance and database object using a valid database name mixed case
   274  		couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   275  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   276  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   277  		_, dberr := CreateCouchDatabase(*couchInstance, "testDB")
   278  		testutil.AssertError(t, dberr, "Error should have been thrown for an invalid db name")
   279  
   280  		//create a new instance and database object using a valid database name letters and numbers
   281  		couchInstance, err = CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   282  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   283  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   284  		_, dberr = CreateCouchDatabase(*couchInstance, "test132")
   285  		testutil.AssertNoError(t, dberr, fmt.Sprintf("Error when testing a valid database name"))
   286  
   287  		//create a new instance and database object using a valid database name - special characters
   288  		couchInstance, err = CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   289  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   290  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   291  		_, dberr = CreateCouchDatabase(*couchInstance, "test1234~!@#$%^&*()[]{}.")
   292  		testutil.AssertError(t, dberr, "Error should have been thrown for an invalid db name")
   293  
   294  		//create a new instance and database object using a invalid database name - too long	/*
   295  		couchInstance, err = CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   296  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   297  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   298  		_, dberr = CreateCouchDatabase(*couchInstance, "a12345678901234567890123456789012345678901234"+
   299  			"56789012345678901234567890123456789012345678901234567890123456789012345678901234567890"+
   300  			"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456"+
   301  			"78901234567890123456789012345678901234567890")
   302  		testutil.AssertError(t, dberr, fmt.Sprintf("Error should have been thrown for invalid database name"))
   303  
   304  	}
   305  }
   306  
   307  func TestDBBadConnection(t *testing.T) {
   308  
   309  	if ledgerconfig.IsCouchDBEnabled() {
   310  
   311  		//create a new instance and database object
   312  		//Limit the maxRetriesOnStartup to 3 in order to reduce time for the failure
   313  		_, err := CreateCouchInstance(badConnectURL, couchDBDef.Username, couchDBDef.Password,
   314  			couchDBDef.MaxRetries, 3, couchDBDef.RequestTimeout)
   315  		testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for a bad connection"))
   316  	}
   317  }
   318  
   319  func TestDBCreateDatabaseAndPersist(t *testing.T) {
   320  
   321  	if ledgerconfig.IsCouchDBEnabled() {
   322  
   323  		database := "testdbcreatedatabaseandpersist"
   324  		err := cleanup(database)
   325  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   326  		defer cleanup(database)
   327  
   328  		if err == nil {
   329  			//create a new instance and database object
   330  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   331  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   332  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   333  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   334  
   335  			//create a new database
   336  			_, errdb := db.CreateDatabaseIfNotExist()
   337  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   338  
   339  			//Retrieve the info for the new database and make sure the name matches
   340  			dbResp, _, errdb := db.GetDatabaseInfo()
   341  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to retrieve database information"))
   342  			testutil.AssertEquals(t, dbResp.DbName, database)
   343  
   344  			//Save the test document
   345  			_, saveerr := db.SaveDoc("idWith/slash", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   346  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   347  
   348  			//Retrieve the test document
   349  			dbGetResp, _, geterr := db.ReadDoc("idWith/slash")
   350  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   351  
   352  			//Unmarshal the document to Asset structure
   353  			assetResp := &Asset{}
   354  			geterr = json.Unmarshal(dbGetResp.JSONValue, &assetResp)
   355  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   356  
   357  			//Verify the owner retrieved matches
   358  			testutil.AssertEquals(t, assetResp.Owner, "jerry")
   359  
   360  			//Save the test document
   361  			_, saveerr = db.SaveDoc("1", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   362  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   363  
   364  			//Retrieve the test document
   365  			dbGetResp, _, geterr = db.ReadDoc("1")
   366  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   367  
   368  			//Unmarshal the document to Asset structure
   369  			assetResp = &Asset{}
   370  			geterr = json.Unmarshal(dbGetResp.JSONValue, &assetResp)
   371  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   372  
   373  			//Verify the owner retrieved matches
   374  			testutil.AssertEquals(t, assetResp.Owner, "jerry")
   375  
   376  			//Change owner to bob
   377  			assetResp.Owner = "bob"
   378  
   379  			//create a byte array of the JSON
   380  			assetDocUpdated, _ := json.Marshal(assetResp)
   381  
   382  			//Save the updated test document
   383  			_, saveerr = db.SaveDoc("1", "", &CouchDoc{JSONValue: assetDocUpdated, Attachments: nil})
   384  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save the updated document"))
   385  
   386  			//Retrieve the updated test document
   387  			dbGetResp, _, geterr = db.ReadDoc("1")
   388  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   389  
   390  			//Unmarshal the document to Asset structure
   391  			assetResp = &Asset{}
   392  			json.Unmarshal(dbGetResp.JSONValue, &assetResp)
   393  
   394  			//Assert that the update was saved and retrieved
   395  			testutil.AssertEquals(t, assetResp.Owner, "bob")
   396  
   397  			testBytes2 := []byte(`test attachment 2`)
   398  
   399  			attachment2 := &Attachment{}
   400  			attachment2.AttachmentBytes = testBytes2
   401  			attachment2.ContentType = "application/octet-stream"
   402  			attachment2.Name = "data"
   403  			attachments2 := []*Attachment{}
   404  			attachments2 = append(attachments2, attachment2)
   405  
   406  			//Save the test document with an attachment
   407  			_, saveerr = db.SaveDoc("2", "", &CouchDoc{JSONValue: nil, Attachments: attachments2})
   408  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   409  
   410  			//Retrieve the test document with attachments
   411  			dbGetResp, _, geterr = db.ReadDoc("2")
   412  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   413  
   414  			//verify the text from the attachment is correct
   415  			testattach := dbGetResp.Attachments[0].AttachmentBytes
   416  			testutil.AssertEquals(t, testattach, testBytes2)
   417  
   418  			testBytes3 := []byte{}
   419  
   420  			attachment3 := &Attachment{}
   421  			attachment3.AttachmentBytes = testBytes3
   422  			attachment3.ContentType = "application/octet-stream"
   423  			attachment3.Name = "data"
   424  			attachments3 := []*Attachment{}
   425  			attachments3 = append(attachments3, attachment3)
   426  
   427  			//Save the test document with a zero length attachment
   428  			_, saveerr = db.SaveDoc("3", "", &CouchDoc{JSONValue: nil, Attachments: attachments3})
   429  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   430  
   431  			//Retrieve the test document with attachments
   432  			dbGetResp, _, geterr = db.ReadDoc("3")
   433  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   434  
   435  			//verify the text from the attachment is correct,  zero bytes
   436  			testattach = dbGetResp.Attachments[0].AttachmentBytes
   437  			testutil.AssertEquals(t, testattach, testBytes3)
   438  
   439  			testBytes4a := []byte(`test attachment 4a`)
   440  			attachment4a := &Attachment{}
   441  			attachment4a.AttachmentBytes = testBytes4a
   442  			attachment4a.ContentType = "application/octet-stream"
   443  			attachment4a.Name = "data1"
   444  
   445  			testBytes4b := []byte(`test attachment 4b`)
   446  			attachment4b := &Attachment{}
   447  			attachment4b.AttachmentBytes = testBytes4b
   448  			attachment4b.ContentType = "application/octet-stream"
   449  			attachment4b.Name = "data2"
   450  
   451  			attachments4 := []*Attachment{}
   452  			attachments4 = append(attachments4, attachment4a)
   453  			attachments4 = append(attachments4, attachment4b)
   454  
   455  			//Save the updated test document with multiple attachments
   456  			_, saveerr = db.SaveDoc("4", "", &CouchDoc{JSONValue: assetJSON, Attachments: attachments4})
   457  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save the updated document"))
   458  
   459  			//Retrieve the test document with attachments
   460  			dbGetResp, _, geterr = db.ReadDoc("4")
   461  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   462  
   463  			for _, attach4 := range dbGetResp.Attachments {
   464  
   465  				currentName := attach4.Name
   466  				if currentName == "data1" {
   467  					testutil.AssertEquals(t, attach4.AttachmentBytes, testBytes4a)
   468  				}
   469  				if currentName == "data2" {
   470  					testutil.AssertEquals(t, attach4.AttachmentBytes, testBytes4b)
   471  				}
   472  
   473  			}
   474  
   475  			testBytes5a := []byte(`test attachment 5a`)
   476  			attachment5a := &Attachment{}
   477  			attachment5a.AttachmentBytes = testBytes5a
   478  			attachment5a.ContentType = "application/octet-stream"
   479  			attachment5a.Name = "data1"
   480  
   481  			testBytes5b := []byte{}
   482  			attachment5b := &Attachment{}
   483  			attachment5b.AttachmentBytes = testBytes5b
   484  			attachment5b.ContentType = "application/octet-stream"
   485  			attachment5b.Name = "data2"
   486  
   487  			attachments5 := []*Attachment{}
   488  			attachments5 = append(attachments5, attachment5a)
   489  			attachments5 = append(attachments5, attachment5b)
   490  
   491  			//Save the updated test document with multiple attachments and zero length attachments
   492  			_, saveerr = db.SaveDoc("5", "", &CouchDoc{JSONValue: assetJSON, Attachments: attachments5})
   493  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save the updated document"))
   494  
   495  			//Retrieve the test document with attachments
   496  			dbGetResp, _, geterr = db.ReadDoc("5")
   497  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   498  
   499  			for _, attach5 := range dbGetResp.Attachments {
   500  
   501  				currentName := attach5.Name
   502  				if currentName == "data1" {
   503  					testutil.AssertEquals(t, attach5.AttachmentBytes, testBytes5a)
   504  				}
   505  				if currentName == "data2" {
   506  					testutil.AssertEquals(t, attach5.AttachmentBytes, testBytes5b)
   507  				}
   508  
   509  			}
   510  
   511  			//Attempt to save the document with an invalid id
   512  			_, saveerr = db.SaveDoc(string([]byte{0xff, 0xfe, 0xfd}), "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   513  			testutil.AssertError(t, saveerr, fmt.Sprintf("Error should have been thrown when saving a document with an invalid ID"))
   514  
   515  			//Attempt to read a document with an invalid id
   516  			_, _, readerr := db.ReadDoc(string([]byte{0xff, 0xfe, 0xfd}))
   517  			testutil.AssertError(t, readerr, fmt.Sprintf("Error should have been thrown when reading a document with an invalid ID"))
   518  
   519  			//Drop the database
   520  			_, errdbdrop := db.DropDatabase()
   521  			testutil.AssertNoError(t, errdbdrop, fmt.Sprintf("Error dropping database"))
   522  
   523  			//Make sure an error is thrown for getting info for a missing database
   524  			_, _, errdbinfo := db.GetDatabaseInfo()
   525  			testutil.AssertError(t, errdbinfo, fmt.Sprintf("Error should have been thrown for missing database"))
   526  
   527  			//Attempt to save a document to a deleted database
   528  			_, saveerr = db.SaveDoc("6", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   529  			testutil.AssertError(t, saveerr, fmt.Sprintf("Error should have been thrown while attempting to save to a deleted database"))
   530  
   531  			//Attempt to read from a deleted database
   532  			_, _, geterr = db.ReadDoc("6")
   533  			testutil.AssertNoError(t, geterr, fmt.Sprintf("Error should not have been thrown for a missing database, nil value is returned"))
   534  
   535  		}
   536  	}
   537  
   538  }
   539  
   540  func TestDBRequestTimeout(t *testing.T) {
   541  
   542  	if ledgerconfig.IsCouchDBEnabled() {
   543  
   544  		database := "testdbrequesttimeout"
   545  		err := cleanup(database)
   546  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   547  		defer cleanup(database)
   548  
   549  		if err == nil {
   550  
   551  			//create an impossibly short timeout
   552  			impossibleTimeout := time.Microsecond * 1
   553  
   554  			//create a new instance and database object with a timeout that will fail
   555  			//Also use a maxRetriesOnStartup=3 to reduce the number of retries
   556  			_, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   557  				couchDBDef.MaxRetries, 3, impossibleTimeout)
   558  			testutil.AssertError(t, err, fmt.Sprintf("Error should have been thown while trying to create a couchdb instance with a connection timeout"))
   559  
   560  			//see if the error message contains the timeout error
   561  			testutil.AssertEquals(t, strings.Count(err.Error(), "Client.Timeout exceeded while awaiting headers"), 1)
   562  
   563  		}
   564  	}
   565  }
   566  
   567  func TestDBTimeoutConflictRetry(t *testing.T) {
   568  
   569  	if ledgerconfig.IsCouchDBEnabled() {
   570  
   571  		database := "testdbtimeoutretry"
   572  		err := cleanup(database)
   573  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   574  		defer cleanup(database)
   575  
   576  		// if there was an error upon cleanup, return here
   577  		if err != nil {
   578  			return
   579  		}
   580  
   581  		//create a new instance and database object
   582  		couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   583  			couchDBDef.MaxRetries, 3, couchDBDef.RequestTimeout)
   584  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   585  		db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   586  
   587  		//create a new database
   588  		_, errdb := db.CreateDatabaseIfNotExist()
   589  		testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   590  
   591  		//Retrieve the info for the new database and make sure the name matches
   592  		dbResp, _, errdb := db.GetDatabaseInfo()
   593  		testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to retrieve database information"))
   594  		testutil.AssertEquals(t, dbResp.DbName, database)
   595  
   596  		//Save the test document
   597  		_, saveerr := db.SaveDoc("1", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   598  		testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   599  
   600  		//Retrieve the test document
   601  		_, _, geterr := db.ReadDoc("1")
   602  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
   603  
   604  		//Save the test document with an invalid rev.  This should cause a retry
   605  		_, saveerr = db.SaveDoc("1", "1-11111111111111111111111111111111", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   606  		testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document with a revision conflict"))
   607  
   608  		//Delete the test document with an invalid rev.  This should cause a retry
   609  		deleteerr := db.DeleteDoc("1", "1-11111111111111111111111111111111")
   610  		testutil.AssertNoError(t, deleteerr, fmt.Sprintf("Error when trying to delete a document with a revision conflict"))
   611  
   612  	}
   613  }
   614  
   615  func TestDBBadNumberOfRetries(t *testing.T) {
   616  
   617  	if ledgerconfig.IsCouchDBEnabled() {
   618  
   619  		database := "testdbbadretries"
   620  		err := cleanup(database)
   621  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   622  		defer cleanup(database)
   623  
   624  		// if there was an error upon cleanup, return here
   625  		if err != nil {
   626  			return
   627  		}
   628  
   629  		//create a new instance and database object
   630  		_, err = CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   631  			0, 3, couchDBDef.RequestTimeout)
   632  		testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown while attempting to create a database"))
   633  
   634  	}
   635  }
   636  
   637  func TestDBBadJSON(t *testing.T) {
   638  
   639  	if ledgerconfig.IsCouchDBEnabled() {
   640  
   641  		database := "testdbbadjson"
   642  		err := cleanup(database)
   643  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   644  		defer cleanup(database)
   645  
   646  		if err == nil {
   647  
   648  			//create a new instance and database object
   649  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   650  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   651  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   652  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   653  
   654  			//create a new database
   655  			_, errdb := db.CreateDatabaseIfNotExist()
   656  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   657  
   658  			//Retrieve the info for the new database and make sure the name matches
   659  			dbResp, _, errdb := db.GetDatabaseInfo()
   660  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to retrieve database information"))
   661  			testutil.AssertEquals(t, dbResp.DbName, database)
   662  
   663  			badJSON := []byte(`{"asset_name"}`)
   664  
   665  			//Save the test document
   666  			_, saveerr := db.SaveDoc("1", "", &CouchDoc{JSONValue: badJSON, Attachments: nil})
   667  			testutil.AssertError(t, saveerr, fmt.Sprintf("Error should have been thrown for a bad JSON"))
   668  
   669  		}
   670  
   671  	}
   672  
   673  }
   674  
   675  func TestPrefixScan(t *testing.T) {
   676  	if !ledgerconfig.IsCouchDBEnabled() {
   677  		return
   678  	}
   679  	database := "testprefixscan"
   680  	err := cleanup(database)
   681  	testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   682  	defer cleanup(database)
   683  
   684  	if err == nil {
   685  		//create a new instance and database object
   686  		couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   687  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   688  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   689  		db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   690  
   691  		//create a new database
   692  		_, errdb := db.CreateDatabaseIfNotExist()
   693  		testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   694  
   695  		//Retrieve the info for the new database and make sure the name matches
   696  		dbResp, _, errdb := db.GetDatabaseInfo()
   697  		testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to retrieve database information"))
   698  		testutil.AssertEquals(t, dbResp.DbName, database)
   699  
   700  		//Save documents
   701  		for i := 0; i < 20; i++ {
   702  			id1 := string(0) + string(i) + string(0)
   703  			id2 := string(0) + string(i) + string(1)
   704  			id3 := string(0) + string(i) + string(utf8.MaxRune-1)
   705  			_, saveerr := db.SaveDoc(id1, "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   706  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   707  			_, saveerr = db.SaveDoc(id2, "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   708  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   709  			_, saveerr = db.SaveDoc(id3, "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   710  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   711  
   712  		}
   713  		startKey := string(0) + string(10)
   714  		endKey := startKey + string(utf8.MaxRune)
   715  		_, _, geterr := db.ReadDoc(endKey)
   716  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to get lastkey"))
   717  
   718  		resultsPtr, geterr := db.ReadDocRange(startKey, endKey, 1000, 0)
   719  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to perform a range scan"))
   720  		testutil.AssertNotNil(t, resultsPtr)
   721  		results := *resultsPtr
   722  		testutil.AssertEquals(t, len(results), 3)
   723  		testutil.AssertEquals(t, results[0].ID, string(0)+string(10)+string(0))
   724  		testutil.AssertEquals(t, results[1].ID, string(0)+string(10)+string(1))
   725  		testutil.AssertEquals(t, results[2].ID, string(0)+string(10)+string(utf8.MaxRune-1))
   726  
   727  		//Drop the database
   728  		_, errdbdrop := db.DropDatabase()
   729  		testutil.AssertNoError(t, errdbdrop, fmt.Sprintf("Error dropping database"))
   730  
   731  		//Retrieve the info for the new database and make sure the name matches
   732  		_, _, errdbinfo := db.GetDatabaseInfo()
   733  		testutil.AssertError(t, errdbinfo, fmt.Sprintf("Error should have been thrown for missing database"))
   734  
   735  	}
   736  }
   737  
   738  func TestDBSaveAttachment(t *testing.T) {
   739  
   740  	if ledgerconfig.IsCouchDBEnabled() {
   741  
   742  		database := "testdbsaveattachment"
   743  		err := cleanup(database)
   744  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   745  		defer cleanup(database)
   746  
   747  		if err == nil {
   748  
   749  			byteText := []byte(`This is a test document.  This is only a test`)
   750  
   751  			attachment := &Attachment{}
   752  			attachment.AttachmentBytes = byteText
   753  			attachment.ContentType = "text/plain"
   754  			attachment.Name = "valueBytes"
   755  
   756  			attachments := []*Attachment{}
   757  			attachments = append(attachments, attachment)
   758  
   759  			//create a new instance and database object
   760  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   761  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   762  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   763  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   764  
   765  			//create a new database
   766  			_, errdb := db.CreateDatabaseIfNotExist()
   767  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   768  
   769  			//Save the test document
   770  			_, saveerr := db.SaveDoc("10", "", &CouchDoc{JSONValue: nil, Attachments: attachments})
   771  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   772  
   773  			//Attempt to retrieve the updated test document with attachments
   774  			couchDoc, _, geterr2 := db.ReadDoc("10")
   775  			testutil.AssertNoError(t, geterr2, fmt.Sprintf("Error when trying to retrieve a document with attachment"))
   776  			testutil.AssertNotNil(t, couchDoc.Attachments)
   777  			testutil.AssertEquals(t, couchDoc.Attachments[0].AttachmentBytes, byteText)
   778  		}
   779  
   780  	}
   781  }
   782  
   783  func TestDBDeleteDocument(t *testing.T) {
   784  
   785  	if ledgerconfig.IsCouchDBEnabled() {
   786  
   787  		database := "testdbdeletedocument"
   788  		err := cleanup(database)
   789  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   790  		defer cleanup(database)
   791  
   792  		if err == nil {
   793  			//create a new instance and database object
   794  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   795  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   796  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   797  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   798  
   799  			//create a new database
   800  			_, errdb := db.CreateDatabaseIfNotExist()
   801  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   802  
   803  			//Save the test document
   804  			_, saveerr := db.SaveDoc("2", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   805  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   806  
   807  			//Attempt to retrieve the test document
   808  			_, _, readErr := db.ReadDoc("2")
   809  			testutil.AssertNoError(t, readErr, fmt.Sprintf("Error when trying to retrieve a document with attachment"))
   810  
   811  			//Delete the test document
   812  			deleteErr := db.DeleteDoc("2", "")
   813  			testutil.AssertNoError(t, deleteErr, fmt.Sprintf("Error when trying to delete a document"))
   814  
   815  			//Attempt to retrieve the test document
   816  			readValue, _, _ := db.ReadDoc("2")
   817  			testutil.AssertNil(t, readValue)
   818  		}
   819  	}
   820  }
   821  
   822  func TestDBDeleteNonExistingDocument(t *testing.T) {
   823  
   824  	if ledgerconfig.IsCouchDBEnabled() {
   825  
   826  		database := "testdbdeletenonexistingdocument"
   827  		err := cleanup(database)
   828  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   829  		defer cleanup(database)
   830  
   831  		if err == nil {
   832  			//create a new instance and database object
   833  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   834  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   835  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   836  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   837  
   838  			//create a new database
   839  			_, errdb := db.CreateDatabaseIfNotExist()
   840  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   841  
   842  			//Save the test document
   843  			deleteErr := db.DeleteDoc("2", "")
   844  			testutil.AssertNoError(t, deleteErr, fmt.Sprintf("Error when trying to delete a non existing document"))
   845  		}
   846  	}
   847  }
   848  
   849  func TestCouchDBVersion(t *testing.T) {
   850  
   851  	err := checkCouchDBVersion("2.0.0")
   852  	testutil.AssertNoError(t, err, fmt.Sprintf("Error should not have been thrown for valid version"))
   853  
   854  	err = checkCouchDBVersion("4.5.0")
   855  	testutil.AssertNoError(t, err, fmt.Sprintf("Error should not have been thrown for valid version"))
   856  
   857  	err = checkCouchDBVersion("1.6.5.4")
   858  	testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for invalid version"))
   859  
   860  	err = checkCouchDBVersion("0.0.0.0")
   861  	testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for invalid version"))
   862  
   863  }
   864  
   865  func TestRichQuery(t *testing.T) {
   866  
   867  	if ledgerconfig.IsCouchDBEnabled() {
   868  
   869  		byteJSON01 := []byte(`{"asset_name":"marble01","color":"blue","size":1,"owner":"jerry"}`)
   870  		byteJSON02 := []byte(`{"asset_name":"marble02","color":"red","size":2,"owner":"tom"}`)
   871  		byteJSON03 := []byte(`{"asset_name":"marble03","color":"green","size":3,"owner":"jerry"}`)
   872  		byteJSON04 := []byte(`{"asset_name":"marble04","color":"purple","size":4,"owner":"tom"}`)
   873  		byteJSON05 := []byte(`{"asset_name":"marble05","color":"blue","size":5,"owner":"jerry"}`)
   874  		byteJSON06 := []byte(`{"asset_name":"marble06","color":"white","size":6,"owner":"tom"}`)
   875  		byteJSON07 := []byte(`{"asset_name":"marble07","color":"white","size":7,"owner":"tom"}`)
   876  		byteJSON08 := []byte(`{"asset_name":"marble08","color":"white","size":8,"owner":"tom"}`)
   877  		byteJSON09 := []byte(`{"asset_name":"marble09","color":"white","size":9,"owner":"tom"}`)
   878  		byteJSON10 := []byte(`{"asset_name":"marble10","color":"white","size":10,"owner":"tom"}`)
   879  		byteJSON11 := []byte(`{"asset_name":"marble11","color":"green","size":11,"owner":"tom"}`)
   880  		byteJSON12 := []byte(`{"asset_name":"marble12","color":"green","size":12,"owner":"frank"}`)
   881  
   882  		attachment1 := &Attachment{}
   883  		attachment1.AttachmentBytes = []byte(`marble01 - test attachment`)
   884  		attachment1.ContentType = "application/octet-stream"
   885  		attachment1.Name = "data"
   886  		attachments1 := []*Attachment{}
   887  		attachments1 = append(attachments1, attachment1)
   888  
   889  		attachment2 := &Attachment{}
   890  		attachment2.AttachmentBytes = []byte(`marble02 - test attachment`)
   891  		attachment2.ContentType = "application/octet-stream"
   892  		attachment2.Name = "data"
   893  		attachments2 := []*Attachment{}
   894  		attachments2 = append(attachments2, attachment2)
   895  
   896  		attachment3 := &Attachment{}
   897  		attachment3.AttachmentBytes = []byte(`marble03 - test attachment`)
   898  		attachment3.ContentType = "application/octet-stream"
   899  		attachment3.Name = "data"
   900  		attachments3 := []*Attachment{}
   901  		attachments3 = append(attachments3, attachment3)
   902  
   903  		attachment4 := &Attachment{}
   904  		attachment4.AttachmentBytes = []byte(`marble04 - test attachment`)
   905  		attachment4.ContentType = "application/octet-stream"
   906  		attachment4.Name = "data"
   907  		attachments4 := []*Attachment{}
   908  		attachments4 = append(attachments4, attachment4)
   909  
   910  		attachment5 := &Attachment{}
   911  		attachment5.AttachmentBytes = []byte(`marble05 - test attachment`)
   912  		attachment5.ContentType = "application/octet-stream"
   913  		attachment5.Name = "data"
   914  		attachments5 := []*Attachment{}
   915  		attachments5 = append(attachments5, attachment5)
   916  
   917  		attachment6 := &Attachment{}
   918  		attachment6.AttachmentBytes = []byte(`marble06 - test attachment`)
   919  		attachment6.ContentType = "application/octet-stream"
   920  		attachment6.Name = "data"
   921  		attachments6 := []*Attachment{}
   922  		attachments6 = append(attachments6, attachment6)
   923  
   924  		attachment7 := &Attachment{}
   925  		attachment7.AttachmentBytes = []byte(`marble07 - test attachment`)
   926  		attachment7.ContentType = "application/octet-stream"
   927  		attachment7.Name = "data"
   928  		attachments7 := []*Attachment{}
   929  		attachments7 = append(attachments7, attachment7)
   930  
   931  		attachment8 := &Attachment{}
   932  		attachment8.AttachmentBytes = []byte(`marble08 - test attachment`)
   933  		attachment8.ContentType = "application/octet-stream"
   934  		attachment7.Name = "data"
   935  		attachments8 := []*Attachment{}
   936  		attachments8 = append(attachments8, attachment8)
   937  
   938  		attachment9 := &Attachment{}
   939  		attachment9.AttachmentBytes = []byte(`marble09 - test attachment`)
   940  		attachment9.ContentType = "application/octet-stream"
   941  		attachment9.Name = "data"
   942  		attachments9 := []*Attachment{}
   943  		attachments9 = append(attachments9, attachment9)
   944  
   945  		attachment10 := &Attachment{}
   946  		attachment10.AttachmentBytes = []byte(`marble10 - test attachment`)
   947  		attachment10.ContentType = "application/octet-stream"
   948  		attachment10.Name = "data"
   949  		attachments10 := []*Attachment{}
   950  		attachments10 = append(attachments10, attachment10)
   951  
   952  		attachment11 := &Attachment{}
   953  		attachment11.AttachmentBytes = []byte(`marble11 - test attachment`)
   954  		attachment11.ContentType = "application/octet-stream"
   955  		attachment11.Name = "data"
   956  		attachments11 := []*Attachment{}
   957  		attachments11 = append(attachments11, attachment11)
   958  
   959  		attachment12 := &Attachment{}
   960  		attachment12.AttachmentBytes = []byte(`marble12 - test attachment`)
   961  		attachment12.ContentType = "application/octet-stream"
   962  		attachment12.Name = "data"
   963  		attachments12 := []*Attachment{}
   964  		attachments12 = append(attachments12, attachment12)
   965  
   966  		database := "testrichquery"
   967  		err := cleanup(database)
   968  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
   969  		defer cleanup(database)
   970  
   971  		if err == nil {
   972  			//create a new instance and database object   --------------------------------------------------------
   973  			couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
   974  				couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
   975  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
   976  			db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
   977  
   978  			//create a new database
   979  			_, errdb := db.CreateDatabaseIfNotExist()
   980  			testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
   981  
   982  			//Save the test document
   983  			_, saveerr := db.SaveDoc("marble01", "", &CouchDoc{JSONValue: byteJSON01, Attachments: attachments1})
   984  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   985  
   986  			//Save the test document
   987  			_, saveerr = db.SaveDoc("marble02", "", &CouchDoc{JSONValue: byteJSON02, Attachments: attachments2})
   988  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   989  
   990  			//Save the test document
   991  			_, saveerr = db.SaveDoc("marble03", "", &CouchDoc{JSONValue: byteJSON03, Attachments: attachments3})
   992  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   993  
   994  			//Save the test document
   995  			_, saveerr = db.SaveDoc("marble04", "", &CouchDoc{JSONValue: byteJSON04, Attachments: attachments4})
   996  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
   997  
   998  			//Save the test document
   999  			_, saveerr = db.SaveDoc("marble05", "", &CouchDoc{JSONValue: byteJSON05, Attachments: attachments5})
  1000  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
  1001  
  1002  			//Save the test document
  1003  			_, saveerr = db.SaveDoc("marble06", "", &CouchDoc{JSONValue: byteJSON06, Attachments: attachments6})
  1004  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
  1005  
  1006  			//Save the test document
  1007  			_, saveerr = db.SaveDoc("marble07", "", &CouchDoc{JSONValue: byteJSON07, Attachments: attachments7})
  1008  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
  1009  
  1010  			//Save the test document
  1011  			_, saveerr = db.SaveDoc("marble08", "", &CouchDoc{JSONValue: byteJSON08, Attachments: attachments8})
  1012  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
  1013  
  1014  			//Save the test document
  1015  			_, saveerr = db.SaveDoc("marble09", "", &CouchDoc{JSONValue: byteJSON09, Attachments: attachments9})
  1016  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
  1017  
  1018  			//Save the test document
  1019  			_, saveerr = db.SaveDoc("marble10", "", &CouchDoc{JSONValue: byteJSON10, Attachments: attachments10})
  1020  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
  1021  
  1022  			//Save the test document
  1023  			_, saveerr = db.SaveDoc("marble11", "", &CouchDoc{JSONValue: byteJSON11, Attachments: attachments11})
  1024  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
  1025  
  1026  			//Save the test document
  1027  			_, saveerr = db.SaveDoc("marble12", "", &CouchDoc{JSONValue: byteJSON12, Attachments: attachments12})
  1028  			testutil.AssertNoError(t, saveerr, fmt.Sprintf("Error when trying to save a document"))
  1029  
  1030  			//Test query with invalid JSON -------------------------------------------------------------------
  1031  			queryString := "{\"selector\":{\"owner\":}}"
  1032  
  1033  			_, err = db.QueryDocuments(queryString)
  1034  			testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for bad json"))
  1035  
  1036  			//Test query with object  -------------------------------------------------------------------
  1037  			queryString = "{\"selector\":{\"owner\":{\"$eq\":\"jerry\"}}}"
  1038  
  1039  			queryResult, err := db.QueryDocuments(queryString)
  1040  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
  1041  
  1042  			//There should be 3 results for owner="jerry"
  1043  			testutil.AssertEquals(t, len(*queryResult), 3)
  1044  
  1045  			//Test query with implicit operator   --------------------------------------------------------------
  1046  			queryString = "{\"selector\":{\"owner\":\"jerry\"}}"
  1047  
  1048  			queryResult, err = db.QueryDocuments(queryString)
  1049  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
  1050  
  1051  			//There should be 3 results for owner="jerry"
  1052  			testutil.AssertEquals(t, len(*queryResult), 3)
  1053  
  1054  			//Test query with specified fields   -------------------------------------------------------------------
  1055  			queryString = "{\"selector\":{\"owner\":{\"$eq\":\"jerry\"}},\"fields\": [\"owner\",\"asset_name\",\"color\",\"size\"]}"
  1056  
  1057  			queryResult, err = db.QueryDocuments(queryString)
  1058  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
  1059  
  1060  			//There should be 3 results for owner="jerry"
  1061  			testutil.AssertEquals(t, len(*queryResult), 3)
  1062  
  1063  			//Test query with a leading operator   -------------------------------------------------------------------
  1064  			queryString = "{\"selector\":{\"$or\":[{\"owner\":{\"$eq\":\"jerry\"}},{\"owner\": {\"$eq\": \"frank\"}}]}}"
  1065  
  1066  			queryResult, err = db.QueryDocuments(queryString)
  1067  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
  1068  
  1069  			//There should be 4 results for owner="jerry" or owner="frank"
  1070  			testutil.AssertEquals(t, len(*queryResult), 4)
  1071  
  1072  			//Test query implicit and explicit operator   ------------------------------------------------------------------
  1073  			queryString = "{\"selector\":{\"color\":\"green\",\"$or\":[{\"owner\":\"tom\"},{\"owner\":\"frank\"}]}}"
  1074  
  1075  			queryResult, err = db.QueryDocuments(queryString)
  1076  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
  1077  
  1078  			//There should be 2 results for color="green" and (owner="jerry" or owner="frank")
  1079  			testutil.AssertEquals(t, len(*queryResult), 2)
  1080  
  1081  			//Test query with a leading operator  -------------------------------------------------------------------------
  1082  			queryString = "{\"selector\":{\"$and\":[{\"size\":{\"$gte\":2}},{\"size\":{\"$lte\":5}}]}}"
  1083  
  1084  			queryResult, err = db.QueryDocuments(queryString)
  1085  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
  1086  
  1087  			//There should be 4 results for size >= 2 and size <= 5
  1088  			testutil.AssertEquals(t, len(*queryResult), 4)
  1089  
  1090  			//Test query with leading and embedded operator  -------------------------------------------------------------
  1091  			queryString = "{\"selector\":{\"$and\":[{\"size\":{\"$gte\":3}},{\"size\":{\"$lte\":10}},{\"$not\":{\"size\":7}}]}}"
  1092  
  1093  			queryResult, err = db.QueryDocuments(queryString)
  1094  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
  1095  
  1096  			//There should be 7 results for size >= 3 and size <= 10 and not 7
  1097  			testutil.AssertEquals(t, len(*queryResult), 7)
  1098  
  1099  			//Test query with leading operator and array of objects ----------------------------------------------------------
  1100  			queryString = "{\"selector\":{\"$and\":[{\"size\":{\"$gte\":2}},{\"size\":{\"$lte\":10}},{\"$nor\":[{\"size\":3},{\"size\":5},{\"size\":7}]}]}}"
  1101  
  1102  			queryResult, err = db.QueryDocuments(queryString)
  1103  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
  1104  
  1105  			//There should be 6 results for size >= 2 and size <= 10 and not 3,5 or 7
  1106  			testutil.AssertEquals(t, len(*queryResult), 6)
  1107  
  1108  			//Test a range query ---------------------------------------------------------------------------------------------
  1109  			queryResult, err = db.ReadDocRange("marble02", "marble06", 10000, 0)
  1110  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a range query"))
  1111  
  1112  			//There should be 4 results
  1113  			testutil.AssertEquals(t, len(*queryResult), 4)
  1114  
  1115  			//Test query with for tom  -------------------------------------------------------------------
  1116  			queryString = "{\"selector\":{\"owner\":{\"$eq\":\"tom\"}}}"
  1117  
  1118  			queryResult, err = db.QueryDocuments(queryString)
  1119  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
  1120  
  1121  			//There should be 8 results for owner="tom"
  1122  			testutil.AssertEquals(t, len(*queryResult), 8)
  1123  
  1124  			//Test query with for tom with limit  -------------------------------------------------------------------
  1125  			queryString = "{\"selector\":{\"owner\":{\"$eq\":\"tom\"}},\"limit\":2}"
  1126  
  1127  			queryResult, err = db.QueryDocuments(queryString)
  1128  			testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to execute a query"))
  1129  
  1130  			//There should be 2 results for owner="tom" with a limit of 2
  1131  			testutil.AssertEquals(t, len(*queryResult), 2)
  1132  
  1133  			//Test query with invalid index  -------------------------------------------------------------------
  1134  			queryString = "{\"selector\":{\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\",\"indexOwner\"]}"
  1135  
  1136  			_, err = db.QueryDocuments(queryString)
  1137  			testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for an invalid index"))
  1138  
  1139  		}
  1140  	}
  1141  }
  1142  
  1143  func TestBatchBatchOperations(t *testing.T) {
  1144  
  1145  	if ledgerconfig.IsCouchDBEnabled() {
  1146  
  1147  		byteJSON01 := []byte(`{"_id":"marble01","asset_name":"marble01","color":"blue","size":"1","owner":"jerry"}`)
  1148  		byteJSON02 := []byte(`{"_id":"marble02","asset_name":"marble02","color":"red","size":"2","owner":"tom"}`)
  1149  		byteJSON03 := []byte(`{"_id":"marble03","asset_name":"marble03","color":"green","size":"3","owner":"jerry"}`)
  1150  		byteJSON04 := []byte(`{"_id":"marble04","asset_name":"marble04","color":"purple","size":"4","owner":"tom"}`)
  1151  		byteJSON05 := []byte(`{"_id":"marble05","asset_name":"marble05","color":"blue","size":"5","owner":"jerry"}`)
  1152  
  1153  		attachment1 := &Attachment{}
  1154  		attachment1.AttachmentBytes = []byte(`marble01 - test attachment`)
  1155  		attachment1.ContentType = "application/octet-stream"
  1156  		attachment1.Name = "data"
  1157  		attachments1 := []*Attachment{}
  1158  		attachments1 = append(attachments1, attachment1)
  1159  
  1160  		attachment2 := &Attachment{}
  1161  		attachment2.AttachmentBytes = []byte(`marble02 - test attachment`)
  1162  		attachment2.ContentType = "application/octet-stream"
  1163  		attachment2.Name = "data"
  1164  		attachments2 := []*Attachment{}
  1165  		attachments2 = append(attachments2, attachment2)
  1166  
  1167  		attachment3 := &Attachment{}
  1168  		attachment3.AttachmentBytes = []byte(`marble03 - test attachment`)
  1169  		attachment3.ContentType = "application/octet-stream"
  1170  		attachment3.Name = "data"
  1171  		attachments3 := []*Attachment{}
  1172  		attachments3 = append(attachments3, attachment3)
  1173  
  1174  		attachment4 := &Attachment{}
  1175  		attachment4.AttachmentBytes = []byte(`marble04 - test attachment`)
  1176  		attachment4.ContentType = "application/octet-stream"
  1177  		attachment4.Name = "data"
  1178  		attachments4 := []*Attachment{}
  1179  		attachments4 = append(attachments4, attachment4)
  1180  
  1181  		attachment5 := &Attachment{}
  1182  		attachment5.AttachmentBytes = []byte(`marble05 - test attachment`)
  1183  		attachment5.ContentType = "application/octet-stream"
  1184  		attachment5.Name = "data"
  1185  		attachments5 := []*Attachment{}
  1186  		attachments5 = append(attachments5, attachment5)
  1187  
  1188  		database := "testbatch"
  1189  		err := cleanup(database)
  1190  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to cleanup  Error: %s", err))
  1191  		defer cleanup(database)
  1192  
  1193  		//create a new instance and database object   --------------------------------------------------------
  1194  		couchInstance, err := CreateCouchInstance(couchDBDef.URL, couchDBDef.Username, couchDBDef.Password,
  1195  			couchDBDef.MaxRetries, couchDBDef.MaxRetriesOnStartup, couchDBDef.RequestTimeout)
  1196  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when trying to create couch instance"))
  1197  		db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}
  1198  
  1199  		//create a new database
  1200  		_, errdb := db.CreateDatabaseIfNotExist()
  1201  		testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))
  1202  
  1203  		batchUpdateDocs := []*CouchDoc{}
  1204  
  1205  		value1 := &CouchDoc{JSONValue: byteJSON01, Attachments: attachments1}
  1206  		value2 := &CouchDoc{JSONValue: byteJSON02, Attachments: attachments2}
  1207  		value3 := &CouchDoc{JSONValue: byteJSON03, Attachments: attachments3}
  1208  		value4 := &CouchDoc{JSONValue: byteJSON04, Attachments: attachments4}
  1209  		value5 := &CouchDoc{JSONValue: byteJSON05, Attachments: attachments5}
  1210  
  1211  		batchUpdateDocs = append(batchUpdateDocs, value1)
  1212  		batchUpdateDocs = append(batchUpdateDocs, value2)
  1213  		batchUpdateDocs = append(batchUpdateDocs, value3)
  1214  		batchUpdateDocs = append(batchUpdateDocs, value4)
  1215  		batchUpdateDocs = append(batchUpdateDocs, value5)
  1216  
  1217  		batchUpdateResp, err := db.BatchUpdateDocuments(batchUpdateDocs)
  1218  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to update a batch of documents"))
  1219  
  1220  		//check to make sure each batch update response was successful
  1221  		for _, updateDoc := range batchUpdateResp {
  1222  			testutil.AssertEquals(t, updateDoc.Ok, true)
  1223  		}
  1224  
  1225  		//----------------------------------------------
  1226  		//Test Retrieve JSON
  1227  		dbGetResp, _, geterr := db.ReadDoc("marble01")
  1228  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when attempting read a document"))
  1229  
  1230  		assetResp := &Asset{}
  1231  		geterr = json.Unmarshal(dbGetResp.JSONValue, &assetResp)
  1232  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
  1233  		//Verify the owner retrieved matches
  1234  		testutil.AssertEquals(t, assetResp.Owner, "jerry")
  1235  
  1236  		//----------------------------------------------
  1237  		//Test retrieve binary
  1238  		dbGetResp, _, geterr = db.ReadDoc("marble03")
  1239  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when attempting read a document"))
  1240  		//Retrieve the attachments
  1241  		attachments := dbGetResp.Attachments
  1242  		//Only one was saved, so take the first
  1243  		retrievedAttachment := attachments[0]
  1244  		//Verify the text matches
  1245  		testutil.AssertEquals(t, attachment3.AttachmentBytes, retrievedAttachment.AttachmentBytes)
  1246  		//----------------------------------------------
  1247  		//Test Bad Updates
  1248  		batchUpdateDocs = []*CouchDoc{}
  1249  		batchUpdateDocs = append(batchUpdateDocs, value1)
  1250  		batchUpdateDocs = append(batchUpdateDocs, value2)
  1251  		batchUpdateResp, err = db.BatchUpdateDocuments(batchUpdateDocs)
  1252  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to update a batch of documents"))
  1253  		//No revision was provided, so these two updates should fail
  1254  		//Verify that the "Ok" field is returned as false
  1255  		for _, updateDoc := range batchUpdateResp {
  1256  			testutil.AssertEquals(t, updateDoc.Ok, false)
  1257  			testutil.AssertEquals(t, updateDoc.Error, updateDocumentConflictError)
  1258  			testutil.AssertEquals(t, updateDoc.Reason, updateDocumentConflictReason)
  1259  		}
  1260  
  1261  		//----------------------------------------------
  1262  		//Test Batch Retrieve Keys and Update
  1263  
  1264  		var keys []string
  1265  
  1266  		keys = append(keys, "marble01")
  1267  		keys = append(keys, "marble03")
  1268  
  1269  		batchRevs, err := db.BatchRetrieveIDRevision(keys)
  1270  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting retrieve revisions"))
  1271  
  1272  		batchUpdateDocs = []*CouchDoc{}
  1273  
  1274  		//iterate through the revision docs
  1275  		for _, revdoc := range batchRevs {
  1276  			if revdoc.ID == "marble01" {
  1277  				//update the json with the rev and add to the batch
  1278  				marble01Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON01, false)
  1279  				batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble01Doc, Attachments: attachments1})
  1280  			}
  1281  
  1282  			if revdoc.ID == "marble03" {
  1283  				//update the json with the rev and add to the batch
  1284  				marble03Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON03, false)
  1285  				batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble03Doc, Attachments: attachments3})
  1286  			}
  1287  		}
  1288  
  1289  		//Update couchdb with the batch
  1290  		batchUpdateResp, err = db.BatchUpdateDocuments(batchUpdateDocs)
  1291  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to update a batch of documents"))
  1292  		//check to make sure each batch update response was successful
  1293  		for _, updateDoc := range batchUpdateResp {
  1294  			testutil.AssertEquals(t, updateDoc.Ok, true)
  1295  		}
  1296  
  1297  		//----------------------------------------------
  1298  		//Test Batch Delete
  1299  
  1300  		keys = []string{}
  1301  
  1302  		keys = append(keys, "marble02")
  1303  		keys = append(keys, "marble04")
  1304  
  1305  		batchRevs, err = db.BatchRetrieveIDRevision(keys)
  1306  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting retrieve revisions"))
  1307  
  1308  		batchUpdateDocs = []*CouchDoc{}
  1309  
  1310  		//iterate through the revision docs
  1311  		for _, revdoc := range batchRevs {
  1312  			if revdoc.ID == "marble02" {
  1313  				//update the json with the rev and add to the batch
  1314  				marble02Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON02, true)
  1315  				batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble02Doc, Attachments: attachments1})
  1316  			}
  1317  			if revdoc.ID == "marble04" {
  1318  				//update the json with the rev and add to the batch
  1319  				marble04Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON04, true)
  1320  				batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble04Doc, Attachments: attachments3})
  1321  			}
  1322  		}
  1323  
  1324  		//Update couchdb with the batch
  1325  		batchUpdateResp, err = db.BatchUpdateDocuments(batchUpdateDocs)
  1326  		testutil.AssertNoError(t, err, fmt.Sprintf("Error when attempting to update a batch of documents"))
  1327  
  1328  		//check to make sure each batch update response was successful
  1329  		for _, updateDoc := range batchUpdateResp {
  1330  			testutil.AssertEquals(t, updateDoc.Ok, true)
  1331  		}
  1332  
  1333  		//Retrieve the test document
  1334  		dbGetResp, _, geterr = db.ReadDoc("marble02")
  1335  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
  1336  
  1337  		//assert the value was deleted
  1338  		testutil.AssertNil(t, dbGetResp)
  1339  
  1340  		//Retrieve the test document
  1341  		dbGetResp, _, geterr = db.ReadDoc("marble04")
  1342  		testutil.AssertNoError(t, geterr, fmt.Sprintf("Error when trying to retrieve a document"))
  1343  
  1344  		//assert the value was deleted
  1345  		testutil.AssertNil(t, dbGetResp)
  1346  	}
  1347  }
  1348  
  1349  //addRevisionAndDeleteStatus adds keys for version and chaincodeID to the JSON value
  1350  func addRevisionAndDeleteStatus(revision string, value []byte, deleted bool) []byte {
  1351  
  1352  	//create a version mapping
  1353  	jsonMap := make(map[string]interface{})
  1354  
  1355  	json.Unmarshal(value, &jsonMap)
  1356  
  1357  	//add the revision
  1358  	if revision != "" {
  1359  		jsonMap["_rev"] = revision
  1360  	}
  1361  
  1362  	//If this record is to be deleted, set the "_deleted" property to true
  1363  	if deleted {
  1364  		jsonMap["_deleted"] = true
  1365  	}
  1366  	//marshal the data to a byte array
  1367  	returnJSON, _ := json.Marshal(jsonMap)
  1368  
  1369  	return returnJSON
  1370  
  1371  }