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