From 59c1e06948da88eea4a4e8a129b4a22de2d3ddf0 Mon Sep 17 00:00:00 2001 From: Armando Trejo Date: Mon, 7 Mar 2016 17:23:50 -0800 Subject: [PATCH] Version 1.5.6 - Fixed RangePartitionResolver.resolveForRead bug where it was not returning links due to a bad concat of results - Move compareFunction from Range class to RangePartitionResolver class --- source/.gitignore | 5 +- ...njsproj => DocumentDB.Node.master.njsproj} | 37 +- ...umentDB.sln => DocumentDB.Node.master.sln} | 12 +- source/changelog.md | 5 + source/lib/constants.js | 30 +- source/lib/range.js | 137 +-- source/package.json | 2 +- source/readme.html | 42 + source/readme.md | 56 - source/test/consistentHashRingTests.js | 2 +- source/test/documentClientTests.js | 238 +++++ source/test/hashPartitionResolverTests.js | 2 +- source/test/murmurHashTests.js | 2 +- source/test/rangePartitionResolverTests.js | 431 ++++++++ source/test/rangeTests.js | 502 +++++++++ source/test/readme.md | 4 +- source/test/test.js | 974 +----------------- 17 files changed, 1338 insertions(+), 1143 deletions(-) rename source/{DocumentDB.njsproj => DocumentDB.Node.master.njsproj} (74%) rename source/{DocumentDB.sln => DocumentDB.Node.master.sln} (51%) create mode 100644 source/readme.html delete mode 100644 source/readme.md create mode 100644 source/test/documentClientTests.js create mode 100644 source/test/rangePartitionResolverTests.js create mode 100644 source/test/rangeTests.js diff --git a/source/.gitignore b/source/.gitignore index 7e4fa3b..392f358 100644 --- a/source/.gitignore +++ b/source/.gitignore @@ -1,8 +1,5 @@ *.csproj -*.njsproj -generateDocs.cmd -RunFunctionalTests.cmd -runtests.cmd +*.cmd config.js .vs/ diff --git a/source/DocumentDB.njsproj b/source/DocumentDB.Node.master.njsproj similarity index 74% rename from source/DocumentDB.njsproj rename to source/DocumentDB.Node.master.njsproj index 9ffcebe..3df083e 100644 --- a/source/DocumentDB.njsproj +++ b/source/DocumentDB.Node.master.njsproj @@ -3,24 +3,24 @@ Debug 2.0 - {21b5837f-38c5-4c27-9cce-7dcb27eeda3f} + {411c2c02-66ef-40c8-a964-af8fdacf3961} test\test.js . . {3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD} 11.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - True - true SAK SAK SAK SAK + + - + @@ -49,6 +49,15 @@ Mocha + + Mocha + + + Mocha + + + Mocha + Mocha @@ -59,29 +68,31 @@ - + - - + + + + + - True + False True 0 / - http://localhost:39938/ + http://localhost:48022/ False - False - - + True + http://localhost:1337 False @@ -102,7 +113,7 @@ - True + False False diff --git a/source/DocumentDB.sln b/source/DocumentDB.Node.master.sln similarity index 51% rename from source/DocumentDB.sln rename to source/DocumentDB.Node.master.sln index fae5058..f77be67 100644 --- a/source/DocumentDB.sln +++ b/source/DocumentDB.Node.master.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "DocumentDB", "DocumentDB.njsproj", "{21B5837F-38C5-4C27-9CCE-7DCB27EEDA3F}" +Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "DocumentDB.Node.master", "DocumentDB.Node.master.njsproj", "{411C2C02-66EF-40C8-A964-AF8FDACF3961}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,10 +11,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {21B5837F-38C5-4C27-9CCE-7DCB27EEDA3F}.Debug|Any CPU.ActiveCfg = Release|Any CPU - {21B5837F-38C5-4C27-9CCE-7DCB27EEDA3F}.Debug|Any CPU.Build.0 = Release|Any CPU - {21B5837F-38C5-4C27-9CCE-7DCB27EEDA3F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {21B5837F-38C5-4C27-9CCE-7DCB27EEDA3F}.Release|Any CPU.Build.0 = Release|Any CPU + {411C2C02-66EF-40C8-A964-AF8FDACF3961}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {411C2C02-66EF-40C8-A964-AF8FDACF3961}.Debug|Any CPU.Build.0 = Debug|Any CPU + {411C2C02-66EF-40C8-A964-AF8FDACF3961}.Release|Any CPU.ActiveCfg = Release|Any CPU + {411C2C02-66EF-40C8-A964-AF8FDACF3961}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/source/changelog.md b/source/changelog.md index 024470b..d8faf35 100644 --- a/source/changelog.md +++ b/source/changelog.md @@ -1,3 +1,8 @@ +## Changes in 1.5.6 : ## + +- Fixed RangePartitionResolver.resolveForRead bug where it was not returning links due to a bad concat of results +- Move compareFunction from Range class to RangePartitionResolver class + ## Changes in 1.5.5 : ## - Fixed hashParitionResolver resolveForRead(): When no partition key supplied was throwing exception, instead of returning a list of all registered links. diff --git a/source/lib/constants.js b/source/lib/constants.js index 77aea52..0f75340 100644 --- a/source/lib/constants.js +++ b/source/lib/constants.js @@ -38,7 +38,7 @@ var Constants = { TextPlain: "text/plain", Xml: "application/xml" }, - + HttpMethods: { Get: "GET", Post: "POST", @@ -47,7 +47,7 @@ var Constants = { Head: "HEAD", Options: "OPTIONS" }, - + HttpHeaders: { Authorization: "authorization", ETag: "etag", @@ -95,15 +95,15 @@ var Constants = { Prefer: "Prefer", Location: "Location", Referer: "referer", - + // Query Query: "x-ms-documentdb-query", IsQuery: "x-ms-documentdb-isquery", - + // Our custom DocumentDB headers Continuation: "x-ms-continuation", PageSize: "x-ms-max-item-count", - + // Request sender generated. Simply echoed by backend. ActivityId: "x-ms-activity-id", PreTriggerInclude: "x-ms-documentdb-pre-trigger-include", @@ -123,7 +123,7 @@ var Constants = { EmitVerboseTracesInQuery: "x-ms-documentdb-query-emit-traces", // Version headers and values Version: "x-ms-version", - + //Quota Info MaxEntityCount: "x-ms-root-entity-max-count", CurrentEntityCount: "x-ms-root-entity-current-count", @@ -132,33 +132,33 @@ var Constants = { MaxMediaStorageUsageInMB: "x-ms-max-media-storage-usage-mb", CurrentMediaStorageUsageInMB: "x-ms-media-storage-usage-mb", RequestCharge: "x-ms-request-charge", - + // Offer header OfferType: "x-ms-offer-type", - + // Index progress headers IndexTransformationProgress: "x-ms-documentdb-collection-index-transformation-progress", LazyIndexingProgress: "x-ms-documentdb-collection-lazy-indexing-progress", - + // Upsert header IsUpsert: "x-ms-documentdb-is-upsert" }, - + CurrentVersion: "2015-08-06", - - UserAgent: "documentdb-nodejs-sdk-1.5.5", - + + UserAgent: "documentdb-nodejs-sdk-1.5.6", + DefaultPrecisions: { DefaultNumberHashPrecision: 3, DefaultNumberRangePrecision: -1, DefaultStringHashPrecision: 3, DefaultStringRangePrecision: -1 }, - + ConsistentHashRing: { DefaultVirtualNodesPerCollection: 128 }, - + RegularExpressions: { TrimLeftSlashes: new RegExp("^[/]+"), TrimRightSlashes: new RegExp("[/]+$") diff --git a/source/lib/range.js b/source/lib/range.js index 343ee7b..60f292e 100644 --- a/source/lib/range.js +++ b/source/lib/range.js @@ -33,7 +33,6 @@ var Range = Base.defineClass( * @param {object} options - The Range constructor options. * @param {any} options.low - The low value in the range. * @param {any} options.high - The high value in the range. - * @param {function} options.compareFunction - Optional function that accepts two arguments x and y and returns a negative value if x < y, zero if x = y, or a positive value if x > y. **/ function(options) { if (options === undefined) { @@ -50,80 +49,11 @@ var Range = Base.defineClass( } this.low = options.low; this.high = options.high; - this.compareFunction = options.compareFunction; - if (this.compareFunction !== undefined && typeof this.compareFunction !== "function") { - throw new Error("Invalid argument: 'options.compareFunction' is not a function"); - } - if (this._compare(this.low, this.high) > 0) { - throw new Error("Invalid argument: 'options.low' must be less than or equal than 'options.high'"); - } Object.freeze(this); }, { - /** - * Compares two ranges - * @memberof Range - * @instance - * @param {object} other - The input range to be compared with this range. - * @returns {number} - A negative value if this < other zero if this = other, or a positive value if this > other. - **/ - compareTo: function(other) { - if (this._compare(this.low, other.low) === 0 && this._compare(this.high, other.high) === 0) { - return 0; - } - if (this._compare(this.low, other.low) < 0 || this._compare(this.high, other.high) < 0) { - return -1; - } - return 1; - }, - - /** - * Check for containment of the other range in this range - * @memberof Range - * @instance - * @param {object} other - The key or range to be checked if in this range. - * @returns {boolean} - Returns true if the input is contained in this range; false otherwise. - **/ - contains: function (other) { - if (Range._isRange(other)) { - return this._containsRange(other); - } - else { - return this._containsPoint(other); - } - }, - - /** - * Checks if the other range intersects with this range. - * @memberof Range - * @instance - * @param {object} other - The input range to be checked for intersection with this range. - * @returns {boolean} - Returns true if the two ranges intersect with each other; false otherwise. - **/ - intersect: function (other) { - if (other === undefined || other === null) { - throw new Error("Invalid Argument: 'other' is undefined or null"); - } - var maxLow = this._compare(this.low, other.low) >= 0 ? this.low : other.low; - var minHigh = this._compare(this.high, other.high) <= 0 ? this.high : other.high; - if (this._compare(maxLow, minHigh) <= 0) { - return true; - } - return false; - }, - - /** - * Converts the range to a string in the form of "low,high" - * @memberof Range - * @instance - * @returns {string} - Returns a string representation of the range. - **/ - toString: function () { - return String(this.low) + "," + String(this.high); - }, - /** @ignore */ - _compare: function (x, y) { + _compare: function (x, y, compareFunction) { // Same semantics as Array.sort // http://www.ecma-international.org/ecma-262/6.0/#sec-sortcompare if (x === undefined && y === undefined) @@ -132,8 +62,8 @@ var Range = Base.defineClass( return 1; if (y === undefined) return -1; - if (this.compareFunction !== undefined) { - var v = Number(this.compareFunction(x, y)); + if (compareFunction !== undefined) { + var v = Number(compareFunction(x, y)); if (v === NaN) return 0; return v; @@ -148,16 +78,39 @@ var Range = Base.defineClass( }, /** @ignore */ - _containsPoint: function (point) { - if (this._compare(point, this.low) >= 0 && this._compare(point, this.high) <= 0) { + _contains: function (other, compareFunction) { + if (Range._isRange(other)) { + return this._containsRange(other, compareFunction); + } + else { + return this._containsPoint(other, compareFunction); + } + }, + + /** @ignore */ + _containsPoint: function (point, compareFunction) { + if (this._compare(point, this.low, compareFunction) >= 0 && this._compare(point, this.high, compareFunction) <= 0) { return true; } return false; }, /** @ignore */ - _containsRange: function (other) { - if (this._compare(other.low, this.low) >= 0 && this._compare(other.high, this.high) <= 0) { + _containsRange: function (other, compareFunction) { + if (this._compare(other.low, this.low, compareFunction) >= 0 && this._compare(other.high, this.high, compareFunction) <= 0) { + return true; + } + return false; + }, + + /** @ignore */ + _intersect: function (other, compareFunction) { + if (other === undefined || other === null) { + throw new Error("Invalid Argument: 'other' is undefined or null"); + } + var maxLow = this._compare(this.low, other.low, compareFunction) >= 0 ? this.low : other.low; + var minHigh = this._compare(this.high, other.high, compareFunction) <= 0 ? this.high : other.high; + if (this._compare(maxLow, minHigh, compareFunction) <= 0) { return true; } return false; @@ -180,6 +133,11 @@ var Range = Base.defineClass( this._state = this._states.ended; callback(undefined, this.resources, this.resHeaders); } + }, + + /** @ignore */ + _toString: function () { + return String(this.low) + "," + String(this.high); } }, { @@ -206,8 +164,9 @@ var RangePartitionResolver = Base.defineClass( * @param {string | function} partitionKeyExtractor - If partitionKeyExtractor is a string, it should be the name of the property in the document to execute the hashing on. * If partitionKeyExtractor is a function, it should be a function to extract the partition key from an object. * @param {Array} partitionKeyMap - The map from Range to collection link that is used for partitioning requests. + * @param {function} compareFunction - Optional function that accepts two arguments x and y and returns a negative value if x < y, zero if x = y, or a positive value if x > y. **/ - function(partitionKeyExtractor, partitionKeyMap) { + function(partitionKeyExtractor, partitionKeyMap, compareFunction) { if (partitionKeyExtractor === undefined || partitionKeyExtractor === null) { throw new Error("partitionKeyExtractor cannot be null or undefined"); } @@ -241,15 +200,20 @@ var RangePartitionResolver = Base.defineClass( if (!allMapEntriesAreValid) { throw new Error("All partitionKeyMap entries have to be a tuple {range: Range, link: string }"); } + if (compareFunction !== undefined && typeof compareFunction !== "function") { + throw new Error("Invalid argument: 'compareFunction' is not a function"); + } + this.partitionKeyExtractor = partitionKeyExtractor; this.partitionKeyMap = partitionKeyMap; + this.compareFunction = compareFunction; }, { /** * Extracts the partition key from the specified document using the partitionKeyExtractor * @memberof RangePartitionResolver * @instance * @param {object} document - The document from which to extract the partition key. - * @returns {} + * @returns {} **/ getPartitionKey: function (document) { if (typeof this.partitionKeyExtractor === "string") { @@ -260,7 +224,7 @@ var RangePartitionResolver = Base.defineClass( } throw new Error("Unable to extract partition key from document. Ensure PartitionKeyExtractor is a valid function or property name."); }, - + /** * Given a partition key, returns the correct collection link for creating a document using the range partition map. * @memberof RangePartitionResolver @@ -274,9 +238,9 @@ var RangePartitionResolver = Base.defineClass( if (mapEntry !== undefined && mapEntry !== null) { return mapEntry.link; } - throw new Error("Invalid operation: A containing range for '" + range.toString() + "' doesn't exist in the partition map."); + throw new Error("Invalid operation: A containing range for '" + range._toString() + "' doesn't exist in the partition map."); }, - + /** * Given a partition key, returns a list of collection links to read from using the range partition map. * @memberof RangePartitionResolver @@ -295,7 +259,8 @@ var RangePartitionResolver = Base.defineClass( /** @ignore */ _getFirstContainingMapEntryOrNull: function (point) { - var containingMapEntries = this.partitionKeyMap.filter(function (p) { return p.range !== undefined && p.range.contains(point); }); + var _this = this; + var containingMapEntries = this.partitionKeyMap.filter(function (p) { return p.range !== undefined && p.range._contains(point, _this.compareFunction); }); if (containingMapEntries && containingMapEntries.length > 0) { return containingMapEntries[0]; } @@ -306,10 +271,12 @@ var RangePartitionResolver = Base.defineClass( _getIntersectingMapEntries: function (partitionKey) { var _this = this; var partitionKeys = (partitionKey instanceof Array) ? partitionKey : [partitionKey]; - var ranges = partitionKeys.map(function (p) { return Range._isRange(p) ? p : new Range(p); }); + var ranges = partitionKeys.map(function (p) { return Range._isRange(p) ? p : new Range({ low: p }); }); var result = new Array(); ranges.forEach(function (range) { - result.concat(_this.partitionKeyMap.filter(function (entry) { return entry.range.intersect(range); })); + result = result.concat(_this.partitionKeyMap.filter(function (entry) { + return entry.range._intersect(range, _this.compareFunction); + })); }); return result; } diff --git a/source/package.json b/source/package.json index ab0e06b..7e7d714 100644 --- a/source/package.json +++ b/source/package.json @@ -9,7 +9,7 @@ "database", "cloud" ], - "version": "1.5.5", + "version": "1.5.6", "author": "Microsoft Corporation", "main": "./index.js", "engine": { diff --git a/source/readme.html b/source/readme.html new file mode 100644 index 0000000..07a60c9 --- /dev/null +++ b/source/readme.html @@ -0,0 +1,42 @@ + + + Windows Azure DocumentDB SDK: node.js + + +

