github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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 "errors" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "strings" 15 "testing" 16 "time" 17 "unicode/utf8" 18 19 "github.com/osdi23p228/fabric/common/flogging" 20 "github.com/osdi23p228/fabric/common/ledger/dataformat" 21 "github.com/osdi23p228/fabric/common/metrics/disabled" 22 "github.com/osdi23p228/fabric/core/ledger" 23 "github.com/osdi23p228/fabric/core/ledger/internal/version" 24 "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb" 25 "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb/commontests" 26 "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb/mock" 27 "github.com/stretchr/testify/require" 28 ) 29 30 // testVDBEnv provides a couch db backed versioned db for testing 31 type testVDBEnv struct { 32 t *testing.T 33 DBProvider statedb.VersionedDBProvider 34 config *ledger.CouchDBConfig 35 cache *cache 36 sysNamespaces []string 37 couchDBEnv *testCouchDBEnv 38 } 39 40 func (env *testVDBEnv) init(t *testing.T, sysNamespaces []string) { 41 t.Logf("Initializing TestVDBEnv") 42 43 if env.couchDBEnv == nil { 44 couchDBEnv := &testCouchDBEnv{} 45 couchDBEnv.startCouchDB(t) 46 env.couchDBEnv = couchDBEnv 47 } 48 49 redoPath, err := ioutil.TempDir("", "cvdbenv") 50 if err != nil { 51 t.Fatalf("Failed to create redo log directory: %s", err) 52 } 53 config := &ledger.CouchDBConfig{ 54 Address: env.couchDBEnv.couchAddress, 55 Username: "admin", 56 Password: "adminpw", 57 InternalQueryLimit: 1000, 58 MaxBatchUpdateSize: 1000, 59 MaxRetries: 3, 60 MaxRetriesOnStartup: 20, 61 RequestTimeout: 35 * time.Second, 62 RedoLogPath: redoPath, 63 UserCacheSizeMBs: 8, 64 } 65 66 dbProvider, err := NewVersionedDBProvider(config, &disabled.Provider{}, sysNamespaces) 67 if err != nil { 68 t.Fatalf("Error creating CouchDB Provider: %s", err) 69 } 70 71 env.t = t 72 env.config = config 73 env.DBProvider = dbProvider 74 env.config = config 75 env.cache = dbProvider.cache 76 env.sysNamespaces = sysNamespaces 77 } 78 79 func (env *testVDBEnv) closeAndReopen() { 80 env.DBProvider.Close() 81 dbProvider, _ := NewVersionedDBProvider(env.config, &disabled.Provider{}, env.sysNamespaces) 82 env.DBProvider = dbProvider 83 env.cache = dbProvider.cache 84 } 85 86 // Cleanup drops the test couch databases and closes the db provider 87 func (env *testVDBEnv) cleanup() { 88 env.t.Logf("Cleaningup TestVDBEnv") 89 if env.DBProvider != nil { 90 env.DBProvider.Close() 91 } 92 env.couchDBEnv.cleanup(env.config) 93 os.RemoveAll(env.config.RedoLogPath) 94 } 95 96 // testVDBEnv provides a couch db for testing 97 type testCouchDBEnv struct { 98 t *testing.T 99 couchAddress string 100 cleanupCouchDB func() 101 } 102 103 // startCouchDB starts external couchDB resources for testCouchDBEnv. 104 func (env *testCouchDBEnv) startCouchDB(t *testing.T) { 105 if env.couchAddress != "" { 106 return 107 } 108 env.t = t 109 env.couchAddress, env.cleanupCouchDB = StartCouchDB(t, nil) 110 } 111 112 // stopCouchDB stops external couchDB resources. 113 func (env *testCouchDBEnv) stopCouchDB() { 114 if env.couchAddress != "" { 115 env.cleanupCouchDB() 116 } 117 } 118 119 func (env *testCouchDBEnv) cleanup(config *ledger.CouchDBConfig) { 120 err := DropApplicationDBs(config) 121 require.NoError(env.t, err) 122 } 123 124 // we create two CouchDB instances/containers---one is used to test the 125 // functionality of the versionedDB and another for testing the CouchDB 126 // util functions. 127 var vdbEnv = &testVDBEnv{} 128 var couchDBEnv = &testCouchDBEnv{} 129 130 func TestMain(m *testing.M) { 131 flogging.ActivateSpec("statecouchdb=debug") 132 133 rc := m.Run() 134 if vdbEnv.couchDBEnv != nil { 135 vdbEnv.couchDBEnv.stopCouchDB() 136 } 137 couchDBEnv.stopCouchDB() 138 os.Exit(rc) 139 } 140 141 func TestBasicRW(t *testing.T) { 142 vdbEnv.init(t, nil) 143 defer vdbEnv.cleanup() 144 145 commontests.TestBasicRW(t, vdbEnv.DBProvider) 146 } 147 148 // TestGetStateFromCache checks cache hits, cache misses, and cache 149 // updates during GetState call. 150 func TestGetStateFromCache(t *testing.T) { 151 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 152 defer vdbEnv.cleanup() 153 154 chainID := "testgetstatefromcache" 155 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 156 require.NoError(t, err) 157 158 // scenario 1: get state would receives a 159 // cache miss as the given key does not exist. 160 // As the key does not exist in the 161 // db also, get state call would not update 162 // the cache. 163 vv, err := db.GetState("ns", "key1") 164 require.NoError(t, err) 165 require.Nil(t, vv) 166 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns", "key1") 167 168 // scenario 2: get state would receive a cache hit. 169 // directly store an entry in the cache 170 cacheValue := &CacheValue{ 171 Value: []byte("value1"), 172 Metadata: []byte("meta1"), 173 Version: version.NewHeight(1, 1).ToBytes(), 174 AdditionalInfo: []byte("rev1"), 175 } 176 require.NoError(t, vdbEnv.cache.putState(chainID, "ns", "key1", cacheValue)) 177 178 vv, err = db.GetState("ns", "key1") 179 require.NoError(t, err) 180 expectedVV, err := constructVersionedValue(cacheValue) 181 require.NoError(t, err) 182 require.Equal(t, expectedVV, vv) 183 184 // scenario 3: get state would receives a 185 // cache miss as the given key does not present. 186 // The value associated with the key would be 187 // fetched from the database and the cache would 188 // be updated accordingly. 189 190 // store an entry in the db 191 batch := statedb.NewUpdateBatch() 192 vv2 := &statedb.VersionedValue{Value: []byte("value2"), Metadata: []byte("meta2"), Version: version.NewHeight(1, 2)} 193 batch.PutValAndMetadata("lscc", "key1", vv2.Value, vv2.Metadata, vv2.Version) 194 savePoint := version.NewHeight(1, 2) 195 db.ApplyUpdates(batch, savePoint) 196 // Note that the ApplyUpdates() updates only the existing entry in the cache. Currently, the 197 // cache has only ns, key1 but we are storing lscc, key1. Hence, no changes would happen in the cache. 198 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "lscc", "key1") 199 200 // calling GetState() would update the cache 201 vv, err = db.GetState("lscc", "key1") 202 require.NoError(t, err) 203 require.Equal(t, vv2, vv) 204 205 // cache should have been updated with lscc, key1 206 nsdb, err := db.(*VersionedDB).getNamespaceDBHandle("lscc") 207 require.NoError(t, err) 208 testExistInCache(t, nsdb, vdbEnv.cache, chainID, "lscc", "key1", vv2) 209 } 210 211 // TestGetVersionFromCache checks cache hits, cache misses, and 212 // updates during GetVersion call. 213 func TestGetVersionFromCache(t *testing.T) { 214 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 215 defer vdbEnv.cleanup() 216 217 chainID := "testgetstatefromcache" 218 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 219 require.NoError(t, err) 220 221 // scenario 1: get version would receives a 222 // cache miss as the given key does not exist. 223 // As the key does not exist in the 224 // db also, get version call would not update 225 // the cache. 226 ver, err := db.GetVersion("ns", "key1") 227 require.Nil(t, err) 228 require.Nil(t, ver) 229 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns", "key1") 230 231 // scenario 2: get version would receive a cache hit. 232 // directly store an entry in the cache 233 cacheValue := &CacheValue{ 234 Value: []byte("value1"), 235 Metadata: []byte("meta1"), 236 Version: version.NewHeight(1, 1).ToBytes(), 237 AdditionalInfo: []byte("rev1"), 238 } 239 require.NoError(t, vdbEnv.cache.putState(chainID, "ns", "key1", cacheValue)) 240 241 ver, err = db.GetVersion("ns", "key1") 242 require.NoError(t, err) 243 expectedVer, _, err := version.NewHeightFromBytes(cacheValue.Version) 244 require.NoError(t, err) 245 require.Equal(t, expectedVer, ver) 246 247 // scenario 3: get version would receives a 248 // cache miss as the given key does not present. 249 // The value associated with the key would be 250 // fetched from the database and the cache would 251 // be updated accordingly. 252 253 // store an entry in the db 254 batch := statedb.NewUpdateBatch() 255 vv2 := &statedb.VersionedValue{Value: []byte("value2"), Metadata: []byte("meta2"), Version: version.NewHeight(1, 2)} 256 batch.PutValAndMetadata("lscc", "key1", vv2.Value, vv2.Metadata, vv2.Version) 257 savePoint := version.NewHeight(1, 2) 258 db.ApplyUpdates(batch, savePoint) 259 // Note that the ApplyUpdates() updates only the existing entry in the cache. Currently, the 260 // cache has only ns, key1 but we are storing lscc, key1. Hence, no changes would happen in the cache. 261 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "lscc", "key1") 262 263 // calling GetVersion() would update the cache 264 ver, err = db.GetVersion("lscc", "key1") 265 require.NoError(t, err) 266 require.Equal(t, vv2.Version, ver) 267 268 // cache should have been updated with lscc, key1 269 nsdb, err := db.(*VersionedDB).getNamespaceDBHandle("lscc") 270 require.NoError(t, err) 271 testExistInCache(t, nsdb, vdbEnv.cache, chainID, "lscc", "key1", vv2) 272 } 273 274 // TestGetMultipleStatesFromCache checks cache hits, cache misses, 275 // and updates during GetStateMultipleKeys call. 276 func TestGetMultipleStatesFromCache(t *testing.T) { 277 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 278 defer vdbEnv.cleanup() 279 280 chainID := "testgetmultiplestatesfromcache" 281 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 282 require.NoError(t, err) 283 284 // scenario: given 5 keys, get multiple states find 285 // 2 keys in the cache. The remaining 2 keys would be fetched 286 // from the database and the cache would be updated. The last 287 // key is not present in the db and hence it won't be sent to 288 // the cache. 289 290 // key1 and key2 exist only in the cache 291 cacheValue1 := &CacheValue{ 292 Value: []byte("value1"), 293 Metadata: []byte("meta1"), 294 Version: version.NewHeight(1, 1).ToBytes(), 295 AdditionalInfo: []byte("rev1"), 296 } 297 require.NoError(t, vdbEnv.cache.putState(chainID, "ns", "key1", cacheValue1)) 298 cacheValue2 := &CacheValue{ 299 Value: []byte("value2"), 300 Metadata: []byte("meta2"), 301 Version: version.NewHeight(1, 1).ToBytes(), 302 AdditionalInfo: []byte("rev2"), 303 } 304 require.NoError(t, vdbEnv.cache.putState(chainID, "ns", "key2", cacheValue2)) 305 306 // key3 and key4 exist only in the db 307 batch := statedb.NewUpdateBatch() 308 vv3 := &statedb.VersionedValue{Value: []byte("value3"), Metadata: []byte("meta3"), Version: version.NewHeight(1, 1)} 309 batch.PutValAndMetadata("ns", "key3", vv3.Value, vv3.Metadata, vv3.Version) 310 vv4 := &statedb.VersionedValue{Value: []byte("value4"), Metadata: []byte("meta4"), Version: version.NewHeight(1, 1)} 311 batch.PutValAndMetadata("ns", "key4", vv4.Value, vv4.Metadata, vv4.Version) 312 savePoint := version.NewHeight(1, 2) 313 db.ApplyUpdates(batch, savePoint) 314 315 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns", "key3") 316 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns", "key4") 317 318 // key5 does not exist at all while key3 and key4 does not exist in the cache 319 vvalues, err := db.GetStateMultipleKeys("ns", []string{"key1", "key2", "key3", "key4", "key5"}) 320 require.Nil(t, err) 321 vv1, err := constructVersionedValue(cacheValue1) 322 require.NoError(t, err) 323 vv2, err := constructVersionedValue(cacheValue2) 324 require.NoError(t, err) 325 require.Equal(t, []*statedb.VersionedValue{vv1, vv2, vv3, vv4, nil}, vvalues) 326 327 // cache should have been updated with key3 and key4 328 nsdb, err := db.(*VersionedDB).getNamespaceDBHandle("ns") 329 require.NoError(t, err) 330 testExistInCache(t, nsdb, vdbEnv.cache, chainID, "ns", "key3", vv3) 331 testExistInCache(t, nsdb, vdbEnv.cache, chainID, "ns", "key4", vv4) 332 } 333 334 // TestCacheUpdatesAfterCommit checks whether the cache is updated 335 // after a commit of a update batch. 336 func TestCacheUpdatesAfterCommit(t *testing.T) { 337 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 338 defer vdbEnv.cleanup() 339 340 chainID := "testcacheupdatesaftercommit" 341 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 342 require.NoError(t, err) 343 344 // scenario: cache has 4 keys while the commit operation 345 // updates 2 of those keys, delete the remaining 2 keys, and 346 // adds a new key. At the end of the commit operation, only 347 // those 2 keys should be present with the recent value 348 // in the cache and the new key should not be present in the cache. 349 350 // store 4 keys in the db 351 batch := statedb.NewUpdateBatch() 352 vv1 := &statedb.VersionedValue{Value: []byte("value1"), Metadata: []byte("meta1"), Version: version.NewHeight(1, 2)} 353 vv2 := &statedb.VersionedValue{Value: []byte("value2"), Metadata: []byte("meta2"), Version: version.NewHeight(1, 2)} 354 vv3 := &statedb.VersionedValue{Value: []byte("value3"), Metadata: []byte("meta3"), Version: version.NewHeight(1, 2)} 355 vv4 := &statedb.VersionedValue{Value: []byte("value4"), Metadata: []byte("meta4"), Version: version.NewHeight(1, 2)} 356 357 batch.PutValAndMetadata("ns1", "key1", vv1.Value, vv1.Metadata, vv1.Version) 358 batch.PutValAndMetadata("ns1", "key2", vv2.Value, vv2.Metadata, vv2.Version) 359 batch.PutValAndMetadata("ns2", "key1", vv3.Value, vv3.Metadata, vv3.Version) 360 batch.PutValAndMetadata("ns2", "key2", vv4.Value, vv4.Metadata, vv4.Version) 361 savePoint := version.NewHeight(1, 5) 362 db.ApplyUpdates(batch, savePoint) 363 364 // key1, key2 in ns1 and ns2 would not be in cache 365 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns1", "key1") 366 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns1", "key2") 367 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns2", "key1") 368 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns2", "key2") 369 370 // add key1 and key2 from ns1 to the cache 371 _, err = db.GetState("ns1", "key1") 372 require.NoError(t, err) 373 _, err = db.GetState("ns1", "key2") 374 require.NoError(t, err) 375 // add key1 and key2 from ns2 to the cache 376 _, err = db.GetState("ns2", "key1") 377 require.NoError(t, err) 378 _, err = db.GetState("ns2", "key2") 379 require.NoError(t, err) 380 381 v, err := vdbEnv.cache.getState(chainID, "ns1", "key1") 382 require.NoError(t, err) 383 ns1key1rev := string(v.AdditionalInfo) 384 385 v, err = vdbEnv.cache.getState(chainID, "ns1", "key2") 386 require.NoError(t, err) 387 ns1key2rev := string(v.AdditionalInfo) 388 389 // update key1 and key2 in ns1. delete key1 and key2 in ns2. add a new key3 in ns2. 390 batch = statedb.NewUpdateBatch() 391 vv1Update := &statedb.VersionedValue{Value: []byte("new-value1"), Metadata: []byte("meta1"), Version: version.NewHeight(2, 2)} 392 vv2Update := &statedb.VersionedValue{Value: []byte("new-value2"), Metadata: []byte("meta2"), Version: version.NewHeight(2, 2)} 393 vv3Update := &statedb.VersionedValue{Version: version.NewHeight(2, 4)} 394 vv4Update := &statedb.VersionedValue{Version: version.NewHeight(2, 5)} 395 vv5 := &statedb.VersionedValue{Value: []byte("value5"), Metadata: []byte("meta5"), Version: version.NewHeight(1, 2)} 396 397 batch.PutValAndMetadata("ns1", "key1", vv1Update.Value, vv1Update.Metadata, vv1Update.Version) 398 batch.PutValAndMetadata("ns1", "key2", vv2Update.Value, vv2Update.Metadata, vv2Update.Version) 399 batch.Delete("ns2", "key1", vv3Update.Version) 400 batch.Delete("ns2", "key2", vv4Update.Version) 401 batch.PutValAndMetadata("ns2", "key3", vv5.Value, vv5.Metadata, vv5.Version) 402 savePoint = version.NewHeight(2, 5) 403 db.ApplyUpdates(batch, savePoint) 404 405 // cache should have only the update key1 and key2 in ns1 406 cacheValue, err := vdbEnv.cache.getState(chainID, "ns1", "key1") 407 require.NoError(t, err) 408 vv, err := constructVersionedValue(cacheValue) 409 require.NoError(t, err) 410 require.Equal(t, vv1Update, vv) 411 require.NotEqual(t, ns1key1rev, string(cacheValue.AdditionalInfo)) 412 413 cacheValue, err = vdbEnv.cache.getState(chainID, "ns1", "key2") 414 require.NoError(t, err) 415 vv, err = constructVersionedValue(cacheValue) 416 require.NoError(t, err) 417 require.Equal(t, vv2Update, vv) 418 require.NotEqual(t, ns1key2rev, string(cacheValue.AdditionalInfo)) 419 420 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns2", "key1") 421 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns2", "key2") 422 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns2", "key3") 423 } 424 425 func TestMultiDBBasicRW(t *testing.T) { 426 vdbEnv.init(t, nil) 427 defer vdbEnv.cleanup() 428 429 commontests.TestMultiDBBasicRW(t, vdbEnv.DBProvider) 430 431 } 432 433 func TestDeletes(t *testing.T) { 434 vdbEnv.init(t, nil) 435 defer vdbEnv.cleanup() 436 437 commontests.TestDeletes(t, vdbEnv.DBProvider) 438 } 439 440 func TestIterator(t *testing.T) { 441 vdbEnv.init(t, nil) 442 defer vdbEnv.cleanup() 443 444 commontests.TestIterator(t, vdbEnv.DBProvider) 445 } 446 447 // The following tests are unique to couchdb, they are not used in leveldb 448 // query test 449 func TestQuery(t *testing.T) { 450 vdbEnv.init(t, nil) 451 defer vdbEnv.cleanup() 452 453 commontests.TestQuery(t, vdbEnv.DBProvider) 454 } 455 456 func TestGetStateMultipleKeys(t *testing.T) { 457 vdbEnv.init(t, nil) 458 defer vdbEnv.cleanup() 459 460 commontests.TestGetStateMultipleKeys(t, vdbEnv.DBProvider) 461 } 462 463 func TestGetVersion(t *testing.T) { 464 vdbEnv.init(t, nil) 465 defer vdbEnv.cleanup() 466 467 commontests.TestGetVersion(t, vdbEnv.DBProvider) 468 } 469 470 func TestSmallBatchSize(t *testing.T) { 471 vdbEnv.init(t, nil) 472 defer vdbEnv.cleanup() 473 474 commontests.TestSmallBatchSize(t, vdbEnv.DBProvider) 475 } 476 477 func TestBatchRetry(t *testing.T) { 478 vdbEnv.init(t, nil) 479 defer vdbEnv.cleanup() 480 481 commontests.TestBatchWithIndividualRetry(t, vdbEnv.DBProvider) 482 } 483 484 func TestValueAndMetadataWrites(t *testing.T) { 485 vdbEnv.init(t, nil) 486 defer vdbEnv.cleanup() 487 488 commontests.TestValueAndMetadataWrites(t, vdbEnv.DBProvider) 489 } 490 491 func TestPaginatedRangeQuery(t *testing.T) { 492 vdbEnv.init(t, nil) 493 defer vdbEnv.cleanup() 494 495 commontests.TestPaginatedRangeQuery(t, vdbEnv.DBProvider) 496 } 497 498 func TestRangeQuerySpecialCharacters(t *testing.T) { 499 vdbEnv.init(t, nil) 500 defer vdbEnv.cleanup() 501 502 commontests.TestRangeQuerySpecialCharacters(t, vdbEnv.DBProvider) 503 } 504 505 // TestUtilityFunctions tests utility functions 506 func TestUtilityFunctions(t *testing.T) { 507 vdbEnv.init(t, nil) 508 defer vdbEnv.cleanup() 509 510 db, err := vdbEnv.DBProvider.GetDBHandle("testutilityfunctions", nil) 511 require.NoError(t, err) 512 513 // BytesKeySupported should be false for CouchDB 514 byteKeySupported := db.BytesKeySupported() 515 require.False(t, byteKeySupported) 516 517 // ValidateKeyValue should return nil for a valid key and value 518 err = db.ValidateKeyValue("testKey", []byte("Some random bytes")) 519 require.Nil(t, err) 520 521 // ValidateKeyValue should return an error for a key that is not a utf-8 valid string 522 err = db.ValidateKeyValue(string([]byte{0xff, 0xfe, 0xfd}), []byte("Some random bytes")) 523 require.Error(t, err, "ValidateKey should have thrown an error for an invalid utf-8 string") 524 525 // ValidateKeyValue should return an error for a key that is an empty string 526 require.EqualError(t, db.ValidateKeyValue("", []byte("validValue")), 527 "invalid key. Empty string is not supported as a key by couchdb") 528 529 reservedFields := []string{"~version", "_id", "_test"} 530 531 // ValidateKeyValue should return an error for a json value that contains one of the reserved fields 532 // at the top level 533 for _, reservedField := range reservedFields { 534 testVal := fmt.Sprintf(`{"%s":"dummyVal"}`, reservedField) 535 err = db.ValidateKeyValue("testKey", []byte(testVal)) 536 require.Error(t, err, fmt.Sprintf( 537 "ValidateKey should have thrown an error for a json value %s, as contains one of the reserved fields", testVal)) 538 } 539 540 // ValidateKeyValue should not return an error for a json value that contains one of the reserved fields 541 // if not at the top level 542 for _, reservedField := range reservedFields { 543 testVal := fmt.Sprintf(`{"data.%s":"dummyVal"}`, reservedField) 544 err = db.ValidateKeyValue("testKey", []byte(testVal)) 545 require.NoError(t, err, fmt.Sprintf( 546 "ValidateKey should not have thrown an error the json value %s since the reserved field was not at the top level", testVal)) 547 } 548 549 // ValidateKeyValue should return an error for a key that begins with an underscore 550 err = db.ValidateKeyValue("_testKey", []byte("testValue")) 551 require.Error(t, err, "ValidateKey should have thrown an error for a key that begins with an underscore") 552 553 } 554 555 // TestInvalidJSONFields tests for invalid JSON fields 556 func TestInvalidJSONFields(t *testing.T) { 557 vdbEnv.init(t, nil) 558 defer vdbEnv.cleanup() 559 560 db, err := vdbEnv.DBProvider.GetDBHandle("testinvalidfields", nil) 561 require.NoError(t, err) 562 563 db.Open() 564 defer db.Close() 565 566 batch := statedb.NewUpdateBatch() 567 jsonValue1 := `{"_id":"key1","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}` 568 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 569 570 savePoint := version.NewHeight(1, 2) 571 err = db.ApplyUpdates(batch, savePoint) 572 require.Error(t, err, "Invalid field _id should have thrown an error") 573 574 batch = statedb.NewUpdateBatch() 575 jsonValue1 = `{"_rev":"rev1","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}` 576 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 577 578 savePoint = version.NewHeight(1, 2) 579 err = db.ApplyUpdates(batch, savePoint) 580 require.Error(t, err, "Invalid field _rev should have thrown an error") 581 582 batch = statedb.NewUpdateBatch() 583 jsonValue1 = `{"_deleted":"true","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}` 584 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 585 586 savePoint = version.NewHeight(1, 2) 587 err = db.ApplyUpdates(batch, savePoint) 588 require.Error(t, err, "Invalid field _deleted should have thrown an error") 589 590 batch = statedb.NewUpdateBatch() 591 jsonValue1 = `{"~version":"v1","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}` 592 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 593 594 savePoint = version.NewHeight(1, 2) 595 err = db.ApplyUpdates(batch, savePoint) 596 require.Error(t, err, "Invalid field ~version should have thrown an error") 597 } 598 599 func TestDebugFunctions(t *testing.T) { 600 //Test printCompositeKeys 601 // initialize a key list 602 loadKeys := []*statedb.CompositeKey{} 603 //create a composite key and add to the key list 604 compositeKey3 := statedb.CompositeKey{Namespace: "ns", Key: "key3"} 605 loadKeys = append(loadKeys, &compositeKey3) 606 compositeKey4 := statedb.CompositeKey{Namespace: "ns", Key: "key4"} 607 loadKeys = append(loadKeys, &compositeKey4) 608 require.Equal(t, "[ns,key3],[ns,key4]", printCompositeKeys(loadKeys)) 609 610 } 611 612 func TestHandleChaincodeDeploy(t *testing.T) { 613 vdbEnv.init(t, nil) 614 defer vdbEnv.cleanup() 615 616 db, err := vdbEnv.DBProvider.GetDBHandle("testinit", nil) 617 require.NoError(t, err) 618 db.Open() 619 defer db.Close() 620 batch := statedb.NewUpdateBatch() 621 622 jsonValue1 := `{"asset_name": "marble1","color": "blue","size": 1,"owner": "tom"}` 623 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 624 jsonValue2 := `{"asset_name": "marble2","color": "blue","size": 2,"owner": "jerry"}` 625 batch.Put("ns1", "key2", []byte(jsonValue2), version.NewHeight(1, 2)) 626 jsonValue3 := `{"asset_name": "marble3","color": "blue","size": 3,"owner": "fred"}` 627 batch.Put("ns1", "key3", []byte(jsonValue3), version.NewHeight(1, 3)) 628 jsonValue4 := `{"asset_name": "marble4","color": "blue","size": 4,"owner": "martha"}` 629 batch.Put("ns1", "key4", []byte(jsonValue4), version.NewHeight(1, 4)) 630 jsonValue5 := `{"asset_name": "marble5","color": "blue","size": 5,"owner": "fred"}` 631 batch.Put("ns1", "key5", []byte(jsonValue5), version.NewHeight(1, 5)) 632 jsonValue6 := `{"asset_name": "marble6","color": "blue","size": 6,"owner": "elaine"}` 633 batch.Put("ns1", "key6", []byte(jsonValue6), version.NewHeight(1, 6)) 634 jsonValue7 := `{"asset_name": "marble7","color": "blue","size": 7,"owner": "fred"}` 635 batch.Put("ns1", "key7", []byte(jsonValue7), version.NewHeight(1, 7)) 636 jsonValue8 := `{"asset_name": "marble8","color": "blue","size": 8,"owner": "elaine"}` 637 batch.Put("ns1", "key8", []byte(jsonValue8), version.NewHeight(1, 8)) 638 jsonValue9 := `{"asset_name": "marble9","color": "green","size": 9,"owner": "fred"}` 639 batch.Put("ns1", "key9", []byte(jsonValue9), version.NewHeight(1, 9)) 640 jsonValue10 := `{"asset_name": "marble10","color": "green","size": 10,"owner": "mary"}` 641 batch.Put("ns1", "key10", []byte(jsonValue10), version.NewHeight(1, 10)) 642 jsonValue11 := `{"asset_name": "marble11","color": "cyan","size": 1000007,"owner": "joe"}` 643 batch.Put("ns1", "key11", []byte(jsonValue11), version.NewHeight(1, 11)) 644 645 //add keys for a separate namespace 646 batch.Put("ns2", "key1", []byte(jsonValue1), version.NewHeight(1, 12)) 647 batch.Put("ns2", "key2", []byte(jsonValue2), version.NewHeight(1, 13)) 648 batch.Put("ns2", "key3", []byte(jsonValue3), version.NewHeight(1, 14)) 649 batch.Put("ns2", "key4", []byte(jsonValue4), version.NewHeight(1, 15)) 650 batch.Put("ns2", "key5", []byte(jsonValue5), version.NewHeight(1, 16)) 651 batch.Put("ns2", "key6", []byte(jsonValue6), version.NewHeight(1, 17)) 652 batch.Put("ns2", "key7", []byte(jsonValue7), version.NewHeight(1, 18)) 653 batch.Put("ns2", "key8", []byte(jsonValue8), version.NewHeight(1, 19)) 654 batch.Put("ns2", "key9", []byte(jsonValue9), version.NewHeight(1, 20)) 655 batch.Put("ns2", "key10", []byte(jsonValue10), version.NewHeight(1, 21)) 656 657 savePoint := version.NewHeight(2, 22) 658 db.ApplyUpdates(batch, savePoint) 659 660 indexData := map[string][]byte{ 661 "META-INF/statedb/couchdb/indexes/indexColorSortName.json": []byte(`{"index":{"fields":[{"color":"desc"}]},"ddoc":"indexColorSortName","name":"indexColorSortName","type":"json"}`), 662 "META-INF/statedb/couchdb/indexes/indexSizeSortName.json": []byte(`{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`), 663 "META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexCollMarbles.json": []byte(`{"index":{"fields":["docType","owner"]},"ddoc":"indexCollectionMarbles", "name":"indexCollectionMarbles","type":"json"}`), 664 "META-INF/statedb/couchdb/collections/collectionMarblesPrivateDetails/indexes/indexCollPrivDetails.json": []byte(`{"index":{"fields":["docType","price"]},"ddoc":"indexPrivateDetails", "name":"indexPrivateDetails","type":"json"}`), 665 } 666 667 //Create a query 668 queryString := `{"selector":{"owner":"fred"}}` 669 670 _, err = db.ExecuteQuery("ns1", queryString) 671 require.NoError(t, err) 672 673 //Create a query with a sort 674 queryString = `{"selector":{"owner":"fred"}, "sort": [{"size": "desc"}]}` 675 676 _, err = db.ExecuteQuery("ns1", queryString) 677 require.Error(t, err, "Error should have been thrown for a missing index") 678 679 indexCapable, ok := db.(statedb.IndexCapable) 680 if !ok { 681 t.Fatalf("Couchdb state impl is expected to implement interface `statedb.IndexCapable`") 682 } 683 require.NoError(t, indexCapable.ProcessIndexesForChaincodeDeploy("ns1", indexData)) 684 685 queryString = `{"selector":{"owner":"fred"}, "sort": [{"size": "desc"}]}` 686 queryUsingIndex := func() bool { 687 _, err = db.ExecuteQuery("ns1", queryString) 688 return err == nil 689 } 690 require.Eventually(t, queryUsingIndex, 2*time.Second, 100*time.Millisecond, "error executing query with sort") 691 692 //Query namespace "ns2", index is only created in "ns1". This should return an error. 693 _, err = db.ExecuteQuery("ns2", queryString) 694 require.Error(t, err, "Error should have been thrown for a missing index") 695 696 } 697 698 func TestTryCastingToJSON(t *testing.T) { 699 sampleJSON := []byte(`{"a":"A", "b":"B"}`) 700 isJSON, jsonVal := tryCastingToJSON(sampleJSON) 701 require.True(t, isJSON) 702 require.Equal(t, "A", jsonVal["a"]) 703 require.Equal(t, "B", jsonVal["b"]) 704 705 sampleNonJSON := []byte(`This is not a json`) 706 isJSON, _ = tryCastingToJSON(sampleNonJSON) 707 require.False(t, isJSON) 708 } 709 710 func TestIndexDeploymentWithOrderAndBadSyntax(t *testing.T) { 711 channelName := "ch1" 712 vdbEnv.init(t, nil) 713 defer vdbEnv.cleanup() 714 db, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 715 require.NoError(t, err) 716 db.Open() 717 defer db.Close() 718 719 batch := statedb.NewUpdateBatch() 720 batch.Put("ns1", "key1", []byte(`{"asset_name": "marble1","color": "blue","size": 1,"owner": "tom"}`), version.NewHeight(1, 1)) 721 batch.Put("ns1", "key2", []byte(`{"asset_name": "marble2","color": "blue","size": 2,"owner": "jerry"}`), version.NewHeight(1, 2)) 722 723 indexCapable, ok := db.(statedb.IndexCapable) 724 if !ok { 725 t.Fatalf("Couchdb state impl is expected to implement interface `statedb.IndexCapable`") 726 } 727 728 badSyntaxFileContent := `{"index":{"fields": This is a bad json}` 729 indexData := map[string][]byte{ 730 "META-INF/statedb/couchdb/indexes/indexColorSortName.json": []byte(`{"index":{"fields":[{"color":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`), 731 "META-INF/statedb/couchdb/indexes/indexSizeSortName.json": []byte(`{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`), 732 "META-INF/statedb/couchdb/indexes/badSyntax.json": []byte(badSyntaxFileContent), 733 "META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexCollMarbles.json": []byte(`{"index":{"fields":["docType","owner"]},"ddoc":"indexCollectionMarbles", "name":"indexCollectionMarbles","type":"json"}`), 734 } 735 736 // as the indexes are sorted by file names, the order of index processing would be 737 // (1) indexCollMarbles.json, (2) badSyntax.json, (3) indexColorSortName, (4) indexSizeSortName. 738 // As the indexColorSortName.json and indexSizeSortName has the same index name but different 739 // index fields, the later would replace the former, i.e., index would be created on size field 740 // rather than the color field. Further, the index with a bad syntax would not stop the processing 741 // of other valid indexes. 742 require.NoError(t, indexCapable.ProcessIndexesForChaincodeDeploy("ns1", indexData)) 743 744 queryString := `{"selector":{"owner":"fred"}, "sort": [{"docType": "desc"}]}` 745 queryUsingIndex := func() bool { 746 _, err = db.ExecuteQuery("ns1", queryString) 747 return err == nil 748 } 749 require.Eventually(t, queryUsingIndex, 2*time.Second, 100*time.Millisecond, "error executing query with sort") 750 751 queryString = `{"selector":{"owner":"fred"}, "sort": [{"size": "desc"}]}` 752 queryUsingIndex = func() bool { 753 _, err = db.ExecuteQuery("ns1", queryString) 754 return err == nil 755 } 756 require.Eventually(t, queryUsingIndex, 2*time.Second, 100*time.Millisecond, "error executing query with sort") 757 758 // though the indexColorSortName.json is processed before indexSizeSortName.json as per the order, 759 // the later would replace the former as the index names are the same. Hence, a query using the color 760 // field in sort should fail. 761 queryString = `{"selector":{"owner":"fred"}, "sort": [{"color": "desc"}]}` 762 queryUsingIndex = func() bool { 763 _, err = db.ExecuteQuery("ns1", queryString) 764 return err == nil 765 } 766 require.Never(t, queryUsingIndex, 2*time.Second, 100*time.Millisecond, "error should have occurred as there is no index on color field") 767 } 768 769 func TestIsBulkOptimizable(t *testing.T) { 770 var db statedb.VersionedDB = &VersionedDB{} 771 _, ok := db.(statedb.BulkOptimizable) 772 if !ok { 773 t.Fatal("state couch db is expected to implement interface statedb.BulkOptimizable") 774 } 775 } 776 777 func printCompositeKeys(keys []*statedb.CompositeKey) string { 778 compositeKeyString := []string{} 779 for _, key := range keys { 780 compositeKeyString = append(compositeKeyString, "["+key.Namespace+","+key.Key+"]") 781 } 782 return strings.Join(compositeKeyString, ",") 783 } 784 785 // TestPaginatedQuery tests queries with pagination 786 func TestPaginatedQuery(t *testing.T) { 787 vdbEnv.init(t, nil) 788 defer vdbEnv.cleanup() 789 790 db, err := vdbEnv.DBProvider.GetDBHandle("testpaginatedquery", nil) 791 require.NoError(t, err) 792 db.Open() 793 defer db.Close() 794 795 batch := statedb.NewUpdateBatch() 796 jsonValue1 := `{"asset_name": "marble1","color": "blue","size": 1,"owner": "tom"}` 797 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 798 jsonValue2 := `{"asset_name": "marble2","color": "red","size": 2,"owner": "jerry"}` 799 batch.Put("ns1", "key2", []byte(jsonValue2), version.NewHeight(1, 2)) 800 jsonValue3 := `{"asset_name": "marble3","color": "red","size": 3,"owner": "fred"}` 801 batch.Put("ns1", "key3", []byte(jsonValue3), version.NewHeight(1, 3)) 802 jsonValue4 := `{"asset_name": "marble4","color": "red","size": 4,"owner": "martha"}` 803 batch.Put("ns1", "key4", []byte(jsonValue4), version.NewHeight(1, 4)) 804 jsonValue5 := `{"asset_name": "marble5","color": "blue","size": 5,"owner": "fred"}` 805 batch.Put("ns1", "key5", []byte(jsonValue5), version.NewHeight(1, 5)) 806 jsonValue6 := `{"asset_name": "marble6","color": "red","size": 6,"owner": "elaine"}` 807 batch.Put("ns1", "key6", []byte(jsonValue6), version.NewHeight(1, 6)) 808 jsonValue7 := `{"asset_name": "marble7","color": "blue","size": 7,"owner": "fred"}` 809 batch.Put("ns1", "key7", []byte(jsonValue7), version.NewHeight(1, 7)) 810 jsonValue8 := `{"asset_name": "marble8","color": "red","size": 8,"owner": "elaine"}` 811 batch.Put("ns1", "key8", []byte(jsonValue8), version.NewHeight(1, 8)) 812 jsonValue9 := `{"asset_name": "marble9","color": "green","size": 9,"owner": "fred"}` 813 batch.Put("ns1", "key9", []byte(jsonValue9), version.NewHeight(1, 9)) 814 jsonValue10 := `{"asset_name": "marble10","color": "green","size": 10,"owner": "mary"}` 815 batch.Put("ns1", "key10", []byte(jsonValue10), version.NewHeight(1, 10)) 816 817 jsonValue11 := `{"asset_name": "marble11","color": "cyan","size": 11,"owner": "joe"}` 818 batch.Put("ns1", "key11", []byte(jsonValue11), version.NewHeight(1, 11)) 819 jsonValue12 := `{"asset_name": "marble12","color": "red","size": 12,"owner": "martha"}` 820 batch.Put("ns1", "key12", []byte(jsonValue12), version.NewHeight(1, 4)) 821 jsonValue13 := `{"asset_name": "marble13","color": "red","size": 13,"owner": "james"}` 822 batch.Put("ns1", "key13", []byte(jsonValue13), version.NewHeight(1, 4)) 823 jsonValue14 := `{"asset_name": "marble14","color": "red","size": 14,"owner": "fred"}` 824 batch.Put("ns1", "key14", []byte(jsonValue14), version.NewHeight(1, 4)) 825 jsonValue15 := `{"asset_name": "marble15","color": "red","size": 15,"owner": "mary"}` 826 batch.Put("ns1", "key15", []byte(jsonValue15), version.NewHeight(1, 4)) 827 jsonValue16 := `{"asset_name": "marble16","color": "red","size": 16,"owner": "robert"}` 828 batch.Put("ns1", "key16", []byte(jsonValue16), version.NewHeight(1, 4)) 829 jsonValue17 := `{"asset_name": "marble17","color": "red","size": 17,"owner": "alan"}` 830 batch.Put("ns1", "key17", []byte(jsonValue17), version.NewHeight(1, 4)) 831 jsonValue18 := `{"asset_name": "marble18","color": "red","size": 18,"owner": "elaine"}` 832 batch.Put("ns1", "key18", []byte(jsonValue18), version.NewHeight(1, 4)) 833 jsonValue19 := `{"asset_name": "marble19","color": "red","size": 19,"owner": "alan"}` 834 batch.Put("ns1", "key19", []byte(jsonValue19), version.NewHeight(1, 4)) 835 jsonValue20 := `{"asset_name": "marble20","color": "red","size": 20,"owner": "elaine"}` 836 batch.Put("ns1", "key20", []byte(jsonValue20), version.NewHeight(1, 4)) 837 838 jsonValue21 := `{"asset_name": "marble21","color": "cyan","size": 21,"owner": "joe"}` 839 batch.Put("ns1", "key21", []byte(jsonValue21), version.NewHeight(1, 11)) 840 jsonValue22 := `{"asset_name": "marble22","color": "red","size": 22,"owner": "martha"}` 841 batch.Put("ns1", "key22", []byte(jsonValue22), version.NewHeight(1, 4)) 842 jsonValue23 := `{"asset_name": "marble23","color": "blue","size": 23,"owner": "james"}` 843 batch.Put("ns1", "key23", []byte(jsonValue23), version.NewHeight(1, 4)) 844 jsonValue24 := `{"asset_name": "marble24","color": "red","size": 24,"owner": "fred"}` 845 batch.Put("ns1", "key24", []byte(jsonValue24), version.NewHeight(1, 4)) 846 jsonValue25 := `{"asset_name": "marble25","color": "red","size": 25,"owner": "mary"}` 847 batch.Put("ns1", "key25", []byte(jsonValue25), version.NewHeight(1, 4)) 848 jsonValue26 := `{"asset_name": "marble26","color": "red","size": 26,"owner": "robert"}` 849 batch.Put("ns1", "key26", []byte(jsonValue26), version.NewHeight(1, 4)) 850 jsonValue27 := `{"asset_name": "marble27","color": "green","size": 27,"owner": "alan"}` 851 batch.Put("ns1", "key27", []byte(jsonValue27), version.NewHeight(1, 4)) 852 jsonValue28 := `{"asset_name": "marble28","color": "red","size": 28,"owner": "elaine"}` 853 batch.Put("ns1", "key28", []byte(jsonValue28), version.NewHeight(1, 4)) 854 jsonValue29 := `{"asset_name": "marble29","color": "red","size": 29,"owner": "alan"}` 855 batch.Put("ns1", "key29", []byte(jsonValue29), version.NewHeight(1, 4)) 856 jsonValue30 := `{"asset_name": "marble30","color": "red","size": 30,"owner": "elaine"}` 857 batch.Put("ns1", "key30", []byte(jsonValue30), version.NewHeight(1, 4)) 858 859 jsonValue31 := `{"asset_name": "marble31","color": "cyan","size": 31,"owner": "joe"}` 860 batch.Put("ns1", "key31", []byte(jsonValue31), version.NewHeight(1, 11)) 861 jsonValue32 := `{"asset_name": "marble32","color": "red","size": 32,"owner": "martha"}` 862 batch.Put("ns1", "key32", []byte(jsonValue32), version.NewHeight(1, 4)) 863 jsonValue33 := `{"asset_name": "marble33","color": "red","size": 33,"owner": "james"}` 864 batch.Put("ns1", "key33", []byte(jsonValue33), version.NewHeight(1, 4)) 865 jsonValue34 := `{"asset_name": "marble34","color": "red","size": 34,"owner": "fred"}` 866 batch.Put("ns1", "key34", []byte(jsonValue34), version.NewHeight(1, 4)) 867 jsonValue35 := `{"asset_name": "marble35","color": "red","size": 35,"owner": "mary"}` 868 batch.Put("ns1", "key35", []byte(jsonValue35), version.NewHeight(1, 4)) 869 jsonValue36 := `{"asset_name": "marble36","color": "orange","size": 36,"owner": "robert"}` 870 batch.Put("ns1", "key36", []byte(jsonValue36), version.NewHeight(1, 4)) 871 jsonValue37 := `{"asset_name": "marble37","color": "red","size": 37,"owner": "alan"}` 872 batch.Put("ns1", "key37", []byte(jsonValue37), version.NewHeight(1, 4)) 873 jsonValue38 := `{"asset_name": "marble38","color": "yellow","size": 38,"owner": "elaine"}` 874 batch.Put("ns1", "key38", []byte(jsonValue38), version.NewHeight(1, 4)) 875 jsonValue39 := `{"asset_name": "marble39","color": "red","size": 39,"owner": "alan"}` 876 batch.Put("ns1", "key39", []byte(jsonValue39), version.NewHeight(1, 4)) 877 jsonValue40 := `{"asset_name": "marble40","color": "red","size": 40,"owner": "elaine"}` 878 batch.Put("ns1", "key40", []byte(jsonValue40), version.NewHeight(1, 4)) 879 880 savePoint := version.NewHeight(2, 22) 881 db.ApplyUpdates(batch, savePoint) 882 883 indexData := map[string][]byte{ 884 "META-INF/statedb/couchdb/indexes/indexSizeSortName.json": []byte(`{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`), 885 } 886 887 // Create a query 888 queryString := `{"selector":{"color":"red"}}` 889 890 _, err = db.ExecuteQuery("ns1", queryString) 891 require.NoError(t, err) 892 893 indexCapable, ok := db.(statedb.IndexCapable) 894 if !ok { 895 t.Fatalf("Couchdb state impl is expected to implement interface `statedb.IndexCapable`") 896 } 897 898 indexCapable.ProcessIndexesForChaincodeDeploy("ns1", indexData) 899 // Sleep to allow time for index creation 900 time.Sleep(100 * time.Millisecond) 901 // Create a query with a sort 902 queryString = `{"selector":{"color":"red"}, "sort": [{"size": "asc"}]}` 903 904 // Query should complete without error 905 _, err = db.ExecuteQuery("ns1", queryString) 906 require.NoError(t, err) 907 908 // Test explicit paging 909 // Execute 3 page queries, there are 28 records with color red, use page size 10 910 returnKeys := []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", "key16"} 911 bookmark, err := executeQuery(t, db, "ns1", queryString, "", int32(10), returnKeys) 912 require.NoError(t, err) 913 returnKeys = []string{"key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29"} 914 bookmark, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys) 915 require.NoError(t, err) 916 917 returnKeys = []string{"key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40"} 918 _, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys) 919 require.NoError(t, err) 920 921 // Test explicit paging 922 // Increase pagesize to 50, should return all values 923 returnKeys = []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", 924 "key16", "key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29", 925 "key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40"} 926 _, err = executeQuery(t, db, "ns1", queryString, "", int32(50), returnKeys) 927 require.NoError(t, err) 928 929 // Test explicit paging 930 // Pagesize is 10, so all 28 records should be return in 3 "pages" 931 returnKeys = []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", "key16"} 932 bookmark, err = executeQuery(t, db, "ns1", queryString, "", int32(10), returnKeys) 933 require.NoError(t, err) 934 returnKeys = []string{"key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29"} 935 bookmark, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys) 936 require.NoError(t, err) 937 returnKeys = []string{"key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40"} 938 _, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys) 939 require.NoError(t, err) 940 941 // Test implicit paging 942 returnKeys = []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", 943 "key16", "key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29", 944 "key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40"} 945 _, err = executeQuery(t, db, "ns1", queryString, "", int32(0), returnKeys) 946 require.NoError(t, err) 947 948 // pagesize greater than querysize will execute with implicit paging 949 returnKeys = []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", "key16"} 950 _, err = executeQuery(t, db, "ns1", queryString, "", int32(10), returnKeys) 951 require.NoError(t, err) 952 } 953 954 func executeQuery(t *testing.T, db statedb.VersionedDB, namespace, query, bookmark string, pageSize int32, returnKeys []string) (string, error) { 955 var itr statedb.ResultsIterator 956 var err error 957 958 if pageSize == int32(0) && bookmark == "" { 959 itr, err = db.ExecuteQuery(namespace, query) 960 if err != nil { 961 return "", err 962 } 963 } else { 964 itr, err = db.ExecuteQueryWithPagination(namespace, query, bookmark, pageSize) 965 if err != nil { 966 return "", err 967 } 968 } 969 970 // Verify the keys returned 971 commontests.TestItrWithoutClose(t, itr, returnKeys) 972 973 returnBookmark := "" 974 if queryResultItr, ok := itr.(statedb.QueryResultsIterator); ok { 975 returnBookmark = queryResultItr.GetBookmarkAndClose() 976 } 977 978 return returnBookmark, nil 979 } 980 981 func TestApplyUpdatesWithNilHeight(t *testing.T) { 982 vdbEnv.init(t, nil) 983 defer vdbEnv.cleanup() 984 commontests.TestApplyUpdatesWithNilHeight(t, vdbEnv.DBProvider) 985 } 986 987 func TestRangeScanWithCouchInternalDocsPresent(t *testing.T) { 988 vdbEnv.init(t, nil) 989 defer vdbEnv.cleanup() 990 db, err := vdbEnv.DBProvider.GetDBHandle("testrangescanfiltercouchinternaldocs", nil) 991 require.NoError(t, err) 992 couchDatabse, err := db.(*VersionedDB).getNamespaceDBHandle("ns") 993 require.NoError(t, err) 994 db.Open() 995 defer db.Close() 996 _, err = couchDatabse.createIndex(`{ 997 "index" : {"fields" : ["asset_name"]}, 998 "ddoc" : "indexAssetName", 999 "name" : "indexAssetName", 1000 "type" : "json" 1001 }`) 1002 require.NoError(t, err) 1003 1004 _, err = couchDatabse.createIndex(`{ 1005 "index" : {"fields" : ["assetValue"]}, 1006 "ddoc" : "indexAssetValue", 1007 "name" : "indexAssetValue", 1008 "type" : "json" 1009 }`) 1010 require.NoError(t, err) 1011 1012 batch := statedb.NewUpdateBatch() 1013 for i := 1; i <= 3; i++ { 1014 keySmallerThanDesignDoc := fmt.Sprintf("Key-%d", i) 1015 keyGreaterThanDesignDoc := fmt.Sprintf("key-%d", i) 1016 jsonValue := fmt.Sprintf(`{"asset_name": "marble-%d"}`, i) 1017 batch.Put("ns", keySmallerThanDesignDoc, []byte(jsonValue), version.NewHeight(1, uint64(i))) 1018 batch.Put("ns", keyGreaterThanDesignDoc, []byte(jsonValue), version.NewHeight(1, uint64(i))) 1019 } 1020 db.ApplyUpdates(batch, version.NewHeight(2, 2)) 1021 require.NoError(t, err) 1022 1023 // The Keys in db are in this order 1024 // Key-1, Key-2, Key-3,_design/indexAssetNam, _design/indexAssetValue, key-1, key-2, key-3 1025 // query different ranges and verify results 1026 s, err := newQueryScanner("ns", couchDatabse, "", 3, 3, "", "", "") 1027 require.NoError(t, err) 1028 assertQueryResults(t, s.resultsInfo.results, []string{"Key-1", "Key-2", "Key-3"}) 1029 require.Equal(t, "key-1", s.queryDefinition.startKey) 1030 1031 s, err = newQueryScanner("ns", couchDatabse, "", 4, 4, "", "", "") 1032 require.NoError(t, err) 1033 assertQueryResults(t, s.resultsInfo.results, []string{"Key-1", "Key-2", "Key-3", "key-1"}) 1034 require.Equal(t, "key-2", s.queryDefinition.startKey) 1035 1036 s, err = newQueryScanner("ns", couchDatabse, "", 2, 2, "", "", "") 1037 require.NoError(t, err) 1038 assertQueryResults(t, s.resultsInfo.results, []string{"Key-1", "Key-2"}) 1039 require.Equal(t, "Key-3", s.queryDefinition.startKey) 1040 s.getNextStateRangeScanResults() 1041 assertQueryResults(t, s.resultsInfo.results, []string{"Key-3", "key-1"}) 1042 require.Equal(t, "key-2", s.queryDefinition.startKey) 1043 1044 s, err = newQueryScanner("ns", couchDatabse, "", 2, 2, "", "_", "") 1045 require.NoError(t, err) 1046 assertQueryResults(t, s.resultsInfo.results, []string{"key-1", "key-2"}) 1047 require.Equal(t, "key-3", s.queryDefinition.startKey) 1048 } 1049 1050 func assertQueryResults(t *testing.T, results []*queryResult, expectedIds []string) { 1051 var actualIds []string 1052 for _, res := range results { 1053 actualIds = append(actualIds, res.id) 1054 } 1055 require.Equal(t, expectedIds, actualIds) 1056 } 1057 1058 func TestFormatCheck(t *testing.T) { 1059 testCases := []struct { 1060 dataFormat string // precondition 1061 dataExists bool // precondition 1062 expectedFormat string // postcondition 1063 expectedErr *dataformat.ErrFormatMismatch // postcondition 1064 }{ 1065 { 1066 dataFormat: "", 1067 dataExists: true, 1068 expectedErr: &dataformat.ErrFormatMismatch{ 1069 DBInfo: "CouchDB for state database", 1070 Format: "", 1071 ExpectedFormat: "2.0", 1072 }, 1073 expectedFormat: "does not matter as the test should not reach to check this", 1074 }, 1075 1076 { 1077 dataFormat: "", 1078 dataExists: false, 1079 expectedErr: nil, 1080 expectedFormat: dataformat.CurrentFormat, 1081 }, 1082 1083 { 1084 dataFormat: dataformat.CurrentFormat, 1085 dataExists: false, 1086 expectedFormat: dataformat.CurrentFormat, 1087 expectedErr: nil, 1088 }, 1089 1090 { 1091 dataFormat: dataformat.CurrentFormat, 1092 dataExists: true, 1093 expectedFormat: dataformat.CurrentFormat, 1094 expectedErr: nil, 1095 }, 1096 1097 { 1098 dataFormat: "3.0", 1099 dataExists: true, 1100 expectedErr: &dataformat.ErrFormatMismatch{ 1101 DBInfo: "CouchDB for state database", 1102 Format: "3.0", 1103 ExpectedFormat: dataformat.CurrentFormat, 1104 }, 1105 expectedFormat: "does not matter as the test should not reach to check this", 1106 }, 1107 } 1108 1109 vdbEnv.init(t, nil) 1110 for i, testCase := range testCases { 1111 t.Run( 1112 fmt.Sprintf("testCase %d", i), 1113 func(t *testing.T) { 1114 testFormatCheck(t, testCase.dataFormat, testCase.dataExists, testCase.expectedErr, testCase.expectedFormat, vdbEnv) 1115 }) 1116 } 1117 } 1118 1119 func testFormatCheck(t *testing.T, dataFormat string, dataExists bool, expectedErr *dataformat.ErrFormatMismatch, expectedFormat string, vdbEnv *testVDBEnv) { 1120 redoPath, err := ioutil.TempDir("", "redoPath") 1121 require.NoError(t, err) 1122 defer os.RemoveAll(redoPath) 1123 config := &ledger.CouchDBConfig{ 1124 Address: vdbEnv.couchDBEnv.couchAddress, 1125 Username: "admin", 1126 Password: "adminpw", 1127 MaxRetries: 3, 1128 MaxRetriesOnStartup: 20, 1129 RequestTimeout: 35 * time.Second, 1130 RedoLogPath: redoPath, 1131 } 1132 dbProvider, err := NewVersionedDBProvider(config, &disabled.Provider{}, nil) 1133 require.NoError(t, err) 1134 1135 // create preconditions for test 1136 if dataExists { 1137 db, err := dbProvider.GetDBHandle("testns", nil) 1138 require.NoError(t, err) 1139 batch := statedb.NewUpdateBatch() 1140 batch.Put("testns", "testkey", []byte("testVal"), version.NewHeight(1, 1)) 1141 require.NoError(t, db.ApplyUpdates(batch, version.NewHeight(1, 1))) 1142 } 1143 if dataFormat == "" { 1144 response, err := dropDB(dbProvider.couchInstance, fabricInternalDBName) 1145 require.NoError(t, err) 1146 require.True(t, response.Ok) 1147 } else { 1148 require.NoError(t, writeDataFormatVersion(dbProvider.couchInstance, dataFormat)) 1149 } 1150 dbProvider.Close() 1151 defer func() { 1152 require.NoError(t, DropApplicationDBs(vdbEnv.config)) 1153 }() 1154 1155 // close and reopen with preconditions set and check the expected behavior 1156 dbProvider, err = NewVersionedDBProvider(config, &disabled.Provider{}, nil) 1157 if expectedErr != nil { 1158 require.Equal(t, expectedErr, err) 1159 return 1160 } 1161 require.NoError(t, err) 1162 defer func() { 1163 if dbProvider != nil { 1164 dbProvider.Close() 1165 } 1166 }() 1167 format, err := readDataformatVersion(dbProvider.couchInstance) 1168 require.NoError(t, err) 1169 require.Equal(t, expectedFormat, format) 1170 } 1171 1172 func testDoesNotExistInCache(t *testing.T, cache *cache, chainID, ns, key string) { 1173 cacheValue, err := cache.getState(chainID, ns, key) 1174 require.NoError(t, err) 1175 require.Nil(t, cacheValue) 1176 } 1177 1178 func testExistInCache(t *testing.T, db *couchDatabase, cache *cache, chainID, ns, key string, expectedVV *statedb.VersionedValue) { 1179 cacheValue, err := cache.getState(chainID, ns, key) 1180 require.NoError(t, err) 1181 vv, err := constructVersionedValue(cacheValue) 1182 require.NoError(t, err) 1183 require.Equal(t, expectedVV, vv) 1184 metadata, err := retrieveNsMetadata(db, []string{key}) 1185 require.NoError(t, err) 1186 require.Equal(t, metadata[0].Rev, string(cacheValue.AdditionalInfo)) 1187 } 1188 1189 func TestLoadCommittedVersion(t *testing.T) { 1190 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 1191 defer vdbEnv.cleanup() 1192 1193 chainID := "testloadcommittedversion" 1194 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 1195 require.NoError(t, err) 1196 1197 // scenario: state cache has (ns1, key1), (ns1, key2), 1198 // and (ns2, key1) but misses (ns2, key2). The 1199 // LoadCommittedVersions will fetch the first 1200 // three keys from the state cache and the remaining one from 1201 // the db. To ensure that, the db contains only 1202 // the missing key (ns2, key2). 1203 1204 // store (ns1, key1), (ns1, key2), (ns2, key1) in the state cache 1205 cacheValue := &CacheValue{ 1206 Value: []byte("value1"), 1207 Metadata: []byte("meta1"), 1208 Version: version.NewHeight(1, 1).ToBytes(), 1209 AdditionalInfo: []byte("rev1"), 1210 } 1211 require.NoError(t, vdbEnv.cache.putState(chainID, "ns1", "key1", cacheValue)) 1212 1213 cacheValue = &CacheValue{ 1214 Value: []byte("value2"), 1215 Metadata: []byte("meta2"), 1216 Version: version.NewHeight(1, 2).ToBytes(), 1217 AdditionalInfo: []byte("rev2"), 1218 } 1219 require.NoError(t, vdbEnv.cache.putState(chainID, "ns1", "key2", cacheValue)) 1220 1221 cacheValue = &CacheValue{ 1222 Value: []byte("value3"), 1223 Metadata: []byte("meta3"), 1224 Version: version.NewHeight(1, 3).ToBytes(), 1225 AdditionalInfo: []byte("rev3"), 1226 } 1227 require.NoError(t, vdbEnv.cache.putState(chainID, "ns2", "key1", cacheValue)) 1228 1229 // store (ns2, key2) in the db 1230 batch := statedb.NewUpdateBatch() 1231 vv := &statedb.VersionedValue{Value: []byte("value4"), Metadata: []byte("meta4"), Version: version.NewHeight(1, 4)} 1232 batch.PutValAndMetadata("ns2", "key2", vv.Value, vv.Metadata, vv.Version) 1233 savePoint := version.NewHeight(2, 2) 1234 db.ApplyUpdates(batch, savePoint) 1235 1236 // version cache should be empty 1237 ver, ok := db.(*VersionedDB).GetCachedVersion("ns1", "key1") 1238 require.Nil(t, ver) 1239 require.False(t, ok) 1240 ver, ok = db.(*VersionedDB).GetCachedVersion("ns1", "key2") 1241 require.Nil(t, ver) 1242 require.False(t, ok) 1243 ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key1") 1244 require.Nil(t, ver) 1245 require.False(t, ok) 1246 ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key2") 1247 require.Nil(t, ver) 1248 require.False(t, ok) 1249 1250 keys := []*statedb.CompositeKey{ 1251 { 1252 Namespace: "ns1", 1253 Key: "key1", 1254 }, 1255 { 1256 Namespace: "ns1", 1257 Key: "key2", 1258 }, 1259 { 1260 Namespace: "ns2", 1261 Key: "key1", 1262 }, 1263 { 1264 Namespace: "ns2", 1265 Key: "key2", 1266 }, 1267 } 1268 1269 require.NoError(t, db.(*VersionedDB).LoadCommittedVersions(keys)) 1270 1271 ver, ok = db.(*VersionedDB).GetCachedVersion("ns1", "key1") 1272 require.Equal(t, version.NewHeight(1, 1), ver) 1273 require.True(t, ok) 1274 ver, ok = db.(*VersionedDB).GetCachedVersion("ns1", "key2") 1275 require.Equal(t, version.NewHeight(1, 2), ver) 1276 require.True(t, ok) 1277 ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key1") 1278 require.Equal(t, version.NewHeight(1, 3), ver) 1279 require.True(t, ok) 1280 ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key2") 1281 require.Equal(t, version.NewHeight(1, 4), ver) 1282 require.True(t, ok) 1283 } 1284 1285 func TestMissingRevisionRetrievalFromDB(t *testing.T) { 1286 vdbEnv.init(t, nil) 1287 defer vdbEnv.cleanup() 1288 chainID := "testmissingrevisionfromdb" 1289 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 1290 require.NoError(t, err) 1291 1292 // store key1, key2, key3 to the DB 1293 batch := statedb.NewUpdateBatch() 1294 vv1 := statedb.VersionedValue{Value: []byte("value1"), Version: version.NewHeight(1, 1)} 1295 vv2 := statedb.VersionedValue{Value: []byte("value2"), Version: version.NewHeight(1, 2)} 1296 vv3 := statedb.VersionedValue{Value: []byte("value3"), Version: version.NewHeight(1, 3)} 1297 batch.Put("ns1", "key1", vv1.Value, vv1.Version) 1298 batch.Put("ns1", "key2", vv2.Value, vv2.Version) 1299 batch.Put("ns1", "key3", vv3.Value, vv3.Version) 1300 savePoint := version.NewHeight(2, 5) 1301 db.ApplyUpdates(batch, savePoint) 1302 1303 // retrieve the versions of key1, key2, and key3 1304 revisions := make(map[string]string) 1305 require.NoError(t, db.(*VersionedDB).addMissingRevisionsFromDB("ns1", []string{"key1", "key2", "key3"}, revisions)) 1306 require.Equal(t, 3, len(revisions)) 1307 1308 // update key1 and key2 but not key3 1309 batch = statedb.NewUpdateBatch() 1310 vv4 := statedb.VersionedValue{Value: []byte("value1"), Version: version.NewHeight(1, 1)} 1311 vv5 := statedb.VersionedValue{Value: []byte("value2"), Version: version.NewHeight(1, 2)} 1312 batch.Put("ns1", "key1", vv4.Value, vv4.Version) 1313 batch.Put("ns1", "key2", vv5.Value, vv5.Version) 1314 savePoint = version.NewHeight(3, 5) 1315 db.ApplyUpdates(batch, savePoint) 1316 1317 // for key3, the revision should be the same but not for key1 and key2 1318 newRevisions := make(map[string]string) 1319 require.NoError(t, db.(*VersionedDB).addMissingRevisionsFromDB("ns1", []string{"key1", "key2", "key3"}, newRevisions)) 1320 require.Equal(t, 3, len(newRevisions)) 1321 require.NotEqual(t, revisions["key1"], newRevisions["key1"]) 1322 require.NotEqual(t, revisions["key2"], newRevisions["key2"]) 1323 require.Equal(t, revisions["key3"], newRevisions["key3"]) 1324 } 1325 1326 func TestMissingRevisionRetrievalFromCache(t *testing.T) { 1327 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 1328 defer vdbEnv.cleanup() 1329 1330 chainID := "testmissingrevisionfromcache" 1331 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 1332 require.NoError(t, err) 1333 1334 // scenario 1: missing from cache. 1335 revisions := make(map[string]string) 1336 stillMissingKeys, err := db.(*VersionedDB).addMissingRevisionsFromCache("ns1", []string{"key1", "key2"}, revisions) 1337 require.NoError(t, err) 1338 require.Equal(t, []string{"key1", "key2"}, stillMissingKeys) 1339 require.Empty(t, revisions) 1340 1341 // scenario 2: key1 is available in the cache 1342 require.NoError(t, vdbEnv.cache.putState(chainID, "ns1", "key1", &CacheValue{AdditionalInfo: []byte("rev1")})) 1343 revisions = make(map[string]string) 1344 stillMissingKeys, err = db.(*VersionedDB).addMissingRevisionsFromCache("ns1", []string{"key1", "key2"}, revisions) 1345 require.NoError(t, err) 1346 require.Equal(t, []string{"key2"}, stillMissingKeys) 1347 require.Equal(t, "rev1", revisions["key1"]) 1348 1349 // scenario 3: both key1 and key2 are available in the cache 1350 require.NoError(t, vdbEnv.cache.putState(chainID, "ns1", "key2", &CacheValue{AdditionalInfo: []byte("rev2")})) 1351 revisions = make(map[string]string) 1352 stillMissingKeys, err = db.(*VersionedDB).addMissingRevisionsFromCache("ns1", []string{"key1", "key2"}, revisions) 1353 require.NoError(t, err) 1354 require.Empty(t, stillMissingKeys) 1355 require.Equal(t, "rev1", revisions["key1"]) 1356 require.Equal(t, "rev2", revisions["key2"]) 1357 } 1358 1359 func TestChannelMetadata(t *testing.T) { 1360 vdbEnv.init(t, sysNamespaces) 1361 defer vdbEnv.cleanup() 1362 channelName := "testchannelmetadata" 1363 1364 db, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1365 require.NoError(t, err) 1366 vdb := db.(*VersionedDB) 1367 expectedChannelMetadata := &channelMetadata{ 1368 ChannelName: channelName, 1369 NamespaceDBsInfo: make(map[string]*namespaceDBInfo), 1370 } 1371 savedChannelMetadata, err := vdb.readChannelMetadata() 1372 require.NoError(t, err) 1373 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1374 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1375 1376 // call getNamespaceDBHandle for new dbs, verify that new db names are added to dbMetadataMapping 1377 namepsaces := make([]string, 10) 1378 for i := 0; i < 10; i++ { 1379 ns := fmt.Sprintf("nsname_%d", i) 1380 _, err := vdb.getNamespaceDBHandle(ns) 1381 require.NoError(t, err) 1382 namepsaces[i] = ns 1383 expectedChannelMetadata.NamespaceDBsInfo[ns] = &namespaceDBInfo{ 1384 Namespace: ns, 1385 DBName: constructNamespaceDBName(channelName, ns), 1386 } 1387 } 1388 1389 savedChannelMetadata, err = vdb.readChannelMetadata() 1390 require.NoError(t, err) 1391 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1392 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1393 1394 // call getNamespaceDBHandle for existing dbs, verify that no new db names are added to dbMetadataMapping 1395 for _, ns := range namepsaces { 1396 _, err := vdb.getNamespaceDBHandle(ns) 1397 require.NoError(t, err) 1398 } 1399 1400 savedChannelMetadata, err = vdb.readChannelMetadata() 1401 require.NoError(t, err) 1402 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1403 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1404 } 1405 1406 func TestChannelMetadata_NegativeTests(t *testing.T) { 1407 vdbEnv.init(t, sysNamespaces) 1408 defer vdbEnv.cleanup() 1409 1410 channelName := "testchannelmetadata-errorpropagation" 1411 origCouchAddress := vdbEnv.config.Address 1412 vdbEnv.config.MaxRetries = 1 1413 vdbEnv.config.MaxRetriesOnStartup = 1 1414 vdbEnv.config.RequestTimeout = 1 * time.Second 1415 1416 // simulate db connection error by setting an invalid address before GetDBHandle, verify error is propagated 1417 vdbEnv.config.Address = "127.0.0.1:1" 1418 expectedErrMsg := fmt.Sprintf("http error calling couchdb: Get \"http://%s/testchannelmetadata-errorpropagation_\": dial tcp %s: connect: connection refused", 1419 vdbEnv.config.Address, vdbEnv.config.Address) 1420 _, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1421 require.EqualError(t, err, expectedErrMsg) 1422 vdbEnv.config.Address = origCouchAddress 1423 1424 // simulate db connection error by setting an invalid address before getNamespaceDBHandle, verify error is propagated 1425 db, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1426 require.NoError(t, err) 1427 vdb := db.(*VersionedDB) 1428 vdbEnv.config.Address = "127.0.0.1:1" 1429 expectedErrMsg = fmt.Sprintf("http error calling couchdb: Put \"http://%s/testchannelmetadata-errorpropagation_/channel_metadata\": dial tcp %s: connect: connection refused", 1430 vdbEnv.config.Address, vdbEnv.config.Address) 1431 _, err = vdb.getNamespaceDBHandle("testnamepsace1") 1432 require.EqualError(t, err, expectedErrMsg) 1433 vdb.couchInstance.conf.Address = origCouchAddress 1434 1435 // call createCouchDatabase to simulate peer crashes after metadataDB is created but before channelMetadata is updated 1436 // then call DBProvider.GetDBHandle and verify channelMetadata is correctly generated 1437 channelName = "testchannelmetadata-simulatefailure-in-between" 1438 couchInstance, err := createCouchInstance(vdbEnv.config, &disabled.Provider{}) 1439 require.NoError(t, err) 1440 metadatadbName := constructMetadataDBName(channelName) 1441 metadataDB, err := createCouchDatabase(couchInstance, metadatadbName) 1442 require.NoError(t, err) 1443 vdb = &VersionedDB{ 1444 metadataDB: metadataDB, 1445 } 1446 savedChannelMetadata, err := vdb.readChannelMetadata() 1447 require.NoError(t, err) 1448 require.Nil(t, savedChannelMetadata) 1449 1450 db, err = vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1451 require.NoError(t, err) 1452 vdb = db.(*VersionedDB) 1453 expectedChannelMetadata := &channelMetadata{ 1454 ChannelName: channelName, 1455 NamespaceDBsInfo: make(map[string]*namespaceDBInfo), 1456 } 1457 savedChannelMetadata, err = vdb.readChannelMetadata() 1458 require.NoError(t, err) 1459 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1460 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1461 1462 // call writeChannelMetadata to simulate peer crashes after channelMetada is saved but before namespace DB is created 1463 // then call vdb.getNamespaceDBHandle and verify namespaceDB is created and channelMetadata is correct 1464 namespace := "testnamepsace2" 1465 namespaceDBName := constructNamespaceDBName(channelName, namespace) 1466 vdb.channelMetadata.NamespaceDBsInfo[namespace] = &namespaceDBInfo{Namespace: namespace, DBName: namespaceDBName} 1467 err = vdb.writeChannelMetadata() 1468 require.NoError(t, err) 1469 expectedChannelMetadata.NamespaceDBsInfo = map[string]*namespaceDBInfo{ 1470 namespace: {Namespace: namespace, DBName: namespaceDBName}, 1471 } 1472 savedChannelMetadata, err = vdb.readChannelMetadata() 1473 require.NoError(t, err) 1474 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1475 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1476 1477 _, err = vdb.getNamespaceDBHandle(namespace) 1478 require.NoError(t, err) 1479 savedChannelMetadata, err = vdb.readChannelMetadata() 1480 require.NoError(t, err) 1481 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1482 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1483 } 1484 1485 func TestInitChannelMetadta(t *testing.T) { 1486 vdbEnv.init(t, sysNamespaces) 1487 defer vdbEnv.cleanup() 1488 channelName1 := "testinithannelmetadata" 1489 channelName2 := "testinithannelmetadata_anotherchannel" 1490 1491 // create versioned DBs for channelName1 and channelName2 1492 db, err := vdbEnv.DBProvider.GetDBHandle(channelName1, nil) 1493 require.NoError(t, err) 1494 vdb := db.(*VersionedDB) 1495 db2, err := vdbEnv.DBProvider.GetDBHandle(channelName2, nil) 1496 require.NoError(t, err) 1497 vdb2 := db2.(*VersionedDB) 1498 1499 // prepare test data: 1500 // create dbs for channelName1: "ns1" and "ns3", which should match channelName1 namespaces 1501 // create dbs for channelName2: "ns2" and "ns4", which should not match any channelName1 namespaces 1502 _, err = vdb.getNamespaceDBHandle("ns1") 1503 require.NoError(t, err) 1504 _, err = vdb.getNamespaceDBHandle("ns3") 1505 require.NoError(t, err) 1506 _, err = vdb2.getNamespaceDBHandle("ns2") 1507 require.NoError(t, err) 1508 _, err = vdb2.getNamespaceDBHandle("ns4") 1509 require.NoError(t, err) 1510 1511 namespaces := []string{"ns1", "ns2", "ns3", "ns4"} 1512 fakeNsProvider := &mock.NamespaceProvider{} 1513 fakeNsProvider.PossibleNamespacesReturns(namespaces, nil) 1514 expectedDBsInfo := map[string]*namespaceDBInfo{ 1515 "ns1": {Namespace: "ns1", DBName: constructNamespaceDBName(channelName1, "ns1")}, 1516 "ns3": {Namespace: "ns3", DBName: constructNamespaceDBName(channelName1, "ns3")}, 1517 } 1518 expectedChannelMetadata := &channelMetadata{ 1519 ChannelName: channelName1, 1520 NamespaceDBsInfo: expectedDBsInfo, 1521 } 1522 1523 // test an existing DB with channelMetadata, namespace provider should not be called 1524 require.NoError(t, vdb.initChannelMetadata(false, fakeNsProvider)) 1525 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1526 require.Equal(t, 0, fakeNsProvider.PossibleNamespacesCallCount()) 1527 1528 // test an existing DB with no channelMetadata by deleting channelMetadata, namespace provider should be called 1529 require.NoError(t, vdb.metadataDB.deleteDoc(channelMetadataDocID, "")) 1530 require.NoError(t, vdb.initChannelMetadata(false, fakeNsProvider)) 1531 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1532 require.Equal(t, 1, fakeNsProvider.PossibleNamespacesCallCount()) 1533 savedChannelMetadata, err := vdb.readChannelMetadata() 1534 require.NoError(t, err) 1535 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1536 1537 // test namespaceProvider error 1538 fakeNsProvider.PossibleNamespacesReturns(nil, errors.New("fake-namespaceprivder-error")) 1539 require.NoError(t, vdb.metadataDB.deleteDoc(channelMetadataDocID, "")) 1540 err = vdb.initChannelMetadata(false, fakeNsProvider) 1541 require.EqualError(t, err, "fake-namespaceprivder-error") 1542 1543 // test db error 1544 origCouchAddress := vdbEnv.config.Address 1545 vdbEnv.config.Address = "127.0.0.1:1" 1546 vdbEnv.config.MaxRetries = 1 1547 vdbEnv.config.MaxRetriesOnStartup = 1 1548 expectedErrMsg := fmt.Sprintf("http error calling couchdb: Get \"http://%s/testinithannelmetadata_/channel_metadata?attachments=true\": dial tcp %s: connect: connection refused", 1549 vdbEnv.config.Address, vdbEnv.config.Address) 1550 vdb.channelMetadata = nil 1551 err = vdb.initChannelMetadata(false, fakeNsProvider) 1552 require.EqualError(t, err, expectedErrMsg) 1553 vdbEnv.config.Address = origCouchAddress 1554 } 1555 1556 func TestRangeQueryWithInternalLimitAndPageSize(t *testing.T) { 1557 // generateSampleData returns a slice of KVs. The returned value contains 12 KVs for a namespace ns1 1558 generateSampleData := func() []*statedb.VersionedKV { 1559 sampleData := []*statedb.VersionedKV{} 1560 ver := version.NewHeight(1, 1) 1561 sampleKV := &statedb.VersionedKV{ 1562 CompositeKey: statedb.CompositeKey{Namespace: "ns1", Key: string('\u0000')}, 1563 VersionedValue: statedb.VersionedValue{Value: []byte("v0"), Version: ver, Metadata: []byte("m0")}, 1564 } 1565 sampleData = append(sampleData, sampleKV) 1566 for i := 0; i < 10; i++ { 1567 sampleKV = &statedb.VersionedKV{ 1568 CompositeKey: statedb.CompositeKey{ 1569 Namespace: "ns1", 1570 Key: fmt.Sprintf("key-%d", i), 1571 }, 1572 VersionedValue: statedb.VersionedValue{ 1573 Value: []byte(fmt.Sprintf("value-for-key-%d-for-ns1", i)), 1574 Version: ver, 1575 Metadata: []byte(fmt.Sprintf("metadata-for-key-%d-for-ns1", i)), 1576 }, 1577 } 1578 sampleData = append(sampleData, sampleKV) 1579 } 1580 sampleKV = &statedb.VersionedKV{ 1581 CompositeKey: statedb.CompositeKey{Namespace: "ns1", Key: string(utf8.MaxRune)}, 1582 VersionedValue: statedb.VersionedValue{Value: []byte("v1"), Version: ver, Metadata: []byte("m1")}, 1583 } 1584 sampleData = append(sampleData, sampleKV) 1585 return sampleData 1586 } 1587 1588 vdbEnv.init(t, nil) 1589 defer vdbEnv.cleanup() 1590 channelName := "ch1" 1591 vdb, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1592 require.NoError(t, err) 1593 db := vdb.(*VersionedDB) 1594 1595 sampleData := generateSampleData() 1596 batch := statedb.NewUpdateBatch() 1597 for _, d := range sampleData { 1598 batch.PutValAndMetadata(d.Namespace, d.Key, d.Value, d.Metadata, d.Version) 1599 } 1600 db.ApplyUpdates(batch, version.NewHeight(1, 1)) 1601 1602 defaultLimit := vdbEnv.config.InternalQueryLimit 1603 1604 // Scenario 1: We try to fetch either 11 records or all 12 records. We pass various internalQueryLimits. 1605 // key utf8.MaxRune would not be included as inclusive_end is always set to false 1606 testRangeQueryWithInternalLimit(t, "ns1", db, 2, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1607 testRangeQueryWithInternalLimit(t, "ns1", db, 5, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1608 testRangeQueryWithInternalLimit(t, "ns1", db, 2, string('\u0000'), "", sampleData) 1609 testRangeQueryWithInternalLimit(t, "ns1", db, 5, string('\u0000'), "", sampleData) 1610 testRangeQueryWithInternalLimit(t, "ns1", db, 2, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1611 testRangeQueryWithInternalLimit(t, "ns1", db, 5, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1612 testRangeQueryWithInternalLimit(t, "ns1", db, 2, "", "", sampleData) 1613 testRangeQueryWithInternalLimit(t, "ns1", db, 5, "", "", sampleData) 1614 1615 // Scenario 2: We try to fetch either 11 records or all 12 records using pagination. We pass various page sizes while 1616 // keeping the internalQueryLimit as the default one, i.e., 1000. 1617 vdbEnv.config.InternalQueryLimit = defaultLimit 1618 testRangeQueryWithPageSize(t, "ns1", db, 2, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1619 testRangeQueryWithPageSize(t, "ns1", db, 15, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1620 testRangeQueryWithPageSize(t, "ns1", db, 2, string('\u0000'), "", sampleData) 1621 testRangeQueryWithPageSize(t, "ns1", db, 15, string('\u0000'), "", sampleData) 1622 testRangeQueryWithPageSize(t, "ns1", db, 2, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1623 testRangeQueryWithPageSize(t, "ns1", db, 15, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1624 testRangeQueryWithPageSize(t, "ns1", db, 2, "", "", sampleData) 1625 testRangeQueryWithPageSize(t, "ns1", db, 15, "", "", sampleData) 1626 1627 // Scenario 3: We try to fetch either 11 records or all 12 records using pagination. We pass various page sizes while 1628 // keeping the internalQueryLimit to 1. 1629 vdbEnv.config.InternalQueryLimit = 1 1630 testRangeQueryWithPageSize(t, "ns1", db, 2, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1631 testRangeQueryWithPageSize(t, "ns1", db, 15, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1632 testRangeQueryWithPageSize(t, "ns1", db, 2, string('\u0000'), "", sampleData) 1633 testRangeQueryWithPageSize(t, "ns1", db, 15, string('\u0000'), "", sampleData) 1634 testRangeQueryWithPageSize(t, "ns1", db, 2, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1635 testRangeQueryWithPageSize(t, "ns1", db, 15, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1636 testRangeQueryWithPageSize(t, "ns1", db, 2, "", "", sampleData) 1637 testRangeQueryWithPageSize(t, "ns1", db, 15, "", "", sampleData) 1638 } 1639 1640 func testRangeQueryWithInternalLimit( 1641 t *testing.T, 1642 ns string, 1643 db *VersionedDB, 1644 limit int, 1645 startKey, endKey string, 1646 expectedResults []*statedb.VersionedKV, 1647 ) { 1648 vdbEnv.config.InternalQueryLimit = limit 1649 require.Equal(t, int32(limit), db.couchInstance.internalQueryLimit()) 1650 itr, err := db.GetStateRangeScanIterator(ns, startKey, endKey) 1651 require.NoError(t, err) 1652 require.Equal(t, int32(limit), itr.(*queryScanner).queryDefinition.internalQueryLimit) 1653 results := []*statedb.VersionedKV{} 1654 for { 1655 result, err := itr.Next() 1656 require.NoError(t, err) 1657 if result == nil { 1658 itr.Close() 1659 break 1660 } 1661 kv := result.(*statedb.VersionedKV) 1662 results = append(results, kv) 1663 } 1664 require.Equal(t, expectedResults, results) 1665 } 1666 1667 func testRangeQueryWithPageSize( 1668 t *testing.T, 1669 ns string, 1670 db *VersionedDB, 1671 pageSize int, 1672 startKey, endKey string, 1673 expectedResults []*statedb.VersionedKV, 1674 ) { 1675 itr, err := db.GetStateRangeScanIteratorWithPagination(ns, startKey, endKey, int32(pageSize)) 1676 require.NoError(t, err) 1677 results := []*statedb.VersionedKV{} 1678 for { 1679 result, err := itr.Next() 1680 require.NoError(t, err) 1681 if result != nil { 1682 kv := result.(*statedb.VersionedKV) 1683 results = append(results, kv) 1684 continue 1685 } 1686 nextStartKey := itr.GetBookmarkAndClose() 1687 if nextStartKey == endKey { 1688 break 1689 } 1690 itr, err = db.GetStateRangeScanIteratorWithPagination(ns, nextStartKey, endKey, int32(pageSize)) 1691 require.NoError(t, err) 1692 continue 1693 } 1694 require.Equal(t, expectedResults, results) 1695 } 1696 1697 func TestFullScanIterator(t *testing.T) { 1698 vdbEnv.init(t, nil) 1699 defer vdbEnv.cleanup() 1700 1701 commontests.TestFullScanIterator( 1702 t, 1703 vdbEnv.DBProvider, 1704 byte(1), 1705 constructVersionedValueForTest, 1706 ) 1707 } 1708 1709 func constructVersionedValueForTest(dbVal []byte) (*statedb.VersionedValue, error) { 1710 v, err := decodeValueVersionMetadata(dbVal) 1711 if err != nil { 1712 return nil, err 1713 } 1714 ver, meta, err := decodeVersionAndMetadata(string(v.VersionAndMetadata)) 1715 if err != nil { 1716 return nil, err 1717 } 1718 return &statedb.VersionedValue{ 1719 Value: v.Value, 1720 Version: ver, 1721 Metadata: meta, 1722 }, nil 1723 } 1724 1725 func TestFullScanIteratorDeterministicJSONOutput(t *testing.T) { 1726 generateSampleData := func(ns string, sortedJSON bool) []*statedb.VersionedKV { 1727 sampleData := []*statedb.VersionedKV{} 1728 ver := version.NewHeight(1, 1) 1729 for i := 0; i < 10; i++ { 1730 sampleKV := &statedb.VersionedKV{ 1731 CompositeKey: statedb.CompositeKey{ 1732 Namespace: ns, 1733 Key: fmt.Sprintf("key-%d", i), 1734 }, 1735 VersionedValue: statedb.VersionedValue{ 1736 Version: ver, 1737 Metadata: []byte(fmt.Sprintf("metadata-for-key-%d-for-ns1", i)), 1738 }, 1739 } 1740 if sortedJSON { 1741 sampleKV.Value = []byte(fmt.Sprintf(`{"a":0,"b":0,"c":%d}`, i)) 1742 } else { 1743 sampleKV.Value = []byte(fmt.Sprintf(`{"c":%d,"b":0,"a":0}`, i)) 1744 } 1745 sampleData = append(sampleData, sampleKV) 1746 } 1747 return sampleData 1748 } 1749 1750 vdbEnv.init(t, nil) 1751 defer vdbEnv.cleanup() 1752 channelName := "ch1" 1753 vdb, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1754 require.NoError(t, err) 1755 db := vdb.(*VersionedDB) 1756 1757 // creating and storing JSON value with sorted keys 1758 sampleDataWithSortedJSON := generateSampleData("ns1", true) 1759 batch := statedb.NewUpdateBatch() 1760 for _, d := range sampleDataWithSortedJSON { 1761 batch.PutValAndMetadata(d.Namespace, d.Key, d.Value, d.Metadata, d.Version) 1762 } 1763 db.ApplyUpdates(batch, version.NewHeight(1, 1)) 1764 1765 retrieveOnlyNs1 := func(ns string) bool { 1766 return ns != "ns1" 1767 } 1768 dbItr, format, err := db.GetFullScanIterator(retrieveOnlyNs1) 1769 require.NoError(t, err) 1770 require.Equal(t, fullScanIteratorValueFormat, format) 1771 require.NotNil(t, dbItr) 1772 verifyFullScanIterator(t, dbItr, sampleDataWithSortedJSON) 1773 1774 // creating and storing JSON value with unsorted JSON-keys 1775 sampleDataWithUnsortedJSON := generateSampleData("ns2", false) 1776 batch = statedb.NewUpdateBatch() 1777 for _, d := range sampleDataWithUnsortedJSON { 1778 batch.PutValAndMetadata(d.Namespace, d.Key, d.Value, d.Metadata, d.Version) 1779 } 1780 db.ApplyUpdates(batch, version.NewHeight(1, 1)) 1781 1782 retrieveOnlyNs2 := func(ns string) bool { 1783 return ns != "ns2" 1784 } 1785 sampleDataWithSortedJSON = generateSampleData("ns2", true) 1786 dbItr, format, err = db.GetFullScanIterator(retrieveOnlyNs2) 1787 require.NoError(t, err) 1788 require.Equal(t, fullScanIteratorValueFormat, format) 1789 require.NotNil(t, dbItr) 1790 verifyFullScanIterator(t, dbItr, sampleDataWithSortedJSON) 1791 } 1792 1793 func TestFullScanIteratorSkipInternalKeys(t *testing.T) { 1794 generateSampleData := func(ns string, keys []string) []*statedb.VersionedKV { 1795 sampleData := []*statedb.VersionedKV{} 1796 ver := version.NewHeight(1, 1) 1797 for i := 0; i < len(keys); i++ { 1798 sampleKV := &statedb.VersionedKV{ 1799 CompositeKey: statedb.CompositeKey{ 1800 Namespace: ns, 1801 Key: keys[i], 1802 }, 1803 VersionedValue: statedb.VersionedValue{ 1804 Value: []byte(fmt.Sprintf("value-for-%s-for-ns1", keys[i])), 1805 Version: ver, 1806 Metadata: []byte(fmt.Sprintf("metadata-for-%s-for-ns1", keys[i])), 1807 }, 1808 } 1809 sampleData = append(sampleData, sampleKV) 1810 } 1811 return sampleData 1812 } 1813 1814 vdbEnv.init(t, nil) 1815 defer vdbEnv.cleanup() 1816 channelName := "ch1" 1817 vdb, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1818 require.NoError(t, err) 1819 db := vdb.(*VersionedDB) 1820 1821 keys := []string{channelMetadataDocID, "key-1", "key-2", "key-3", "key-4", "key-5", savepointDocID} 1822 sampleData := generateSampleData("ns1", keys) 1823 batch := statedb.NewUpdateBatch() 1824 for _, d := range sampleData { 1825 batch.PutValAndMetadata(d.Namespace, d.Key, d.Value, d.Metadata, d.Version) 1826 } 1827 db.ApplyUpdates(batch, version.NewHeight(1, 1)) 1828 1829 retrieveOnlyNs1 := func(ns string) bool { 1830 return ns != "ns1" 1831 } 1832 dbItr, format, err := db.GetFullScanIterator(retrieveOnlyNs1) 1833 require.NoError(t, err) 1834 require.Equal(t, fullScanIteratorValueFormat, format) 1835 require.NotNil(t, dbItr) 1836 verifyFullScanIterator(t, dbItr, sampleData) 1837 1838 sampleData = generateSampleData("", keys) 1839 batch = statedb.NewUpdateBatch() 1840 for _, d := range sampleData { 1841 batch.PutValAndMetadata(d.Namespace, d.Key, d.Value, d.Metadata, d.Version) 1842 } 1843 db.ApplyUpdates(batch, version.NewHeight(1, 1)) 1844 1845 retrieveOnlyEmptyNs := func(ns string) bool { 1846 return ns != "" 1847 } 1848 // remove internal keys such as savepointDocID and channelMetadataDocID 1849 // as it is an empty namespace 1850 keys = []string{"key-1", "key-2", "key-3", "key-4", "key-5"} 1851 sampleData = generateSampleData("", keys) 1852 dbItr, format, err = db.GetFullScanIterator(retrieveOnlyEmptyNs) 1853 require.NoError(t, err) 1854 require.Equal(t, fullScanIteratorValueFormat, format) 1855 require.NotNil(t, dbItr) 1856 verifyFullScanIterator(t, dbItr, sampleData) 1857 } 1858 1859 func verifyFullScanIterator( 1860 t *testing.T, 1861 dbIter statedb.FullScanIterator, 1862 expectedResult []*statedb.VersionedKV, 1863 ) { 1864 results := []*statedb.VersionedKV{} 1865 for { 1866 ck, valBytes, err := dbIter.Next() 1867 require.NoError(t, err) 1868 if ck == nil { 1869 break 1870 } 1871 val, err := constructVersionedValueForTest(valBytes) 1872 require.NoError(t, err) 1873 results = append(results, &statedb.VersionedKV{CompositeKey: *ck, VersionedValue: *val}) 1874 } 1875 require.Equal(t, expectedResult, results) 1876 } 1877 1878 func TestReadFromDBInvalidKey(t *testing.T) { 1879 vdbEnv.init(t, sysNamespaces) 1880 defer vdbEnv.cleanup() 1881 channelName := "test_getstate_invalidkey" 1882 db, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1883 require.NoError(t, err) 1884 vdb := db.(*VersionedDB) 1885 1886 testcase := []struct { 1887 key string 1888 expectedErrorMsg string 1889 }{ 1890 { 1891 key: string([]byte{0xff, 0xfe, 0xfd}), 1892 expectedErrorMsg: "invalid key [fffefd], must be a UTF-8 string", 1893 }, 1894 { 1895 key: "", 1896 expectedErrorMsg: "invalid key. Empty string is not supported as a key by couchdb", 1897 }, 1898 { 1899 key: "_key_starting_with_an_underscore", 1900 expectedErrorMsg: `invalid key [_key_starting_with_an_underscore], cannot begin with "_"`, 1901 }, 1902 } 1903 1904 for i, tc := range testcase { 1905 t.Run(fmt.Sprintf("testcase-%d", i), func(t *testing.T) { 1906 _, err = vdb.readFromDB("ns", tc.key) 1907 require.EqualError(t, err, tc.expectedErrorMsg) 1908 }) 1909 } 1910 }