github.com/ewagmig/fabric@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package statecouchdb
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/hyperledger/fabric/common/flogging"
    18  	"github.com/hyperledger/fabric/common/ledger/dataformat"
    19  	"github.com/hyperledger/fabric/common/ledger/testutil"
    20  	"github.com/hyperledger/fabric/common/metrics/disabled"
    21  	"github.com/hyperledger/fabric/core/common/ccprovider"
    22  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
    23  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/commontests"
    24  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
    25  	"github.com/hyperledger/fabric/core/ledger/util/couchdb"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  // couchDB backed versioned DB test environment.
    31  var testEnv = &testVDBEnv{}
    32  
    33  func TestMain(m *testing.M) {
    34  	flogging.ActivateSpec("statecouchdb=debug")
    35  
    36  	rc := m.Run()
    37  	testEnv.stopExternalResource()
    38  	os.Exit(rc)
    39  }
    40  
    41  func TestBasicRW(t *testing.T) {
    42  	env := testEnv
    43  	env.init(t, &statedb.Cache{})
    44  
    45  	defer env.cleanup()
    46  	commontests.TestBasicRW(t, env.DBProvider)
    47  
    48  }
    49  
    50  // TestGetStateFromCache checks cache hits, cache misses, and cache
    51  // updates during GetState call.
    52  func TestGetStateFromCache(t *testing.T) {
    53  	cache := statedb.NewCache(32, []string{"lscc"})
    54  	env := testEnv
    55  	env.init(t, cache)
    56  	defer env.cleanup()
    57  
    58  	chainID := "testgetstatefromcache"
    59  	db, err := env.DBProvider.GetDBHandle(chainID)
    60  	require.NoError(t, err)
    61  
    62  	// scenario 1: get state would receives a
    63  	// cache miss as the given key does not exist.
    64  	// As the key does not exist in the
    65  	// db also, get state call would not update
    66  	// the cache.
    67  	vv, err := db.GetState("ns", "key1")
    68  	require.NoError(t, err)
    69  	require.Nil(t, vv)
    70  	testDoesNotExistInCache(t, cache, chainID, "ns", "key1")
    71  
    72  	// scenario 2: get state would receive a cache hit.
    73  	// directly store an entry in the cache
    74  	cacheValue := &statedb.CacheValue{
    75  		Value:          []byte("value1"),
    76  		Metadata:       []byte("meta1"),
    77  		VersionBytes:   version.NewHeight(1, 1).ToBytes(),
    78  		AdditionalInfo: []byte("rev1"),
    79  	}
    80  	require.NoError(t, cache.PutState(chainID, "ns", "key1", cacheValue))
    81  
    82  	vv, err = db.GetState("ns", "key1")
    83  	expectedVV, err := constructVersionedValue(cacheValue)
    84  	require.NoError(t, err)
    85  	require.Equal(t, expectedVV, vv)
    86  
    87  	// scenario 3: get state would receives a
    88  	// cache miss as the given key does not present.
    89  	// The value associated with the key would be
    90  	// fetched from the database and the cache would
    91  	// be updated accordingly.
    92  
    93  	// store an entry in the db
    94  	batch := statedb.NewUpdateBatch()
    95  	vv2 := &statedb.VersionedValue{Value: []byte("value2"), Metadata: []byte("meta2"), Version: version.NewHeight(1, 2)}
    96  	batch.PutValAndMetadata("lscc", "key1", vv2.Value, vv2.Metadata, vv2.Version)
    97  	savePoint := version.NewHeight(1, 2)
    98  	db.ApplyUpdates(batch, savePoint)
    99  	// Note that the ApplyUpdates() updates only the existing entry in the cache. Currently, the
   100  	// cache has only ns, key1 but we are storing lscc, key1. Hence, no changes would happen in the cache.
   101  	testDoesNotExistInCache(t, cache, chainID, "lscc", "key1")
   102  
   103  	// calling GetState() would update the cache
   104  	vv, err = db.GetState("lscc", "key1")
   105  	require.NoError(t, err)
   106  	require.Equal(t, vv2, vv)
   107  
   108  	// cache should have been updated with lscc, key1
   109  	nsdb, err := db.(*VersionedDB).getNamespaceDBHandle("lscc")
   110  	require.NoError(t, err)
   111  	testExistInCache(t, nsdb, cache, chainID, "lscc", "key1", vv2)
   112  }
   113  
   114  // TestGetVersionFromCache checks cache hits, cache misses, and
   115  // updates during GetVersion call.
   116  func TestGetVersionFromCache(t *testing.T) {
   117  	cache := statedb.NewCache(32, []string{"lscc"})
   118  	env := testEnv
   119  	env.init(t, cache)
   120  	defer env.cleanup()
   121  
   122  	chainID := "testgetstatefromcache"
   123  	db, err := env.DBProvider.GetDBHandle(chainID)
   124  	require.NoError(t, err)
   125  
   126  	// scenario 1: get version would receives a
   127  	// cache miss as the given key does not exist.
   128  	// As the key does not exist in the
   129  	// db also, get version call would not update
   130  	// the cache.
   131  	ver, err := db.GetVersion("ns", "key1")
   132  	require.Nil(t, err)
   133  	require.Nil(t, ver)
   134  	testDoesNotExistInCache(t, cache, chainID, "ns", "key1")
   135  
   136  	// scenario 2: get version would receive a cache hit.
   137  	// directly store an entry in the cache
   138  	cacheValue := &statedb.CacheValue{
   139  		Value:          []byte("value1"),
   140  		Metadata:       []byte("meta1"),
   141  		VersionBytes:   version.NewHeight(1, 1).ToBytes(),
   142  		AdditionalInfo: []byte("rev1"),
   143  	}
   144  	require.NoError(t, cache.PutState(chainID, "ns", "key1", cacheValue))
   145  
   146  	ver, err = db.GetVersion("ns", "key1")
   147  	expectedVer, _, err := version.NewHeightFromBytes(cacheValue.VersionBytes)
   148  	require.NoError(t, err)
   149  	require.Equal(t, expectedVer, ver)
   150  
   151  	// scenario 3: get version would receives a
   152  	// cache miss as the given key does not present.
   153  	// The value associated with the key would be
   154  	// fetched from the database and the cache would
   155  	// be updated accordingly.
   156  
   157  	// store an entry in the db
   158  	batch := statedb.NewUpdateBatch()
   159  	vv2 := &statedb.VersionedValue{Value: []byte("value2"), Metadata: []byte("meta2"), Version: version.NewHeight(1, 2)}
   160  	batch.PutValAndMetadata("lscc", "key1", vv2.Value, vv2.Metadata, vv2.Version)
   161  	savePoint := version.NewHeight(1, 2)
   162  	db.ApplyUpdates(batch, savePoint)
   163  	// Note that the ApplyUpdates() updates only the existing entry in the cache. Currently, the
   164  	// cache has only ns, key1 but we are storing lscc, key1. Hence, no changes would happen in the cache.
   165  	testDoesNotExistInCache(t, cache, chainID, "lscc", "key1")
   166  
   167  	// calling GetVersion() would update the cache
   168  	ver, err = db.GetVersion("lscc", "key1")
   169  	require.NoError(t, err)
   170  	require.Equal(t, vv2.Version, ver)
   171  
   172  	// cache should have been updated with lscc, key1
   173  	nsdb, err := db.(*VersionedDB).getNamespaceDBHandle("lscc")
   174  	require.NoError(t, err)
   175  	testExistInCache(t, nsdb, cache, chainID, "lscc", "key1", vv2)
   176  }
   177  
   178  // TestGetMultipleStatesFromCache checks cache hits, cache misses,
   179  // and updates during GetStateMultipleKeys call.
   180  func TestGetMultipleStatesFromCache(t *testing.T) {
   181  	cache := statedb.NewCache(32, []string{"lscc"})
   182  	env := testEnv
   183  	env.init(t, cache)
   184  	defer env.cleanup()
   185  
   186  	chainID := "testgetmultiplestatesfromcache"
   187  	db, err := env.DBProvider.GetDBHandle(chainID)
   188  	require.NoError(t, err)
   189  
   190  	// scenario: given 5 keys, get multiple states find
   191  	// 2 keys in the cache. The remaining 2 keys would be fetched
   192  	// from the database and the cache would be updated. The last
   193  	// key is not present in the db and hence it won't be sent to
   194  	// the cache.
   195  
   196  	// key1 and key2 exist only in the cache
   197  	cacheValue1 := &statedb.CacheValue{
   198  		Value:          []byte("value1"),
   199  		Metadata:       []byte("meta1"),
   200  		VersionBytes:   version.NewHeight(1, 1).ToBytes(),
   201  		AdditionalInfo: []byte("rev1"),
   202  	}
   203  	require.NoError(t, cache.PutState(chainID, "ns", "key1", cacheValue1))
   204  	cacheValue2 := &statedb.CacheValue{
   205  		Value:          []byte("value2"),
   206  		Metadata:       []byte("meta2"),
   207  		VersionBytes:   version.NewHeight(1, 1).ToBytes(),
   208  		AdditionalInfo: []byte("rev2"),
   209  	}
   210  	require.NoError(t, cache.PutState(chainID, "ns", "key2", cacheValue2))
   211  
   212  	// key3 and key4 exist only in the db
   213  	batch := statedb.NewUpdateBatch()
   214  	vv3 := &statedb.VersionedValue{Value: []byte("value3"), Metadata: []byte("meta3"), Version: version.NewHeight(1, 1)}
   215  	batch.PutValAndMetadata("ns", "key3", vv3.Value, vv3.Metadata, vv3.Version)
   216  	vv4 := &statedb.VersionedValue{Value: []byte("value4"), Metadata: []byte("meta4"), Version: version.NewHeight(1, 1)}
   217  	batch.PutValAndMetadata("ns", "key4", vv4.Value, vv4.Metadata, vv4.Version)
   218  	savePoint := version.NewHeight(1, 2)
   219  	db.ApplyUpdates(batch, savePoint)
   220  
   221  	testDoesNotExistInCache(t, cache, chainID, "ns", "key3")
   222  	testDoesNotExistInCache(t, cache, chainID, "ns", "key4")
   223  
   224  	// key5 does not exist at all while key3 and key4 does not exist in the cache
   225  	vvalues, err := db.GetStateMultipleKeys("ns", []string{"key1", "key2", "key3", "key4", "key5"})
   226  	require.Nil(t, err)
   227  	vv1, err := constructVersionedValue(cacheValue1)
   228  	require.NoError(t, err)
   229  	vv2, err := constructVersionedValue(cacheValue2)
   230  	require.NoError(t, err)
   231  	require.Equal(t, []*statedb.VersionedValue{vv1, vv2, vv3, vv4, nil}, vvalues)
   232  
   233  	// cache should have been updated with key3 and key4
   234  	nsdb, err := db.(*VersionedDB).getNamespaceDBHandle("ns")
   235  	require.NoError(t, err)
   236  	testExistInCache(t, nsdb, cache, chainID, "ns", "key3", vv3)
   237  	testExistInCache(t, nsdb, cache, chainID, "ns", "key4", vv4)
   238  }
   239  
   240  // TestCacheUpdatesAfterCommit checks whether the cache is updated
   241  // after a commit of a update batch.
   242  func TestCacheUpdatesAfterCommit(t *testing.T) {
   243  	cache := statedb.NewCache(32, []string{"lscc"})
   244  	env := testEnv
   245  	env.init(t, cache)
   246  	defer env.cleanup()
   247  
   248  	chainID := "testcacheupdatesaftercommit"
   249  	db, err := env.DBProvider.GetDBHandle(chainID)
   250  	require.NoError(t, err)
   251  
   252  	// scenario: cache has 4 keys while the commit operation
   253  	// updates 2 of those keys, delete the remaining 2 keys, and
   254  	// adds a new key. At the end of the commit operation, only
   255  	// those 2 keys should be present with the recent value
   256  	// in the cache and the new key should not be present in the cache.
   257  
   258  	// store 4 keys in the db
   259  	batch := statedb.NewUpdateBatch()
   260  	vv1 := &statedb.VersionedValue{Value: []byte("value1"), Metadata: []byte("meta1"), Version: version.NewHeight(1, 2)}
   261  	vv2 := &statedb.VersionedValue{Value: []byte("value2"), Metadata: []byte("meta2"), Version: version.NewHeight(1, 2)}
   262  	vv3 := &statedb.VersionedValue{Value: []byte("value3"), Metadata: []byte("meta3"), Version: version.NewHeight(1, 2)}
   263  	vv4 := &statedb.VersionedValue{Value: []byte("value4"), Metadata: []byte("meta4"), Version: version.NewHeight(1, 2)}
   264  
   265  	batch.PutValAndMetadata("ns1", "key1", vv1.Value, vv1.Metadata, vv1.Version)
   266  	batch.PutValAndMetadata("ns1", "key2", vv2.Value, vv2.Metadata, vv2.Version)
   267  	batch.PutValAndMetadata("ns2", "key1", vv3.Value, vv3.Metadata, vv3.Version)
   268  	batch.PutValAndMetadata("ns2", "key2", vv4.Value, vv4.Metadata, vv4.Version)
   269  	savePoint := version.NewHeight(1, 5)
   270  	db.ApplyUpdates(batch, savePoint)
   271  
   272  	// key1, key2 in ns1 and ns2 would not be in cache
   273  	testDoesNotExistInCache(t, cache, chainID, "ns1", "key1")
   274  	testDoesNotExistInCache(t, cache, chainID, "ns1", "key2")
   275  	testDoesNotExistInCache(t, cache, chainID, "ns2", "key1")
   276  	testDoesNotExistInCache(t, cache, chainID, "ns2", "key2")
   277  
   278  	// add key1 and key2 from ns1 to the cache
   279  	_, err = db.GetState("ns1", "key1")
   280  	require.NoError(t, err)
   281  	_, err = db.GetState("ns1", "key2")
   282  	require.NoError(t, err)
   283  	// add key1 and key2 from ns2 to the cache
   284  	_, err = db.GetState("ns2", "key1")
   285  	require.NoError(t, err)
   286  	_, err = db.GetState("ns2", "key2")
   287  	require.NoError(t, err)
   288  
   289  	v, err := cache.GetState(chainID, "ns1", "key1")
   290  	require.NoError(t, err)
   291  	ns1key1rev := string(v.AdditionalInfo)
   292  
   293  	v, err = cache.GetState(chainID, "ns1", "key2")
   294  	require.NoError(t, err)
   295  	ns1key2rev := string(v.AdditionalInfo)
   296  
   297  	// update key1 and key2 in ns1. delete key1 and key2 in ns2. add a new key3 in ns2.
   298  	batch = statedb.NewUpdateBatch()
   299  	vv1Update := &statedb.VersionedValue{Value: []byte("new-value1"), Metadata: []byte("meta1"), Version: version.NewHeight(2, 2)}
   300  	vv2Update := &statedb.VersionedValue{Value: []byte("new-value2"), Metadata: []byte("meta2"), Version: version.NewHeight(2, 2)}
   301  	vv3Update := &statedb.VersionedValue{Version: version.NewHeight(2, 4)}
   302  	vv4Update := &statedb.VersionedValue{Version: version.NewHeight(2, 5)}
   303  	vv5 := &statedb.VersionedValue{Value: []byte("value5"), Metadata: []byte("meta5"), Version: version.NewHeight(1, 2)}
   304  
   305  	batch.PutValAndMetadata("ns1", "key1", vv1Update.Value, vv1Update.Metadata, vv1Update.Version)
   306  	batch.PutValAndMetadata("ns1", "key2", vv2Update.Value, vv2Update.Metadata, vv2Update.Version)
   307  	batch.Delete("ns2", "key1", vv3Update.Version)
   308  	batch.Delete("ns2", "key2", vv4Update.Version)
   309  	batch.PutValAndMetadata("ns2", "key3", vv5.Value, vv5.Metadata, vv5.Version)
   310  	savePoint = version.NewHeight(2, 5)
   311  	db.ApplyUpdates(batch, savePoint)
   312  
   313  	// cache should have only the update key1 and key2 in ns1
   314  	cacheValue, err := cache.GetState(chainID, "ns1", "key1")
   315  	require.NoError(t, err)
   316  	vv, err := constructVersionedValue(cacheValue)
   317  	require.NoError(t, err)
   318  	require.Equal(t, vv1Update, vv)
   319  	require.NotEqual(t, ns1key1rev, string(cacheValue.AdditionalInfo))
   320  
   321  	cacheValue, err = cache.GetState(chainID, "ns1", "key2")
   322  	require.NoError(t, err)
   323  	vv, err = constructVersionedValue(cacheValue)
   324  	require.NoError(t, err)
   325  	require.Equal(t, vv2Update, vv)
   326  	require.NotEqual(t, ns1key2rev, string(cacheValue.AdditionalInfo))
   327  
   328  	testDoesNotExistInCache(t, cache, chainID, "ns2", "key1")
   329  	testDoesNotExistInCache(t, cache, chainID, "ns2", "key2")
   330  	testDoesNotExistInCache(t, cache, chainID, "ns2", "key3")
   331  }
   332  
   333  func TestMultiDBBasicRW(t *testing.T) {
   334  	env := testEnv
   335  	env.init(t, &statedb.Cache{})
   336  	defer env.cleanup()
   337  
   338  	commontests.TestMultiDBBasicRW(t, env.DBProvider)
   339  
   340  }
   341  
   342  func TestDeletes(t *testing.T) {
   343  	env := testEnv
   344  	env.init(t, &statedb.Cache{})
   345  	defer env.cleanup()
   346  
   347  	commontests.TestDeletes(t, env.DBProvider)
   348  }
   349  
   350  func TestIterator(t *testing.T) {
   351  	env := testEnv
   352  	env.init(t, &statedb.Cache{})
   353  	defer env.cleanup()
   354  
   355  	commontests.TestIterator(t, env.DBProvider)
   356  }
   357  
   358  // The following tests are unique to couchdb, they are not used in leveldb
   359  //  query test
   360  func TestQuery(t *testing.T) {
   361  	env := testEnv
   362  	env.init(t, &statedb.Cache{})
   363  	defer env.cleanup()
   364  
   365  	commontests.TestQuery(t, env.DBProvider)
   366  }
   367  
   368  func TestGetStateMultipleKeys(t *testing.T) {
   369  	env := testEnv
   370  	env.init(t, &statedb.Cache{})
   371  	defer env.cleanup()
   372  
   373  	commontests.TestGetStateMultipleKeys(t, env.DBProvider)
   374  }
   375  
   376  func TestGetVersion(t *testing.T) {
   377  	env := testEnv
   378  	env.init(t, &statedb.Cache{})
   379  	defer env.cleanup()
   380  
   381  	commontests.TestGetVersion(t, env.DBProvider)
   382  }
   383  
   384  func TestSmallBatchSize(t *testing.T) {
   385  	env := testEnv
   386  	env.init(t, &statedb.Cache{})
   387  	defer env.cleanup()
   388  
   389  	commontests.TestSmallBatchSize(t, env.DBProvider)
   390  }
   391  
   392  func TestBatchRetry(t *testing.T) {
   393  	env := testEnv
   394  	env.init(t, &statedb.Cache{})
   395  	defer env.cleanup()
   396  
   397  	commontests.TestBatchWithIndividualRetry(t, env.DBProvider)
   398  }
   399  
   400  func TestValueAndMetadataWrites(t *testing.T) {
   401  	env := testEnv
   402  	env.init(t, &statedb.Cache{})
   403  	defer env.cleanup()
   404  
   405  	commontests.TestValueAndMetadataWrites(t, env.DBProvider)
   406  }
   407  
   408  func TestPaginatedRangeQuery(t *testing.T) {
   409  	env := testEnv
   410  	env.init(t, &statedb.Cache{})
   411  	defer env.cleanup()
   412  
   413  	commontests.TestPaginatedRangeQuery(t, env.DBProvider)
   414  }
   415  
   416  func TestRangeQuerySpecialCharacters(t *testing.T) {
   417  	env := testEnv
   418  	env.init(t, &statedb.Cache{})
   419  	defer env.cleanup()
   420  
   421  	commontests.TestRangeQuerySpecialCharacters(t, env.DBProvider)
   422  }
   423  
   424  // TestUtilityFunctions tests utility functions
   425  func TestUtilityFunctions(t *testing.T) {
   426  	env := testEnv
   427  	env.init(t, &statedb.Cache{})
   428  	defer env.cleanup()
   429  
   430  	db, err := env.DBProvider.GetDBHandle("testutilityfunctions")
   431  	assert.NoError(t, err)
   432  
   433  	// BytesKeySupported should be false for CouchDB
   434  	byteKeySupported := db.BytesKeySupported()
   435  	assert.False(t, byteKeySupported)
   436  
   437  	// ValidateKeyValue should return nil for a valid key and value
   438  	err = db.ValidateKeyValue("testKey", []byte("Some random bytes"))
   439  	assert.Nil(t, err)
   440  
   441  	// ValidateKeyValue should return an error for a key that is not a utf-8 valid string
   442  	err = db.ValidateKeyValue(string([]byte{0xff, 0xfe, 0xfd}), []byte("Some random bytes"))
   443  	assert.Error(t, err, "ValidateKey should have thrown an error for an invalid utf-8 string")
   444  
   445  	// ValidateKeyValue should return an error for a key that is an empty string
   446  	assert.EqualError(t, db.ValidateKeyValue("", []byte("validValue")),
   447  		"invalid key. Empty string is not supported as a key by couchdb")
   448  
   449  	reservedFields := []string{"~version", "_id", "_test"}
   450  
   451  	// ValidateKeyValue should return an error for a json value that contains one of the reserved fields
   452  	// at the top level
   453  	for _, reservedField := range reservedFields {
   454  		testVal := fmt.Sprintf(`{"%s":"dummyVal"}`, reservedField)
   455  		err = db.ValidateKeyValue("testKey", []byte(testVal))
   456  		assert.Error(t, err, fmt.Sprintf(
   457  			"ValidateKey should have thrown an error for a json value %s, as contains one of the reserved fields", testVal))
   458  	}
   459  
   460  	// ValidateKeyValue should not return an error for a json value that contains one of the reserved fields
   461  	// if not at the top level
   462  	for _, reservedField := range reservedFields {
   463  		testVal := fmt.Sprintf(`{"data.%s":"dummyVal"}`, reservedField)
   464  		err = db.ValidateKeyValue("testKey", []byte(testVal))
   465  		assert.NoError(t, err, fmt.Sprintf(
   466  			"ValidateKey should not have thrown an error the json value %s since the reserved field was not at the top level", testVal))
   467  	}
   468  
   469  	// ValidateKeyValue should return an error for a key that begins with an underscore
   470  	err = db.ValidateKeyValue("_testKey", []byte("testValue"))
   471  	assert.Error(t, err, "ValidateKey should have thrown an error for a key that begins with an underscore")
   472  
   473  }
   474  
   475  // TestInvalidJSONFields tests for invalid JSON fields
   476  func TestInvalidJSONFields(t *testing.T) {
   477  	env := testEnv
   478  	env.init(t, &statedb.Cache{})
   479  	defer env.cleanup()
   480  
   481  	db, err := env.DBProvider.GetDBHandle("testinvalidfields")
   482  	assert.NoError(t, err)
   483  
   484  	db.Open()
   485  	defer db.Close()
   486  
   487  	batch := statedb.NewUpdateBatch()
   488  	jsonValue1 := `{"_id":"key1","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}`
   489  	batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1))
   490  
   491  	savePoint := version.NewHeight(1, 2)
   492  	err = db.ApplyUpdates(batch, savePoint)
   493  	assert.Error(t, err, "Invalid field _id should have thrown an error")
   494  
   495  	batch = statedb.NewUpdateBatch()
   496  	jsonValue1 = `{"_rev":"rev1","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}`
   497  	batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1))
   498  
   499  	savePoint = version.NewHeight(1, 2)
   500  	err = db.ApplyUpdates(batch, savePoint)
   501  	assert.Error(t, err, "Invalid field _rev should have thrown an error")
   502  
   503  	batch = statedb.NewUpdateBatch()
   504  	jsonValue1 = `{"_deleted":"true","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}`
   505  	batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1))
   506  
   507  	savePoint = version.NewHeight(1, 2)
   508  	err = db.ApplyUpdates(batch, savePoint)
   509  	assert.Error(t, err, "Invalid field _deleted should have thrown an error")
   510  
   511  	batch = statedb.NewUpdateBatch()
   512  	jsonValue1 = `{"~version":"v1","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}`
   513  	batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1))
   514  
   515  	savePoint = version.NewHeight(1, 2)
   516  	err = db.ApplyUpdates(batch, savePoint)
   517  	assert.Error(t, err, "Invalid field ~version should have thrown an error")
   518  }
   519  
   520  func TestDebugFunctions(t *testing.T) {
   521  
   522  	//Test printCompositeKeys
   523  	// initialize a key list
   524  	loadKeys := []*statedb.CompositeKey{}
   525  	//create a composite key and add to the key list
   526  	compositeKey3 := statedb.CompositeKey{Namespace: "ns", Key: "key3"}
   527  	loadKeys = append(loadKeys, &compositeKey3)
   528  	compositeKey4 := statedb.CompositeKey{Namespace: "ns", Key: "key4"}
   529  	loadKeys = append(loadKeys, &compositeKey4)
   530  	assert.Equal(t, "[ns,key3],[ns,key4]", printCompositeKeys(loadKeys))
   531  
   532  }
   533  
   534  func TestHandleChaincodeDeploy(t *testing.T) {
   535  	env := testEnv
   536  	env.init(t, &statedb.Cache{})
   537  	defer env.cleanup()
   538  
   539  	db, err := env.DBProvider.GetDBHandle("testinit")
   540  	assert.NoError(t, err)
   541  	db.Open()
   542  	defer db.Close()
   543  	batch := statedb.NewUpdateBatch()
   544  
   545  	jsonValue1 := `{"asset_name": "marble1","color": "blue","size": 1,"owner": "tom"}`
   546  	batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1))
   547  	jsonValue2 := `{"asset_name": "marble2","color": "blue","size": 2,"owner": "jerry"}`
   548  	batch.Put("ns1", "key2", []byte(jsonValue2), version.NewHeight(1, 2))
   549  	jsonValue3 := `{"asset_name": "marble3","color": "blue","size": 3,"owner": "fred"}`
   550  	batch.Put("ns1", "key3", []byte(jsonValue3), version.NewHeight(1, 3))
   551  	jsonValue4 := `{"asset_name": "marble4","color": "blue","size": 4,"owner": "martha"}`
   552  	batch.Put("ns1", "key4", []byte(jsonValue4), version.NewHeight(1, 4))
   553  	jsonValue5 := `{"asset_name": "marble5","color": "blue","size": 5,"owner": "fred"}`
   554  	batch.Put("ns1", "key5", []byte(jsonValue5), version.NewHeight(1, 5))
   555  	jsonValue6 := `{"asset_name": "marble6","color": "blue","size": 6,"owner": "elaine"}`
   556  	batch.Put("ns1", "key6", []byte(jsonValue6), version.NewHeight(1, 6))
   557  	jsonValue7 := `{"asset_name": "marble7","color": "blue","size": 7,"owner": "fred"}`
   558  	batch.Put("ns1", "key7", []byte(jsonValue7), version.NewHeight(1, 7))
   559  	jsonValue8 := `{"asset_name": "marble8","color": "blue","size": 8,"owner": "elaine"}`
   560  	batch.Put("ns1", "key8", []byte(jsonValue8), version.NewHeight(1, 8))
   561  	jsonValue9 := `{"asset_name": "marble9","color": "green","size": 9,"owner": "fred"}`
   562  	batch.Put("ns1", "key9", []byte(jsonValue9), version.NewHeight(1, 9))
   563  	jsonValue10 := `{"asset_name": "marble10","color": "green","size": 10,"owner": "mary"}`
   564  	batch.Put("ns1", "key10", []byte(jsonValue10), version.NewHeight(1, 10))
   565  	jsonValue11 := `{"asset_name": "marble11","color": "cyan","size": 1000007,"owner": "joe"}`
   566  	batch.Put("ns1", "key11", []byte(jsonValue11), version.NewHeight(1, 11))
   567  
   568  	//add keys for a separate namespace
   569  	batch.Put("ns2", "key1", []byte(jsonValue1), version.NewHeight(1, 12))
   570  	batch.Put("ns2", "key2", []byte(jsonValue2), version.NewHeight(1, 13))
   571  	batch.Put("ns2", "key3", []byte(jsonValue3), version.NewHeight(1, 14))
   572  	batch.Put("ns2", "key4", []byte(jsonValue4), version.NewHeight(1, 15))
   573  	batch.Put("ns2", "key5", []byte(jsonValue5), version.NewHeight(1, 16))
   574  	batch.Put("ns2", "key6", []byte(jsonValue6), version.NewHeight(1, 17))
   575  	batch.Put("ns2", "key7", []byte(jsonValue7), version.NewHeight(1, 18))
   576  	batch.Put("ns2", "key8", []byte(jsonValue8), version.NewHeight(1, 19))
   577  	batch.Put("ns2", "key9", []byte(jsonValue9), version.NewHeight(1, 20))
   578  	batch.Put("ns2", "key10", []byte(jsonValue10), version.NewHeight(1, 21))
   579  
   580  	savePoint := version.NewHeight(2, 22)
   581  	db.ApplyUpdates(batch, savePoint)
   582  
   583  	//Create a tar file for test with 4 index definitions and 2 side dbs
   584  	dbArtifactsTarBytes := testutil.CreateTarBytesForTest(
   585  		[]*testutil.TarFileEntry{
   586  			{Name: "META-INF/statedb/couchdb/indexes/indexColorSortName.json", Body: `{"index":{"fields":[{"color":"desc"}]},"ddoc":"indexColorSortName","name":"indexColorSortName","type":"json"}`},
   587  			{Name: "META-INF/statedb/couchdb/indexes/indexSizeSortName.json", Body: `{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`},
   588  			{Name: "META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexCollMarbles.json", Body: `{"index":{"fields":["docType","owner"]},"ddoc":"indexCollectionMarbles", "name":"indexCollectionMarbles","type":"json"}`},
   589  			{Name: "META-INF/statedb/couchdb/collections/collectionMarblesPrivateDetails/indexes/indexCollPrivDetails.json", Body: `{"index":{"fields":["docType","price"]},"ddoc":"indexPrivateDetails", "name":"indexPrivateDetails","type":"json"}`},
   590  		},
   591  	)
   592  
   593  	//Create a query
   594  	queryString := `{"selector":{"owner":"fred"}}`
   595  
   596  	_, err = db.ExecuteQuery("ns1", queryString)
   597  	assert.NoError(t, err)
   598  
   599  	//Create a query with a sort
   600  	queryString = `{"selector":{"owner":"fred"}, "sort": [{"size": "desc"}]}`
   601  
   602  	_, err = db.ExecuteQuery("ns1", queryString)
   603  	assert.Error(t, err, "Error should have been thrown for a missing index")
   604  
   605  	indexCapable, ok := db.(statedb.IndexCapable)
   606  
   607  	if !ok {
   608  		t.Fatalf("Couchdb state impl is expected to implement interface `statedb.IndexCapable`")
   609  	}
   610  
   611  	fileEntries, errExtract := ccprovider.ExtractFileEntries(dbArtifactsTarBytes, "couchdb")
   612  	assert.NoError(t, errExtract)
   613  
   614  	indexCapable.ProcessIndexesForChaincodeDeploy("ns1", fileEntries["META-INF/statedb/couchdb/indexes"])
   615  	//Sleep to allow time for index creation
   616  	time.Sleep(100 * time.Millisecond)
   617  	//Create a query with a sort
   618  	queryString = `{"selector":{"owner":"fred"}, "sort": [{"size": "desc"}]}`
   619  
   620  	//Query should complete without error
   621  	_, err = db.ExecuteQuery("ns1", queryString)
   622  	assert.NoError(t, err)
   623  
   624  	//Query namespace "ns2", index is only created in "ns1".  This should return an error.
   625  	_, err = db.ExecuteQuery("ns2", queryString)
   626  	assert.Error(t, err, "Error should have been thrown for a missing index")
   627  
   628  }
   629  
   630  func TestTryCastingToJSON(t *testing.T) {
   631  	sampleJSON := []byte(`{"a":"A", "b":"B"}`)
   632  	isJSON, jsonVal := tryCastingToJSON(sampleJSON)
   633  	assert.True(t, isJSON)
   634  	assert.Equal(t, "A", jsonVal["a"])
   635  	assert.Equal(t, "B", jsonVal["b"])
   636  
   637  	sampleNonJSON := []byte(`This is not a json`)
   638  	isJSON, jsonVal = tryCastingToJSON(sampleNonJSON)
   639  	assert.False(t, isJSON)
   640  }
   641  
   642  func TestHandleChaincodeDeployErroneousIndexFile(t *testing.T) {
   643  	channelName := "ch1"
   644  	env := testEnv
   645  	env.init(t, &statedb.Cache{})
   646  	defer env.cleanup()
   647  	db, err := env.DBProvider.GetDBHandle(channelName)
   648  	assert.NoError(t, err)
   649  	db.Open()
   650  	defer db.Close()
   651  
   652  	batch := statedb.NewUpdateBatch()
   653  	batch.Put("ns1", "key1", []byte(`{"asset_name": "marble1","color": "blue","size": 1,"owner": "tom"}`), version.NewHeight(1, 1))
   654  	batch.Put("ns1", "key2", []byte(`{"asset_name": "marble2","color": "blue","size": 2,"owner": "jerry"}`), version.NewHeight(1, 2))
   655  
   656  	// Create a tar file for test with 2 index definitions - one of them being errorneous
   657  	badSyntaxFileContent := `{"index":{"fields": This is a bad json}`
   658  	dbArtifactsTarBytes := testutil.CreateTarBytesForTest(
   659  		[]*testutil.TarFileEntry{
   660  			{Name: "META-INF/statedb/couchdb/indexes/indexSizeSortName.json", Body: `{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`},
   661  			{Name: "META-INF/statedb/couchdb/indexes/badSyntax.json", Body: badSyntaxFileContent},
   662  		},
   663  	)
   664  
   665  	indexCapable, ok := db.(statedb.IndexCapable)
   666  	if !ok {
   667  		t.Fatalf("Couchdb state impl is expected to implement interface `statedb.IndexCapable`")
   668  	}
   669  
   670  	fileEntries, errExtract := ccprovider.ExtractFileEntries(dbArtifactsTarBytes, "couchdb")
   671  	assert.NoError(t, errExtract)
   672  
   673  	indexCapable.ProcessIndexesForChaincodeDeploy("ns1", fileEntries["META-INF/statedb/couchdb/indexes"])
   674  
   675  	//Sleep to allow time for index creation
   676  	time.Sleep(100 * time.Millisecond)
   677  	//Query should complete without error
   678  	_, err = db.ExecuteQuery("ns1", `{"selector":{"owner":"fred"}, "sort": [{"size": "desc"}]}`)
   679  	assert.NoError(t, err)
   680  }
   681  
   682  func TestIsBulkOptimizable(t *testing.T) {
   683  	var db statedb.VersionedDB = &VersionedDB{}
   684  	_, ok := db.(statedb.BulkOptimizable)
   685  	if !ok {
   686  		t.Fatal("state couch db is expected to implement interface statedb.BulkOptimizable")
   687  	}
   688  }
   689  
   690  func printCompositeKeys(keys []*statedb.CompositeKey) string {
   691  
   692  	compositeKeyString := []string{}
   693  	for _, key := range keys {
   694  		compositeKeyString = append(compositeKeyString, "["+key.Namespace+","+key.Key+"]")
   695  	}
   696  	return strings.Join(compositeKeyString, ",")
   697  }
   698  
   699  // TestPaginatedQuery tests queries with pagination
   700  func TestPaginatedQuery(t *testing.T) {
   701  	env := testEnv
   702  	env.init(t, &statedb.Cache{})
   703  	defer env.cleanup()
   704  
   705  	db, err := env.DBProvider.GetDBHandle("testpaginatedquery")
   706  	assert.NoError(t, err)
   707  	db.Open()
   708  	defer db.Close()
   709  
   710  	batch := statedb.NewUpdateBatch()
   711  	jsonValue1 := `{"asset_name": "marble1","color": "blue","size": 1,"owner": "tom"}`
   712  	batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1))
   713  	jsonValue2 := `{"asset_name": "marble2","color": "red","size": 2,"owner": "jerry"}`
   714  	batch.Put("ns1", "key2", []byte(jsonValue2), version.NewHeight(1, 2))
   715  	jsonValue3 := `{"asset_name": "marble3","color": "red","size": 3,"owner": "fred"}`
   716  	batch.Put("ns1", "key3", []byte(jsonValue3), version.NewHeight(1, 3))
   717  	jsonValue4 := `{"asset_name": "marble4","color": "red","size": 4,"owner": "martha"}`
   718  	batch.Put("ns1", "key4", []byte(jsonValue4), version.NewHeight(1, 4))
   719  	jsonValue5 := `{"asset_name": "marble5","color": "blue","size": 5,"owner": "fred"}`
   720  	batch.Put("ns1", "key5", []byte(jsonValue5), version.NewHeight(1, 5))
   721  	jsonValue6 := `{"asset_name": "marble6","color": "red","size": 6,"owner": "elaine"}`
   722  	batch.Put("ns1", "key6", []byte(jsonValue6), version.NewHeight(1, 6))
   723  	jsonValue7 := `{"asset_name": "marble7","color": "blue","size": 7,"owner": "fred"}`
   724  	batch.Put("ns1", "key7", []byte(jsonValue7), version.NewHeight(1, 7))
   725  	jsonValue8 := `{"asset_name": "marble8","color": "red","size": 8,"owner": "elaine"}`
   726  	batch.Put("ns1", "key8", []byte(jsonValue8), version.NewHeight(1, 8))
   727  	jsonValue9 := `{"asset_name": "marble9","color": "green","size": 9,"owner": "fred"}`
   728  	batch.Put("ns1", "key9", []byte(jsonValue9), version.NewHeight(1, 9))
   729  	jsonValue10 := `{"asset_name": "marble10","color": "green","size": 10,"owner": "mary"}`
   730  	batch.Put("ns1", "key10", []byte(jsonValue10), version.NewHeight(1, 10))
   731  
   732  	jsonValue11 := `{"asset_name": "marble11","color": "cyan","size": 11,"owner": "joe"}`
   733  	batch.Put("ns1", "key11", []byte(jsonValue11), version.NewHeight(1, 11))
   734  	jsonValue12 := `{"asset_name": "marble12","color": "red","size": 12,"owner": "martha"}`
   735  	batch.Put("ns1", "key12", []byte(jsonValue12), version.NewHeight(1, 4))
   736  	jsonValue13 := `{"asset_name": "marble13","color": "red","size": 13,"owner": "james"}`
   737  	batch.Put("ns1", "key13", []byte(jsonValue13), version.NewHeight(1, 4))
   738  	jsonValue14 := `{"asset_name": "marble14","color": "red","size": 14,"owner": "fred"}`
   739  	batch.Put("ns1", "key14", []byte(jsonValue14), version.NewHeight(1, 4))
   740  	jsonValue15 := `{"asset_name": "marble15","color": "red","size": 15,"owner": "mary"}`
   741  	batch.Put("ns1", "key15", []byte(jsonValue15), version.NewHeight(1, 4))
   742  	jsonValue16 := `{"asset_name": "marble16","color": "red","size": 16,"owner": "robert"}`
   743  	batch.Put("ns1", "key16", []byte(jsonValue16), version.NewHeight(1, 4))
   744  	jsonValue17 := `{"asset_name": "marble17","color": "red","size": 17,"owner": "alan"}`
   745  	batch.Put("ns1", "key17", []byte(jsonValue17), version.NewHeight(1, 4))
   746  	jsonValue18 := `{"asset_name": "marble18","color": "red","size": 18,"owner": "elaine"}`
   747  	batch.Put("ns1", "key18", []byte(jsonValue18), version.NewHeight(1, 4))
   748  	jsonValue19 := `{"asset_name": "marble19","color": "red","size": 19,"owner": "alan"}`
   749  	batch.Put("ns1", "key19", []byte(jsonValue19), version.NewHeight(1, 4))
   750  	jsonValue20 := `{"asset_name": "marble20","color": "red","size": 20,"owner": "elaine"}`
   751  	batch.Put("ns1", "key20", []byte(jsonValue20), version.NewHeight(1, 4))
   752  
   753  	jsonValue21 := `{"asset_name": "marble21","color": "cyan","size": 21,"owner": "joe"}`
   754  	batch.Put("ns1", "key21", []byte(jsonValue21), version.NewHeight(1, 11))
   755  	jsonValue22 := `{"asset_name": "marble22","color": "red","size": 22,"owner": "martha"}`
   756  	batch.Put("ns1", "key22", []byte(jsonValue22), version.NewHeight(1, 4))
   757  	jsonValue23 := `{"asset_name": "marble23","color": "blue","size": 23,"owner": "james"}`
   758  	batch.Put("ns1", "key23", []byte(jsonValue23), version.NewHeight(1, 4))
   759  	jsonValue24 := `{"asset_name": "marble24","color": "red","size": 24,"owner": "fred"}`
   760  	batch.Put("ns1", "key24", []byte(jsonValue24), version.NewHeight(1, 4))
   761  	jsonValue25 := `{"asset_name": "marble25","color": "red","size": 25,"owner": "mary"}`
   762  	batch.Put("ns1", "key25", []byte(jsonValue25), version.NewHeight(1, 4))
   763  	jsonValue26 := `{"asset_name": "marble26","color": "red","size": 26,"owner": "robert"}`
   764  	batch.Put("ns1", "key26", []byte(jsonValue26), version.NewHeight(1, 4))
   765  	jsonValue27 := `{"asset_name": "marble27","color": "green","size": 27,"owner": "alan"}`
   766  	batch.Put("ns1", "key27", []byte(jsonValue27), version.NewHeight(1, 4))
   767  	jsonValue28 := `{"asset_name": "marble28","color": "red","size": 28,"owner": "elaine"}`
   768  	batch.Put("ns1", "key28", []byte(jsonValue28), version.NewHeight(1, 4))
   769  	jsonValue29 := `{"asset_name": "marble29","color": "red","size": 29,"owner": "alan"}`
   770  	batch.Put("ns1", "key29", []byte(jsonValue29), version.NewHeight(1, 4))
   771  	jsonValue30 := `{"asset_name": "marble30","color": "red","size": 30,"owner": "elaine"}`
   772  	batch.Put("ns1", "key30", []byte(jsonValue30), version.NewHeight(1, 4))
   773  
   774  	jsonValue31 := `{"asset_name": "marble31","color": "cyan","size": 31,"owner": "joe"}`
   775  	batch.Put("ns1", "key31", []byte(jsonValue31), version.NewHeight(1, 11))
   776  	jsonValue32 := `{"asset_name": "marble32","color": "red","size": 32,"owner": "martha"}`
   777  	batch.Put("ns1", "key32", []byte(jsonValue32), version.NewHeight(1, 4))
   778  	jsonValue33 := `{"asset_name": "marble33","color": "red","size": 33,"owner": "james"}`
   779  	batch.Put("ns1", "key33", []byte(jsonValue33), version.NewHeight(1, 4))
   780  	jsonValue34 := `{"asset_name": "marble34","color": "red","size": 34,"owner": "fred"}`
   781  	batch.Put("ns1", "key34", []byte(jsonValue34), version.NewHeight(1, 4))
   782  	jsonValue35 := `{"asset_name": "marble35","color": "red","size": 35,"owner": "mary"}`
   783  	batch.Put("ns1", "key35", []byte(jsonValue35), version.NewHeight(1, 4))
   784  	jsonValue36 := `{"asset_name": "marble36","color": "orange","size": 36,"owner": "robert"}`
   785  	batch.Put("ns1", "key36", []byte(jsonValue36), version.NewHeight(1, 4))
   786  	jsonValue37 := `{"asset_name": "marble37","color": "red","size": 37,"owner": "alan"}`
   787  	batch.Put("ns1", "key37", []byte(jsonValue37), version.NewHeight(1, 4))
   788  	jsonValue38 := `{"asset_name": "marble38","color": "yellow","size": 38,"owner": "elaine"}`
   789  	batch.Put("ns1", "key38", []byte(jsonValue38), version.NewHeight(1, 4))
   790  	jsonValue39 := `{"asset_name": "marble39","color": "red","size": 39,"owner": "alan"}`
   791  	batch.Put("ns1", "key39", []byte(jsonValue39), version.NewHeight(1, 4))
   792  	jsonValue40 := `{"asset_name": "marble40","color": "red","size": 40,"owner": "elaine"}`
   793  	batch.Put("ns1", "key40", []byte(jsonValue40), version.NewHeight(1, 4))
   794  
   795  	savePoint := version.NewHeight(2, 22)
   796  	db.ApplyUpdates(batch, savePoint)
   797  
   798  	// Create a tar file for test with an index for size
   799  	dbArtifactsTarBytes := testutil.CreateTarBytesForTest(
   800  		[]*testutil.TarFileEntry{
   801  			{Name: "META-INF/statedb/couchdb/indexes/indexSizeSortName.json", Body: `{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`},
   802  		},
   803  	)
   804  
   805  	// Create a query
   806  	queryString := `{"selector":{"color":"red"}}`
   807  
   808  	_, err = db.ExecuteQuery("ns1", queryString)
   809  	assert.NoError(t, err)
   810  
   811  	// Create a query with a sort
   812  	queryString = `{"selector":{"color":"red"}, "sort": [{"size": "asc"}]}`
   813  
   814  	indexCapable, ok := db.(statedb.IndexCapable)
   815  
   816  	if !ok {
   817  		t.Fatalf("Couchdb state impl is expected to implement interface `statedb.IndexCapable`")
   818  	}
   819  
   820  	fileEntries, errExtract := ccprovider.ExtractFileEntries(dbArtifactsTarBytes, "couchdb")
   821  	assert.NoError(t, errExtract)
   822  
   823  	indexCapable.ProcessIndexesForChaincodeDeploy("ns1", fileEntries["META-INF/statedb/couchdb/indexes"])
   824  	// Sleep to allow time for index creation
   825  	time.Sleep(100 * time.Millisecond)
   826  	// Create a query with a sort
   827  	queryString = `{"selector":{"color":"red"}, "sort": [{"size": "asc"}]}`
   828  
   829  	// Query should complete without error
   830  	_, err = db.ExecuteQuery("ns1", queryString)
   831  	assert.NoError(t, err)
   832  
   833  	// Test explicit paging
   834  	// Execute 3 page queries, there are 28 records with color red, use page size 10
   835  	returnKeys := []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", "key16"}
   836  	bookmark, err := executeQuery(t, db, "ns1", queryString, "", int32(10), returnKeys)
   837  	assert.NoError(t, err)
   838  	returnKeys = []string{"key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29"}
   839  	bookmark, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys)
   840  	assert.NoError(t, err)
   841  
   842  	returnKeys = []string{"key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40"}
   843  	_, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys)
   844  	assert.NoError(t, err)
   845  
   846  	// Test explicit paging
   847  	// Increase pagesize to 50,  should return all values
   848  	returnKeys = []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15",
   849  		"key16", "key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29",
   850  		"key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40"}
   851  	_, err = executeQuery(t, db, "ns1", queryString, "", int32(50), returnKeys)
   852  	assert.NoError(t, err)
   853  
   854  	// Test explicit paging
   855  	// Pagesize is 10, so all 28 records should be return in 3 "pages"
   856  	returnKeys = []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", "key16"}
   857  	bookmark, err = executeQuery(t, db, "ns1", queryString, "", int32(10), returnKeys)
   858  	assert.NoError(t, err)
   859  	returnKeys = []string{"key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29"}
   860  	bookmark, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys)
   861  	assert.NoError(t, err)
   862  	returnKeys = []string{"key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40"}
   863  	_, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys)
   864  	assert.NoError(t, err)
   865  
   866  	// Test implicit paging
   867  	returnKeys = []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15",
   868  		"key16", "key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29",
   869  		"key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40"}
   870  	_, err = executeQuery(t, db, "ns1", queryString, "", int32(0), returnKeys)
   871  	assert.NoError(t, err)
   872  
   873  	// pagesize greater than querysize will execute with implicit paging
   874  	returnKeys = []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", "key16"}
   875  	_, err = executeQuery(t, db, "ns1", queryString, "", int32(10), returnKeys)
   876  	assert.NoError(t, err)
   877  }
   878  
   879  func executeQuery(t *testing.T, db statedb.VersionedDB, namespace, query, bookmark string, limit int32, returnKeys []string) (string, error) {
   880  
   881  	var itr statedb.ResultsIterator
   882  	var err error
   883  
   884  	if limit == int32(0) && bookmark == "" {
   885  		itr, err = db.ExecuteQuery(namespace, query)
   886  		if err != nil {
   887  			return "", err
   888  		}
   889  	} else {
   890  		queryOptions := make(map[string]interface{})
   891  		if bookmark != "" {
   892  			queryOptions["bookmark"] = bookmark
   893  		}
   894  		if limit != 0 {
   895  			queryOptions["limit"] = limit
   896  		}
   897  
   898  		itr, err = db.ExecuteQueryWithMetadata(namespace, query, queryOptions)
   899  		if err != nil {
   900  			return "", err
   901  		}
   902  	}
   903  
   904  	// Verify the keys returned
   905  	commontests.TestItrWithoutClose(t, itr, returnKeys)
   906  
   907  	returnBookmark := ""
   908  	if queryResultItr, ok := itr.(statedb.QueryResultsIterator); ok {
   909  		returnBookmark = queryResultItr.GetBookmarkAndClose()
   910  	}
   911  
   912  	return returnBookmark, nil
   913  }
   914  
   915  // TestPaginatedQueryValidation tests queries with pagination
   916  func TestPaginatedQueryValidation(t *testing.T) {
   917  
   918  	queryOptions := make(map[string]interface{})
   919  	queryOptions["bookmark"] = "Test1"
   920  	queryOptions["limit"] = int32(10)
   921  
   922  	err := validateQueryMetadata(queryOptions)
   923  	assert.NoError(t, err, "An error was thrown for a valid options")
   924  	queryOptions = make(map[string]interface{})
   925  	queryOptions["bookmark"] = "Test1"
   926  	queryOptions["limit"] = float64(10.2)
   927  
   928  	err = validateQueryMetadata(queryOptions)
   929  	assert.Error(t, err, "An should have been thrown for an invalid options")
   930  
   931  	queryOptions = make(map[string]interface{})
   932  	queryOptions["bookmark"] = "Test1"
   933  	queryOptions["limit"] = "10"
   934  
   935  	err = validateQueryMetadata(queryOptions)
   936  	assert.Error(t, err, "An should have been thrown for an invalid options")
   937  
   938  	queryOptions = make(map[string]interface{})
   939  	queryOptions["bookmark"] = int32(10)
   940  	queryOptions["limit"] = "10"
   941  
   942  	err = validateQueryMetadata(queryOptions)
   943  	assert.Error(t, err, "An should have been thrown for an invalid options")
   944  
   945  	queryOptions = make(map[string]interface{})
   946  	queryOptions["bookmark"] = "Test1"
   947  	queryOptions["limit1"] = int32(10)
   948  
   949  	err = validateQueryMetadata(queryOptions)
   950  	assert.Error(t, err, "An should have been thrown for an invalid options")
   951  
   952  	queryOptions = make(map[string]interface{})
   953  	queryOptions["bookmark1"] = "Test1"
   954  	queryOptions["limit1"] = int32(10)
   955  
   956  	err = validateQueryMetadata(queryOptions)
   957  	assert.Error(t, err, "An should have been thrown for an invalid options")
   958  }
   959  
   960  func TestApplyUpdatesWithNilHeight(t *testing.T) {
   961  	env := testEnv
   962  	env.init(t, &statedb.Cache{})
   963  	defer env.cleanup()
   964  	commontests.TestApplyUpdatesWithNilHeight(t, env.DBProvider)
   965  }
   966  
   967  func TestRangeScanWithCouchInternalDocsPresent(t *testing.T) {
   968  	env := testEnv
   969  	env.init(t, &statedb.Cache{})
   970  	defer env.cleanup()
   971  	db, err := env.DBProvider.GetDBHandle("testrangescanfiltercouchinternaldocs")
   972  	assert.NoError(t, err)
   973  	couchDatabse, err := db.(*VersionedDB).getNamespaceDBHandle("ns")
   974  	assert.NoError(t, err)
   975  	db.Open()
   976  	defer db.Close()
   977  	_, err = couchDatabse.CreateIndex(`{
   978  		"index" : {"fields" : ["asset_name"]},
   979  			"ddoc" : "indexAssetName",
   980  			"name" : "indexAssetName",
   981  			"type" : "json"
   982  		}`)
   983  	assert.NoError(t, err)
   984  
   985  	_, err = couchDatabse.CreateIndex(`{
   986  		"index" : {"fields" : ["assetValue"]},
   987  			"ddoc" : "indexAssetValue",
   988  			"name" : "indexAssetValue",
   989  			"type" : "json"
   990  		}`)
   991  	assert.NoError(t, err)
   992  
   993  	batch := statedb.NewUpdateBatch()
   994  	for i := 1; i <= 3; i++ {
   995  		keySmallerThanDesignDoc := fmt.Sprintf("Key-%d", i)
   996  		keyGreaterThanDesignDoc := fmt.Sprintf("key-%d", i)
   997  		jsonValue := fmt.Sprintf(`{"asset_name": "marble-%d"}`, i)
   998  		batch.Put("ns", keySmallerThanDesignDoc, []byte(jsonValue), version.NewHeight(1, uint64(i)))
   999  		batch.Put("ns", keyGreaterThanDesignDoc, []byte(jsonValue), version.NewHeight(1, uint64(i)))
  1000  	}
  1001  	db.ApplyUpdates(batch, version.NewHeight(2, 2))
  1002  	assert.NoError(t, err)
  1003  
  1004  	// The Keys in db are in this order
  1005  	// Key-1, Key-2, Key-3,_design/indexAssetNam, _design/indexAssetValue, key-1, key-2, key-3
  1006  	// query different ranges and verify results
  1007  	s, err := newQueryScanner("ns", couchDatabse, "", 3, 3, "", "", "")
  1008  	assert.NoError(t, err)
  1009  	assertQueryResults(t, s.resultsInfo.results, []string{"Key-1", "Key-2", "Key-3"})
  1010  	assert.Equal(t, "key-1", s.queryDefinition.startKey)
  1011  
  1012  	s, err = newQueryScanner("ns", couchDatabse, "", 4, 4, "", "", "")
  1013  	assert.NoError(t, err)
  1014  	assertQueryResults(t, s.resultsInfo.results, []string{"Key-1", "Key-2", "Key-3", "key-1"})
  1015  	assert.Equal(t, "key-2", s.queryDefinition.startKey)
  1016  
  1017  	s, err = newQueryScanner("ns", couchDatabse, "", 2, 2, "", "", "")
  1018  	assert.NoError(t, err)
  1019  	assertQueryResults(t, s.resultsInfo.results, []string{"Key-1", "Key-2"})
  1020  	assert.Equal(t, "Key-3", s.queryDefinition.startKey)
  1021  	s.getNextStateRangeScanResults()
  1022  	assertQueryResults(t, s.resultsInfo.results, []string{"Key-3", "key-1"})
  1023  	assert.Equal(t, "key-2", s.queryDefinition.startKey)
  1024  
  1025  	s, err = newQueryScanner("ns", couchDatabse, "", 2, 2, "", "_", "")
  1026  	assert.NoError(t, err)
  1027  	assertQueryResults(t, s.resultsInfo.results, []string{"key-1", "key-2"})
  1028  	assert.Equal(t, "key-3", s.queryDefinition.startKey)
  1029  }
  1030  
  1031  func assertQueryResults(t *testing.T, results []*couchdb.QueryResult, expectedIds []string) {
  1032  	var actualIds []string
  1033  	for _, res := range results {
  1034  		actualIds = append(actualIds, res.ID)
  1035  	}
  1036  	assert.Equal(t, expectedIds, actualIds)
  1037  }
  1038  
  1039  func TestFormatCheck(t *testing.T) {
  1040  	testCases := []struct {
  1041  		dataFormat     string                         // precondition
  1042  		dataExists     bool                           // precondition
  1043  		expectedFormat string                         // postcondition
  1044  		expectedErr    *dataformat.ErrVersionMismatch // postcondition
  1045  	}{
  1046  		{
  1047  			dataFormat: "",
  1048  			dataExists: true,
  1049  			expectedErr: &dataformat.ErrVersionMismatch{
  1050  				DBInfo:          "CouchDB for state database",
  1051  				Version:         "",
  1052  				ExpectedVersion: "2.0",
  1053  			},
  1054  			expectedFormat: "does not matter as the test should not reach to check this",
  1055  		},
  1056  
  1057  		{
  1058  			dataFormat:     "",
  1059  			dataExists:     false,
  1060  			expectedErr:    nil,
  1061  			expectedFormat: dataformat.Version20,
  1062  		},
  1063  
  1064  		{
  1065  			dataFormat:     dataformat.Version20,
  1066  			dataExists:     false,
  1067  			expectedFormat: dataformat.Version20,
  1068  			expectedErr:    nil,
  1069  		},
  1070  
  1071  		{
  1072  			dataFormat:     dataformat.Version20,
  1073  			dataExists:     true,
  1074  			expectedFormat: dataformat.Version20,
  1075  			expectedErr:    nil,
  1076  		},
  1077  
  1078  		{
  1079  			dataFormat: "3.0",
  1080  			dataExists: true,
  1081  			expectedErr: &dataformat.ErrVersionMismatch{
  1082  				DBInfo:          "CouchDB for state database",
  1083  				Version:         "3.0",
  1084  				ExpectedVersion: dataformat.Version20,
  1085  			},
  1086  			expectedFormat: "does not matter as the test should not reach to check this",
  1087  		},
  1088  	}
  1089  
  1090  	testEnv.init(t, &statedb.Cache{})
  1091  	for i, testCase := range testCases {
  1092  		t.Run(
  1093  			fmt.Sprintf("testCase %d", i),
  1094  			func(t *testing.T) {
  1095  				testFormatCheck(t, testCase.dataFormat, testCase.dataExists, testCase.expectedErr, testCase.expectedFormat, testEnv.couchAddress)
  1096  			})
  1097  	}
  1098  }
  1099  
  1100  func testFormatCheck(t *testing.T, dataFormat string, dataExists bool, expectedErr *dataformat.ErrVersionMismatch, expectedFormat, couchAddress string) {
  1101  	redoPath, err := ioutil.TempDir("", "redoPath")
  1102  	require.NoError(t, err)
  1103  	defer os.RemoveAll(redoPath)
  1104  	config := &couchdb.Config{
  1105  		Address:             couchAddress,
  1106  		MaxRetries:          3,
  1107  		MaxRetriesOnStartup: 20,
  1108  		RequestTimeout:      35 * time.Second,
  1109  		RedoLogPath:         redoPath,
  1110  	}
  1111  	dbProvider, err := NewVersionedDBProvider(config, &disabled.Provider{}, &statedb.Cache{})
  1112  	require.NoError(t, err)
  1113  
  1114  	// create preconditions for test
  1115  	if dataExists {
  1116  		db, err := dbProvider.GetDBHandle("testns")
  1117  		require.NoError(t, err)
  1118  		batch := statedb.NewUpdateBatch()
  1119  		batch.Put("testns", "testkey", []byte("testVal"), version.NewHeight(1, 1))
  1120  		require.NoError(t, db.ApplyUpdates(batch, version.NewHeight(1, 1)))
  1121  	}
  1122  	if dataFormat == "" {
  1123  		testutilDropDB(t, dbProvider.couchInstance, fabricInternalDBName)
  1124  	} else {
  1125  		require.NoError(t, writeDataFormatVersion(dbProvider.couchInstance, dataFormat))
  1126  	}
  1127  	dbProvider.Close()
  1128  	defer cleanupDB(t, dbProvider.couchInstance)
  1129  
  1130  	// close and reopen with preconditions set and check the expected behavior
  1131  	dbProvider, err = NewVersionedDBProvider(config, &disabled.Provider{}, &statedb.Cache{})
  1132  	if expectedErr != nil {
  1133  		require.Equal(t, expectedErr, err)
  1134  		return
  1135  	}
  1136  	require.NoError(t, err)
  1137  	defer func() {
  1138  		if dbProvider != nil {
  1139  			dbProvider.Close()
  1140  		}
  1141  	}()
  1142  	format, err := readDataformatVersion(dbProvider.couchInstance)
  1143  	require.NoError(t, err)
  1144  	require.Equal(t, expectedFormat, format)
  1145  }
  1146  
  1147  func testDoesNotExistInCache(t *testing.T, cache *statedb.Cache, chainID, ns, key string) {
  1148  	cacheValue, err := cache.GetState(chainID, ns, key)
  1149  	require.NoError(t, err)
  1150  	require.Nil(t, cacheValue)
  1151  }
  1152  
  1153  func testExistInCache(t *testing.T, db *couchdb.CouchDatabase, cache *statedb.Cache, chainID, ns, key string, expectedVV *statedb.VersionedValue) {
  1154  	cacheValue, err := cache.GetState(chainID, ns, key)
  1155  	require.NoError(t, err)
  1156  	vv, err := constructVersionedValue(cacheValue)
  1157  	require.NoError(t, err)
  1158  	require.Equal(t, expectedVV, vv)
  1159  	metadata, err := retrieveNsMetadata(db, []string{key})
  1160  	require.NoError(t, err)
  1161  	require.Equal(t, metadata[0].Rev, string(cacheValue.AdditionalInfo))
  1162  }
  1163  
  1164  func TestLoadCommittedVersion(t *testing.T) {
  1165  	cache := statedb.NewCache(32, []string{"lscc"})
  1166  	env := testEnv
  1167  	env.init(t, cache)
  1168  	defer env.cleanup()
  1169  
  1170  	chainID := "testloadcommittedversion"
  1171  	db, err := env.DBProvider.GetDBHandle(chainID)
  1172  	require.NoError(t, err)
  1173  
  1174  	// scenario: state cache has (ns1, key1), (ns1, key2),
  1175  	// and (ns2, key1) but misses (ns2, key2). The
  1176  	// LoadCommittedVersions will fetch the first
  1177  	// three keys from the state cache and the remaining one from
  1178  	// the db. To ensure that, the db contains only
  1179  	// the missing key (ns2, key2).
  1180  
  1181  	// store (ns1, key1), (ns1, key2), (ns2, key1) in the state cache
  1182  	cacheValue := &statedb.CacheValue{
  1183  		Value:          []byte("value1"),
  1184  		Metadata:       []byte("meta1"),
  1185  		VersionBytes:   version.NewHeight(1, 1).ToBytes(),
  1186  		AdditionalInfo: []byte("rev1"),
  1187  	}
  1188  	require.NoError(t, cache.PutState(chainID, "ns1", "key1", cacheValue))
  1189  
  1190  	cacheValue = &statedb.CacheValue{
  1191  		Value:          []byte("value2"),
  1192  		Metadata:       []byte("meta2"),
  1193  		VersionBytes:   version.NewHeight(1, 2).ToBytes(),
  1194  		AdditionalInfo: []byte("rev2"),
  1195  	}
  1196  	require.NoError(t, cache.PutState(chainID, "ns1", "key2", cacheValue))
  1197  
  1198  	cacheValue = &statedb.CacheValue{
  1199  		Value:          []byte("value3"),
  1200  		Metadata:       []byte("meta3"),
  1201  		VersionBytes:   version.NewHeight(1, 3).ToBytes(),
  1202  		AdditionalInfo: []byte("rev3"),
  1203  	}
  1204  	require.NoError(t, cache.PutState(chainID, "ns2", "key1", cacheValue))
  1205  
  1206  	// store (ns2, key2) in the db
  1207  	batch := statedb.NewUpdateBatch()
  1208  	vv := &statedb.VersionedValue{Value: []byte("value4"), Metadata: []byte("meta4"), Version: version.NewHeight(1, 4)}
  1209  	batch.PutValAndMetadata("ns2", "key2", vv.Value, vv.Metadata, vv.Version)
  1210  	savePoint := version.NewHeight(2, 2)
  1211  	db.ApplyUpdates(batch, savePoint)
  1212  
  1213  	// version cache should be empty
  1214  	ver, ok := db.(*VersionedDB).GetCachedVersion("ns1", "key1")
  1215  	require.Nil(t, ver)
  1216  	require.False(t, ok)
  1217  	ver, ok = db.(*VersionedDB).GetCachedVersion("ns1", "key2")
  1218  	require.Nil(t, ver)
  1219  	require.False(t, ok)
  1220  	ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key1")
  1221  	require.Nil(t, ver)
  1222  	require.False(t, ok)
  1223  	ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key2")
  1224  	require.Nil(t, ver)
  1225  	require.False(t, ok)
  1226  
  1227  	keys := []*statedb.CompositeKey{
  1228  		{
  1229  			Namespace: "ns1",
  1230  			Key:       "key1",
  1231  		},
  1232  		{
  1233  			Namespace: "ns1",
  1234  			Key:       "key2",
  1235  		},
  1236  		{
  1237  			Namespace: "ns2",
  1238  			Key:       "key1",
  1239  		},
  1240  		{
  1241  			Namespace: "ns2",
  1242  			Key:       "key2",
  1243  		},
  1244  	}
  1245  
  1246  	require.NoError(t, db.(*VersionedDB).LoadCommittedVersions(keys))
  1247  
  1248  	ver, ok = db.(*VersionedDB).GetCachedVersion("ns1", "key1")
  1249  	require.Equal(t, version.NewHeight(1, 1), ver)
  1250  	require.True(t, ok)
  1251  	ver, ok = db.(*VersionedDB).GetCachedVersion("ns1", "key2")
  1252  	require.Equal(t, version.NewHeight(1, 2), ver)
  1253  	require.True(t, ok)
  1254  	ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key1")
  1255  	require.Equal(t, version.NewHeight(1, 3), ver)
  1256  	require.True(t, ok)
  1257  	ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key2")
  1258  	require.Equal(t, version.NewHeight(1, 4), ver)
  1259  	require.True(t, ok)
  1260  }
  1261  
  1262  func TestMissingRevisionRetrievalFromDB(t *testing.T) {
  1263  	env := testEnv
  1264  	env.init(t, &statedb.Cache{})
  1265  	defer env.cleanup()
  1266  	chainID := "testmissingrevisionfromdb"
  1267  	db, err := env.DBProvider.GetDBHandle(chainID)
  1268  	require.NoError(t, err)
  1269  
  1270  	// store key1, key2, key3 to the DB
  1271  	batch := statedb.NewUpdateBatch()
  1272  	vv1 := statedb.VersionedValue{Value: []byte("value1"), Version: version.NewHeight(1, 1)}
  1273  	vv2 := statedb.VersionedValue{Value: []byte("value2"), Version: version.NewHeight(1, 2)}
  1274  	vv3 := statedb.VersionedValue{Value: []byte("value3"), Version: version.NewHeight(1, 3)}
  1275  	batch.Put("ns1", "key1", vv1.Value, vv1.Version)
  1276  	batch.Put("ns1", "key2", vv2.Value, vv2.Version)
  1277  	batch.Put("ns1", "key3", vv3.Value, vv3.Version)
  1278  	savePoint := version.NewHeight(2, 5)
  1279  	db.ApplyUpdates(batch, savePoint)
  1280  
  1281  	// retrieve the versions of key1, key2, and key3
  1282  	revisions := make(map[string]string)
  1283  	require.NoError(t, db.(*VersionedDB).addMissingRevisionsFromDB("ns1", []string{"key1", "key2", "key3"}, revisions))
  1284  	require.Equal(t, 3, len(revisions))
  1285  
  1286  	// update key1 and key2 but not key3
  1287  	batch = statedb.NewUpdateBatch()
  1288  	vv4 := statedb.VersionedValue{Value: []byte("value1"), Version: version.NewHeight(1, 1)}
  1289  	vv5 := statedb.VersionedValue{Value: []byte("value2"), Version: version.NewHeight(1, 2)}
  1290  	batch.Put("ns1", "key1", vv4.Value, vv4.Version)
  1291  	batch.Put("ns1", "key2", vv5.Value, vv5.Version)
  1292  	savePoint = version.NewHeight(3, 5)
  1293  	db.ApplyUpdates(batch, savePoint)
  1294  
  1295  	// for key3, the revision should be the same but not for key1 and key2
  1296  	newRevisions := make(map[string]string)
  1297  	require.NoError(t, db.(*VersionedDB).addMissingRevisionsFromDB("ns1", []string{"key1", "key2", "key3"}, newRevisions))
  1298  	require.Equal(t, 3, len(newRevisions))
  1299  	require.NotEqual(t, revisions["key1"], newRevisions["key1"])
  1300  	require.NotEqual(t, revisions["key2"], newRevisions["key2"])
  1301  	require.Equal(t, revisions["key3"], newRevisions["key3"])
  1302  }
  1303  
  1304  func TestMissingRevisionRetrievalFromCache(t *testing.T) {
  1305  	cache := statedb.NewCache(32, []string{"lscc"})
  1306  	env := testEnv
  1307  	env.init(t, cache)
  1308  	defer env.cleanup()
  1309  
  1310  	chainID := "testmissingrevisionfromcache"
  1311  	db, err := env.DBProvider.GetDBHandle(chainID)
  1312  	require.NoError(t, err)
  1313  
  1314  	// scenario 1: missing from cache.
  1315  	revisions := make(map[string]string)
  1316  	stillMissingKeys, err := db.(*VersionedDB).addMissingRevisionsFromCache("ns1", []string{"key1", "key2"}, revisions)
  1317  	require.NoError(t, err)
  1318  	require.Equal(t, []string{"key1", "key2"}, stillMissingKeys)
  1319  	require.Empty(t, revisions)
  1320  
  1321  	// scenario 2: key1 is available in the cache
  1322  	require.NoError(t, cache.PutState(chainID, "ns1", "key1", &statedb.CacheValue{AdditionalInfo: []byte("rev1")}))
  1323  	revisions = make(map[string]string)
  1324  	stillMissingKeys, err = db.(*VersionedDB).addMissingRevisionsFromCache("ns1", []string{"key1", "key2"}, revisions)
  1325  	require.NoError(t, err)
  1326  	require.Equal(t, []string{"key2"}, stillMissingKeys)
  1327  	require.Equal(t, "rev1", revisions["key1"])
  1328  
  1329  	// scenario 3: both key1 and key2 are available in the cache
  1330  	require.NoError(t, cache.PutState(chainID, "ns1", "key2", &statedb.CacheValue{AdditionalInfo: []byte("rev2")}))
  1331  	revisions = make(map[string]string)
  1332  	stillMissingKeys, err = db.(*VersionedDB).addMissingRevisionsFromCache("ns1", []string{"key1", "key2"}, revisions)
  1333  	require.NoError(t, err)
  1334  	require.Empty(t, stillMissingKeys)
  1335  	require.Equal(t, "rev1", revisions["key1"])
  1336  	require.Equal(t, "rev2", revisions["key2"])
  1337  }