Windows Azure DocumentDB Node.js SDK

+

+ This project provides a node module that makes it easy to interact with Azure DocumentDB. For documentation please see the Microsoft Azure DocumentDB Node.js SDK Documentation. +

+

Installation

Core Module

The core module uses the callbacks model for responses, exposed through the DocumentClient

+
npm install documentdb
+

Usage

+

To use this SDK to call Azure DocumentDB, you need to first create an account.

+

You can follow this tutorial to help you get started.

+

Examples

Hello World using Callbacks via the Core Module

var DocumentClient = require('documentdb').DocumentClient;
+var host = "[hostendpoint]";                     // Add your endpoint
+var masterKey = "[database account masterkey]";  // Add the massterkey of the endpoint
+var client = new DocumentClient(host, {masterKey: masterKey});
+var databaseDefinition = { id: "sample database" };
+var collectionDefinition = { id: "sample collection" };
+var documentDefinition = { id: "hello world doc", content: "Hello World!" };
+client.createDatabase(databaseDefinition, function(err, database) {
+    if(err) return console.log(err);
+    console.log('created db');
+    client.createCollection(database._self, collectionDefinition, function(err, collection) {
+        if(err) return console.log(err);
+        console.log('created collection');
+        client.createDocument(collection._self, documentDefinition, function(err, document) {
+            if(err) return console.log(err);
+            console.log('Created Document with content: ', document.content);
+            cleanup(client, database);
+        });
+    });
+});
+function cleanup(client, database) {
+    client.deleteDatabase(database._self, function(err) {
+        if(err) console.log(err);
+    })
+}
+ + + diff --git a/source/readme.md b/source/readme.md deleted file mode 100644 index 214a534..0000000 --- a/source/readme.md +++ /dev/null @@ -1,56 +0,0 @@ - - - Windows Azure DocumentDB SDK: node.js - - -

Windows Azure DocumentDB nodejs SDK

-

- DocumentDB is a purpose built NoSQL JSON document database designed for modern mobile and web applications. DocumentDB supports rich queries over JSON data as well as,
- transactional execution of JavaScript based application logic. DocumentDB is built with a deep commitment to the JSON data model enabling it to offer differentiated query and data
- processing capabilities that scale to meet the needs of the most demanding modern applications. -

-

- The Nodejs sdk uses callbacks model for responses and this is exposed in DocumentClient
-

-

Installing the library using npm

-

> npm install documentdb

-

Hello world example code using callbacks

-

-        
-var DocumentClient = require('documentdb').DocumentClient;
-
-var host = [hostendpoint];                     // Add your endpoint
-var masterKey = [database account masterkey];  // Add the massterkey of the endpoint
-
-var client = new DocumentClient(host, {masterKey: masterKey});
-var databaseDefinition = { id: "sample database" };
-var collectionDefinition = { id: "sample collection" };
-var documentDefinition = { id: "hello world doc", content: "Hello World!" };
-
-client.createDatabase(databaseDefinition, function(err, database) {
-    if(err) return console.log(err);
-    console.log('created db');
-
-    client.createCollection(database._self, collectionDefinition, function(err, collection) {
-        if(err) return console.log(err);
-        
-        console.log('created collection');
-        
-        client.createDocument(collection._self, documentDefinition, function(err, document) {
-            if(err) return console.log(err);
-            
-            console.log('Created Document with content: ', document.content);
-            cleanup(client, database);
-        });
-    });
-});
-
-function cleanup(client, database) {
-    client.deleteDatabase(database._self, function(err) {
-        if(err) console.log(err);
-    })
-}
-

