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