UNPKG

motorq-documentdb

Version:
917 lines (826 loc) 266 kB
/* The MIT License (MIT) Copyright (c) 2017 Microsoft Corporation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ "use strict"; var lib = require("../lib/"), assert = require("assert"), testConfig = require("./_testConfig"), sinon = require("sinon"), Stream = require("stream"), StatusCodes = require('../lib/statusCodes').StatusCodes; var Base = lib.Base, DocumentDBClient = lib.DocumentClient, DocumentBase = lib.DocumentBase, Constants = lib.Constants, Range = lib.Range, RangePartitionResolver = lib.RangePartitionResolver, HashPartitionResolver = lib.HashPartitionResolver, AzureDocuments = lib.AzureDocuments, RetryOptions = lib.RetryOptions; var host = testConfig.host; var masterKey = testConfig.masterKey; describe("NodeJS CRUD Tests", function () { // remove all databases from the endpoint before each test beforeEach(function (done) { var client = new DocumentDBClient(host, { masterKey: masterKey }); client.readDatabases().toArray(function (err, databases) { if (err !== undefined) { console.log("An error occured", err); return done(); } var length = databases.length; if (length === 0) { return done(); } var count = 0; databases.forEach(function (database) { client.deleteDatabase(database._self, function (err, db) { if (err !== undefined) { console.log("An error occured", err); return done(); } count++; if (count === length) { done(); } }); }); }); }); var addUpsertWrapperMethods = function (client, isUpsertTest) { // Document client["createOrUpsertDocument"] = function (collectionLink, body, options, callback) { if (isUpsertTest) { this.upsertDocument(collectionLink, body, options, callback); } else { this.createDocument(collectionLink, body, options, callback); } }; client["replaceOrUpsertDocument"] = function (collectionLink, documentLink, body, options, callback) { if (isUpsertTest) { this.upsertDocument(collectionLink, body, options, callback); } else { this.replaceDocument(documentLink, body, options, callback); } }; // Attachment client["createOrUpsertAttachment"] = function (documentLink, body, options, callback) { if (isUpsertTest) { this.upsertAttachment(documentLink, body, options, callback); } else { this.createAttachment(documentLink, body, options, callback); } }; client["replaceOrUpsertAttachment"] = function (documentLink, attachmentLink, body, options, callback) { if (isUpsertTest) { this.upsertAttachment(documentLink, body, options, callback); } else { this.replaceAttachment(attachmentLink, body, options, callback); } }; // User client["createOrUpsertUser"] = function (databaseLink, body, options, callback) { if (isUpsertTest) { this.upsertUser(databaseLink, body, options, callback); } else { this.createUser(databaseLink, body, options, callback); } }; client["replaceOrUpsertUser"] = function (databaseLink, userLink, body, options, callback) { if (isUpsertTest) { this.upsertUser(databaseLink, body, options, callback); } else { this.replaceUser(userLink, body, options, callback); } }; // Permission client["createOrUpsertPermission"] = function (userLink, body, options, callback) { if (isUpsertTest) { this.upsertPermission(userLink, body, options, callback); } else { this.createPermission(userLink, body, options, callback); } }; client["replaceOrUpsertPermission"] = function (userLink, permissionLink, body, options, callback) { if (isUpsertTest) { this.upsertPermission(userLink, body, options, callback); } else { this.replacePermission(permissionLink, body, options, callback); } }; // Trigger client["createOrUpsertTrigger"] = function (collectionLink, body, options, callback) { if (isUpsertTest) { this.upsertTrigger(collectionLink, body, options, callback); } else { this.createTrigger(collectionLink, body, options, callback); } }; client["replaceOrUpsertTrigger"] = function (collectionLink, triggerLink, body, options, callback) { if (isUpsertTest) { this.upsertTrigger(collectionLink, body, options, callback); } else { this.replaceTrigger(triggerLink, body, options, callback); } }; // User Defined Function client["createOrUpsertUserDefinedFunction"] = function (collectionLink, body, options, callback) { if (isUpsertTest) { this.upsertUserDefinedFunction(collectionLink, body, options, callback); } else { this.createUserDefinedFunction(collectionLink, body, options, callback); } }; client["replaceOrUpsertUserDefinedFunction"] = function (collectionLink, udfLink, body, options, callback) { if (isUpsertTest) { this.upsertUserDefinedFunction(collectionLink, body, options, callback); } else { this.replaceUserDefinedFunction(udfLink, body, options, callback); } }; // Stored Procedure client["createOrUpsertStoredProcedure"] = function (collectionLink, body, options, callback) { if (isUpsertTest) { this.upsertStoredProcedure(collectionLink, body, options, callback); } else { this.createStoredProcedure(collectionLink, body, options, callback); } }; client["replaceOrUpsertStoredProcedure"] = function (collectionLink, sprocLink, body, options, callback) { if (isUpsertTest) { this.upsertStoredProcedure(collectionLink, body, options, callback); } else { this.replaceStoredProcedure(sprocLink, body, options, callback); } }; // Attachment and Upload Media client["createOrUpsertAttachmentAndUploadMedia"] = function (documentLink, readableStream, options, callback) { if (isUpsertTest) { this.upsertAttachmentAndUploadMedia(documentLink, readableStream, options, callback); } else { this.createAttachmentAndUploadMedia(documentLink, readableStream, options, callback); } }; client["updateOrUpsertMedia"] = function (documentLink, mediaLink, readableStream, options, callback) { if (isUpsertTest) { this.upsertAttachmentAndUploadMedia(documentLink, readableStream, options, callback); } else { this.updateMedia(mediaLink, readableStream, options, callback); } }; }; var getDatabaseLink = function (isNameBasedLink, db) { if (isNameBasedLink) { return "dbs/" + db.id; } else { return db._self; } }; var getCollectionLink = function (isNameBasedLink, db, coll) { if (isNameBasedLink) { return "dbs/" + db.id + "/colls/" + coll.id; } else { return coll._self; } }; var getDocumentLink = function (isNameBasedLink, db, coll, doc) { if (isNameBasedLink) { return "dbs/" + db.id + "/colls/" + coll.id + "/docs/" + doc.id; } else { return doc._self; } }; var getAttachmentLink = function (isNameBasedLink, db, coll, doc, attachment) { if (isNameBasedLink) { return "dbs/" + db.id + "/colls/" + coll.id + "/docs/" + doc.id + "/attachments/" + attachment.id; } else { return attachment._self; } }; var getUserLink = function (isNameBasedLink, db, user) { if (isNameBasedLink) { return "dbs/" + db.id + "/users/" + user.id; } else { return user._self; } }; var getPermissionLink = function (isNameBasedLink, db, user, permission) { if (isNameBasedLink) { return "dbs/" + db.id + "/users/" + user.id + "/permissions/" + permission.id; } else { return permission._self; } }; var getTriggerLink = function (isNameBasedLink, db, coll, trigger) { if (isNameBasedLink) { return "dbs/" + db.id + "/colls/" + coll.id + "/triggers/" + trigger.id; } else { return trigger._self; } }; var getUserDefinedFunctionLink = function (isNameBasedLink, db, coll, udf) { if (isNameBasedLink) { return "dbs/" + db.id + "/colls/" + coll.id + "/udfs/" + udf.id; } else { return udf._self; } }; var getStoredProcedureLink = function (isNameBasedLink, db, coll, sproc) { if (isNameBasedLink) { return "dbs/" + db.id + "/colls/" + coll.id + "/sprocs/" + sproc.id; } else { return sproc._self; } }; var getConflictLink = function (isNameBasedLink, db, coll, conflict) { if (isNameBasedLink) { return "dbs/" + db.id + "/colls/" + coll.id + "/conflicts/" + conflict.id; } else { return conflict._self; } }; var bulkInsertDocuments = function (client, isNameBased, db, collection, documents, callback) { var returnedDocuments = []; var insertDocument = function (currentIndex) { if (currentIndex >= documents.length) { callback(returnedDocuments); } else { client.createDocument(getCollectionLink(isNameBased, db, collection), documents[currentIndex], function (err, document) { assert.equal(err, undefined, "error creating document " + JSON.stringify(documents[currentIndex])); returnedDocuments.push(document); insertDocument(++currentIndex); }); } }; insertDocument(0); }; var bulkReadDocuments = function (client, isNameBased, db, collection, documents, partitionKey, callback) { var readDocument = function (currentIndex) { if (currentIndex >= documents.length) { callback(); } else { var options = undefined; if (partitionKey) { if (documents[currentIndex].hasOwnProperty(partitionKey)) { options = { partitionKey: documents[currentIndex][partitionKey] }; } else { options = { partitionKey: {} }; } } client.readDocument(getDocumentLink(isNameBased, db, collection, documents[currentIndex]), options, function (err, document) { assert.equal(err, undefined, "error reading document " + JSON.stringify(documents[currentIndex])); assert.equal(JSON.stringify(document), JSON.stringify(documents[currentIndex])); readDocument(++currentIndex); }); } }; readDocument(0); }; var bulkReplaceDocuments = function (client, isNameBased, db, collection, documents, partitionKey, callback) { var returnedDocuments = []; var replaceDocument = function (currentIndex) { if (currentIndex >= documents.length) { callback(returnedDocuments); } else { client.replaceDocument(getDocumentLink(isNameBased, db, collection, documents[currentIndex]), documents[currentIndex], function (err, document) { assert.equal(err, undefined, "error replacing document " + JSON.stringify(documents[currentIndex])); var expectedModifiedDocument = JSON.parse(JSON.stringify(documents[currentIndex])); delete expectedModifiedDocument._etag; delete expectedModifiedDocument._ts; var actualModifiedDocument = JSON.parse(JSON.stringify(document)); delete actualModifiedDocument._etag; delete actualModifiedDocument._ts; assert.equal(JSON.stringify(actualModifiedDocument), JSON.stringify(expectedModifiedDocument)); returnedDocuments.push(document); replaceDocument(++currentIndex); }); } }; replaceDocument(0); }; var bulkDeleteDocuments = function (client, isNameBased, db, collection, documents, partitionKey, callback) { var deleteDocument = function (currentIndex) { if (currentIndex >= documents.length) { callback(); } else { var options = undefined; if (partitionKey) { if (documents[currentIndex].hasOwnProperty(partitionKey)) { options = { partitionKey: documents[currentIndex][partitionKey] }; } else { options = { partitionKey: {} }; } } client.deleteDocument(getDocumentLink(isNameBased, db, collection, documents[currentIndex]), options, function (err, result) { assert.equal(err, undefined, "error deleting document " + JSON.stringify(documents[currentIndex])); deleteDocument(++currentIndex); }); } }; deleteDocument(0); }; var bulkQueryDocumentsWithPartitionKey = function (client, isNameBased, db, collection, documents, partitionKey, callback) { var queryDocument = function (currentIndex) { if (currentIndex >= documents.length) { callback(); } else { if (!documents[currentIndex].hasOwnProperty(partitionKey)) { return queryDocument(++currentIndex); } var querySpec = { query: "SELECT * FROM root r WHERE r." + partitionKey + "=@key", parameters: [ { name: "@key", value: documents[currentIndex][partitionKey] } ] }; client.queryDocuments(getCollectionLink(isNameBased, db, collection), querySpec).toArray(function (err, results) { assert.equal(err, undefined, "error querying document " + JSON.stringify(documents[currentIndex])); assert.equal(results.length, 1, "Expected exactly 1 document"); assert.equal(JSON.stringify(results[0]), JSON.stringify(documents[currentIndex])); queryDocument(++currentIndex); }); } }; queryDocument(0); }; describe("Validate Database CRUD", function () { var databaseCRUDTest = function (isNameBased, done) { var client = new DocumentDBClient(host, { masterKey: masterKey }); // read databases client.readDatabases().toArray(function (err, databases) { assert.equal(err, undefined, "error reading databases"); assert.equal(databases.constructor, Array, "Value should be an array"); // create a database var beforeCreateDatabasesCount = databases.length; var databaseDefinition = { id: "sample database" }; client.createDatabase(databaseDefinition, function (err, db) { assert.equal(err, undefined, "error creating database"); assert.equal(db.id, databaseDefinition.id); // read databases after creation client.readDatabases().toArray(function (err, databases) { assert.equal(err, undefined, "error reading databases"); assert.equal(databases.length, beforeCreateDatabasesCount + 1, "create should increase the number of databases"); // query databases var querySpec = { query: "SELECT * FROM root r WHERE r.id=@id", parameters: [ { name: "@id", value: databaseDefinition.id } ] }; client.queryDatabases(querySpec).toArray(function (err, results) { assert.equal(err, undefined, "error querying databases"); assert(results.length > 0, "number of results for the query should be > 0"); // delete database client.deleteDatabase(getDatabaseLink(isNameBased, db), function (err, res) { assert.equal(err, undefined, "error deleting database"); // read database after deletion client.readDatabase(getDatabaseLink(isNameBased, db), function (err, database) { var notFoundErrorCode = 404; assert.equal(err.code, notFoundErrorCode, "response should return error code 404"); done(); }); }); }); }); }); }); }; it("nativeApi Should do database CRUD operations successfully name based", function (done) { databaseCRUDTest(true, done); }); it("nativeApi Should do database CRUD operations successfully rid based", function (done) { databaseCRUDTest(false, done); }); }); describe("Validate Queries CRUD", function () { var queriesCRUDTest = function (isNameBased, done) { var client = new DocumentDBClient(host, { masterKey: masterKey }); // create a database var databaseDefinition = { id: "sample database" }; client.createDatabase(databaseDefinition, function (err, db) { assert.equal(err, undefined, "error creating database"); assert.equal(db.id, databaseDefinition.id); // query databases var querySpec0 = { query: "SELECT * FROM root r WHERE r.id=@id", parameters: [ { name: "@id", value: databaseDefinition.id } ] }; client.queryDatabases(querySpec0).toArray(function (err, results) { assert.equal(err, undefined, "error querying databases"); assert(results.length > 0, "number of results for the query should be > 0"); var querySpec1 = { query: "SELECT * FROM root r WHERE r.id='" + databaseDefinition.id + "'" }; client.queryDatabases(querySpec1).toArray(function (err, results) { assert.equal(err, undefined, "error creating databases"); assert(results.length > 0, "number of results for the query should be > 0"); var querySpec2 = "SELECT * FROM root r WHERE r.id='" + databaseDefinition.id + "'"; client.queryDatabases(querySpec2).toArray(function (err, results) { assert.equal(err, undefined, "error querying databases"); assert(results.length > 0, "number of results for the query should be > 0"); done(); }); }); }); }); }; it("nativeApi Should do queries CRUD operations successfully name based", function (done) { queriesCRUDTest(true, done); }); it("nativeApi Should do queries CRUD operations successfully rid based", function (done) { queriesCRUDTest(false, done); }); }); describe("Validate Collection CRUD", function () { var collectionCRUDTest = function (isNameBased, hasPartitionKey, done) { var client = new DocumentDBClient(host, { masterKey: masterKey }); // create database client.createDatabase({ id: "sample database" }, function (err, db) { assert.equal(err, undefined, "error creating database"); client.readCollections(getDatabaseLink(isNameBased, db)).toArray(function (err, collections) { assert.equal(err, undefined, "error reading collections"); assert.equal(collections.constructor, Array, "Value should be an array"); // create a collection var beforeCreateCollectionsCount = collections.length; var collectionDefinition = { id: "sample collection", indexingPolicy: { indexingMode: "Consistent" } }; if (hasPartitionKey) { collectionDefinition.partitionKey = { paths: ["/id"], kind: DocumentBase.PartitionKind.Hash }; } client.createCollection(getDatabaseLink(isNameBased, db), collectionDefinition, function (err, collection) { assert.equal(err, undefined, "error creating collection"); assert.equal(collectionDefinition.id, collection.id); assert.equal("consistent", collection.indexingPolicy.indexingMode); assert.equal(JSON.stringify(collection.partitionKey), JSON.stringify(collectionDefinition.partitionKey)); // read collections after creation client.readCollections(getDatabaseLink(isNameBased, db)).toArray(function (err, collections) { assert.equal(err, undefined, "error reading collections"); assert.equal(collections.length, beforeCreateCollectionsCount + 1, "create should increase the number of collections"); // query collections var querySpec = { query: "SELECT * FROM root r WHERE r.id=@id", parameters: [ { name: "@id", value: collectionDefinition.id } ] }; client.queryCollections(getDatabaseLink(isNameBased, db), querySpec).toArray(function (err, results) { assert.equal(err, undefined, "error querying collections"); assert(results.length > 0, "number of results for the query should be > 0"); // Replacing indexing policy is allowed. collection.indexingPolicy.indexingMode = "Lazy"; client.replaceCollection(getCollectionLink(isNameBased, db, collection), collection, function (err, replacedCollection) { assert.equal(err, undefined, "replaceCollection should work successfully"); assert.equal("lazy", replacedCollection.indexingPolicy.indexingMode); // Replacing partition key is not allowed. collection.partitionKey = { paths: ["/key"], kind: DocumentBase.PartitionKind.Hash }; client.replaceCollection(getCollectionLink(isNameBased, db, collection), collection, function (err, replacedCollection) { var badRequestErrorCode = 400; assert.equal(err.code, badRequestErrorCode, "response should return error code " + badRequestErrorCode); collection.partitionKey = collectionDefinition.partitionKey; // Resume partition key // Replacing id is not allowed. collection.id = "try_to_replace_id"; client.replaceCollection(getCollectionLink(isNameBased, db, collection), collection, function (err, replacedCollection) { if (isNameBased) { var notFoundErrorCode = 404; assert.equal(err.code, notFoundErrorCode, "response should return error code 404"); } else { var badRequestErrorCode = 400; assert.equal(err.code, badRequestErrorCode, "response should return error code 400"); } // read collection collection.id = collectionDefinition.id; // Resume Id. client.readCollection(getCollectionLink(isNameBased, db, collection), function (err, collection) { assert.equal(err, undefined, "readCollection should work successfully"); assert.equal(collectionDefinition.id, collection.id); // delete collection client.deleteCollection(getCollectionLink(isNameBased, db, collection), function (err, res) { assert.equal(err, undefined, "error deleting collection"); // read collection after deletion client.readCollection(getCollectionLink(isNameBased, db, collection), function (err, collection) { var notFoundErrorCode = 404; assert.equal(err.code, notFoundErrorCode, "response should return error code 404"); done(); }); }); }); }); }); }); }); }); }); }); }); }; var badPartitionKeyDefinitionTest = function (isNameBased, done) { var client = new DocumentDBClient(host, { masterKey: masterKey }); // create database client.createDatabase({ id: "sample database" }, function (err, db) { assert.equal(err, undefined, "error creating database"); // create a collection var collectionDefinition = { id: "sample collection", indexingPolicy: { indexingMode: "Consistent" }, partitionKey: { paths: "/id", kind: DocumentBase.PartitionKind.Hash } }; client.createCollection(getDatabaseLink(isNameBased, db), collectionDefinition, function (err, collection) { assert.equal(err.code, 400); done(); }); }); }; it("nativeApi Should do collection CRUD operations successfully name based", function (done) { collectionCRUDTest(true, false, done); }); it("nativeApi Should do collection CRUD operations successfully rid based", function (done) { collectionCRUDTest(false, false, done); }); it("nativeApi Should do elastic collection CRUD operations successfully name based", function (done) { collectionCRUDTest(true, true, done); }); it("nativeApi Should do elastic collection CRUD operations successfully rid based", function (done) { collectionCRUDTest(false, true, done); }); it("nativeApi Collection with bad partition key definition name based", function (done) { badPartitionKeyDefinitionTest(true, done); }); it("nativeApi Collection with bad partition key definition name based", function (done) { badPartitionKeyDefinitionTest(false, done); }); }); describe("Validate Document CRUD", function () { var documentCRUDTest = function (isNameBased, isUpsertTest, done) { var client = new DocumentDBClient(host, { masterKey: masterKey }); addUpsertWrapperMethods(client, isUpsertTest); // create database client.createDatabase({ id: "sample 中文 database" }, function (err, db) { assert.equal(err, undefined, "error creating database"); // create collection client.createCollection("dbs/sample 中文 database", { id: "sample collection" }, function (err, collection) { assert.equal(err, undefined, "error creating collection"); // read documents client.readDocuments(getCollectionLink(isNameBased, db, collection)).toArray(function (err, documents) { assert.equal(err, undefined, "error reading documents"); assert.equal(documents.constructor, Array, "Value should be an array"); // create a document var beforeCreateDocumentsCount = documents.length; var documentDefinition = { name: "sample document", foo: "bar", key: "value", replace: "new property" }; client.createOrUpsertDocument(getCollectionLink(isNameBased, db, collection), documentDefinition, { disableAutomaticIdGeneration: true }, function (err, document) { assert(err !== undefined, "should throw an error because automatic id generation is disabled"); client.createOrUpsertDocument(getCollectionLink(isNameBased, db, collection), documentDefinition, function (err, document) { assert.equal(err, undefined, "error creating document"); assert.equal(document.name, documentDefinition.name); assert(document.id !== undefined); // read documents after creation client.readDocuments(getCollectionLink(isNameBased, db, collection)).toArray(function (err, documents) { assert.equal(err, undefined, "error reading documents"); assert.equal(documents.length, beforeCreateDocumentsCount + 1, "create should increase the number of documents"); // query documents var querySpec = { query: "SELECT * FROM root r WHERE r.id=@id", parameters: [ { name: "@id", value: document.id } ] }; client.queryDocuments(getCollectionLink(isNameBased, db, collection), querySpec).toArray(function (err, results) { assert.equal(err, undefined, "error querying documents"); assert(results.length > 0, "number of results for the query should be > 0"); client.queryDocuments(getCollectionLink(isNameBased, db, collection), querySpec, { enableScanInQuery: true }).toArray(function (err, results) { assert.equal(err, undefined, "error querying documents"); assert(results.length > 0, "number of results for the query should be > 0"); //replace document document.name = "replaced document"; document.foo = "not bar"; client.replaceOrUpsertDocument(getCollectionLink(isNameBased, db, collection), getDocumentLink(isNameBased, db, collection, document), document, function (error, replacedDocument) { assert.equal(replacedDocument.name, "replaced document", "document name property should change"); assert.equal(replacedDocument.foo, "not bar", "property should have changed"); assert.equal(document.id, replacedDocument.id, "document id should stay the same"); // read document client.readDocument(getDocumentLink(isNameBased, db, collection, replacedDocument), function (err, document) { assert.equal(err, undefined, "readDocument should work successfully"); assert.equal(replacedDocument.id, document.id); // delete document client.deleteDocument(getDocumentLink(isNameBased, db, collection, replacedDocument), function (err, res) { assert.equal(err, undefined, "error deleting document"); // read documents after deletion client.readDocument(getDocumentLink(isNameBased, db, collection, document), function (err, document) { var notFoundErrorCode = 404; assert.equal(err.code, notFoundErrorCode, "response should return error code 404"); done(); }); }); }); }); }); }); }); }); }); }); }); }); }; var documentCRUDMultiplePartitionsTest = function (isNameBased, done) { var client = new DocumentDBClient(host, { masterKey: masterKey }); // create database client.createDatabase({ id: "db1" }, function (err, db) { assert.equal(err, undefined, "error creating database"); var partitionKey = "key"; // create collection var collectionDefinition = { id: "coll1", partitionKey: { paths: ["/" + partitionKey], kind: DocumentBase.PartitionKind.Hash } }; client.createCollection(getDatabaseLink(isNameBased, db), collectionDefinition, { offerThroughput: 12000 }, function (err, collection) { assert.equal(err, undefined, "error creating collection"); var documents = [ { id: "document1" }, { id: "document2", key: null, prop: 1 }, { id: "document3", key: false, prop: 1 }, { id: "document4", key: true, prop: 1 }, { id: "document5", key: 1, prop: 1 }, { id: "document6", key: "A", prop: 1 } ]; bulkInsertDocuments(client, isNameBased, db, collection, documents, function (returnedDocuments) { assert.equal(returnedDocuments.length, documents.length); returnedDocuments.sort(function (doc1, doc2) { return doc1.id.localeCompare(doc2.id); }); bulkReadDocuments(client, isNameBased, db, collection, returnedDocuments, partitionKey, function () { client.readDocuments(getCollectionLink(isNameBased, db, collection)).toArray(function (err, successDocuments) { assert.equal(err, undefined, "error reading documents"); assert(successDocuments !== undefined, "error reading documents"); assert.equal(successDocuments.length, returnedDocuments.length, "Expected " + returnedDocuments.length + " documents to be succesfully read"); successDocuments.sort(function (doc1, doc2) { return doc1.id.localeCompare(doc2.id); }); assert.equal(JSON.stringify(successDocuments), JSON.stringify(returnedDocuments), "Unexpected documents are returned"); returnedDocuments.forEach(function (document) { ++document.prop; }); bulkReplaceDocuments(client, isNameBased, db, collection, returnedDocuments, partitionKey, function (newReturnedDocuments) { returnedDocuments = newReturnedDocuments; bulkQueryDocumentsWithPartitionKey(client, isNameBased, db, collection, returnedDocuments, partitionKey, function () { var querySpec = { query: "SELECT * FROM Root" }; client.queryDocuments(getCollectionLink(isNameBased, db, collection), querySpec, { enableScanInQuery: true }).toArray(function (err, results) { var badRequestErrorCode = 400; assert.equal(err.code, badRequestErrorCode, "response should return error code " + badRequestErrorCode); client.queryDocuments(getCollectionLink(isNameBased, db, collection), querySpec, { enableScanInQuery: true, enableCrossPartitionQuery: true }).toArray(function (err, results) { assert.equal(err, undefined, "error querying documents"); assert(results !== undefined, "error querying documents"); results.sort(function (doc1, doc2) { return doc1.id.localeCompare(doc2.id); }); assert.equal(results.length, returnedDocuments.length, "Expected " + returnedDocuments.length + " documents to be succesfully queried"); assert.equal(JSON.stringify(results), JSON.stringify(returnedDocuments), "Unexpected query results"); bulkDeleteDocuments(client, isNameBased, db, collection, returnedDocuments, partitionKey, function () { done(); }); }); }); }); }); }); }); }); }); }); }; it("nativeApi Should do document CRUD operations successfully name based", function (done) { documentCRUDTest(true, false, done); }); it("nativeApi Should do document CRUD operations successfully rid based", function (done) { documentCRUDTest(false, false, done); }); it("nativeApi Should do document CRUD operations successfully name based with upsert", function (done) { documentCRUDTest(true, true, done); }); it("nativeApi Should do document CRUD operations successfully rid based with upsert", function (done) { documentCRUDTest(false, true, done); }); it("nativeApi Should do document CRUD operations over multiple partitions successfully name based", function (done) { documentCRUDMultiplePartitionsTest(true, done); }); it("nativeApi Should do document CRUD operations over multiple partitions successfully rid based", function (done) { documentCRUDMultiplePartitionsTest(false, done); }); }); describe("Validate Attachment CRUD", function () { var createReadableStream = function (firstChunk, secondChunk) { var readableStream = new Stream.Readable(); var chunkCount = 0; readableStream._read = function (n) { if (chunkCount === 0) { this.push(firstChunk || "first chunk "); } else if (chunkCount === 1) { this.push(secondChunk || "second chunk"); } else { this.push(null); } chunkCount++; }; return readableStream; }; var readMediaResponse = function (response, callback) { var data = ""; response.on("data", function (chunk) { data += chunk; }); response.on("end", function () { if (response.statusCode >= 300) { return callback({ code: response.statusCode, body: data }); } return callback(undefined, data); }); }; var attachmentCRUDTest = function (isNameBased, isUpsertTest, done) { var client = new DocumentDBClient(host, { masterKey: masterKey }); addUpsertWrapperMethods(client, isUpsertTest); // create database client.createDatabase({ id: "sample database" }, function (err, db) { assert.equal(err, undefined, "error creating database"); // create collection client.createCollection(getDatabaseLink(isNameBased, db), { id: "sample collection" }, function (err, collection) { assert.equal(err, undefined, "error creating collection"); // create document client.createDocument(getCollectionLink(isNameBased, db, collection), { id: "sample document", foo: "bar", key: "value" }, function (err, document) { assert.equal(err, undefined, "error creating document"); // list all attachments client.readAttachments(getDocumentLink(isNameBased, db, collection, document)).toArray(function (err, attachments) { assert.equal(err, undefined, "error reading attachments"); assert.equal(attachments.constructor, Array, "Value should be an array"); var initialCount = attachments.length; var validMediaOptions = { slug: "attachment name", contentType: "application/text" }; var invalidMediaOptions = { slug: "attachment name", contentType: "junt/test" }; // create attachment with invalid content-type var contentStream = createReadableStream(); client.createOrUpsertAttachmentAndUploadMedia(getDocumentLink(isNameBased, db, collection, document), contentStream, invalidMediaOptions, function (err, attachment) { assert(err !== undefined, "create attachment should return error on invalid mediatypes"); var badRequestErrorCode = 400; assert.equal(err.code, badRequestErrorCode); contentStream = createReadableStream(); // create streamed attachment with valid content-type client.createOrUpsertAttachmentAndUploadMedia(getDocumentLink(isNameBased, db, collection, document), contentStream, validMediaOptions, function (err, validAttachment) { assert.equal(err, undefined, "error creating valid attachment"); assert.equal(validAttachment.id, "attachment name", "name of created attachment should be the same as the one in the request"); contentStream = createReadableStream(); // create colliding attachment var content2 = "bug";