- -##Community -- [DoQmentDB](https://github.com/a8m/doqmentdb) - A Promise-based DocumentDB client for MongoDB users diff --git a/source/test/consistentHashRingTests.js b/source/test/consistentHashRingTests.js index a66f6a8..c992360 100644 --- a/source/test/consistentHashRingTests.js +++ b/source/test/consistentHashRingTests.js @@ -24,7 +24,7 @@ SOFTWARE. "use strict"; var assert = require("assert"); -var ConsistentHashRing = require("../lib/Hash/consistentHashRing").ConsistentHashRing; +var ConsistentHashRing = require("../lib/hash/consistentHashRing").ConsistentHashRing; describe("ConsistentHashRing new()", function () { it("valid arguments does not throw", function () { diff --git a/source/test/documentClientTests.js b/source/test/documentClientTests.js new file mode 100644 index 0000000..1a82545 --- /dev/null +++ b/source/test/documentClientTests.js @@ -0,0 +1,238 @@ +/* +The MIT License (MIT) +Copyright (c) 2014 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/"), + testConfig = require("./_testConfig"), + assert = require("assert"); + +var DocumentClient = lib.DocumentClient, + Constants = lib.Constants; + +describe("DocumentClient Tests", function () { + var host = testConfig.host; + var masterKey = testConfig.masterKey; + var client = new DocumentClient(host, { masterKey: masterKey }); + + describe("setIsUpsertHeader", function () { + it("Should add is-upsert header.", function (done) { + var headers = client.defaultHeaders; + assert.equal(undefined, headers[Constants.HttpHeaders.IsUpsert]); + client.setIsUpsertHeader(headers); + assert.equal(true, headers[Constants.HttpHeaders.IsUpsert]); + done(); + }); + + it("Should update is-upsert header.", function (done) { + var headers = {}; + headers[Constants.HttpHeaders.IsUpsert] = false; + assert.equal(false, headers[Constants.HttpHeaders.IsUpsert]); + client.setIsUpsertHeader(headers); + assert.equal(true, headers[Constants.HttpHeaders.IsUpsert]); + done(); + }); + + it("Should throw on undefined headers", function (done) { + assert.throws( + function () { client.setIsUpsertHeader(); }, + /The "headers" parameter must not be null or undefined/ + ); + done(); + }); + + it("Should throw on null headers", function (done) { + assert.throws( + function () { client.setIsUpsertHeader(null); }, + /The "headers" parameter must not be null or undefined/ + ); + done(); + }); + + it("Should throw on invalid string headers", function (done) { + assert.throws( + function () { client.setIsUpsertHeader(""); }, + /The "headers" parameter must be an instance of "Object". Actual type is: "string"./ + ); + done(); + }); + + it("Should throw on invalid number headers", function (done) { + assert.throws( + function () { client.setIsUpsertHeader(0); }, + /The "headers" parameter must be an instance of "Object". Actual type is: "number"./ + ); + done(); + }); + + it("Should throw on invalid boolean headers", function (done) { + assert.throws( + function () { client.setIsUpsertHeader(false); }, + /The "headers" parameter must be an instance of "Object". Actual type is: "boolean"./ + ); + done(); + }); + }); + + describe("sprintf", function () { + it("0 strings", function (done) { + assert.equal("foo", client.sprintf("foo")); + done(); + }); + + it("1 string", function (done) { + assert.equal("foo", client.sprintf("%s", "foo")); + done(); + }); + + it("2 strings", function (done) { + assert.equal("foobar", client.sprintf("%s%s", "foo", "bar")); + done(); + }); + + it("3 strings", function (done) { + assert.equal("foobarbaz", client.sprintf("%s%s%s", "foo", "bar", "baz")); + done(); + }); + + it("%% escapes", function (done) { + assert.equal('%s', client.sprintf("%%s", 'foo')); + done(); + }); + }); + + describe("validateOptionsAndCallback Unit Tests", function () { + it("no parameters", function (done) { + var result = client.validateOptionsAndCallback(); + + assert.notEqual(null, result.options); + assert.equal("object", typeof result.options); + + assert.equal(undefined, result.callback); + done(); + }); + + it("options", function (done) { + var result = client.validateOptionsAndCallback({}); + + assert.notEqual(null, result.options); + assert.equal("object", typeof result.options); + + assert.equal(undefined, result.callback); + done(); + }); + + it("callback", function (done) { + var result = client.validateOptionsAndCallback(function () { }); + assert.notEqual(null, result.options); + assert.equal("object", typeof result.options); + + assert.equal("function", typeof result.callback); + done(); + }); + + it("options, callback.", function (done) { + var result = client.validateOptionsAndCallback({}, function () { }); + assert.notEqual(null, result.options); + assert.equal("object", typeof result.options); + + assert.equal("function", typeof result.callback); + done(); + }); + + it("undefined, callback", function (done) { + var result = client.validateOptionsAndCallback(undefined, function () { }); + assert.notEqual(null, result.options); + assert.equal("object", typeof result.options); + + assert.equal("function", typeof result.callback); + done(); + }); + + it("null, callback", function (done) { + var result = client.validateOptionsAndCallback(null, function () { }); + assert.equal(null, result.options); + assert.equal("object", typeof result.options); + + assert.equal("function", typeof result.callback); + done(); + }); + + + it("invalid string options", function (done) { + assert.throws( + function () { client.validateOptionsAndCallback("foo", function () { }); }, + /The "options" parameter must be of type "object". Actual type is: "string"/ + ); + done(); + }); + + it("invalid number options", function (done) { + assert.throws( + function () { client.validateOptionsAndCallback(0, function () { }); }, + /The "options" parameter must be of type "object". Actual type is: "number"/ + ); + done(); + }); + + it("invalid bool options", function (done) { + assert.throws( + function () { client.validateOptionsAndCallback(false, function () { }); }, + /The "options" parameter must be of type "object". Actual type is: "boolean"/ + ); + done(); + }); + + it("invalid string callback", function (done) { + assert.throws( + function () { client.validateOptionsAndCallback({}, "bar"); }, + /The "callback" parameter must be of type "function". Actual type is: "string"/ + ); + done(); + }); + + it("invalid number callback", function (done) { + assert.throws( + function () { client.validateOptionsAndCallback({}, 0); }, + /The "callback" parameter must be of type "function". Actual type is: "number"/ + ); + done(); + }); + + it("invalid boolean callback", function (done) { + assert.throws( + function () { client.validateOptionsAndCallback({}, false); }, + /The "callback" parameter must be of type "function". Actual type is: "boolean"/ + ); + done(); + }); + + it("invalid options, invalid callback", function (done) { + assert.throws( + function () { client.validateOptionsAndCallback("foo", "bar"); }, + /The "options" parameter must be of type "object". Actual type is: "string"/ + ); + done(); + }); + }); +}); \ No newline at end of file diff --git a/source/test/hashPartitionResolverTests.js b/source/test/hashPartitionResolverTests.js index 6e819d8..05429bb 100644 --- a/source/test/hashPartitionResolverTests.js +++ b/source/test/hashPartitionResolverTests.js @@ -24,7 +24,7 @@ SOFTWARE. "use strict"; var assert = require("assert"); -var HashPartitionResolver = require("../lib/Hash/hashPartitionResolver").HashPartitionResolver; +var HashPartitionResolver = require("../lib/hash/hashPartitionResolver").HashPartitionResolver; describe("HashPartitionResolver new()", function () { it(" does not throw", function () { diff --git a/source/test/murmurHashTests.js b/source/test/murmurHashTests.js index de540d3..0b2674b 100644 --- a/source/test/murmurHashTests.js +++ b/source/test/murmurHashTests.js @@ -23,7 +23,7 @@ SOFTWARE. "use strict"; -var MurmurHash = require("../lib/Hash/murmurHash").MurmurHash; +var MurmurHash = require("../lib/hash/murmurHash").MurmurHash; var assert = require("assert") describe("MurmurHash.hash", function () { diff --git a/source/test/rangePartitionResolverTests.js b/source/test/rangePartitionResolverTests.js new file mode 100644 index 0000000..5aa2ab1 --- /dev/null +++ b/source/test/rangePartitionResolverTests.js @@ -0,0 +1,431 @@ +/* +The MIT License (MIT) +Copyright (c) 2014 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"); + +var Range = lib.Range, + RangePartitionResolver = lib.RangePartitionResolver; + +describe("RangePartitionResolver", function () { + describe("constructor", function () { + it("missing partitionKeyExtractor throws", function (done) { + var expetcedError = /Error: partitionKeyExtractor cannot be null or undefined/; + + assert.throws( + function () { + var r = new RangePartitionResolver(); + }, + expetcedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver(undefined); + }, + expetcedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver(null); + }, + expetcedError + ); + + done(); + }); + + it("invalid partitionKeyExtractor throws", function (done) { + var expetcedError = /partitionKeyExtractor must be either a 'string' or a 'function'/; + + assert.throws( + function () { + var r = new RangePartitionResolver(0); + }, + expetcedError + ); + + assert.throws( + function () { + var r = RangePartitionResolver(true); + }, + expetcedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver(NaN); + }, + expetcedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver([]); + }, + expetcedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver({}); + }, + expetcedError + ); + + + done(); + }); + + it("missing partitionKeyMap throws", function (done) { + var expectedError = /Error: partitionKeyMap cannot be null or undefined/; + + assert.throws( + function () { + var r = new RangePartitionResolver(""); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver(function () { + }); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver("", null); + }, + expectedError + ); + + done(); + }); + + it("invalid partitionKeyMap throws", function (done) { + var expectedError = /Error: partitionKeyMap has to be an Array/; + + assert.throws( + function () { + var r = new RangePartitionResolver("", 0); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver("", ""); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver("", true); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver("", NaN); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver("", {}); + }, + expectedError + ); + + var r = new RangePartitionResolver("", new Array()); + done(); + }); + + it("valid RangePartitionResolver", function (done) { + var resolver = new RangePartitionResolver("", []); + assert(resolver); + assert.strictEqual(resolver.partitionKeyExtractor, ""); + assert.deepEqual(resolver.partitionKeyMap, []); + done(); + }); + }); + + describe("_getFirstContainingMapEntryOrNull", function () { + it("_getFirstContainingMapEntryOrNull - empty map returns null", function (done) { + var ranges = [undefined, null, 0, "", true, [], {}, NaN, new Range()]; + var resolver = new RangePartitionResolver("", []); + ranges.forEach(function (r) { + var result = resolver._getFirstContainingMapEntryOrNull(r); + assert.equal(result, null); + }); + done(); + }); + + it("_tryGetContainingRange - map with no containing entry returns null", function (done) { + var mapEntry = { range: new Range({ low: "A" }), link: "link1" }; + var resolver = new RangePartitionResolver("key", [mapEntry]); + var result = resolver._getFirstContainingMapEntryOrNull(new Range({ low: "B" })); + assert.equal(result, null); + done(); + }); + + it("_tryGetContainingRange - map with single containing entry returns entry", function (done) { + var mapEntry = { range: new Range(), link: "link1" }; + var resolver = new RangePartitionResolver("key", [mapEntry]); + var result = resolver._getFirstContainingMapEntryOrNull(new Range()); + assert.deepEqual(result, { range: new Range(), link: "link1" }); + done(); + }); + + it("_tryGetContainingRange - map with more multiple containing entries returns first entry", function (done) { + var map1 = [ + { range: new Range({ low: "A", high: "B" }), link: "link1" }, + { range: new Range({ low: "A" }), link: "link2" } + ]; + + var resolver1 = new RangePartitionResolver("key", map1); + var result1 = resolver1._getFirstContainingMapEntryOrNull(new Range({ low: "A" })); + assert.strictEqual(result1.link, "link1"); + + var map2 = [ + { range: new Range({ low: "A" }), link: "link2" }, + { range: new Range({ low: "A", high: "Z" }), link: "link1" } + ]; + + var resolver2 = new RangePartitionResolver("key", map2); + var result2 = resolver2._getFirstContainingMapEntryOrNull(new Range({ low: "A" })); + assert.strictEqual(result2.link, "link2"); + done(); + }); + }); + + describe("resolveForCreate", function () { + it("_tryGetContainingRange - map containing parition key returns corresponding link", function (done) { + var resolver = new RangePartitionResolver("key", [ + { range: new Range({ low: "A", high: "M" }), link: "link1" }, + { range: new Range({ low: "N", high: "Z" }), link: "link2" } + ]); + var result = resolver.resolveForCreate("X"); + assert.strictEqual(result, "link2"); + done(); + }); + + it("_tryGetContainingRange - map not containing parition key throws", function (done) { + var resolver = new RangePartitionResolver("key", [ + { range: new Range({ low: "A", high: "M" }), link: "link1" } + ]); + + assert.throws( + function () { + var result = resolver.resolveForCreate("X"); + }, + /Error: Invalid operation: A containing range for 'X,X' doesn't exist in the partition map./ + ); + done(); + }); + }); + + var resolveForReadTest = function (resolver, partitionKey, expectedLinks) { + var result = resolver.resolveForRead(partitionKey); + assert.deepEqual(expectedLinks, result); + }; + + describe("resolveForRead", function () { + var resolver = new RangePartitionResolver( + function (doc) { + return doc.key; + }, + [ + { + range: new Range({ low: "A", high: "M" }), + link: "link1" + }, + { + range: new Range({ low: "N", high: "Z" }), + link: "link2" + } + ]); + + it("undefined", function (done) { + var partitionKey = undefined; + var expectedLinks = ["link1", "link2"]; + resolveForReadTest(resolver, partitionKey, expectedLinks); + done(); + }); + + it("null", function (done) { + var partitionKey = null; + var expectedLinks = ["link1", "link2"]; + resolveForReadTest(resolver, partitionKey, expectedLinks); + done(); + }); + }); + + describe("resolveForRead string", function () { + var resolver = new RangePartitionResolver( + function (doc) { + return doc.key; + }, + [ + { + range: new Range({ low: "A", high: "M" }), + link: "link1" + }, + { + range: new Range({ low: "N", high: "Z" }), + link: "link2" + } + ]); + + it("point", function (done) { + var partitionKey = new Range({ low: "D" }); + var expectedLinks = ["link1"]; + resolveForReadTest(resolver, partitionKey, expectedLinks); + + var partitionKey2 = new Range({ low: "Q" }); + var expectedLinks2 = ["link2"]; + resolveForReadTest(resolver, partitionKey2, expectedLinks2); + done(); + }); + + it("range", function (done) { + var partitionKey = new Range({ low: "D", high: "Q" }); + var expectedLinks = ["link1", "link2"]; + resolveForReadTest(resolver, partitionKey, expectedLinks); + done(); + }); + + it("array of ranges", function (done) { + var partitionKey = [ + new Range({ low: "A", high: "B" }), + new Range({ low: "Q" }) + ]; + var expectedLinks = ["link1", "link2"]; + resolveForReadTest(resolver, partitionKey, expectedLinks); + done(); + }); + }); + + describe("resolveForRead number", function () { + var partitionKeyExtractor = function (doc) { + return doc.key; + }; + + var partitionKeyMap = [ + { + range: new Range({ low: 1, high: 15 }), + link: "link1" + }, + { + range: new Range({ low: 16, high: 30 }), + link: "link2" + } + ]; + + it("point, default compareFunction", function (done) { + var resolver = new RangePartitionResolver(partitionKeyExtractor, partitionKeyMap); + + var partitionKey = new Range({ low: 2 }); + var expectedLinks = ["link2"]; + + resolveForReadTest(resolver, partitionKey, expectedLinks); + done(); + }); + + it("point, custom compareFunction", function (done) { + var resolver = new RangePartitionResolver(partitionKeyExtractor, partitionKeyMap, function (a, b) { + return a - b; + }); + + var partitionKey = new Range({ low: 2 }); + var expectedLinks = ["link1"]; + + resolveForReadTest(resolver, partitionKey, expectedLinks); + done(); + }); + }); + + describe("compareFunction", function () { + var invalidCompareFunctionTest = function (compareFunction, done) { + assert.throws( + function () { + var resolver = new RangePartitionResolver( + "key", + [{ range: new Range({ low: "A" }), link: "link1" }], + compareFunction + ); + }, + /Invalid argument: 'compareFunction' is not a function/); + done(); + } + + it("invalid compareFunction - null", function (done) { + var compareFunction = null; + invalidCompareFunctionTest(compareFunction, done); + }); + + it("invalid compareFunction - string", function (done) { + var compareFunction = ""; + invalidCompareFunctionTest(compareFunction, done); + }); + + it("invalid compareFunction - number", function (done) { + var compareFunction = 0; + invalidCompareFunctionTest(compareFunction, done); + }); + + it("invalid compareFunction - boolean", function (done) { + var compareFunction = false; + invalidCompareFunctionTest(compareFunction, done); + }); + + it("invalid compareFunction - object", function (done) { + var compareFunction = {}; + invalidCompareFunctionTest(compareFunction, done); + }); + + it("compareFunction throws", function (done) { + var resolver = new RangePartitionResolver( + "key", + [{ range: new Range({ low: "A" }), link: "link1" }], + function (a, b) { throw new Error("Compare error"); } + ); + + assert.throws( + function () { + var result = resolver.resolveForRead("A", ["link1"]); + }, + /Error: Compare error/); + done(); + }); + }); +}); diff --git a/source/test/rangeTests.js b/source/test/rangeTests.js new file mode 100644 index 0000000..9cddabe --- /dev/null +++ b/source/test/rangeTests.js @@ -0,0 +1,502 @@ +/* +The MIT License (MIT) +Copyright (c) 2014 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"); + +var Range = lib.Range; + +describe("Range Tests", function () { + describe("constructor", function () { + var invalidOptionsTest = function (options, expectedError, done) { + assert.throws( + function () { + var r = new Range(options); + }, + expectedError); + done(); + } + + var optionsIsNullTest = function (options, done) { + invalidOptionsTest(options, /Invalid argument: 'options' is null/, done); + } + + var optionsIsNotAnObjectTest = function (options, done) { + invalidOptionsTest(options, /Invalid argument: 'options' is not an object/, done); + } + + var invalidRangeTest = function (options, done) { + invalidOptionsTest(options, /Invalid argument: 'options.low' must be less than or equal than 'options.high'/, done); + } + + it("options - undefined (ommited argument)", function (done) { + assert(new Range()); + done(); + }); + + it("options - undefined (literal argument)", function (done) { + assert(new Range(undefined)); + done(); + }); + + it("options - null ", function (done) { + var options = null; + optionsIsNullTest(options, done); + }); + + it("options - number", function (done) { + var options = 0; + optionsIsNotAnObjectTest(options, done); + }); + + it("invalid options - string", function (done) { + var options = ""; + optionsIsNotAnObjectTest(options, done); + }); + + it("invalid options - boolean", function (done) { + var options = false; + optionsIsNotAnObjectTest(options, done); + }); + + it("Range instances are frozen", function (done) { + var r = new Range(); + + assert.throws( + function () { + r.compareFunction = 1; + }, + /Can't add property compareFunction, object is not extensible/ + ); + + done(); + }); + }); + + describe("_contains", function () { + it("undefined,undefined contains undefined is true", function (done) { + var r = new Range(); + assert(r._contains(undefined)); + done(); + }); + + it("undefined,undefined contains null is false", function (done) { + var r = new Range(); + assert(!r._contains(null)); + done(); + }); + + it("null,null contains undefined is true", function (done) { + var r = new Range({ low: null }); + assert(r._contains(null)); + done(); + }); + + it("null,null contains null is true", function (done) { + var r = new Range({ low: null }); + assert(r._contains(null)); + done(); + }); + + it("range contains self is true - default range", function (done) { + var r = new Range(); + assert(r._contains(r)); + done(); + }); + + it("range contains self is true - non-default range", function (done) { + var r = new Range({ low: "A" }); + assert(r._contains(r)); + done(); + }); + + it("A,D contains B,C is true", function (done) { + var r1 = new Range({ low: "A", high: "D" }); + var r2 = new Range({ low: "B", high: "C" }); + assert(r1._contains(r2)); + done(); + }); + + it("B,C contains A,D is false", function (done) { + var r1 = new Range({ low: "B", high: "C" }); + var r2 = new Range({ low: "A", high: "D" }); + assert(!r1._contains(r2)); + done(); + }); + + it("A,C contains B,D is false", function (done) { + var r1 = new Range({ low: "A", high: "C" }); + var r2 = new Range({ low: "B", high: "D" }); + assert(!r1._contains(r2)); + done(); + }); + + it("B,D contains A,C is false", function (done) { + var r1 = new Range({ low: "B", high: "D" }); + var r2 = new Range({ low: "A", high: "C" }); + assert(!r1._contains(r2)); + done(); + }); + + it("A,B contains B,C is false", function (done) { + var r1 = new Range({ low: "A", high: "B" }); + var r2 = new Range({ low: "B", high: "C" }); + assert(!r1._contains(r2)); + done(); + }); + + it("B,C contains A,B is false", function (done) { + var r1 = new Range({ low: "B", high: "C" }); + var r2 = new Range({ low: "A", high: "B" }); + assert(!r1._contains(r2)); + done(); + }); + + it("A,B contains C,D is false", function (done) { + var r1 = new Range({ low: "A", high: "B" }); + var r2 = new Range({ low: "C", high: "D" }); + assert(!r1._contains(r2)); + done(); + }); + + it("C,D contains A,B is false", function (done) { + var r1 = new Range({ low: "C", high: "D" }); + var r2 = new Range({ low: "A", high: "B" }); + assert(!r1._contains(r2)); + done(); + }); + + it("A,C contains B is true", function (done) { + var r1 = new Range({ low: "A", high: "C" }); + assert(r1._contains("B")); + done(); + }); + + it("B,C contains A is false", function (done) { + var r1 = new Range({ low: "B", high: "C" }); + assert(!r1._contains("A")); + done(); + }); + + it("A,B contains C is false", function (done) { + var r1 = new Range({ low: "A", high: "B" }); + assert(!r1._contains("C")); + done(); + }); + }); + + describe("_containsPoint", function () { + var range = new Range({ low: 1, high: 3 }); + + it("numbers, default comparison", function (done) { + assert(range._containsPoint(20)); + done(); + }); + + it("numbers, custom comparison", function (done) { + + assert(!range._containsPoint(20, function (a, b) { + return a - b; + })); + + done(); + }); + }); + + describe("_containsRange", function () { + var range = new Range({ low: 1, high: 3 }); + + it("numbers, default comparison", function (done) { + assert(range._containsRange({ low: 20, high: 29})); + done(); + }); + + it("numbers, custom comparison", function (done) { + assert(!range._containsRange({ low: 20, high: 29 }, function (a, b) { + return a - b; + })); + + done(); + }); + }); + + describe("_intersect", function () { + var otherIsUndefinedOrNullTest = function (other, done) { + var r = new Range(); + assert.throws( + function () { + r._intersect(other); + }, + /Invalid Argument: 'other' is undefined or null/ + ); + done(); + }; + + it("error - other is undefined", function (done) { + otherIsUndefinedOrNullTest(undefined, done); + }); + + it("error - other is null", function (done) { + otherIsUndefinedOrNullTest(null, done); + }); + + it("range intersect self is true - default range", function (done) { + var r = new Range(); + assert(r._intersect(r)); + done(); + }); + + it("R intersect R is true - non default range", function (done) { + var r = new Range({ low: 1, high: "2" }); + assert(r._intersect(r)); + done(); + }); + + it("A,D insersects B,C is true", function (done) { + var r1 = new Range({ low: "A", high: "D" }); + var r2 = new Range({ low: "B", high: "C" }); + assert(r1._intersect(r2)); + done(); + }); + + it("B,C insersects A,D is true", function (done) { + var r1 = new Range({ low: "B", high: "C" }); + var r2 = new Range({ low: "A", high: "D" }); + assert(r1._intersect(r2)); + done(); + }); + + it("A,C insersects B,D is true", function (done) { + var r1 = new Range({ low: "A", high: "C" }); + var r2 = new Range({ low: "B", high: "D" }); + assert(r1._intersect(r2)); + assert(r2._intersect(r1)); + done(); + }); + + it("B,D insersects A,C is true", function (done) { + var r1 = new Range({ low: "B", high: "D" }); + var r2 = new Range({ low: "A", high: "C" }); + assert(r1._intersect(r2)); + done(); + }); + + it("A,B insersects B,C is true", function (done) { + var r1 = new Range({ low: "A", high: "B" }); + var r2 = new Range({ low: "B", high: "C" }); + assert(r1._intersect(r2)); + assert(r2._intersect(r1)); + done(); + }); + + it("B,C insersects A,B is true", function (done) { + var r1 = new Range({ low: "B", high: "C" }); + var r2 = new Range({ low: "A", high: "B" }); + assert(r1._intersect(r2)); + done(); + }); + + it("A,B insersects C,D is false", function (done) { + var r1 = new Range({ low: "A", high: "B" }); + var r2 = new Range({ low: "C", high: "D" }); + assert(!r1._intersect(r2)); + done(); + }); + + it("C,D insersects A,B is false", function (done) { + var r1 = new Range({ low: "C", high: "D" }); + var r2 = new Range({ low: "A", high: "B" }); + assert(!r1._intersect(r2)); + done(); + }); + }); + + describe("_toString", function () { + var toStringTest = function (options, expectedString, done) { + var r = new Range(options); + assert.strictEqual(r._toString(), expectedString); + done(); + }; + + it("undefined values", function (done) { + toStringTest(undefined, "undefined,undefined", done); + }); + it("null values", function (done) { + toStringTest({ low: null }, "null,null", done); + }); + it("NaN values", function (done) { + toStringTest({ low: NaN }, "NaN,NaN", done); + }); + it("number values", function (done) { + toStringTest({ low: 1 }, "1,1", done); + }); + it("string values", function (done) { + toStringTest({ low: "a" }, "a,a", done); + }); + it("boolean values", function (done) { + toStringTest({ low: false, high: true }, "false,true", done); + }); + it("object values", function (done) { + toStringTest({ low: {} }, "[object Object],[object Object]", done); + }); + }); + + describe("_compare", function () { + var r = new Range(); + + var compareAsNumbers = function (a, b) { + return a - b; + } + + var constantCompareFunction = function (a, b) { + return 0; + }; + + it("(undefined, undefined) === 0", function (done) { + assert(r._compare() === 0); + assert(r._compare(undefined) === 0); + assert(r._compare(undefined, undefined) === 0); + done(); + }); + + it("(undefined, y) > 0", function (done) { + assert(r._compare(undefined, null) > 0); + assert(r._compare(undefined, -NaN) > 0); + assert(r._compare(undefined, 0) > 0); + assert(r._compare(undefined, NaN) > 0); + assert(r._compare(undefined, true) > 0); + assert(r._compare(undefined, false) > 0); + assert(r._compare(undefined, "a") > 0); + assert(r._compare(undefined, "undefined") > 0); + assert(r._compare(undefined, "z") > 0); + assert(r._compare(undefined, []) > 0); + assert(r._compare(undefined, {}) > 0); + assert(r._compare(undefined, 2, constantCompareFunction) > 0); + assert(r._compare(undefined, 2, compareAsNumbers) > 0); + + done(); + }); + + it("(x, undefined) < 0", function (done) { + assert(r._compare(null) < 0); + assert(r._compare(-NaN) < 0); + assert(r._compare(0) < 0); + assert(r._compare(NaN) < 0); + assert(r._compare(true) < 0); + assert(r._compare(false) < 0); + assert(r._compare("a") < 0); + assert(r._compare("undefined") < 0); + assert(r._compare("z") < 0); + assert(r._compare([]) < 0); + assert(r._compare({}) < 0); + assert(r._compare(1, undefined, constantCompareFunction) < 0); + assert(r._compare(1, undefined, compareAsNumbers) < 0); + done(); + }); + + it("values as strings (default)", function (done) { + assert(r._compare("A", "B") < 0); + assert(r._compare("", "") === 0); + assert(r._compare("B", "A") > 0); + assert(r._compare("10", "2") < 0); + assert(r._compare(10, "02") > 0); + assert(r._compare(10, 2) < 0); + assert(r._compare(null, "nulm") < 0); + assert(r._compare(null, "null") === 0); + assert(r._compare(null, "nulk") > 0); + assert(r._compare(true, "truf") < 0); + assert(r._compare(true, "true") === 0); + assert(r._compare(true, "trud") > 0); + assert(r._compare({}, "[object Object]") === 0); + done(); + }); + + it("values as numbers", function (done) { + + assert(r._compare(undefined, 2, compareAsNumbers) > 0); + assert(r._compare(1, 2, compareAsNumbers) < 0); + assert(r._compare(0, 0, compareAsNumbers) === 0); + assert(r._compare(10, 2, compareAsNumbers) > 0); + done(); + }); + + it("always return 0", function (done) { + assert(r._compare(1, 2, constantCompareFunction) === 0); + assert(r._compare(2, 1, constantCompareFunction) === 0); + done(); + }); + }); + + describe("_isRange", function () { + it("_isRange(undefined) is false", function (done) { + assert(!Range._isRange()); + done(); + }); + + it("_isRange(null) is false", function (done) { + assert(!Range._isRange(null)); + done(); + }); + + it("_isRange(non-object) is false", function (done) { + var points = [ + undefined, + null, + 1, + "", + true, + NaN, + function () { + }, + {}, + { + low: "" + } + ]; + + for (var i = 0; i < points.length; i++) { + assert(!Range._isRange(points[i])); + } + + done(); + }); + + it("_isRange(point) is false", function (done) { + var ranges = [ + { + low: "", + high: 1 + }, + new Range() + ]; + + for (var i = 0; i < ranges.length; i++) { + assert(Range._isRange(ranges[i])); + } + + done(); + }); + }); +}); diff --git a/source/test/readme.md b/source/test/readme.md index 7285fcf..e1a2b28 100644 --- a/source/test/readme.md +++ b/source/test/readme.md @@ -1,6 +1,6 @@ Follow these instructions to run the tests locally. -##Prerequisites +## Prerequisites 1. Clone Azure/azure-documentdb-node repository Please clone the source and tests from [https://github.com/Azure/azure-documentdb-node](https://github.com/Azure/azure-documentdb-node) @@ -11,7 +11,7 @@ Please clone the source and tests from [https://github.com/Azure/azure-documentd 3. Install mocha package globally > npm install -g mocha -##Running the tests +## Running the tests Using your command-line tool, from the root of your local copy of azure-documentdb-node repository: If you are contributing changes and submitting PR then you need to ensure that you run the tests against your local copy of the source, and not the published npm package. diff --git a/source/test/test.js b/source/test/test.js index 0644a2e..dfd7949 100644 --- a/source/test/test.js +++ b/source/test/test.js @@ -23,16 +23,18 @@ SOFTWARE. "use strict"; -var Base = require("documentdb").Base - , DocumentDBClient = require("documentdb").DocumentClient - , DocumentBase = require("documentdb").DocumentBase - , Constants = require("documentdb").Constants - , assert = require("assert") - , testConfig = require("./_testConfig") - , Stream = require("stream") - , Range = require("documentdb").Range - , RangePartitionResolver = require("documentdb").RangePartitionResolver - , HashPartitionResolver = require("documentdb").HashPartitionResolver; +var lib = require("../lib/"), + assert = require("assert"), + testConfig = require("./_testConfig"), + Stream = require("stream"); + +var Base = lib.Base, + DocumentDBClient = lib.DocumentClient, + DocumentBase = lib.DocumentBase, + Constants = lib.Constants, + Range = lib.Range, + RangePartitionResolver = lib.RangePartitionResolver, + HashPartitionResolver = lib.HashPartitionResolver; process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; @@ -2285,951 +2287,7 @@ describe("NodeJS CRUD Tests", function() { }); }); - describe("setIsUpsertHeader Unit Tests", function () { - var client = new DocumentDBClient(host, { masterKey: masterKey }); - - it("[nativeApi] Should add is-upsert header.", function (done) { - var headers = client.defaultHeaders; - assert.equal(undefined, headers[Constants.HttpHeaders.IsUpsert]); - client.setIsUpsertHeader(headers); - assert.equal(true, headers[Constants.HttpHeaders.IsUpsert]); - done(); - }); - - it("[nativeApi] Should update is-upsert header.", function (done) { - var headers = {}; - headers[Constants.HttpHeaders.IsUpsert] = false; - assert.equal(false, headers[Constants.HttpHeaders.IsUpsert]); - client.setIsUpsertHeader(headers); - assert.equal(true, headers[Constants.HttpHeaders.IsUpsert]); - done(); - }); - - it("[nativeApi] Should throw on undefined headers", function (done) { - assert.throws( - function () { client.setIsUpsertHeader(); }, - /The "headers" parameter must not be null or undefined/ - ); - done(); - }); - - it("[nativeApi] Should throw on null headers", function (done) { - assert.throws( - function () { client.setIsUpsertHeader(null); }, - /The "headers" parameter must not be null or undefined/ - ); - done(); - }); - - it("[nativeApi] Should throw on invalid string headers", function (done) { - assert.throws( - function () { client.setIsUpsertHeader(""); }, - /The "headers" parameter must be an instance of "Object". Actual type is: "string"./ - ); - done(); - }); - - it("[nativeApi] Should throw on invalid number headers", function (done) { - assert.throws( - function () { client.setIsUpsertHeader(0); }, - /The "headers" parameter must be an instance of "Object". Actual type is: "number"./ - ); - done(); - }); - - it("[nativeApi] Should throw on invalid boolean headers", function (done) { - assert.throws( - function () { client.setIsUpsertHeader(false); }, - /The "headers" parameter must be an instance of "Object". Actual type is: "boolean"./ - ); - done(); - }); - - }); - - describe("validateOptionsAndCallback Unit Tests", function () { - var client = new DocumentDBClient(host, { masterKey: masterKey }); - - it("[nativeApi] no parameters", function (done) { - var result = client.validateOptionsAndCallback(); - - assert.notEqual(null, result.options); - assert.equal("object", typeof result.options); - - assert.equal(undefined, result.callback); - done(); - }); - - it("[nativeApi] options", function (done) { - var result = client.validateOptionsAndCallback({}); - - assert.notEqual(null, result.options); - assert.equal("object", typeof result.options); - - assert.equal(undefined, result.callback); - done(); - }); - - it("[nativeApi] callback", function (done) { - var result = client.validateOptionsAndCallback(function(){}); - assert.notEqual(null, result.options); - assert.equal("object", typeof result.options); - - assert.equal("function", typeof result.callback); - done(); - }); - - it("[nativeApi] options, callback.", function (done) { - var result = client.validateOptionsAndCallback({}, function(){}); - assert.notEqual(null, result.options); - assert.equal("object", typeof result.options); - - assert.equal("function", typeof result.callback); - done(); - }); - - it("[nativeApi] undefined, callback", function (done) { - var result = client.validateOptionsAndCallback(undefined, function(){}); - assert.notEqual(null, result.options); - assert.equal("object", typeof result.options); - - assert.equal("function", typeof result.callback); - done(); - }); - - it("[nativeApi] null, callback", function (done) { - var result = client.validateOptionsAndCallback(null, function () { }); - assert.equal(null, result.options); - assert.equal("object", typeof result.options); - - assert.equal("function", typeof result.callback); - done(); - }); - - - it("[nativeApi] invalid string options", function (done) { - assert.throws( - function () { client.validateOptionsAndCallback("foo", function () { }); }, - /The "options" parameter must be of type "object". Actual type is: "string"/ - ); - done(); - }); - - it("[nativeApi] invalid number options", function (done) { - assert.throws( - function () { client.validateOptionsAndCallback(0, function () { }); }, - /The "options" parameter must be of type "object". Actual type is: "number"/ - ); - done(); - }); - - it("[nativeApi] invalid bool options", function (done) { - assert.throws( - function () { client.validateOptionsAndCallback(false, function () { }); }, - /The "options" parameter must be of type "object". Actual type is: "boolean"/ - ); - done(); - }); - - it("[nativeApi] invalid string callback", function (done) { - assert.throws( - function () { client.validateOptionsAndCallback({}, "bar"); }, - /The "callback" parameter must be of type "function". Actual type is: "string"/ - ); - done(); - }); - - it("[nativeApi] invalid number callback", function (done) { - assert.throws( - function () { client.validateOptionsAndCallback({}, 0); }, - /The "callback" parameter must be of type "function". Actual type is: "number"/ - ); - done(); - }); - - it("[nativeApi] invalid boolean callback", function (done) { - assert.throws( - function () { client.validateOptionsAndCallback({}, false); }, - /The "callback" parameter must be of type "function". Actual type is: "boolean"/ - ); - done(); - }); - - it("[nativeApi] invalid options, invalid callback", function (done) { - assert.throws( - function () { client.validateOptionsAndCallback("foo", "bar"); }, - /The "options" parameter must be of type "object". Actual type is: "string"/ - ); - done(); - }); - }); - - describe("sprintf Unit Tests", function () { - var client = new DocumentDBClient(host, { masterKey: masterKey }); - - it("[nativeApi] 0 strings", function (done) { - assert.equal("foo", client.sprintf("foo")); - done(); - }); - - it("[nativeApi] 1 string", function (done) { - assert.equal("foo", client.sprintf("%s", "foo")); - done(); - }); - - it("[nativeApi] 2 strings", function (done) { - assert.equal("foobar", client.sprintf("%s%s", "foo", "bar")); - done(); - }); - - it("[nativeApi] 3 strings", function (done) { - assert.equal("foobarbaz", client.sprintf("%s%s%s", "foo", "bar", "baz")); - done(); - }); - - it("[nativeApi] %% escapes", function (done) { - assert.equal('%s', client.sprintf("%%s", 'foo')); - done(); - }); - }); - - describe("Range Tests", function () { - describe("constructor", function () { - var invalidOptionsTest = function (options, expectedError, done) { - assert.throws( - function () { - var r = new Range(options); - }, - expectedError); - done(); - } - - var optionsIsNullTest = function (options, done) { - invalidOptionsTest(options, /Invalid argument: 'options' is null/, done); - } - - var optionsIsNotAnObjectTest = function (options, done) { - invalidOptionsTest(options, /Invalid argument: 'options' is not an object/, done); - } - - var comparisonFunctionIsNotAFunctionTest = function (options, done) { - invalidOptionsTest(options, /Invalid argument: 'options.compareFunction' is not a function/, done); - } - - var invalidRangeTest = function (options, done) { - invalidOptionsTest(options, /Invalid argument: 'options.low' must be less than or equal than 'options.high'/, done); - } - - it("[nativeApi] options - undefined (ommited argument)", function (done) { - assert(new Range()); - done(); - }); - - it("[nativeApi] options - undefined (literal argument)", function (done) { - assert(new Range(undefined)); - done(); - }); - - it("[nativeApi] options - null ", function (done) { - var options = null; - optionsIsNullTest(options, done); - }); - - it("[nativeApi] options - number", function (done) { - var options = 0; - optionsIsNotAnObjectTest(options, done); - }); - - it("[nativeApi] invalid options - string", function (done) { - var options = ""; - optionsIsNotAnObjectTest(options, done); - }); - - it("[nativeApi] invalid options - boolean", function (done) { - var options = false; - optionsIsNotAnObjectTest(options, done); - }); - - it("[nativeApi] invalid compareFunction - null", function (done) { - var options = { compareFunction: null }; - comparisonFunctionIsNotAFunctionTest(options, done); - }); - - it("[nativeApi] invalid compareFunction - string", function (done) { - var options = { compareFunction: "" }; - comparisonFunctionIsNotAFunctionTest(options, done); - }); - - it("[nativeApi] invalid compareFunction - number", function (done) { - var options = { compareFunction: 0 }; - comparisonFunctionIsNotAFunctionTest(options, done); - }); - - it("[nativeApi] invalid compareFunction - boolean", function (done) { - var options = { compareFunction: false }; - comparisonFunctionIsNotAFunctionTest(options, done); - }); - - it("[nativeApi] invalid compareFunction - object", function (done) { - var options = { compareFunction: {} }; - comparisonFunctionIsNotAFunctionTest(options, done); - }); - - it("[nativeApi] invalid range - low greater than high", function (done) { - var options = { low: 2, high: 1 }; - invalidRangeTest(options, done); - }); - - it("[nativeApi] compareFunction exception is thrown - inside constructor ", function (done) { - var options = { - low: 0, - high: 1, - compareFunction: function (a, b) { - throw new Error("Compare error"); - } - }; - assert.throws( - function () { - var r = new Range(options); - }, - /Error: Compare error/); - done(); - }); - - it("[nativeApi] compareFunction exception is thrown - outside constructor ", function (done) { - var options = { - low: 0, - high: 1, - compareFunction: function (a, b) { - if (a === 1) { - throw new Error("Compare error"); - } - } - }; - var r = new Range(options); - assert.throws( - function () { - r._compare(1, 0); - }, - /Error: Compare error/); - done(); - }); - - it("[nativeApi] Range instances are frozen", function (done) { - var r = new Range(); - - assert.throws( - function () { - r.compareFunction = 1; - }, - /Cannot assign to read only property 'compareFunction' of / - ); - - done(); - }); - - }); - - describe("contains", function () { - it("[nativeApi] undefined,undefined contains undefined is true", function (done) { - var r = new Range(); - assert(r.contains(undefined)); - done(); - }); - - it("[nativeApi] undefined,undefined contains null is false", function (done) { - var r = new Range(); - assert(!r.contains(null)); - done(); - }); - - it("[nativeApi] null,null contains undefined is true", function (done) { - var r = new Range({ low: null }); - assert(r.contains(null)); - done(); - }); - - it("[nativeApi] null,null contains null is true", function (done) { - var r = new Range({ low: null }); - assert(r.contains(null)); - done(); - }); - - it("[nativeApi] range contains self is true - default range", function (done) { - var r = new Range(); - assert(r.contains(r)); - done(); - }); - - it("[nativeApi] range contains self is true - non-default range", function (done) { - var r = new Range({ low: "A" }); - assert(r.contains(r)); - done(); - }); - - it("[nativeApi] A,D contains B,C is true", function (done) { - var r1 = new Range({ low: "A", high: "D" }); - var r2 = new Range({ low: "B", high: "C" }); - assert(r1.contains(r2)); - done(); - }); - - it("[nativeApi] B,C contains A,D is false", function (done) { - var r1 = new Range({ low: "B", high: "C" }); - var r2 = new Range({ low: "A", high: "D" }); - assert(!r1.contains(r2)); - done(); - }); - - it("[nativeApi] A,C contains B,D is false", function (done) { - var r1 = new Range({ low: "A", high: "C" }); - var r2 = new Range({ low: "B", high: "D" }); - assert(!r1.contains(r2)); - done(); - }); - - it("[nativeApi] B,D contains A,C is false", function (done) { - var r1 = new Range({ low: "B", high: "D" }); - var r2 = new Range({ low: "A", high: "C" }); - assert(!r1.contains(r2)); - done(); - }); - - it("[nativeApi] A,B contains B,C is false", function (done) { - var r1 = new Range({ low: "A", high: "B" }); - var r2 = new Range({ low: "B", high: "C" }); - assert(!r1.contains(r2)); - done(); - }); - - it("[nativeApi] B,C contains A,B is false", function (done) { - var r1 = new Range({ low: "B", high: "C" }); - var r2 = new Range({ low: "A", high: "B" }); - assert(!r1.contains(r2)); - done(); - }); - - it("[nativeApi] A,B contains C,D is false", function (done) { - var r1 = new Range({ low: "A", high: "B" }); - var r2 = new Range({ low: "C", high: "D" }); - assert(!r1.contains(r2)); - done(); - }); - - it("[nativeApi] C,D contains A,B is false", function (done) { - var r1 = new Range({ low: "C", high: "D" }); - var r2 = new Range({ low: "A", high: "B" }); - assert(!r1.contains(r2)); - done(); - }); - - it("[nativeApi] A,C contains B is true", function (done) { - var r1 = new Range({ low: "A", high: "C" }); - assert(r1.contains("B")); - done(); - }); - - it("[nativeApi] B,C contains A is false", function (done) { - var r1 = new Range({ low: "B", high: "C" }); - assert(!r1.contains("A")); - done(); - }); - - it("[nativeApi] A,B contains C is false", function (done) { - var r1 = new Range({ low: "A", high: "B" }); - assert(!r1.contains("C")); - done(); - }); - }); - - describe("intersect", function () { - var otherIsUndefinedOrNullTest = function (other, done) { - var r = new Range(); - assert.throws( - function () { - r.intersect(other); - }, - /Invalid Argument: 'other' is undefined or null/ - ); - done(); - }; - - it("[nativeApi] error - other is undefined", function (done) { - otherIsUndefinedOrNullTest(undefined, done); - }); - - it("[nativeApi] error - other is null", function (done) { - otherIsUndefinedOrNullTest(null, done); - }); - - it("[nativeApi] range intersect self is true - default range", function (done) { - var r = new Range(); - assert(r.intersect(r)); - done(); - }); - - it("[nativeApi] R intersect R is true - non default range", function (done) { - var r = new Range({ low: 1, high: "2" }); - assert(r.intersect(r)); - done(); - }); - - it("[nativeApi] A,D insersects B,C is true", function (done) { - var r1 = new Range({ low: "A", high: "D" }); - var r2 = new Range({ low: "B", high: "C" }); - assert(r1.intersect(r2)); - done(); - }); - - it("[nativeApi] B,C insersects A,D is true", function (done) { - var r1 = new Range({ low: "B", high: "C" }); - var r2 = new Range({ low: "A", high: "D" }); - assert(r1.intersect(r2)); - done(); - }); - - it("[nativeApi] A,C insersects B,D is true", function (done) { - var r1 = new Range({ low: "A", high: "C" }); - var r2 = new Range({ low: "B", high: "D" }); - assert(r1.intersect(r2)); - assert(r2.intersect(r1)); - done(); - }); - - it("[nativeApi] B,D insersects A,C is true", function (done) { - var r1 = new Range({ low: "B", high: "D" }); - var r2 = new Range({ low: "A", high: "C" }); - assert(r1.intersect(r2)); - done(); - }); - - it("[nativeApi] A,B insersects B,C is true", function (done) { - var r1 = new Range({ low: "A", high: "B" }); - var r2 = new Range({ low: "B", high: "C" }); - assert(r1.intersect(r2)); - assert(r2.intersect(r1)); - done(); - }); - - it("[nativeApi] B,C insersects A,B is true", function (done) { - var r1 = new Range({ low: "B", high: "C" }); - var r2 = new Range({ low: "A", high: "B" }); - assert(r1.intersect(r2)); - done(); - }); - - it("[nativeApi] A,B insersects C,D is false", function (done) { - var r1 = new Range({ low: "A", high: "B" }); - var r2 = new Range({ low: "C", high: "D" }); - assert(!r1.intersect(r2)); - done(); - }); - - it("[nativeApi] C,D insersects A,B is false", function (done) { - var r1 = new Range({ low: "C", high: "D" }); - var r2 = new Range({ low: "A", high: "B" }); - assert(!r1.intersect(r2)); - done(); - }); - }); - - describe("toString", function () { - var toStringTest = function (options, expectedString, done) { - var r = new Range(options); - assert.strictEqual(r.toString(), expectedString); - done(); - }; - - it("[nativeApi] undefined values", function (done) { - toStringTest(undefined, "undefined,undefined", done); - }); - it("[nativeApi] null values", function (done) { - toStringTest({ low: null }, "null,null", done); - }); - it("[nativeApi] NaN values", function (done) { - toStringTest({ low: NaN }, "NaN,NaN", done); - }); - it("[nativeApi] number values", function (done) { - toStringTest({ low: 1 }, "1,1", done); - }); - it("[nativeApi] string values", function (done) { - toStringTest({ low: "a" }, "a,a", done); - }); - it("[nativeApi] boolean values", function (done) { - toStringTest({ low: false, high: true }, "false,true", done); - }); - it("[nativeApi] object values", function (done) { - toStringTest({ low: {} }, "[object Object],[object Object]", done); - }); - }); - - describe("_compare", function () { - var r = new Range(); - - var rangeWithComparisonAsNumbers = new Range({ - compareFunction: function (a, b) { - return a - b; - } - }); - - var rangeWithConstantComparison = new Range({ - compareFunction: function (a, b) { - return 0; - } - }); - - - it("[nativeApi] _compare(undefined, undefined) === 0", function (done) { - assert(r._compare() === 0); - assert(r._compare(undefined) === 0); - assert(r._compare(undefined, undefined) === 0); - done(); - }); - - it("[nativeApi] _compare(undefined, y) > 0", function (done) { - assert(r._compare(undefined, null) > 0); - assert(r._compare(undefined, -NaN) > 0); - assert(r._compare(undefined, 0) > 0); - assert(r._compare(undefined, NaN) > 0); - assert(r._compare(undefined, true) > 0); - assert(r._compare(undefined, false) > 0); - assert(r._compare(undefined, "a") > 0); - assert(r._compare(undefined, "undefined") > 0); - assert(r._compare(undefined, "z") > 0); - assert(r._compare(undefined, []) > 0); - assert(r._compare(undefined, {}) > 0); - done(); - }); - - it("[nativeApi] _compare(x, undefined) < -1", function (done) { - assert(r._compare(null) < 0); - assert(r._compare(-NaN) < 0); - assert(r._compare(0) < 0); - assert(r._compare(NaN) < 0); - assert(r._compare(true) < 0); - assert(r._compare(false) < 0); - assert(r._compare("a") < 0); - assert(r._compare("undefined") < 0); - assert(r._compare("z") < 0); - assert(r._compare([]) < 0); - assert(r._compare({}) < 0); - done(); - }); - - it("[nativeApi] _compare values as strings (default)", function (done) { - assert(r._compare("A", "B") < 0); - assert(r._compare("", "") === 0); - assert(r._compare("B", "A") > 0); - assert(r._compare("10", "2") < 0); - assert(r._compare(10, "02") > 0); - assert(r._compare(10, 2) < 0); - assert(r._compare(null, "nulm") < 0); - assert(r._compare(null, "null") === 0); - assert(r._compare(null, "nulk") > 0); - assert(r._compare(true, "truf") < 0); - assert(r._compare(true, "true") === 0); - assert(r._compare(true, "trud") > 0); - assert(r._compare({}, "[object Object]") === 0); - done(); - }); - - it("[nativeApi] _compare values as numbers", function (done) { - assert(rangeWithComparisonAsNumbers._compare(undefined, 2) > 0); - assert(rangeWithComparisonAsNumbers._compare(1, 2) < 0); - assert(rangeWithComparisonAsNumbers._compare(0, 0) === 0); - assert(rangeWithComparisonAsNumbers._compare(10, 2) > 0); - done(); - }); - - it("[nativeApi] _compare always return 0", function (done) { - assert(rangeWithConstantComparison._compare(1, 2) === 0); - assert(rangeWithConstantComparison._compare(2, 1) === 0); - assert(rangeWithConstantComparison._compare(undefined, 2) > 0); - assert(rangeWithConstantComparison._compare(1, undefined) < 0); - - done(); - }); - }); - - describe("_isRange", function () { - it("[nativeApi] _isRange(undefined) is false", function (done) { - assert(!Range._isRange()); - done(); - }); - - it("[nativeApi] _isRange(null) is false", function (done) { - assert(!Range._isRange(null)); - done(); - }); - - it("[nativeApi] _isRange(non-object) is false", function (done) { - var points = [ - undefined, - null, - 1, - "", - true, - NaN, - function () { - }, - {}, - { - low: "" - } - ]; - - for (var i = 0; i < points.length; i++) { - assert(!Range._isRange(points[i])); - } - - done(); - }); - - it("[nativeApi] _isRange(point) is false", function (done) { - var ranges = [ - { - low: "", - high: 1 - }, - new Range() - ]; - - for (var i = 0; i < ranges.length; i++) { - assert(Range._isRange(ranges[i])); - } - - done(); - }); - }); - }); - - describe("RangePartitionResolver Tests", function () { - describe("constructor", function () { - it("[nativeApi] missing partitionKeyExtractor throws", function (done) { - var expetcedError = /Error: partitionKeyExtractor cannot be null or undefined/; - - assert.throws( - function () { - var r = new RangePartitionResolver(); - }, - expetcedError - ); - - assert.throws( - function () { - var r = new RangePartitionResolver(undefined); - }, - expetcedError - ); - - assert.throws( - function () { - var r = new RangePartitionResolver(null); - }, - expetcedError - ); - - done(); - }); - - it("[nativeApi] invalid partitionKeyExtractor throws", function (done) { - var expetcedError = /partitionKeyExtractor must be either a 'string' or a 'function'/; - - assert.throws( - function () { - var r = new RangePartitionResolver(0); - }, - expetcedError - ); - - assert.throws( - function () { - var r = RangePartitionResolver(true); - }, - expetcedError - ); - - assert.throws( - function () { - var r = new RangePartitionResolver(NaN); - }, - expetcedError - ); - - assert.throws( - function () { - var r = new RangePartitionResolver([]); - }, - expetcedError - ); - - assert.throws( - function () { - var r = new RangePartitionResolver({}); - }, - expetcedError - ); - - - done(); - }); - - it("[nativeApi] missing partitionKeyMap throws", function (done) { - var expectedError = /Error: partitionKeyMap cannot be null or undefined/; - - assert.throws( - function () { - var r = new RangePartitionResolver(""); - }, - expectedError - ); - - assert.throws( - function () { - var r = new RangePartitionResolver(function () { - }); - }, - expectedError - ); - - assert.throws( - function () { - var r = new RangePartitionResolver("", null); - }, - expectedError - ); - - done(); - }); - - it("[nativeApi] invalid partitionKeyMap throws", function (done) { - var expectedError = /Error: partitionKeyMap has to be an Array/; - - assert.throws( - function () { - var r = new RangePartitionResolver("", 0); - }, - expectedError - ); - - assert.throws( - function () { - var r = new RangePartitionResolver("", ""); - }, - expectedError - ); - - assert.throws( - function () { - var r = new RangePartitionResolver("", true); - }, - expectedError - ); - - assert.throws( - function () { - var r = new RangePartitionResolver("", NaN); - }, - expectedError - ); - - assert.throws( - function () { - var r = new RangePartitionResolver("", {}); - }, - expectedError - ); - - var r = new RangePartitionResolver("", new Array()); - done(); - }); - - it("[nativeApi] valid RangePartitionResolver", function (done) { - var resolver = new RangePartitionResolver("", []); - assert(resolver); - assert.strictEqual(resolver.partitionKeyExtractor, ""); - assert.deepEqual(resolver.partitionKeyMap, []); - done(); - }); - }); - - describe("_getFirstContainingMapEntryOrNull", function () { - it("[nativeApi] _getFirstContainingMapEntryOrNull - empty map returns null", function (done) { - var ranges = [undefined, null, 0, "", true, [], {}, NaN, new Range()]; - var resolver = new RangePartitionResolver("", []); - ranges.forEach(function (r) { - var result = resolver._getFirstContainingMapEntryOrNull(r); - assert.equal(result, null); - }); - done(); - }); - - it("[nativeApi] _tryGetContainingRange - map with no containing entry returns null", function (done) { - var mapEntry = { range: new Range({ low: "A" }), link: "link1" }; - var resolver = new RangePartitionResolver("key", [mapEntry]); - var result = resolver._getFirstContainingMapEntryOrNull(new Range({ low: "B" })); - assert.equal(result, null); - done(); - }); - - it("[nativeApi] _tryGetContainingRange - map with single containing entry returns entry", function (done) { - var mapEntry = { range: new Range(), link: "link1" }; - var resolver = new RangePartitionResolver("key", [mapEntry]); - var result = resolver._getFirstContainingMapEntryOrNull(new Range()); - assert.deepEqual(result, { range: new Range(), link: "link1" }); - done(); - }); - - it("[nativeApi] _tryGetContainingRange - map with more multiple containing entries returns first entry", function (done) { - var map1 = [ - { range: new Range({ low: "A", high: "B" }), link: "link1" }, - { range: new Range({ low: "A" }), link: "link2" } - ]; - - var resolver1 = new RangePartitionResolver("key", map1); - var result1 = resolver1._getFirstContainingMapEntryOrNull(new Range({ low: "A" })); - assert.strictEqual(result1.link, "link1"); - - var map2 = [ - { range: new Range({ low: "A" }), link: "link2" }, - { range: new Range({ low: "A", high: "Z" }), link: "link1" } - ]; - - var resolver2 = new RangePartitionResolver("key", map2); - var result2 = resolver2._getFirstContainingMapEntryOrNull(new Range({ low: "A" })); - assert.strictEqual(result2.link, "link2"); - done(); - }); - }); - - describe("resolveForCreate", function () { - it("[nativeApi] _tryGetContainingRange - map containing parition key returns corresponding link", function (done) { - var resolver = new RangePartitionResolver("key", [ - { range: new Range({ low: "A", high: "M" }), link: "link1" }, - { range: new Range({ low: "N", high: "Z" }), link: "link2" } - ]); - var result = resolver.resolveForCreate("X"); - assert.strictEqual(result, "link2"); - done(); - }); - - it("[nativeApi] _tryGetContainingRange - map not containing parition key throws", function (done) { - var resolver = new RangePartitionResolver("key", [ - { range: new Range({ low: "A", high: "M" }), link: "link1" } - ]); - - assert.throws( - function () { - var result = resolver.resolveForCreate("X"); - }, - /Error: Invalid operation: A containing range for 'X,X' doesn't exist in the partition map./ - ); - done(); - }); - }); - }); - - describe("HashPartitionResolver Tests", function () { + describe("HashPartitionResolver", function () { var test = function (useUpsert, done) { var client = new DocumentDBClient(host, { masterKey: masterKey }); @@ -3273,7 +2331,7 @@ describe("NodeJS CRUD Tests", function() { }); }; - it("[promiseApi] Should do document CRUD operations with a partition resolver successfully", function (done) { test(false, done) }); - it("[promiseApi] Should do document CRUD operations with a partition resolver successfully with upsert", function (done) { test(true, done) }); + it("CRUD operations", function (done) { test(false, done) }); + it("CRUD operations with upsert", function (done) { test(true, done) }); }); -}); +}); \ No newline at end of file