github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/core/ledger/util/couchdb/couchdb_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package couchdb
     8  
     9  import (
    10  	"context"
    11  	"encoding/json"
    12  	"fmt"
    13  	"net/http"
    14  	"net/url"
    15  	"os"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  	"unicode/utf8"
    20  
    21  	"github.com/hyperledger/fabric/common/flogging"
    22  	"github.com/hyperledger/fabric/common/metrics/disabled"
    23  	"github.com/hyperledger/fabric/integration/runner"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  const badConnectURL = "couchdb:5990"
    29  const badParseConnectURL = "http://host.com|5432"
    30  const updateDocumentConflictError = "conflict"
    31  const updateDocumentConflictReason = "Document update conflict."
    32  
    33  func cleanup(database string) error {
    34  	//create a new connection
    35  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
    36  	if err != nil {
    37  		fmt.Println("Unexpected error", err)
    38  		return err
    39  	}
    40  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
    41  	//drop the test database
    42  	db.DropDatabase()
    43  	return nil
    44  }
    45  
    46  type Asset struct {
    47  	ID        string `json:"_id"`
    48  	Rev       string `json:"_rev"`
    49  	AssetName string `json:"asset_name"`
    50  	Color     string `json:"color"`
    51  	Size      string `json:"size"`
    52  	Owner     string `json:"owner"`
    53  }
    54  
    55  var assetJSON = []byte(`{"asset_name":"marble1","color":"blue","size":"35","owner":"jerry"}`)
    56  
    57  var testAddress string
    58  
    59  func testConfig() *Config {
    60  	return &Config{
    61  		Address:               testAddress,
    62  		Username:              "",
    63  		Password:              "",
    64  		MaxRetries:            3,
    65  		MaxRetriesOnStartup:   20,
    66  		RequestTimeout:        35 * time.Second,
    67  		CreateGlobalChangesDB: true,
    68  	}
    69  }
    70  
    71  func TestMain(m *testing.M) {
    72  	os.Exit(testMain(m))
    73  }
    74  
    75  func testMain(m *testing.M) int {
    76  	// Switch to CouchDB
    77  	address, cleanup := couchDBSetup()
    78  	testAddress = address
    79  	defer cleanup()
    80  
    81  	//set the logging level to DEBUG to test debug only code
    82  	flogging.ActivateSpec("couchdb=debug")
    83  
    84  	//run the tests
    85  	return m.Run()
    86  }
    87  
    88  func couchDBSetup() (addr string, cleanup func()) {
    89  	externalCouch, set := os.LookupEnv("COUCHDB_ADDR")
    90  	if set {
    91  		return externalCouch, func() {}
    92  	}
    93  
    94  	couchDB := &runner.CouchDB{}
    95  	if err := couchDB.Start(); err != nil {
    96  		err := fmt.Errorf("failed to start couchDB: %s", err)
    97  		panic(err)
    98  	}
    99  	return couchDB.Address(), func() { couchDB.Stop() }
   100  }
   101  
   102  func TestDBBadConnectionDef(t *testing.T) {
   103  	config := testConfig()
   104  	config.Address = badParseConnectURL
   105  	_, err := CreateCouchInstance(config, &disabled.Provider{})
   106  	assert.Error(t, err, "Did not receive error when trying to create database connection definition with a bad hostname")
   107  }
   108  
   109  func TestEncodePathElement(t *testing.T) {
   110  
   111  	encodedString := encodePathElement("testelement")
   112  	assert.Equal(t, "testelement", encodedString)
   113  
   114  	encodedString = encodePathElement("test element")
   115  	assert.Equal(t, "test%20element", encodedString)
   116  
   117  	encodedString = encodePathElement("/test element")
   118  	assert.Equal(t, "%2Ftest%20element", encodedString)
   119  
   120  	encodedString = encodePathElement("/test element:")
   121  	assert.Equal(t, "%2Ftest%20element:", encodedString)
   122  
   123  	encodedString = encodePathElement("/test+ element:")
   124  	assert.Equal(t, "%2Ftest%2B%20element:", encodedString)
   125  
   126  }
   127  
   128  func TestHealthCheck(t *testing.T) {
   129  	client := &http.Client{}
   130  
   131  	config := testConfig()
   132  	config.Address = testAddress + "1"
   133  	badCouchDBInstance := CouchInstance{
   134  		conf:   config,
   135  		client: client,
   136  		stats:  newStats(&disabled.Provider{}),
   137  	}
   138  	err := badCouchDBInstance.HealthCheck(context.Background())
   139  	assert.Error(t, err, "Health check should result in an error if unable to connect to couch db")
   140  	assert.Contains(t, err.Error(), "failed to connect to couch db")
   141  
   142  	//Create a good couchdb instance
   143  	goodCouchDBInstance := CouchInstance{
   144  		conf:   testConfig(),
   145  		client: client,
   146  		stats:  newStats(&disabled.Provider{}),
   147  	}
   148  	err = goodCouchDBInstance.HealthCheck(context.Background())
   149  	assert.NoError(t, err)
   150  }
   151  
   152  func TestBadCouchDBInstance(t *testing.T) {
   153  
   154  	client := &http.Client{}
   155  
   156  	//Create a bad couchdb instance
   157  	badCouchDBInstance := CouchInstance{
   158  		conf: &Config{
   159  			Address:             badParseConnectURL,
   160  			Username:            "",
   161  			Password:            "",
   162  			MaxRetries:          3,
   163  			MaxRetriesOnStartup: 10,
   164  			RequestTimeout:      30 * time.Second,
   165  		},
   166  		client: client,
   167  		stats:  newStats(&disabled.Provider{}),
   168  	}
   169  
   170  	//Create a bad CouchDatabase
   171  	badDB := CouchDatabase{&badCouchDBInstance, "baddb", 1}
   172  
   173  	//Test CreateCouchDatabase with bad connection
   174  	_, err := CreateCouchDatabase(&badCouchDBInstance, "baddbtest")
   175  	assert.Error(t, err, "Error should have been thrown with CreateCouchDatabase and invalid connection")
   176  
   177  	//Test CreateSystemDatabasesIfNotExist with bad connection
   178  	err = CreateSystemDatabasesIfNotExist(&badCouchDBInstance)
   179  	assert.Error(t, err, "Error should have been thrown with CreateSystemDatabasesIfNotExist and invalid connection")
   180  
   181  	//Test CreateDatabaseIfNotExist with bad connection
   182  	err = badDB.CreateDatabaseIfNotExist()
   183  	assert.Error(t, err, "Error should have been thrown with CreateDatabaseIfNotExist and invalid connection")
   184  
   185  	//Test GetDatabaseInfo with bad connection
   186  	_, _, err = badDB.GetDatabaseInfo()
   187  	assert.Error(t, err, "Error should have been thrown with GetDatabaseInfo and invalid connection")
   188  
   189  	//Test VerifyCouchConfig with bad connection
   190  	_, _, err = badCouchDBInstance.VerifyCouchConfig()
   191  	assert.Error(t, err, "Error should have been thrown with VerifyCouchConfig and invalid connection")
   192  
   193  	//Test EnsureFullCommit with bad connection
   194  	_, err = badDB.EnsureFullCommit()
   195  	assert.Error(t, err, "Error should have been thrown with EnsureFullCommit and invalid connection")
   196  
   197  	//Test DropDatabase with bad connection
   198  	_, err = badDB.DropDatabase()
   199  	assert.Error(t, err, "Error should have been thrown with DropDatabase and invalid connection")
   200  
   201  	//Test ReadDoc with bad connection
   202  	_, _, err = badDB.ReadDoc("1")
   203  	assert.Error(t, err, "Error should have been thrown with ReadDoc and invalid connection")
   204  
   205  	//Test SaveDoc with bad connection
   206  	_, err = badDB.SaveDoc("1", "1", nil)
   207  	assert.Error(t, err, "Error should have been thrown with SaveDoc and invalid connection")
   208  
   209  	//Test DeleteDoc with bad connection
   210  	err = badDB.DeleteDoc("1", "1")
   211  	assert.Error(t, err, "Error should have been thrown with DeleteDoc and invalid connection")
   212  
   213  	//Test ReadDocRange with bad connection
   214  	_, _, err = badDB.ReadDocRange("1", "2", 1000)
   215  	assert.Error(t, err, "Error should have been thrown with ReadDocRange and invalid connection")
   216  
   217  	//Test QueryDocuments with bad connection
   218  	_, _, err = badDB.QueryDocuments("1")
   219  	assert.Error(t, err, "Error should have been thrown with QueryDocuments and invalid connection")
   220  
   221  	//Test BatchRetrieveDocumentMetadata with bad connection
   222  	_, err = badDB.BatchRetrieveDocumentMetadata(nil)
   223  	assert.Error(t, err, "Error should have been thrown with BatchRetrieveDocumentMetadata and invalid connection")
   224  
   225  	//Test BatchUpdateDocuments with bad connection
   226  	_, err = badDB.BatchUpdateDocuments(nil)
   227  	assert.Error(t, err, "Error should have been thrown with BatchUpdateDocuments and invalid connection")
   228  
   229  	//Test ListIndex with bad connection
   230  	_, err = badDB.ListIndex()
   231  	assert.Error(t, err, "Error should have been thrown with ListIndex and invalid connection")
   232  
   233  	//Test CreateIndex with bad connection
   234  	_, err = badDB.CreateIndex("")
   235  	assert.Error(t, err, "Error should have been thrown with CreateIndex and invalid connection")
   236  
   237  	//Test DeleteIndex with bad connection
   238  	err = badDB.DeleteIndex("", "")
   239  	assert.Error(t, err, "Error should have been thrown with DeleteIndex and invalid connection")
   240  
   241  }
   242  
   243  func TestDBCreateSaveWithoutRevision(t *testing.T) {
   244  
   245  	database := "testdbcreatesavewithoutrevision"
   246  	err := cleanup(database)
   247  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   248  	defer cleanup(database)
   249  
   250  	//create a new instance and database object
   251  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
   252  	assert.NoError(t, err, "Error when trying to create couch instance")
   253  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
   254  
   255  	//create a new database
   256  	errdb := db.CreateDatabaseIfNotExist()
   257  	assert.NoError(t, errdb, "Error when trying to create database")
   258  
   259  	//Save the test document
   260  	_, saveerr := db.SaveDoc("2", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   261  	assert.NoError(t, saveerr, "Error when trying to save a document")
   262  
   263  }
   264  
   265  func TestDBCreateEnsureFullCommit(t *testing.T) {
   266  
   267  	database := "testdbensurefullcommit"
   268  	err := cleanup(database)
   269  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   270  	defer cleanup(database)
   271  
   272  	//create a new instance and database object
   273  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
   274  	assert.NoError(t, err, "Error when trying to create couch instance")
   275  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
   276  
   277  	//create a new database
   278  	errdb := db.CreateDatabaseIfNotExist()
   279  	assert.NoError(t, errdb, "Error when trying to create database")
   280  
   281  	//Save the test document
   282  	_, saveerr := db.SaveDoc("2", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   283  	assert.NoError(t, saveerr, "Error when trying to save a document")
   284  
   285  	//Ensure a full commit
   286  	_, commiterr := db.EnsureFullCommit()
   287  	assert.NoError(t, commiterr, "Error when trying to ensure a full commit")
   288  }
   289  
   290  func TestIsEmpty(t *testing.T) {
   291  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
   292  	assert.NoError(t, err)
   293  
   294  	isEmpty, err := couchInstance.IsEmpty(nil)
   295  	require.NoError(t, err)
   296  	require.True(t, isEmpty)
   297  
   298  	testdbs := []string{"testdb1", "testdb2"}
   299  	defer func() {
   300  		for _, d := range testdbs {
   301  			cleanup(d)
   302  		}
   303  	}()
   304  
   305  	for _, d := range testdbs {
   306  		db := CouchDatabase{CouchInstance: couchInstance, DBName: d}
   307  		require.NoError(t, db.CreateDatabaseIfNotExist())
   308  	}
   309  	isEmpty, err = couchInstance.IsEmpty(nil)
   310  	require.NoError(t, err)
   311  	require.False(t, isEmpty)
   312  
   313  	ignore := []string{"testdb1"}
   314  	isEmpty, err = couchInstance.IsEmpty(ignore)
   315  	require.NoError(t, err)
   316  	require.False(t, isEmpty)
   317  
   318  	ignore = []string{"testdb1", "testdb2"}
   319  	isEmpty, err = couchInstance.IsEmpty(ignore)
   320  	require.NoError(t, err)
   321  	require.True(t, isEmpty)
   322  
   323  	couchInstance.conf.Address = "junk"
   324  	couchInstance.conf.MaxRetries = 0
   325  	isEmpty, err = couchInstance.IsEmpty(ignore)
   326  	require.Error(t, err)
   327  	require.Contains(t, err.Error(), "unable to connect to CouchDB, check the hostname and port: Get http://junk/_all_dbs")
   328  }
   329  
   330  func TestDBBadDatabaseName(t *testing.T) {
   331  
   332  	//create a new instance and database object using a valid database name mixed case
   333  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
   334  	assert.NoError(t, err, "Error when trying to create couch instance")
   335  	_, dberr := CreateCouchDatabase(couchInstance, "testDB")
   336  	assert.Error(t, dberr, "Error should have been thrown for an invalid db name")
   337  
   338  	//create a new instance and database object using a valid database name letters and numbers
   339  	couchInstance, err = CreateCouchInstance(testConfig(), &disabled.Provider{})
   340  	assert.NoError(t, err, "Error when trying to create couch instance")
   341  	_, dberr = CreateCouchDatabase(couchInstance, "test132")
   342  	assert.NoError(t, dberr, "Error when testing a valid database name")
   343  
   344  	//create a new instance and database object using a valid database name - special characters
   345  	couchInstance, err = CreateCouchInstance(testConfig(), &disabled.Provider{})
   346  	assert.NoError(t, err, "Error when trying to create couch instance")
   347  	_, dberr = CreateCouchDatabase(couchInstance, "test1234~!@#$%^&*()[]{}.")
   348  	assert.Error(t, dberr, "Error should have been thrown for an invalid db name")
   349  
   350  	//create a new instance and database object using a invalid database name - too long	/*
   351  	couchInstance, err = CreateCouchInstance(testConfig(), &disabled.Provider{})
   352  	assert.NoError(t, err, "Error when trying to create couch instance")
   353  	_, dberr = CreateCouchDatabase(couchInstance, "a12345678901234567890123456789012345678901234"+
   354  		"56789012345678901234567890123456789012345678901234567890123456789012345678901234567890"+
   355  		"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456"+
   356  		"78901234567890123456789012345678901234567890")
   357  	assert.Error(t, dberr, "Error should have been thrown for invalid database name")
   358  
   359  }
   360  
   361  func TestDBBadConnection(t *testing.T) {
   362  
   363  	//create a new instance and database object
   364  	//Limit the maxRetriesOnStartup to 3 in order to reduce time for the failure
   365  	config := testConfig()
   366  	config.Address = badConnectURL
   367  	config.MaxRetriesOnStartup = 3
   368  	_, err := CreateCouchInstance(config, &disabled.Provider{})
   369  	assert.Error(t, err, "Error should have been thrown for a bad connection")
   370  }
   371  
   372  func TestBadDBCredentials(t *testing.T) {
   373  
   374  	database := "testdbbadcredentials"
   375  	err := cleanup(database)
   376  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   377  	defer cleanup(database)
   378  
   379  	badConfig := testConfig()
   380  	badConfig.Username = "fred"
   381  	badConfig.Password = "fred"
   382  	//create a new instance and database object
   383  	_, err = CreateCouchInstance(badConfig, &disabled.Provider{})
   384  	assert.Error(t, err, "Error should have been thrown for bad credentials")
   385  
   386  }
   387  
   388  func TestDBCreateDatabaseAndPersist(t *testing.T) {
   389  
   390  	//Test create and persist with default configured maxRetries
   391  	testDBCreateDatabaseAndPersist(t, testConfig().MaxRetries)
   392  
   393  	//Test create and persist with 0 retries
   394  	testDBCreateDatabaseAndPersist(t, 0)
   395  
   396  	//Test batch operations with default configured maxRetries
   397  	testBatchBatchOperations(t, testConfig().MaxRetries)
   398  
   399  	//Test batch operations with 0 retries
   400  	testBatchBatchOperations(t, 0)
   401  
   402  }
   403  
   404  func testDBCreateDatabaseAndPersist(t *testing.T, maxRetries int) {
   405  
   406  	database := "testdbcreatedatabaseandpersist"
   407  	err := cleanup(database)
   408  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   409  	defer cleanup(database)
   410  
   411  	//create a new instance and database object
   412  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
   413  	assert.NoError(t, err, "Error when trying to create couch instance")
   414  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
   415  
   416  	//create a new database
   417  	errdb := db.CreateDatabaseIfNotExist()
   418  	assert.NoError(t, errdb, "Error when trying to create database")
   419  
   420  	//Retrieve the info for the new database and make sure the name matches
   421  	dbResp, _, errdb := db.GetDatabaseInfo()
   422  	assert.NoError(t, errdb, "Error when trying to retrieve database information")
   423  	assert.Equal(t, database, dbResp.DbName)
   424  
   425  	//Save the test document
   426  	_, saveerr := db.SaveDoc("idWith/slash", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   427  	assert.NoError(t, saveerr, "Error when trying to save a document")
   428  
   429  	//Retrieve the test document
   430  	dbGetResp, _, geterr := db.ReadDoc("idWith/slash")
   431  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
   432  
   433  	//Unmarshal the document to Asset structure
   434  	assetResp := &Asset{}
   435  	geterr = json.Unmarshal(dbGetResp.JSONValue, &assetResp)
   436  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
   437  
   438  	//Verify the owner retrieved matches
   439  	assert.Equal(t, "jerry", assetResp.Owner)
   440  
   441  	//Save the test document
   442  	_, saveerr = db.SaveDoc("1", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   443  	assert.NoError(t, saveerr, "Error when trying to save a document")
   444  
   445  	//Retrieve the test document
   446  	dbGetResp, _, geterr = db.ReadDoc("1")
   447  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
   448  
   449  	//Unmarshal the document to Asset structure
   450  	assetResp = &Asset{}
   451  	geterr = json.Unmarshal(dbGetResp.JSONValue, &assetResp)
   452  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
   453  
   454  	//Verify the owner retrieved matches
   455  	assert.Equal(t, "jerry", assetResp.Owner)
   456  
   457  	//Change owner to bob
   458  	assetResp.Owner = "bob"
   459  
   460  	//create a byte array of the JSON
   461  	assetDocUpdated, _ := json.Marshal(assetResp)
   462  
   463  	//Save the updated test document
   464  	_, saveerr = db.SaveDoc("1", "", &CouchDoc{JSONValue: assetDocUpdated, Attachments: nil})
   465  	assert.NoError(t, saveerr, "Error when trying to save the updated document")
   466  
   467  	//Retrieve the updated test document
   468  	dbGetResp, _, geterr = db.ReadDoc("1")
   469  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
   470  
   471  	//Unmarshal the document to Asset structure
   472  	assetResp = &Asset{}
   473  	json.Unmarshal(dbGetResp.JSONValue, &assetResp)
   474  
   475  	//Assert that the update was saved and retrieved
   476  	assert.Equal(t, "bob", assetResp.Owner)
   477  
   478  	testBytes2 := []byte(`test attachment 2`)
   479  
   480  	attachment2 := &AttachmentInfo{}
   481  	attachment2.AttachmentBytes = testBytes2
   482  	attachment2.ContentType = "application/octet-stream"
   483  	attachment2.Name = "data"
   484  	attachments2 := []*AttachmentInfo{}
   485  	attachments2 = append(attachments2, attachment2)
   486  
   487  	//Save the test document with an attachment
   488  	_, saveerr = db.SaveDoc("2", "", &CouchDoc{JSONValue: nil, Attachments: attachments2})
   489  	assert.NoError(t, saveerr, "Error when trying to save a document")
   490  
   491  	//Retrieve the test document with attachments
   492  	dbGetResp, _, geterr = db.ReadDoc("2")
   493  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
   494  
   495  	//verify the text from the attachment is correct
   496  	testattach := dbGetResp.Attachments[0].AttachmentBytes
   497  	assert.Equal(t, testBytes2, testattach)
   498  
   499  	testBytes3 := []byte{}
   500  
   501  	attachment3 := &AttachmentInfo{}
   502  	attachment3.AttachmentBytes = testBytes3
   503  	attachment3.ContentType = "application/octet-stream"
   504  	attachment3.Name = "data"
   505  	attachments3 := []*AttachmentInfo{}
   506  	attachments3 = append(attachments3, attachment3)
   507  
   508  	//Save the test document with a zero length attachment
   509  	_, saveerr = db.SaveDoc("3", "", &CouchDoc{JSONValue: nil, Attachments: attachments3})
   510  	assert.NoError(t, saveerr, "Error when trying to save a document")
   511  
   512  	//Retrieve the test document with attachments
   513  	dbGetResp, _, geterr = db.ReadDoc("3")
   514  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
   515  
   516  	//verify the text from the attachment is correct,  zero bytes
   517  	testattach = dbGetResp.Attachments[0].AttachmentBytes
   518  	assert.Equal(t, testBytes3, testattach)
   519  
   520  	testBytes4a := []byte(`test attachment 4a`)
   521  	attachment4a := &AttachmentInfo{}
   522  	attachment4a.AttachmentBytes = testBytes4a
   523  	attachment4a.ContentType = "application/octet-stream"
   524  	attachment4a.Name = "data1"
   525  
   526  	testBytes4b := []byte(`test attachment 4b`)
   527  	attachment4b := &AttachmentInfo{}
   528  	attachment4b.AttachmentBytes = testBytes4b
   529  	attachment4b.ContentType = "application/octet-stream"
   530  	attachment4b.Name = "data2"
   531  
   532  	attachments4 := []*AttachmentInfo{}
   533  	attachments4 = append(attachments4, attachment4a)
   534  	attachments4 = append(attachments4, attachment4b)
   535  
   536  	//Save the updated test document with multiple attachments
   537  	_, saveerr = db.SaveDoc("4", "", &CouchDoc{JSONValue: assetJSON, Attachments: attachments4})
   538  	assert.NoError(t, saveerr, "Error when trying to save the updated document")
   539  
   540  	//Retrieve the test document with attachments
   541  	dbGetResp, _, geterr = db.ReadDoc("4")
   542  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
   543  
   544  	for _, attach4 := range dbGetResp.Attachments {
   545  
   546  		currentName := attach4.Name
   547  		if currentName == "data1" {
   548  			assert.Equal(t, testBytes4a, attach4.AttachmentBytes)
   549  		}
   550  		if currentName == "data2" {
   551  			assert.Equal(t, testBytes4b, attach4.AttachmentBytes)
   552  		}
   553  
   554  	}
   555  
   556  	testBytes5a := []byte(`test attachment 5a`)
   557  	attachment5a := &AttachmentInfo{}
   558  	attachment5a.AttachmentBytes = testBytes5a
   559  	attachment5a.ContentType = "application/octet-stream"
   560  	attachment5a.Name = "data1"
   561  
   562  	testBytes5b := []byte{}
   563  	attachment5b := &AttachmentInfo{}
   564  	attachment5b.AttachmentBytes = testBytes5b
   565  	attachment5b.ContentType = "application/octet-stream"
   566  	attachment5b.Name = "data2"
   567  
   568  	attachments5 := []*AttachmentInfo{}
   569  	attachments5 = append(attachments5, attachment5a)
   570  	attachments5 = append(attachments5, attachment5b)
   571  
   572  	//Save the updated test document with multiple attachments and zero length attachments
   573  	_, saveerr = db.SaveDoc("5", "", &CouchDoc{JSONValue: assetJSON, Attachments: attachments5})
   574  	assert.NoError(t, saveerr, "Error when trying to save the updated document")
   575  
   576  	//Retrieve the test document with attachments
   577  	dbGetResp, _, geterr = db.ReadDoc("5")
   578  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
   579  
   580  	for _, attach5 := range dbGetResp.Attachments {
   581  
   582  		currentName := attach5.Name
   583  		if currentName == "data1" {
   584  			assert.Equal(t, testBytes5a, attach5.AttachmentBytes)
   585  		}
   586  		if currentName == "data2" {
   587  			assert.Equal(t, testBytes5b, attach5.AttachmentBytes)
   588  		}
   589  
   590  	}
   591  
   592  	//Attempt to save the document with an invalid id
   593  	_, saveerr = db.SaveDoc(string([]byte{0xff, 0xfe, 0xfd}), "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   594  	assert.Error(t, saveerr, "Error should have been thrown when saving a document with an invalid ID")
   595  
   596  	//Attempt to read a document with an invalid id
   597  	_, _, readerr := db.ReadDoc(string([]byte{0xff, 0xfe, 0xfd}))
   598  	assert.Error(t, readerr, "Error should have been thrown when reading a document with an invalid ID")
   599  
   600  	//Drop the database
   601  	_, errdbdrop := db.DropDatabase()
   602  	assert.NoError(t, errdbdrop, "Error dropping database")
   603  
   604  	//Make sure an error is thrown for getting info for a missing database
   605  	_, _, errdbinfo := db.GetDatabaseInfo()
   606  	assert.Error(t, errdbinfo, "Error should have been thrown for missing database")
   607  
   608  	//Attempt to save a document to a deleted database
   609  	_, saveerr = db.SaveDoc("6", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   610  	assert.Error(t, saveerr, "Error should have been thrown while attempting to save to a deleted database")
   611  
   612  	//Attempt to read from a deleted database
   613  	_, _, geterr = db.ReadDoc("6")
   614  	assert.NoError(t, geterr, "Error should not have been thrown for a missing database, nil value is returned")
   615  
   616  }
   617  
   618  func TestDBRequestTimeout(t *testing.T) {
   619  
   620  	database := "testdbrequesttimeout"
   621  	err := cleanup(database)
   622  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   623  	defer cleanup(database)
   624  
   625  	//create an impossibly short timeout
   626  	impossibleTimeout := time.Microsecond * 1
   627  
   628  	//create a new instance and database object with a timeout that will fail
   629  	//Also use a maxRetriesOnStartup=3 to reduce the number of retries
   630  	config := testConfig()
   631  	config.MaxRetriesOnStartup = 3
   632  	config.RequestTimeout = impossibleTimeout
   633  	_, err = CreateCouchInstance(config, &disabled.Provider{})
   634  	assert.Error(t, err, "Error should have been thown while trying to create a couchdb instance with a connection timeout")
   635  
   636  	//create a new instance and database object
   637  	config = testConfig()
   638  	config.MaxRetries = -1
   639  	config.MaxRetriesOnStartup = 3
   640  	_, err = CreateCouchInstance(config, &disabled.Provider{})
   641  	assert.Error(t, err, "Error should have been thrown while attempting to create a database")
   642  
   643  }
   644  
   645  func TestDBTimeoutConflictRetry(t *testing.T) {
   646  
   647  	database := "testdbtimeoutretry"
   648  	err := cleanup(database)
   649  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   650  	defer cleanup(database)
   651  
   652  	//create a new instance and database object
   653  	config := testConfig()
   654  	config.MaxRetriesOnStartup = 3
   655  	couchInstance, err := CreateCouchInstance(config, &disabled.Provider{})
   656  	assert.NoError(t, err, "Error when trying to create couch instance")
   657  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
   658  
   659  	//create a new database
   660  	errdb := db.CreateDatabaseIfNotExist()
   661  	assert.NoError(t, errdb, "Error when trying to create database")
   662  
   663  	//Retrieve the info for the new database and make sure the name matches
   664  	dbResp, _, errdb := db.GetDatabaseInfo()
   665  	assert.NoError(t, errdb, "Error when trying to retrieve database information")
   666  	assert.Equal(t, database, dbResp.DbName)
   667  
   668  	//Save the test document
   669  	_, saveerr := db.SaveDoc("1", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   670  	assert.NoError(t, saveerr, "Error when trying to save a document")
   671  
   672  	//Retrieve the test document
   673  	_, _, geterr := db.ReadDoc("1")
   674  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
   675  
   676  	//Save the test document with an invalid rev.  This should cause a retry
   677  	_, saveerr = db.SaveDoc("1", "1-11111111111111111111111111111111", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   678  	assert.NoError(t, saveerr, "Error when trying to save a document with a revision conflict")
   679  
   680  	//Delete the test document with an invalid rev.  This should cause a retry
   681  	deleteerr := db.DeleteDoc("1", "1-11111111111111111111111111111111")
   682  	assert.NoError(t, deleteerr, "Error when trying to delete a document with a revision conflict")
   683  
   684  }
   685  
   686  func TestDBBadNumberOfRetries(t *testing.T) {
   687  
   688  	database := "testdbbadretries"
   689  	err := cleanup(database)
   690  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   691  	defer cleanup(database)
   692  
   693  	//create a new instance and database object
   694  	config := testConfig()
   695  	config.MaxRetries = -1
   696  	config.MaxRetriesOnStartup = 3
   697  	_, err = CreateCouchInstance(config, &disabled.Provider{})
   698  	assert.Error(t, err, "Error should have been thrown while attempting to create a database")
   699  
   700  }
   701  
   702  func TestDBBadJSON(t *testing.T) {
   703  
   704  	database := "testdbbadjson"
   705  	err := cleanup(database)
   706  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   707  	defer cleanup(database)
   708  
   709  	//create a new instance and database object
   710  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
   711  	assert.NoError(t, err, "Error when trying to create couch instance")
   712  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
   713  
   714  	//create a new database
   715  	errdb := db.CreateDatabaseIfNotExist()
   716  	assert.NoError(t, errdb, "Error when trying to create database")
   717  
   718  	//Retrieve the info for the new database and make sure the name matches
   719  	dbResp, _, errdb := db.GetDatabaseInfo()
   720  	assert.NoError(t, errdb, "Error when trying to retrieve database information")
   721  	assert.Equal(t, database, dbResp.DbName)
   722  
   723  	badJSON := []byte(`{"asset_name"}`)
   724  
   725  	//Save the test document
   726  	_, saveerr := db.SaveDoc("1", "", &CouchDoc{JSONValue: badJSON, Attachments: nil})
   727  	assert.Error(t, saveerr, "Error should have been thrown for a bad JSON")
   728  
   729  }
   730  
   731  func TestPrefixScan(t *testing.T) {
   732  
   733  	database := "testprefixscan"
   734  	err := cleanup(database)
   735  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   736  	defer cleanup(database)
   737  
   738  	//create a new instance and database object
   739  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
   740  	assert.NoError(t, err, "Error when trying to create couch instance")
   741  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
   742  
   743  	//create a new database
   744  	errdb := db.CreateDatabaseIfNotExist()
   745  	assert.NoError(t, errdb, "Error when trying to create database")
   746  
   747  	//Retrieve the info for the new database and make sure the name matches
   748  	dbResp, _, errdb := db.GetDatabaseInfo()
   749  	assert.NoError(t, errdb, "Error when trying to retrieve database information")
   750  	assert.Equal(t, database, dbResp.DbName)
   751  
   752  	//Save documents
   753  	for i := 0; i < 20; i++ {
   754  		id1 := string(0) + string(i) + string(0)
   755  		id2 := string(0) + string(i) + string(1)
   756  		id3 := string(0) + string(i) + string(utf8.MaxRune-1)
   757  		_, saveerr := db.SaveDoc(id1, "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   758  		assert.NoError(t, saveerr, "Error when trying to save a document")
   759  		_, saveerr = db.SaveDoc(id2, "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   760  		assert.NoError(t, saveerr, "Error when trying to save a document")
   761  		_, saveerr = db.SaveDoc(id3, "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   762  		assert.NoError(t, saveerr, "Error when trying to save a document")
   763  
   764  	}
   765  	startKey := string(0) + string(10)
   766  	endKey := startKey + string(utf8.MaxRune)
   767  	_, _, geterr := db.ReadDoc(endKey)
   768  	assert.NoError(t, geterr, "Error when trying to get lastkey")
   769  
   770  	resultsPtr, _, geterr := db.ReadDocRange(startKey, endKey, 1000)
   771  	assert.NoError(t, geterr, "Error when trying to perform a range scan")
   772  	assert.NotNil(t, resultsPtr)
   773  	results := resultsPtr
   774  	assert.Equal(t, 3, len(results))
   775  	assert.Equal(t, string(0)+string(10)+string(0), results[0].ID)
   776  	assert.Equal(t, string(0)+string(10)+string(1), results[1].ID)
   777  	assert.Equal(t, string(0)+string(10)+string(utf8.MaxRune-1), results[2].ID)
   778  
   779  	//Drop the database
   780  	_, errdbdrop := db.DropDatabase()
   781  	assert.NoError(t, errdbdrop, "Error dropping database")
   782  
   783  	//Retrieve the info for the new database and make sure the name matches
   784  	_, _, errdbinfo := db.GetDatabaseInfo()
   785  	assert.Error(t, errdbinfo, "Error should have been thrown for missing database")
   786  
   787  }
   788  
   789  func TestDBSaveAttachment(t *testing.T) {
   790  
   791  	database := "testdbsaveattachment"
   792  	err := cleanup(database)
   793  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   794  	defer cleanup(database)
   795  
   796  	byteText := []byte(`This is a test document.  This is only a test`)
   797  
   798  	attachment := &AttachmentInfo{}
   799  	attachment.AttachmentBytes = byteText
   800  	attachment.ContentType = "text/plain"
   801  	attachment.Length = uint64(len(byteText))
   802  	attachment.Name = "valueBytes"
   803  
   804  	attachments := []*AttachmentInfo{}
   805  	attachments = append(attachments, attachment)
   806  
   807  	//create a new instance and database object
   808  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
   809  	assert.NoError(t, err, "Error when trying to create couch instance")
   810  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
   811  
   812  	//create a new database
   813  	errdb := db.CreateDatabaseIfNotExist()
   814  	assert.NoError(t, errdb, "Error when trying to create database")
   815  
   816  	//Save the test document
   817  	_, saveerr := db.SaveDoc("10", "", &CouchDoc{JSONValue: nil, Attachments: attachments})
   818  	assert.NoError(t, saveerr, "Error when trying to save a document")
   819  
   820  	//Attempt to retrieve the updated test document with attachments
   821  	couchDoc, _, geterr2 := db.ReadDoc("10")
   822  	assert.NoError(t, geterr2, "Error when trying to retrieve a document with attachment")
   823  	assert.NotNil(t, couchDoc.Attachments)
   824  	assert.Equal(t, byteText, couchDoc.Attachments[0].AttachmentBytes)
   825  	assert.Equal(t, attachment.Length, couchDoc.Attachments[0].Length)
   826  
   827  }
   828  
   829  func TestDBDeleteDocument(t *testing.T) {
   830  
   831  	database := "testdbdeletedocument"
   832  	err := cleanup(database)
   833  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   834  	defer cleanup(database)
   835  
   836  	//create a new instance and database object
   837  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
   838  	assert.NoError(t, err, "Error when trying to create couch instance")
   839  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
   840  
   841  	//create a new database
   842  	errdb := db.CreateDatabaseIfNotExist()
   843  	assert.NoError(t, errdb, "Error when trying to create database")
   844  
   845  	//Save the test document
   846  	_, saveerr := db.SaveDoc("2", "", &CouchDoc{JSONValue: assetJSON, Attachments: nil})
   847  	assert.NoError(t, saveerr, "Error when trying to save a document")
   848  
   849  	//Attempt to retrieve the test document
   850  	_, _, readErr := db.ReadDoc("2")
   851  	assert.NoError(t, readErr, "Error when trying to retrieve a document with attachment")
   852  
   853  	//Delete the test document
   854  	deleteErr := db.DeleteDoc("2", "")
   855  	assert.NoError(t, deleteErr, "Error when trying to delete a document")
   856  
   857  	//Attempt to retrieve the test document
   858  	readValue, _, _ := db.ReadDoc("2")
   859  	assert.Nil(t, readValue)
   860  
   861  }
   862  
   863  func TestDBDeleteNonExistingDocument(t *testing.T) {
   864  
   865  	database := "testdbdeletenonexistingdocument"
   866  	err := cleanup(database)
   867  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   868  	defer cleanup(database)
   869  
   870  	//create a new instance and database object
   871  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
   872  	assert.NoError(t, err, "Error when trying to create couch instance")
   873  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
   874  
   875  	//create a new database
   876  	errdb := db.CreateDatabaseIfNotExist()
   877  	assert.NoError(t, errdb, "Error when trying to create database")
   878  
   879  	//Save the test document
   880  	deleteErr := db.DeleteDoc("2", "")
   881  	assert.NoError(t, deleteErr, "Error when trying to delete a non existing document")
   882  }
   883  
   884  func TestCouchDBVersion(t *testing.T) {
   885  
   886  	err := checkCouchDBVersion("2.0.0")
   887  	assert.NoError(t, err, "Error should not have been thrown for valid version")
   888  
   889  	err = checkCouchDBVersion("4.5.0")
   890  	assert.NoError(t, err, "Error should not have been thrown for valid version")
   891  
   892  	err = checkCouchDBVersion("1.6.5.4")
   893  	assert.Error(t, err, "Error should have been thrown for invalid version")
   894  
   895  	err = checkCouchDBVersion("0.0.0.0")
   896  	assert.Error(t, err, "Error should have been thrown for invalid version")
   897  
   898  }
   899  
   900  func TestIndexOperations(t *testing.T) {
   901  
   902  	database := "testindexoperations"
   903  	err := cleanup(database)
   904  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
   905  	defer cleanup(database)
   906  
   907  	byteJSON1 := []byte(`{"_id":"1", "asset_name":"marble1","color":"blue","size":1,"owner":"jerry"}`)
   908  	byteJSON2 := []byte(`{"_id":"2", "asset_name":"marble2","color":"red","size":2,"owner":"tom"}`)
   909  	byteJSON3 := []byte(`{"_id":"3", "asset_name":"marble3","color":"green","size":3,"owner":"jerry"}`)
   910  	byteJSON4 := []byte(`{"_id":"4", "asset_name":"marble4","color":"purple","size":4,"owner":"tom"}`)
   911  	byteJSON5 := []byte(`{"_id":"5", "asset_name":"marble5","color":"blue","size":5,"owner":"jerry"}`)
   912  	byteJSON6 := []byte(`{"_id":"6", "asset_name":"marble6","color":"white","size":6,"owner":"tom"}`)
   913  	byteJSON7 := []byte(`{"_id":"7", "asset_name":"marble7","color":"white","size":7,"owner":"tom"}`)
   914  	byteJSON8 := []byte(`{"_id":"8", "asset_name":"marble8","color":"white","size":8,"owner":"tom"}`)
   915  	byteJSON9 := []byte(`{"_id":"9", "asset_name":"marble9","color":"white","size":9,"owner":"tom"}`)
   916  	byteJSON10 := []byte(`{"_id":"10", "asset_name":"marble10","color":"white","size":10,"owner":"tom"}`)
   917  
   918  	//create a new instance and database object   --------------------------------------------------------
   919  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
   920  	assert.NoError(t, err, "Error when trying to create couch instance")
   921  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
   922  
   923  	//create a new database
   924  	errdb := db.CreateDatabaseIfNotExist()
   925  	assert.NoError(t, errdb, "Error when trying to create database")
   926  
   927  	batchUpdateDocs := []*CouchDoc{}
   928  
   929  	batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: byteJSON1, Attachments: nil})
   930  	batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: byteJSON2, Attachments: nil})
   931  	batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: byteJSON3, Attachments: nil})
   932  	batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: byteJSON4, Attachments: nil})
   933  	batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: byteJSON5, Attachments: nil})
   934  	batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: byteJSON6, Attachments: nil})
   935  	batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: byteJSON7, Attachments: nil})
   936  	batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: byteJSON8, Attachments: nil})
   937  	batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: byteJSON9, Attachments: nil})
   938  	batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: byteJSON10, Attachments: nil})
   939  
   940  	_, err = db.BatchUpdateDocuments(batchUpdateDocs)
   941  	assert.NoError(t, err, "Error adding batch of documents")
   942  
   943  	//Create an index definition
   944  	indexDefSize := `{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortDoc", "name":"indexSizeSortName","type":"json"}`
   945  
   946  	//Create the index
   947  	_, err = db.CreateIndex(indexDefSize)
   948  	assert.NoError(t, err, "Error thrown while creating an index")
   949  
   950  	//Retrieve the list of indexes
   951  	//Delay for 100ms since CouchDB index list is updated async after index create/drop
   952  	time.Sleep(100 * time.Millisecond)
   953  	listResult, err := db.ListIndex()
   954  	assert.NoError(t, err, "Error thrown while retrieving indexes")
   955  
   956  	//There should only be one item returned
   957  	assert.Equal(t, 1, len(listResult))
   958  
   959  	//Verify the returned definition
   960  	for _, elem := range listResult {
   961  		assert.Equal(t, "indexSizeSortDoc", elem.DesignDocument)
   962  		assert.Equal(t, "indexSizeSortName", elem.Name)
   963  		//ensure the index definition is correct,  CouchDB 2.1.1 will also return "partial_filter_selector":{}
   964  		assert.Equal(t, true, strings.Contains(elem.Definition, `"fields":[{"size":"desc"}]`))
   965  	}
   966  
   967  	//Create an index definition with no DesignDocument or name
   968  	indexDefColor := `{"index":{"fields":[{"color":"desc"}]}}`
   969  
   970  	//Create the index
   971  	_, err = db.CreateIndex(indexDefColor)
   972  	assert.NoError(t, err, "Error thrown while creating an index")
   973  
   974  	//Retrieve the list of indexes
   975  	//Delay for 100ms since CouchDB index list is updated async after index create/drop
   976  	time.Sleep(100 * time.Millisecond)
   977  	listResult, err = db.ListIndex()
   978  	assert.NoError(t, err, "Error thrown while retrieving indexes")
   979  
   980  	//There should be two indexes returned
   981  	assert.Equal(t, 2, len(listResult))
   982  
   983  	//Delete the named index
   984  	err = db.DeleteIndex("indexSizeSortDoc", "indexSizeSortName")
   985  	assert.NoError(t, err, "Error thrown while deleting an index")
   986  
   987  	//Retrieve the list of indexes
   988  	//Delay for 100ms since CouchDB index list is updated async after index create/drop
   989  	time.Sleep(100 * time.Millisecond)
   990  	listResult, err = db.ListIndex()
   991  	assert.NoError(t, err, "Error thrown while retrieving indexes")
   992  
   993  	//There should be one index returned
   994  	assert.Equal(t, 1, len(listResult))
   995  
   996  	//Delete the unnamed index
   997  	for _, elem := range listResult {
   998  		err = db.DeleteIndex(elem.DesignDocument, elem.Name)
   999  		assert.NoError(t, err, "Error thrown while deleting an index")
  1000  	}
  1001  
  1002  	//Retrieve the list of indexes
  1003  	//Delay for 100ms since CouchDB index list is updated async after index create/drop
  1004  	time.Sleep(100 * time.Millisecond)
  1005  	listResult, err = db.ListIndex()
  1006  	assert.NoError(t, err, "Error thrown while retrieving indexes")
  1007  	assert.Equal(t, 0, len(listResult))
  1008  
  1009  	//Create a query string with a descending sort, this will require an index
  1010  	queryString := `{"selector":{"size": {"$gt": 0}},"fields": ["_id", "_rev", "owner", "asset_name", "color", "size"], "sort":[{"size":"desc"}], "limit": 10,"skip": 0}`
  1011  
  1012  	//Execute a query with a sort, this should throw the exception
  1013  	_, _, err = db.QueryDocuments(queryString)
  1014  	assert.Error(t, err, "Error should have thrown while querying without a valid index")
  1015  
  1016  	//Create the index
  1017  	_, err = db.CreateIndex(indexDefSize)
  1018  	assert.NoError(t, err, "Error thrown while creating an index")
  1019  
  1020  	//Delay for 100ms since CouchDB index list is updated async after index create/drop
  1021  	time.Sleep(100 * time.Millisecond)
  1022  
  1023  	//Execute a query with an index,  this should succeed
  1024  	_, _, err = db.QueryDocuments(queryString)
  1025  	assert.NoError(t, err, "Error thrown while querying with an index")
  1026  
  1027  	//Create another index definition
  1028  	indexDefSize = `{"index":{"fields":[{"data.size":"desc"},{"data.owner":"desc"}]},"ddoc":"indexSizeOwnerSortDoc", "name":"indexSizeOwnerSortName","type":"json"}`
  1029  
  1030  	//Create the index
  1031  	dbResp, err := db.CreateIndex(indexDefSize)
  1032  	assert.NoError(t, err, "Error thrown while creating an index")
  1033  
  1034  	//verify the response is "created" for an index creation
  1035  	assert.Equal(t, "created", dbResp.Result)
  1036  
  1037  	//Delay for 100ms since CouchDB index list is updated async after index create/drop
  1038  	time.Sleep(100 * time.Millisecond)
  1039  
  1040  	//Update the index
  1041  	dbResp, err = db.CreateIndex(indexDefSize)
  1042  	assert.NoError(t, err, "Error thrown while creating an index")
  1043  
  1044  	//verify the response is "exists" for an update
  1045  	assert.Equal(t, "exists", dbResp.Result)
  1046  
  1047  	//Retrieve the list of indexes
  1048  	//Delay for 100ms since CouchDB index list is updated async after index create/drop
  1049  	time.Sleep(100 * time.Millisecond)
  1050  	listResult, err = db.ListIndex()
  1051  	assert.NoError(t, err, "Error thrown while retrieving indexes")
  1052  
  1053  	//There should only be two definitions
  1054  	assert.Equal(t, 2, len(listResult))
  1055  
  1056  	//Create an invalid index definition with an invalid JSON
  1057  	indexDefSize = `{"index"{"fields":[{"data.size":"desc"},{"data.owner":"desc"}]},"ddoc":"indexSizeOwnerSortDoc", "name":"indexSizeOwnerSortName","type":"json"}`
  1058  
  1059  	//Create the index
  1060  	_, err = db.CreateIndex(indexDefSize)
  1061  	assert.Error(t, err, "Error should have been thrown for an invalid index JSON")
  1062  
  1063  	//Create an invalid index definition with a valid JSON and an invalid index definition
  1064  	indexDefSize = `{"index":{"fields2":[{"data.size":"desc"},{"data.owner":"desc"}]},"ddoc":"indexSizeOwnerSortDoc", "name":"indexSizeOwnerSortName","type":"json"}`
  1065  
  1066  	//Create the index
  1067  	_, err = db.CreateIndex(indexDefSize)
  1068  	assert.Error(t, err, "Error should have been thrown for an invalid index definition")
  1069  
  1070  }
  1071  
  1072  func TestRichQuery(t *testing.T) {
  1073  
  1074  	byteJSON01 := []byte(`{"asset_name":"marble01","color":"blue","size":1,"owner":"jerry"}`)
  1075  	byteJSON02 := []byte(`{"asset_name":"marble02","color":"red","size":2,"owner":"tom"}`)
  1076  	byteJSON03 := []byte(`{"asset_name":"marble03","color":"green","size":3,"owner":"jerry"}`)
  1077  	byteJSON04 := []byte(`{"asset_name":"marble04","color":"purple","size":4,"owner":"tom"}`)
  1078  	byteJSON05 := []byte(`{"asset_name":"marble05","color":"blue","size":5,"owner":"jerry"}`)
  1079  	byteJSON06 := []byte(`{"asset_name":"marble06","color":"white","size":6,"owner":"tom"}`)
  1080  	byteJSON07 := []byte(`{"asset_name":"marble07","color":"white","size":7,"owner":"tom"}`)
  1081  	byteJSON08 := []byte(`{"asset_name":"marble08","color":"white","size":8,"owner":"tom"}`)
  1082  	byteJSON09 := []byte(`{"asset_name":"marble09","color":"white","size":9,"owner":"tom"}`)
  1083  	byteJSON10 := []byte(`{"asset_name":"marble10","color":"white","size":10,"owner":"tom"}`)
  1084  	byteJSON11 := []byte(`{"asset_name":"marble11","color":"green","size":11,"owner":"tom"}`)
  1085  	byteJSON12 := []byte(`{"asset_name":"marble12","color":"green","size":12,"owner":"frank"}`)
  1086  
  1087  	attachment1 := &AttachmentInfo{}
  1088  	attachment1.AttachmentBytes = []byte(`marble01 - test attachment`)
  1089  	attachment1.ContentType = "application/octet-stream"
  1090  	attachment1.Name = "data"
  1091  	attachments1 := []*AttachmentInfo{}
  1092  	attachments1 = append(attachments1, attachment1)
  1093  
  1094  	attachment2 := &AttachmentInfo{}
  1095  	attachment2.AttachmentBytes = []byte(`marble02 - test attachment`)
  1096  	attachment2.ContentType = "application/octet-stream"
  1097  	attachment2.Name = "data"
  1098  	attachments2 := []*AttachmentInfo{}
  1099  	attachments2 = append(attachments2, attachment2)
  1100  
  1101  	attachment3 := &AttachmentInfo{}
  1102  	attachment3.AttachmentBytes = []byte(`marble03 - test attachment`)
  1103  	attachment3.ContentType = "application/octet-stream"
  1104  	attachment3.Name = "data"
  1105  	attachments3 := []*AttachmentInfo{}
  1106  	attachments3 = append(attachments3, attachment3)
  1107  
  1108  	attachment4 := &AttachmentInfo{}
  1109  	attachment4.AttachmentBytes = []byte(`marble04 - test attachment`)
  1110  	attachment4.ContentType = "application/octet-stream"
  1111  	attachment4.Name = "data"
  1112  	attachments4 := []*AttachmentInfo{}
  1113  	attachments4 = append(attachments4, attachment4)
  1114  
  1115  	attachment5 := &AttachmentInfo{}
  1116  	attachment5.AttachmentBytes = []byte(`marble05 - test attachment`)
  1117  	attachment5.ContentType = "application/octet-stream"
  1118  	attachment5.Name = "data"
  1119  	attachments5 := []*AttachmentInfo{}
  1120  	attachments5 = append(attachments5, attachment5)
  1121  
  1122  	attachment6 := &AttachmentInfo{}
  1123  	attachment6.AttachmentBytes = []byte(`marble06 - test attachment`)
  1124  	attachment6.ContentType = "application/octet-stream"
  1125  	attachment6.Name = "data"
  1126  	attachments6 := []*AttachmentInfo{}
  1127  	attachments6 = append(attachments6, attachment6)
  1128  
  1129  	attachment7 := &AttachmentInfo{}
  1130  	attachment7.AttachmentBytes = []byte(`marble07 - test attachment`)
  1131  	attachment7.ContentType = "application/octet-stream"
  1132  	attachment7.Name = "data"
  1133  	attachments7 := []*AttachmentInfo{}
  1134  	attachments7 = append(attachments7, attachment7)
  1135  
  1136  	attachment8 := &AttachmentInfo{}
  1137  	attachment8.AttachmentBytes = []byte(`marble08 - test attachment`)
  1138  	attachment8.ContentType = "application/octet-stream"
  1139  	attachment7.Name = "data"
  1140  	attachments8 := []*AttachmentInfo{}
  1141  	attachments8 = append(attachments8, attachment8)
  1142  
  1143  	attachment9 := &AttachmentInfo{}
  1144  	attachment9.AttachmentBytes = []byte(`marble09 - test attachment`)
  1145  	attachment9.ContentType = "application/octet-stream"
  1146  	attachment9.Name = "data"
  1147  	attachments9 := []*AttachmentInfo{}
  1148  	attachments9 = append(attachments9, attachment9)
  1149  
  1150  	attachment10 := &AttachmentInfo{}
  1151  	attachment10.AttachmentBytes = []byte(`marble10 - test attachment`)
  1152  	attachment10.ContentType = "application/octet-stream"
  1153  	attachment10.Name = "data"
  1154  	attachments10 := []*AttachmentInfo{}
  1155  	attachments10 = append(attachments10, attachment10)
  1156  
  1157  	attachment11 := &AttachmentInfo{}
  1158  	attachment11.AttachmentBytes = []byte(`marble11 - test attachment`)
  1159  	attachment11.ContentType = "application/octet-stream"
  1160  	attachment11.Name = "data"
  1161  	attachments11 := []*AttachmentInfo{}
  1162  	attachments11 = append(attachments11, attachment11)
  1163  
  1164  	attachment12 := &AttachmentInfo{}
  1165  	attachment12.AttachmentBytes = []byte(`marble12 - test attachment`)
  1166  	attachment12.ContentType = "application/octet-stream"
  1167  	attachment12.Name = "data"
  1168  	attachments12 := []*AttachmentInfo{}
  1169  	attachments12 = append(attachments12, attachment12)
  1170  
  1171  	database := "testrichquery"
  1172  	err := cleanup(database)
  1173  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
  1174  	defer cleanup(database)
  1175  
  1176  	//create a new instance and database object   --------------------------------------------------------
  1177  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
  1178  	assert.NoError(t, err, "Error when trying to create couch instance")
  1179  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
  1180  
  1181  	//create a new database
  1182  	errdb := db.CreateDatabaseIfNotExist()
  1183  	assert.NoError(t, errdb, "Error when trying to create database")
  1184  
  1185  	//Save the test document
  1186  	_, saveerr := db.SaveDoc("marble01", "", &CouchDoc{JSONValue: byteJSON01, Attachments: attachments1})
  1187  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1188  
  1189  	//Save the test document
  1190  	_, saveerr = db.SaveDoc("marble02", "", &CouchDoc{JSONValue: byteJSON02, Attachments: attachments2})
  1191  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1192  
  1193  	//Save the test document
  1194  	_, saveerr = db.SaveDoc("marble03", "", &CouchDoc{JSONValue: byteJSON03, Attachments: attachments3})
  1195  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1196  
  1197  	//Save the test document
  1198  	_, saveerr = db.SaveDoc("marble04", "", &CouchDoc{JSONValue: byteJSON04, Attachments: attachments4})
  1199  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1200  
  1201  	//Save the test document
  1202  	_, saveerr = db.SaveDoc("marble05", "", &CouchDoc{JSONValue: byteJSON05, Attachments: attachments5})
  1203  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1204  
  1205  	//Save the test document
  1206  	_, saveerr = db.SaveDoc("marble06", "", &CouchDoc{JSONValue: byteJSON06, Attachments: attachments6})
  1207  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1208  
  1209  	//Save the test document
  1210  	_, saveerr = db.SaveDoc("marble07", "", &CouchDoc{JSONValue: byteJSON07, Attachments: attachments7})
  1211  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1212  
  1213  	//Save the test document
  1214  	_, saveerr = db.SaveDoc("marble08", "", &CouchDoc{JSONValue: byteJSON08, Attachments: attachments8})
  1215  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1216  
  1217  	//Save the test document
  1218  	_, saveerr = db.SaveDoc("marble09", "", &CouchDoc{JSONValue: byteJSON09, Attachments: attachments9})
  1219  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1220  
  1221  	//Save the test document
  1222  	_, saveerr = db.SaveDoc("marble10", "", &CouchDoc{JSONValue: byteJSON10, Attachments: attachments10})
  1223  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1224  
  1225  	//Save the test document
  1226  	_, saveerr = db.SaveDoc("marble11", "", &CouchDoc{JSONValue: byteJSON11, Attachments: attachments11})
  1227  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1228  
  1229  	//Save the test document
  1230  	_, saveerr = db.SaveDoc("marble12", "", &CouchDoc{JSONValue: byteJSON12, Attachments: attachments12})
  1231  	assert.NoError(t, saveerr, "Error when trying to save a document")
  1232  
  1233  	//Test query with invalid JSON -------------------------------------------------------------------
  1234  	queryString := `{"selector":{"owner":}}`
  1235  
  1236  	_, _, err = db.QueryDocuments(queryString)
  1237  	assert.Error(t, err, "Error should have been thrown for bad json")
  1238  
  1239  	//Test query with object  -------------------------------------------------------------------
  1240  	queryString = `{"selector":{"owner":{"$eq":"jerry"}}}`
  1241  
  1242  	queryResult, _, err := db.QueryDocuments(queryString)
  1243  	assert.NoError(t, err, "Error when attempting to execute a query")
  1244  
  1245  	//There should be 3 results for owner="jerry"
  1246  	assert.Equal(t, 3, len(queryResult))
  1247  
  1248  	//Test query with implicit operator   --------------------------------------------------------------
  1249  	queryString = `{"selector":{"owner":"jerry"}}`
  1250  
  1251  	queryResult, _, err = db.QueryDocuments(queryString)
  1252  	assert.NoError(t, err, "Error when attempting to execute a query")
  1253  
  1254  	//There should be 3 results for owner="jerry"
  1255  	assert.Equal(t, 3, len(queryResult))
  1256  
  1257  	//Test query with specified fields   -------------------------------------------------------------------
  1258  	queryString = `{"selector":{"owner":{"$eq":"jerry"}},"fields": ["owner","asset_name","color","size"]}`
  1259  
  1260  	queryResult, _, err = db.QueryDocuments(queryString)
  1261  	assert.NoError(t, err, "Error when attempting to execute a query")
  1262  
  1263  	//There should be 3 results for owner="jerry"
  1264  	assert.Equal(t, 3, len(queryResult))
  1265  
  1266  	//Test query with a leading operator   -------------------------------------------------------------------
  1267  	queryString = `{"selector":{"$or":[{"owner":{"$eq":"jerry"}},{"owner": {"$eq": "frank"}}]}}`
  1268  
  1269  	queryResult, _, err = db.QueryDocuments(queryString)
  1270  	assert.NoError(t, err, "Error when attempting to execute a query")
  1271  
  1272  	//There should be 4 results for owner="jerry" or owner="frank"
  1273  	assert.Equal(t, 4, len(queryResult))
  1274  
  1275  	//Test query implicit and explicit operator   ------------------------------------------------------------------
  1276  	queryString = `{"selector":{"color":"green","$or":[{"owner":"tom"},{"owner":"frank"}]}}`
  1277  
  1278  	queryResult, _, err = db.QueryDocuments(queryString)
  1279  	assert.NoError(t, err, "Error when attempting to execute a query")
  1280  
  1281  	//There should be 2 results for color="green" and (owner="jerry" or owner="frank")
  1282  	assert.Equal(t, 2, len(queryResult))
  1283  
  1284  	//Test query with a leading operator  -------------------------------------------------------------------------
  1285  	queryString = `{"selector":{"$and":[{"size":{"$gte":2}},{"size":{"$lte":5}}]}}`
  1286  
  1287  	queryResult, _, err = db.QueryDocuments(queryString)
  1288  	assert.NoError(t, err, "Error when attempting to execute a query")
  1289  
  1290  	//There should be 4 results for size >= 2 and size <= 5
  1291  	assert.Equal(t, 4, len(queryResult))
  1292  
  1293  	//Test query with leading and embedded operator  -------------------------------------------------------------
  1294  	queryString = `{"selector":{"$and":[{"size":{"$gte":3}},{"size":{"$lte":10}},{"$not":{"size":7}}]}}`
  1295  
  1296  	queryResult, _, err = db.QueryDocuments(queryString)
  1297  	assert.NoError(t, err, "Error when attempting to execute a query")
  1298  
  1299  	//There should be 7 results for size >= 3 and size <= 10 and not 7
  1300  	assert.Equal(t, 7, len(queryResult))
  1301  
  1302  	//Test query with leading operator and array of objects ----------------------------------------------------------
  1303  	queryString = `{"selector":{"$and":[{"size":{"$gte":2}},{"size":{"$lte":10}},{"$nor":[{"size":3},{"size":5},{"size":7}]}]}}`
  1304  
  1305  	queryResult, _, err = db.QueryDocuments(queryString)
  1306  	assert.NoError(t, err, "Error when attempting to execute a query")
  1307  
  1308  	//There should be 6 results for size >= 2 and size <= 10 and not 3,5 or 7
  1309  	assert.Equal(t, 6, len(queryResult))
  1310  
  1311  	//Test a range query ---------------------------------------------------------------------------------------------
  1312  	queryResult, _, err = db.ReadDocRange("marble02", "marble06", 10000)
  1313  	assert.NoError(t, err, "Error when attempting to execute a range query")
  1314  
  1315  	//There should be 4 results
  1316  	assert.Equal(t, 4, len(queryResult))
  1317  
  1318  	//Attachments retrieved should be correct
  1319  	assert.Equal(t, attachment2.AttachmentBytes, queryResult[0].Attachments[0].AttachmentBytes)
  1320  	assert.Equal(t, attachment3.AttachmentBytes, queryResult[1].Attachments[0].AttachmentBytes)
  1321  	assert.Equal(t, attachment4.AttachmentBytes, queryResult[2].Attachments[0].AttachmentBytes)
  1322  	assert.Equal(t, attachment5.AttachmentBytes, queryResult[3].Attachments[0].AttachmentBytes)
  1323  
  1324  	//Test query with for tom  -------------------------------------------------------------------
  1325  	queryString = `{"selector":{"owner":{"$eq":"tom"}}}`
  1326  
  1327  	queryResult, _, err = db.QueryDocuments(queryString)
  1328  	assert.NoError(t, err, "Error when attempting to execute a query")
  1329  
  1330  	//There should be 8 results for owner="tom"
  1331  	assert.Equal(t, 8, len(queryResult))
  1332  
  1333  	//Test query with for tom with limit  -------------------------------------------------------------------
  1334  	queryString = `{"selector":{"owner":{"$eq":"tom"}},"limit":2}`
  1335  
  1336  	queryResult, _, err = db.QueryDocuments(queryString)
  1337  	assert.NoError(t, err, "Error when attempting to execute a query")
  1338  
  1339  	//There should be 2 results for owner="tom" with a limit of 2
  1340  	assert.Equal(t, 2, len(queryResult))
  1341  
  1342  	//Create an index definition
  1343  	indexDefSize := `{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortDoc", "name":"indexSizeSortName","type":"json"}`
  1344  
  1345  	//Create the index
  1346  	_, err = db.CreateIndex(indexDefSize)
  1347  	assert.NoError(t, err, "Error thrown while creating an index")
  1348  
  1349  	//Delay for 100ms since CouchDB index list is updated async after index create/drop
  1350  	time.Sleep(100 * time.Millisecond)
  1351  
  1352  	//Test query with valid index  -------------------------------------------------------------------
  1353  	queryString = `{"selector":{"size":{"$gt":0}}, "use_index":["indexSizeSortDoc","indexSizeSortName"]}`
  1354  
  1355  	_, _, err = db.QueryDocuments(queryString)
  1356  	assert.NoError(t, err, "Error when attempting to execute a query with a valid index")
  1357  
  1358  }
  1359  
  1360  func testBatchBatchOperations(t *testing.T, maxRetries int) {
  1361  
  1362  	byteJSON01 := []byte(`{"_id":"marble01","asset_name":"marble01","color":"blue","size":"1","owner":"jerry"}`)
  1363  	byteJSON02 := []byte(`{"_id":"marble02","asset_name":"marble02","color":"red","size":"2","owner":"tom"}`)
  1364  	byteJSON03 := []byte(`{"_id":"marble03","asset_name":"marble03","color":"green","size":"3","owner":"jerry"}`)
  1365  	byteJSON04 := []byte(`{"_id":"marble04","asset_name":"marble04","color":"purple","size":"4","owner":"tom"}`)
  1366  	byteJSON05 := []byte(`{"_id":"marble05","asset_name":"marble05","color":"blue","size":"5","owner":"jerry"}`)
  1367  	byteJSON06 := []byte(`{"_id":"marble06#$&'()*+,/:;=?@[]","asset_name":"marble06#$&'()*+,/:;=?@[]","color":"blue","size":"6","owner":"jerry"}`)
  1368  
  1369  	attachment1 := &AttachmentInfo{}
  1370  	attachment1.AttachmentBytes = []byte(`marble01 - test attachment`)
  1371  	attachment1.ContentType = "application/octet-stream"
  1372  	attachment1.Name = "data"
  1373  	attachments1 := []*AttachmentInfo{}
  1374  	attachments1 = append(attachments1, attachment1)
  1375  
  1376  	attachment2 := &AttachmentInfo{}
  1377  	attachment2.AttachmentBytes = []byte(`marble02 - test attachment`)
  1378  	attachment2.ContentType = "application/octet-stream"
  1379  	attachment2.Name = "data"
  1380  	attachments2 := []*AttachmentInfo{}
  1381  	attachments2 = append(attachments2, attachment2)
  1382  
  1383  	attachment3 := &AttachmentInfo{}
  1384  	attachment3.AttachmentBytes = []byte(`marble03 - test attachment`)
  1385  	attachment3.ContentType = "application/octet-stream"
  1386  	attachment3.Name = "data"
  1387  	attachments3 := []*AttachmentInfo{}
  1388  	attachments3 = append(attachments3, attachment3)
  1389  
  1390  	attachment4 := &AttachmentInfo{}
  1391  	attachment4.AttachmentBytes = []byte(`marble04 - test attachment`)
  1392  	attachment4.ContentType = "application/octet-stream"
  1393  	attachment4.Name = "data"
  1394  	attachments4 := []*AttachmentInfo{}
  1395  	attachments4 = append(attachments4, attachment4)
  1396  
  1397  	attachment5 := &AttachmentInfo{}
  1398  	attachment5.AttachmentBytes = []byte(`marble05 - test attachment`)
  1399  	attachment5.ContentType = "application/octet-stream"
  1400  	attachment5.Name = "data"
  1401  	attachments5 := []*AttachmentInfo{}
  1402  	attachments5 = append(attachments5, attachment5)
  1403  
  1404  	attachment6 := &AttachmentInfo{}
  1405  	attachment6.AttachmentBytes = []byte(`marble06#$&'()*+,/:;=?@[] - test attachment`)
  1406  	attachment6.ContentType = "application/octet-stream"
  1407  	attachment6.Name = "data"
  1408  	attachments6 := []*AttachmentInfo{}
  1409  	attachments6 = append(attachments6, attachment6)
  1410  
  1411  	database := "testbatch"
  1412  	err := cleanup(database)
  1413  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
  1414  	defer cleanup(database)
  1415  
  1416  	//create a new instance and database object   --------------------------------------------------------
  1417  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
  1418  	assert.NoError(t, err, "Error when trying to create couch instance")
  1419  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
  1420  
  1421  	//create a new database
  1422  	errdb := db.CreateDatabaseIfNotExist()
  1423  	assert.NoError(t, errdb, "Error when trying to create database")
  1424  
  1425  	batchUpdateDocs := []*CouchDoc{}
  1426  
  1427  	value1 := &CouchDoc{JSONValue: byteJSON01, Attachments: attachments1}
  1428  	value2 := &CouchDoc{JSONValue: byteJSON02, Attachments: attachments2}
  1429  	value3 := &CouchDoc{JSONValue: byteJSON03, Attachments: attachments3}
  1430  	value4 := &CouchDoc{JSONValue: byteJSON04, Attachments: attachments4}
  1431  	value5 := &CouchDoc{JSONValue: byteJSON05, Attachments: attachments5}
  1432  	value6 := &CouchDoc{JSONValue: byteJSON06, Attachments: attachments6}
  1433  
  1434  	batchUpdateDocs = append(batchUpdateDocs, value1)
  1435  	batchUpdateDocs = append(batchUpdateDocs, value2)
  1436  	batchUpdateDocs = append(batchUpdateDocs, value3)
  1437  	batchUpdateDocs = append(batchUpdateDocs, value4)
  1438  	batchUpdateDocs = append(batchUpdateDocs, value5)
  1439  	batchUpdateDocs = append(batchUpdateDocs, value6)
  1440  
  1441  	batchUpdateResp, err := db.BatchUpdateDocuments(batchUpdateDocs)
  1442  	assert.NoError(t, err, "Error when attempting to update a batch of documents")
  1443  
  1444  	//check to make sure each batch update response was successful
  1445  	for _, updateDoc := range batchUpdateResp {
  1446  		assert.Equal(t, true, updateDoc.Ok)
  1447  	}
  1448  
  1449  	//----------------------------------------------
  1450  	//Test Retrieve JSON
  1451  	dbGetResp, _, geterr := db.ReadDoc("marble01")
  1452  	assert.NoError(t, geterr, "Error when attempting read a document")
  1453  
  1454  	assetResp := &Asset{}
  1455  	geterr = json.Unmarshal(dbGetResp.JSONValue, &assetResp)
  1456  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
  1457  	//Verify the owner retrieved matches
  1458  	assert.Equal(t, "jerry", assetResp.Owner)
  1459  
  1460  	//----------------------------------------------
  1461  	// Test Retrieve JSON using ID with URL special characters,
  1462  	// this will confirm that batch document IDs and URL IDs are consistent, even if they include special characters
  1463  	dbGetResp, _, geterr = db.ReadDoc("marble06#$&'()*+,/:;=?@[]")
  1464  	assert.NoError(t, geterr, "Error when attempting read a document")
  1465  
  1466  	assetResp = &Asset{}
  1467  	geterr = json.Unmarshal(dbGetResp.JSONValue, &assetResp)
  1468  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
  1469  	//Verify the owner retrieved matches
  1470  	assert.Equal(t, "jerry", assetResp.Owner)
  1471  
  1472  	//----------------------------------------------
  1473  	//Test retrieve binary
  1474  	dbGetResp, _, geterr = db.ReadDoc("marble03")
  1475  	assert.NoError(t, geterr, "Error when attempting read a document")
  1476  	//Retrieve the attachments
  1477  	attachments := dbGetResp.Attachments
  1478  	//Only one was saved, so take the first
  1479  	retrievedAttachment := attachments[0]
  1480  	//Verify the text matches
  1481  	assert.Equal(t, retrievedAttachment.AttachmentBytes, attachment3.AttachmentBytes)
  1482  	//----------------------------------------------
  1483  	//Test Bad Updates
  1484  	batchUpdateDocs = []*CouchDoc{}
  1485  	batchUpdateDocs = append(batchUpdateDocs, value1)
  1486  	batchUpdateDocs = append(batchUpdateDocs, value2)
  1487  	batchUpdateResp, err = db.BatchUpdateDocuments(batchUpdateDocs)
  1488  	assert.NoError(t, err, "Error when attempting to update a batch of documents")
  1489  	//No revision was provided, so these two updates should fail
  1490  	//Verify that the "Ok" field is returned as false
  1491  	for _, updateDoc := range batchUpdateResp {
  1492  		assert.Equal(t, false, updateDoc.Ok)
  1493  		assert.Equal(t, updateDocumentConflictError, updateDoc.Error)
  1494  		assert.Equal(t, updateDocumentConflictReason, updateDoc.Reason)
  1495  	}
  1496  
  1497  	//----------------------------------------------
  1498  	//Test Batch Retrieve Keys and Update
  1499  
  1500  	var keys []string
  1501  
  1502  	keys = append(keys, "marble01")
  1503  	keys = append(keys, "marble03")
  1504  
  1505  	batchRevs, err := db.BatchRetrieveDocumentMetadata(keys)
  1506  	assert.NoError(t, err, "Error when attempting retrieve revisions")
  1507  
  1508  	batchUpdateDocs = []*CouchDoc{}
  1509  
  1510  	//iterate through the revision docs
  1511  	for _, revdoc := range batchRevs {
  1512  		if revdoc.ID == "marble01" {
  1513  			//update the json with the rev and add to the batch
  1514  			marble01Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON01, false)
  1515  			batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble01Doc, Attachments: attachments1})
  1516  		}
  1517  
  1518  		if revdoc.ID == "marble03" {
  1519  			//update the json with the rev and add to the batch
  1520  			marble03Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON03, false)
  1521  			batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble03Doc, Attachments: attachments3})
  1522  		}
  1523  	}
  1524  
  1525  	//Update couchdb with the batch
  1526  	batchUpdateResp, err = db.BatchUpdateDocuments(batchUpdateDocs)
  1527  	assert.NoError(t, err, "Error when attempting to update a batch of documents")
  1528  	//check to make sure each batch update response was successful
  1529  	for _, updateDoc := range batchUpdateResp {
  1530  		assert.Equal(t, true, updateDoc.Ok)
  1531  	}
  1532  
  1533  	//----------------------------------------------
  1534  	//Test Batch Delete
  1535  
  1536  	keys = []string{}
  1537  
  1538  	keys = append(keys, "marble02")
  1539  	keys = append(keys, "marble04")
  1540  
  1541  	batchRevs, err = db.BatchRetrieveDocumentMetadata(keys)
  1542  	assert.NoError(t, err, "Error when attempting retrieve revisions")
  1543  
  1544  	batchUpdateDocs = []*CouchDoc{}
  1545  
  1546  	//iterate through the revision docs
  1547  	for _, revdoc := range batchRevs {
  1548  		if revdoc.ID == "marble02" {
  1549  			//update the json with the rev and add to the batch
  1550  			marble02Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON02, true)
  1551  			batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble02Doc, Attachments: attachments1})
  1552  		}
  1553  		if revdoc.ID == "marble04" {
  1554  			//update the json with the rev and add to the batch
  1555  			marble04Doc := addRevisionAndDeleteStatus(revdoc.Rev, byteJSON04, true)
  1556  			batchUpdateDocs = append(batchUpdateDocs, &CouchDoc{JSONValue: marble04Doc, Attachments: attachments3})
  1557  		}
  1558  	}
  1559  
  1560  	//Update couchdb with the batch
  1561  	batchUpdateResp, err = db.BatchUpdateDocuments(batchUpdateDocs)
  1562  	assert.NoError(t, err, "Error when attempting to update a batch of documents")
  1563  
  1564  	//check to make sure each batch update response was successful
  1565  	for _, updateDoc := range batchUpdateResp {
  1566  		assert.Equal(t, true, updateDoc.Ok)
  1567  	}
  1568  
  1569  	//Retrieve the test document
  1570  	dbGetResp, _, geterr = db.ReadDoc("marble02")
  1571  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
  1572  
  1573  	//assert the value was deleted
  1574  	assert.Nil(t, dbGetResp)
  1575  
  1576  	//Retrieve the test document
  1577  	dbGetResp, _, geterr = db.ReadDoc("marble04")
  1578  	assert.NoError(t, geterr, "Error when trying to retrieve a document")
  1579  
  1580  	//assert the value was deleted
  1581  	assert.Nil(t, dbGetResp)
  1582  
  1583  }
  1584  
  1585  //addRevisionAndDeleteStatus adds keys for version and chaincodeID to the JSON value
  1586  func addRevisionAndDeleteStatus(revision string, value []byte, deleted bool) []byte {
  1587  
  1588  	//create a version mapping
  1589  	jsonMap := make(map[string]interface{})
  1590  
  1591  	json.Unmarshal(value, &jsonMap)
  1592  
  1593  	//add the revision
  1594  	if revision != "" {
  1595  		jsonMap["_rev"] = revision
  1596  	}
  1597  
  1598  	//If this record is to be deleted, set the "_deleted" property to true
  1599  	if deleted {
  1600  		jsonMap["_deleted"] = true
  1601  	}
  1602  	//marshal the data to a byte array
  1603  	returnJSON, _ := json.Marshal(jsonMap)
  1604  
  1605  	return returnJSON
  1606  
  1607  }
  1608  
  1609  func TestDatabaseSecuritySettings(t *testing.T) {
  1610  
  1611  	database := "testdbsecuritysettings"
  1612  	err := cleanup(database)
  1613  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
  1614  	defer cleanup(database)
  1615  
  1616  	//create a new instance and database object   --------------------------------------------------------
  1617  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
  1618  	assert.NoError(t, err, "Error when trying to create couch instance")
  1619  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
  1620  
  1621  	//create a new database
  1622  	errdb := db.CreateDatabaseIfNotExist()
  1623  	assert.NoError(t, errdb, "Error when trying to create database")
  1624  
  1625  	//Create a database security object
  1626  	securityPermissions := &DatabaseSecurity{}
  1627  	securityPermissions.Admins.Names = append(securityPermissions.Admins.Names, "admin")
  1628  	securityPermissions.Members.Names = append(securityPermissions.Members.Names, "admin")
  1629  
  1630  	//Apply security
  1631  	err = db.ApplyDatabaseSecurity(securityPermissions)
  1632  	assert.NoError(t, err, "Error when trying to apply database security")
  1633  
  1634  	//Retrieve database security
  1635  	databaseSecurity, err := db.GetDatabaseSecurity()
  1636  	assert.NoError(t, err, "Error when retrieving database security")
  1637  
  1638  	//Verify retrieval of admins
  1639  	assert.Equal(t, "admin", databaseSecurity.Admins.Names[0])
  1640  
  1641  	//Verify retrieval of members
  1642  	assert.Equal(t, "admin", databaseSecurity.Members.Names[0])
  1643  
  1644  	//Create an empty database security object
  1645  	securityPermissions = &DatabaseSecurity{}
  1646  
  1647  	//Apply the security
  1648  	err = db.ApplyDatabaseSecurity(securityPermissions)
  1649  	assert.NoError(t, err, "Error when trying to apply database security")
  1650  
  1651  	//Retrieve database security
  1652  	databaseSecurity, err = db.GetDatabaseSecurity()
  1653  	assert.NoError(t, err, "Error when retrieving database security")
  1654  
  1655  	//Verify retrieval of admins, should be an empty array
  1656  	assert.Equal(t, 0, len(databaseSecurity.Admins.Names))
  1657  
  1658  	//Verify retrieval of members, should be an empty array
  1659  	assert.Equal(t, 0, len(databaseSecurity.Members.Names))
  1660  
  1661  }
  1662  
  1663  func TestURLWithSpecialCharacters(t *testing.T) {
  1664  
  1665  	database := "testdb+with+plus_sign"
  1666  	err := cleanup(database)
  1667  	assert.NoError(t, err, "Error when trying to cleanup  Error: %s", err)
  1668  	defer cleanup(database)
  1669  
  1670  	// parse a contructed URL
  1671  	finalURL, err := url.Parse("http://127.0.0.1:5984")
  1672  	assert.NoError(t, err, "error thrown while parsing couchdb url")
  1673  
  1674  	// test the constructCouchDBUrl function with multiple path elements
  1675  	couchdbURL := constructCouchDBUrl(finalURL, database, "_index", "designdoc", "json", "indexname")
  1676  	assert.Equal(t, "http://127.0.0.1:5984/testdb%2Bwith%2Bplus_sign/_index/designdoc/json/indexname", couchdbURL.String())
  1677  
  1678  	//create a new instance and database object   --------------------------------------------------------
  1679  	couchInstance, err := CreateCouchInstance(testConfig(), &disabled.Provider{})
  1680  	assert.NoError(t, err, "Error when trying to create couch instance")
  1681  	db := CouchDatabase{CouchInstance: couchInstance, DBName: database}
  1682  
  1683  	//create a new database
  1684  	errdb := db.CreateDatabaseIfNotExist()
  1685  	assert.NoError(t, errdb, "Error when trying to create database")
  1686  
  1687  	dbInfo, _, errInfo := db.GetDatabaseInfo()
  1688  	assert.NoError(t, errInfo, "Error when trying to get database info")
  1689  
  1690  	assert.Equal(t, database, dbInfo.DbName)
  1691  
  1692  }