github.com/renegr87/renegr87@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 }