From 77acd4ffe5b61b60e1ae342c9255feda999b8e41 Mon Sep 17 00:00:00 2001 From: asorrin-msft Date: Wed, 20 Aug 2014 18:15:11 -0700 Subject: [PATCH] Storage Client Library - 0.3.3 --- ChangeLog.txt | 10 ++ lib/common/services/storageserviceclient.js | 1 + lib/common/util/constants.js | 9 +- lib/services/blob/blobservice.js | 167 +++++++++--------- lib/services/file/fileservice.js | 9 +- package.json | 2 +- test/services/blob/blobservice-tests.js | 88 +++++++++ .../file/fileservice-directory-tests.js | 32 +++- test/services/file/fileservice-file-tests.js | 39 ++++ 9 files changed, 264 insertions(+), 93 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 607dcb40..ab5974d2 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,6 +1,16 @@ Note: This is an Azure Storage only package. The all up Azure node sdk still has the old storage bits in there. In a future release, those storage bits will be removed and an npm dependency to this storage node sdk will be taken. This is a CTP v1 release and the changes described below indicate the changes from the Azure node SDK 0.9.8 available here - https://github.com/Azure/azure-sdk-for-node. +2014.08.20 Version 0.3.3 + +BLOB +* Fixed an issue where SAS tokens were being incorrectly generated for the root container and when the blob name required encoding. +* Documented the 'parallelOperationThreadCount' option as input to various uploadBlob APIs. + +FILE +* Fixed an issue where signing was incorrect when the URI contained '.' or '..'. +* Fixed an issue where "getURI" was requiring a file parameter, although the parameter should be optional. + 2014.07.25 Version 0.3.2 ALL diff --git a/lib/common/services/storageserviceclient.js b/lib/common/services/storageserviceclient.js index 1e70b0f6..aa65465d 100644 --- a/lib/common/services/storageserviceclient.js +++ b/lib/common/services/storageserviceclient.js @@ -743,6 +743,7 @@ StorageServiceClient.prototype._setRequestUrl = function (webResource, options) } webResource.uri = url.resolve(host, url.format({pathname: webResource.path, query: webResource.queryString})); + webResource.path = url.parse(webResource.uri).pathname; }; /** diff --git a/lib/common/util/constants.js b/lib/common/util/constants.js index b8c145e9..93064ba3 100644 --- a/lib/common/util/constants.js +++ b/lib/common/util/constants.js @@ -31,7 +31,7 @@ var Constants = { /* * Specifies the value to use for UserAgent header. */ - USER_AGENT_PRODUCT_VERSION: '0.3.2', + USER_AGENT_PRODUCT_VERSION: '0.3.3', /** * The number of default concurrent requests for parallel operation. @@ -653,6 +653,13 @@ var Constants = { */ ODATA_TYPE_MARKER: '$', + /** + * The value to set the maximum data service version header. + * @const + * @type {string} + */ + DEFAULT_DATA_SERVICE_VERSION: '3.0;NetFx', + /** * The name of the property that stores the table name. * diff --git a/lib/services/blob/blobservice.js b/lib/services/blob/blobservice.js index c3e73886..a45a8294 100644 --- a/lib/services/blob/blobservice.js +++ b/lib/services/blob/blobservice.js @@ -110,26 +110,22 @@ util.inherits(BlobService, StorageServiceClient); * @param {string} blobName Blob name * @return {string} The encoded resource name. */ -function createResourceName(containerName, blobName) { +function createResourceName(containerName, blobName, forSAS) { // Resource name - if (blobName) { + if (blobName && !forSAS) { blobName = encodeURIComponent(blobName); blobName = blobName.replace(/%2F/g, '/'); blobName = blobName.replace(/%5C/g, '/'); blobName = blobName.replace(/\+/g, '%20'); } - var resourceName = containerName + '/' + blobName; - if (!containerName || containerName === '$root') { - resourceName = blobName; + // return URI encoded resource name + if (blobName) { + return containerName + '/' + blobName; } - - if (!blobName) { - resourceName = containerName; + else { + return containerName; } - - // return URI encoded resource name - return resourceName; } // Blob service methods @@ -2175,7 +2171,7 @@ BlobService.prototype.generateSharedAccessSignatureWithVersion = function (conta } } - var resourceName = createResourceName(container, blob); + var resourceName = createResourceName(container, blob, true); return this.storageCredentials.generateSignedQueryString(resourceName, sharedAccessPolicy, sasVersion, {headers: headers, resourceType: resourceType}); }; @@ -2290,6 +2286,7 @@ BlobService.prototype.createPageBlob = function (container, blob, length, option * @param (string) localFileName The local path to the file to be uploaded. * @param {object} [options] The request options. * @param {SpeedSummary} [options.speedSummary] The upload tracker objects. +* @param {int} [options.parallelOperationThreadCount] Parallel operation thread count * @param {string} [options.leaseId] The lease identifier. * @param {object} [options.metadata] The metadata key/value pairs. * @param {bool} [options.storeBlobContentMD5] Specifies whether the blob's ContentMD5 header should be set on uploads. @@ -2356,6 +2353,7 @@ BlobService.prototype.createPageBlobFromLocalFile = function (container, blob, l * @param {int} streamLength The length of the stream to upload. * @param {object} [options] The request options. * @param {SpeedSummary} [options.speedSummary] The download tracker objects; +* @param {int} [options.parallelOperationThreadCount] Parallel operation thread count * @param {string} [options.leaseId] The lease identifier. * @param {object} [options.metadata] The metadata key/value pairs. * @param {bool} [options.storeBlobContentMD5] Specifies whether the blob's ContentMD5 header should be set on uploads. @@ -2415,6 +2413,7 @@ BlobService.prototype.createPageBlobFromStream = function(container, blob, strea * @param {object} [options] The request options. * @param {string} [options.leaseId] The lease identifier. * @param {object} [options.metadata] The metadata key/value pairs. +* @param {int} [options.parallelOperationThreadCount] Parallel operation thread count * @param {bool} [options.storeBlobContentMD5] Specifies whether the blob's ContentMD5 header should be set on uploads. * The default value is false for page blobs and true for block blobs. * @param {bool} [options.useTransactionalMD5] Calculate and send/validate content MD5 for transactions. @@ -2477,6 +2476,7 @@ BlobService.prototype.createWriteStreamToExistingPageBlob = function (container, * @param {object} [options] The request options. * @param {string} [options.leaseId] The lease identifier. * @param {object} [options.metadata] The metadata key/value pairs. +* @param {int} [options.parallelOperationThreadCount] Parallel operation thread count * @param {bool} [options.storeBlobContentMD5] Specifies whether the blob's ContentMD5 header should be set on uploads. * The default value is false for page blobs and true for block blobs. * @param {bool} [options.useTransactionalMD5] Calculate and send/validate content MD5 for transactions. @@ -2856,28 +2856,29 @@ BlobService.prototype.setPageBlobSequenceNumber = function (container, blob, seq * Calling Put Blob to create a page blob only initializes the blob. To add content to a page blob, call the Put Page operation. * * @this {BlobService} -* @param {string} container The container name. -* @param {string} blob The blob name. -* @param {string} localFileName The local path to the file to be uploaded. -* @param {object} [options] The request options. -* @param {string} [options.blockIdPrefix] The prefix to be used to generate the block id. -* @param {string} [options.leaseId] The lease identifier. -* @param {object} [options.metadata] The metadata key/value pairs. -* @param {bool} [options.storeBlobContentMD5] Specifies whether the blob's ContentMD5 header should be set on uploads. The default value is true for block blobs. -* @param {string} [options.contentType] The MIME content type of the blob. The default type is application/octet-stream. -* @param {string} [options.contentEncoding] The content encodings that have been applied to the blob. -* @param {string} [options.contentLanguage] The natural languages used by this resource. -* @param {string} [options.contentMD5] The MD5 hash of the blob content. -* @param {string} [options.cacheControl] The Blob service stores this value but does not use or modify it. -* @param {string} [options.contentDisposition] The blob's content disposition. -* @param {object} [options.accessConditions] The access conditions. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. -* @param {LocationMode} [options.locationMode] Specifies the location mode used to decide which location the request should be sent to. -* Please see StorageUtilities.LocationMode for the possible values. -* @param {int} [options.timeoutIntervalInMs] The server timeout interval, in milliseconds, to use for the request. -* @param {int} [options.maximumExecutionTimeInMs] The maximum execution time, in milliseconds, across all potential retries, to use when making this request. -* The maximum execution time interval begins at the time that the client begins building the request. The maximum -* execution time is checked intermittently while performing requests, and before executing retries. -* @param {errorOrResult} callback The callback function. +* @param {string} container The container name. +* @param {string} blob The blob name. +* @param {string} localFileName The local path to the file to be uploaded. +* @param {object} [options] The request options. +* @param {string} [options.blockIdPrefix] The prefix to be used to generate the block id. +* @param {string} [options.leaseId] The lease identifier. +* @param {object} [options.metadata] The metadata key/value pairs. +* @param {int} [options.parallelOperationThreadCount] Parallel operation thread count +* @param {bool} [options.storeBlobContentMD5] Specifies whether the blob's ContentMD5 header should be set on uploads. The default value is true for block blobs. +* @param {string} [options.contentType] The MIME content type of the blob. The default type is application/octet-stream. +* @param {string} [options.contentEncoding] The content encodings that have been applied to the blob. +* @param {string} [options.contentLanguage] The natural languages used by this resource. +* @param {string} [options.contentMD5] The MD5 hash of the blob content. +* @param {string} [options.cacheControl] The Blob service stores this value but does not use or modify it. +* @param {string} [options.contentDisposition] The blob's content disposition. +* @param {object} [options.accessConditions] The access conditions. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. +* @param {LocationMode} [options.locationMode] Specifies the location mode used to decide which location the request should be sent to. +* Please see StorageUtilities.LocationMode for the possible values. +* @param {int} [options.timeoutIntervalInMs] The server timeout interval, in milliseconds, to use for the request. +* @param {int} [options.maximumExecutionTimeInMs] The maximum execution time, in milliseconds, across all potential retries, to use when making this request. +* The maximum execution time interval begins at the time that the client begins building the request. The maximum +* execution time is checked intermittently while performing requests, and before executing retries. +* @param {errorOrResult} callback The callback function. * @return {SpeedSummary} */ BlobService.prototype.createBlockBlobFromLocalFile = function (container, blob, localFileName, optionsOrCallback, callback) { @@ -2934,31 +2935,32 @@ BlobService.prototype.createBlockBlobFromLocalFile = function (container, blob, * Uploads a block blob from a stream. * * @this {BlobService} -* @param {string} container The container name. -* @param {string} blob The blob name. -* @param (Stream) stream Stream to the data to store. -* @param {int} streamLength The length of the stream to upload. -* @param {object} [options] The request options. -* @param {SpeedSummary} [options.speedSummary] The download tracker objects. -* @param {string} [options.blockIdPrefix] The prefix to be used to generate the block id. -* @param {string} [options.leaseId] The lease identifier. -* @param {object} [options.metadata] The metadata key/value pairs. -* @param {bool} [options.storeBlobContentMD5] Specifies whether the blob's ContentMD5 header should be set on uploads. The default value is true for block blobs. -* @param {bool} [options.useTransactionalMD5] Calculate and send/validate content MD5 for transactions. -* @param {string} [options.contentType] The MIME content type of the blob. The default type is application/octet-stream. -* @param {string} [options.contentEncoding] The content encodings that have been applied to the blob. -* @param {string} [options.contentLanguage] The natural languages used by this resource. -* @param {string} [options.contentMD5] The MD5 hash of the blob content. -* @param {string} [options.cacheControl] The Blob service stores this value but does not use or modify it. -* @param {string} [options.contentDisposition] The blob's content disposition. -* @param {object} [options.accessConditions] The access conditions. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. -* @param {LocationMode} [options.locationMode] Specifies the location mode used to decide which location the request should be sent to. -* Please see StorageUtilities.LocationMode for the possible values. -* @param {int} [options.timeoutIntervalInMs] The server timeout interval, in milliseconds, to use for the request. -* @param {int} [options.maximumExecutionTimeInMs] The maximum execution time, in milliseconds, across all potential retries, to use when making this request. -* The maximum execution time interval begins at the time that the client begins building the request. The maximum -* execution time is checked intermittently while performing requests, and before executing retries. -* @param {errorOrResult} callback The callback function. +* @param {string} container The container name. +* @param {string} blob The blob name. +* @param (Stream) stream Stream to the data to store. +* @param {int} streamLength The length of the stream to upload. +* @param {object} [options] The request options. +* @param {SpeedSummary} [options.speedSummary] The download tracker objects. +* @param {string} [options.blockIdPrefix] The prefix to be used to generate the block id. +* @param {string} [options.leaseId] The lease identifier. +* @param {object} [options.metadata] The metadata key/value pairs. +* @param {int} [options.parallelOperationThreadCount] Parallel operation thread count +* @param {bool} [options.storeBlobContentMD5] Specifies whether the blob's ContentMD5 header should be set on uploads. The default value is true for block blobs. +* @param {bool} [options.useTransactionalMD5] Calculate and send/validate content MD5 for transactions. +* @param {string} [options.contentType] The MIME content type of the blob. The default type is application/octet-stream. +* @param {string} [options.contentEncoding] The content encodings that have been applied to the blob. +* @param {string} [options.contentLanguage] The natural languages used by this resource. +* @param {string} [options.contentMD5] The MD5 hash of the blob content. +* @param {string} [options.cacheControl] The Blob service stores this value but does not use or modify it. +* @param {string} [options.contentDisposition] The blob's content disposition. +* @param {object} [options.accessConditions] The access conditions. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. +* @param {LocationMode} [options.locationMode] Specifies the location mode used to decide which location the request should be sent to. +* Please see StorageUtilities.LocationMode for the possible values. +* @param {int} [options.timeoutIntervalInMs] The server timeout interval, in milliseconds, to use for the request. +* @param {int} [options.maximumExecutionTimeInMs] The maximum execution time, in milliseconds, across all potential retries, to use when making this request. +* The maximum execution time interval begins at the time that the client begins building the request. The maximum +* execution time is checked intermittently while performing requests, and before executing retries. +* @param {errorOrResult} callback The callback function. * @return {SpeedSummary} */ BlobService.prototype.createBlockBlobFromStream = function (container, blob, stream, streamLength, optionsOrCallback, callback) { @@ -3055,29 +3057,30 @@ BlobService.prototype.createBlockBlobFromText = function (container, blob, text, * Provides a stream to write to a block blob. * * @this {BlobService} -* @param {string} container The container name. -* @param {string} blob The blob name. -* @param {object} [options] The request options. -* @param {string} [options.blockIdPrefix] The prefix to be used to generate the block id. -* @param {string} [options.leaseId] The lease identifier. -* @param {object} [options.metadata] The metadata key/value pairs. -* @param {bool} [options.storeBlobContentMD5] Specifies whether the blob's ContentMD5 header should be set on uploads. -* The default value is false for page blobs and true for block blobs. -* @param {bool} [options.useTransactionalMD5] Calculate and send/validate content MD5 for transactions. -* @param {string} [options.contentType] The MIME content type of the blob. The default type is application/octet-stream. -* @param {string} [options.contentEncoding] The content encodings that have been applied to the blob. -* @param {string} [options.contentLanguage] The natural languages used by this resource. -* @param {string} [options.contentMD5] The MD5 hash of the blob content. -* @param {string} [options.cacheControl] The Blob service stores this value but does not use or modify it. -* @param {string} [options.position] The blob's content disposition. (x-ms-blob-content-disposition) -* @param {object} [options.accessConditions] The access conditions. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. -* @param {LocationMode} [options.locationMode] Specifies the location mode used to decide which location the request should be sent to. -* Please see StorageUtilities.LocationMode for the possible values. -* @param {int} [options.timeoutIntervalInMs] The server timeout interval, in milliseconds, to use for the request. -* @param {int} [options.maximumExecutionTimeInMs] The maximum execution time, in milliseconds, across all potential retries, to use when making this request. -* The maximum execution time interval begins at the time that the client begins building the request. The maximum -* execution time is checked intermittently while performing requests, and before executing retries. -* @param {errorOrResponse} callback The callback function. +* @param {string} container The container name. +* @param {string} blob The blob name. +* @param {object} [options] The request options. +* @param {string} [options.blockIdPrefix] The prefix to be used to generate the block id. +* @param {string} [options.leaseId] The lease identifier. +* @param {object} [options.metadata] The metadata key/value pairs. +* @param {int} [options.parallelOperationThreadCount] Parallel operation thread count +* @param {bool} [options.storeBlobContentMD5] Specifies whether the blob's ContentMD5 header should be set on uploads. +* The default value is false for page blobs and true for block blobs. +* @param {bool} [options.useTransactionalMD5] Calculate and send/validate content MD5 for transactions. +* @param {string} [options.contentType] The MIME content type of the blob. The default type is application/octet-stream. +* @param {string} [options.contentEncoding] The content encodings that have been applied to the blob. +* @param {string} [options.contentLanguage] The natural languages used by this resource. +* @param {string} [options.contentMD5] The MD5 hash of the blob content. +* @param {string} [options.cacheControl] The Blob service stores this value but does not use or modify it. +* @param {string} [options.position] The blob's content disposition. (x-ms-blob-content-disposition) +* @param {object} [options.accessConditions] The access conditions. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information. +* @param {LocationMode} [options.locationMode] Specifies the location mode used to decide which location the request should be sent to. +* Please see StorageUtilities.LocationMode for the possible values. +* @param {int} [options.timeoutIntervalInMs] The server timeout interval, in milliseconds, to use for the request. +* @param {int} [options.maximumExecutionTimeInMs] The maximum execution time, in milliseconds, across all potential retries, to use when making this request. +* The maximum execution time interval begins at the time that the client begins building the request. The maximum +* execution time is checked intermittently while performing requests, and before executing retries. +* @param {errorOrResponse} callback The callback function. * @return {Stream} * @example * var azure = require('azure-storage'); diff --git a/lib/services/file/fileservice.js b/lib/services/file/fileservice.js index e79543fd..51eaf0da 100644 --- a/lib/services/file/fileservice.js +++ b/lib/services/file/fileservice.js @@ -1069,7 +1069,6 @@ FileService.prototype.getUrl = function (share, directory, file, primary) { validate.validateArgs('getUrl', function (v) { v.string(share, 'share'); v.stringAllowEmpty(directory, 'directory'); - v.string(file, 'file'); v.shareNameIsValid(share); }); @@ -1081,13 +1080,7 @@ FileService.prototype.getUrl = function (share, directory, file, primary) { host = this.host.primaryHost; } - var name; - if (!directory && !file) { - name = createResourceName(share, directory); - } else { - name = createResourceName(share, directory, file); - } - + var name = createResourceName(share, directory, file); return url.resolve(host, url.format({pathname: this._getPath('/' + name)})); }; diff --git a/package.json b/package.json index 5209fc41..ac13e9f2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "azure-storage", "author": "Microsoft Corporation", - "version": "0.3.2", + "version": "0.3.3", "description": "Microsoft Azure Storage Client Library for Node.js", "tags": [ "azure", diff --git a/test/services/blob/blobservice-tests.js b/test/services/blob/blobservice-tests.js index 22793b64..c7aaea4c 100644 --- a/test/services/blob/blobservice-tests.js +++ b/test/services/blob/blobservice-tests.js @@ -1270,6 +1270,94 @@ describe('BlobService', function () { }); }); }); + + describe('SasStrangeChars', function() { + it('SasStrangeCharsBlobName', function (done) { + var containerName = testutil.generateId(containerNamesPrefix, containerNames, false); + var blobName = 'def@#/abef?def/& &/abcde+=-'; + var blobService = azure.createBlobService().withFilter(new azure.ExponentialRetryPolicyFilter()); + var blobText = 'sampletext!'; + + blobService.createContainer(containerName, function (error) { + assert.equal(error, null); + + blobService.createBlockBlobFromText(containerName, blobName, blobText, function (error2) { + assert.equal(error2, null); + + var startDate = new Date(); + var expiryDate = new Date(startDate); + expiryDate.setMinutes(startDate.getMinutes() + 5); + + var sharedAccessPolicy = { + AccessPolicy: { + Permissions: BlobUtilities.SharedAccessPermissions.READ, + Expiry: expiryDate + } + }; + + var token = blobService.generateSharedAccessSignature(containerName, blobName, sharedAccessPolicy); + + var sharedAccessBlobService = azure.createBlobServiceWithSas(blobService.host, token); + + sharedAccessBlobService.getBlobToText(containerName, blobName, function (downloadErr, blobTextResponse) { + assert.equal(downloadErr, null); + assert.equal(blobTextResponse, blobText); + + done(); + }); + }); + }); + }); + + describe('SasRootContainer', function() { + var containerName = '$root'; + var blobName = 'sampleBlobName'; + var blobService = azure.createBlobService().withFilter(new azure.ExponentialRetryPolicyFilter()); + var blobText = 'sampletext!'; + + // This is testing the root container functionality, which we don't want to pollute with random blobs. + // Thus, trying to delete blob both before and after the actual test. + before(function (done) { + blobService.deleteBlobIfExists(containerName, blobName, function() { + done(); + }); + }); + + after(function (done) { + blobService.deleteBlobIfExists(containerName, blobName, function() { + done(); + }); + }); + + it('should work', function(done) { + blobService.createBlockBlobFromText(containerName, blobName, blobText, function (error2) { + assert.equal(error2, null); + + var startDate = new Date(); + var expiryDate = new Date(startDate); + expiryDate.setMinutes(startDate.getMinutes() + 5); + + var sharedAccessPolicy = { + AccessPolicy: { + Permissions: BlobUtilities.SharedAccessPermissions.READ, + Expiry: expiryDate + } + }; + + var token = blobService.generateSharedAccessSignature(containerName, blobName, sharedAccessPolicy); + + var sharedAccessBlobService = azure.createBlobServiceWithSas(blobService.host, token); + + sharedAccessBlobService.getBlobToText(containerName, blobName, function (downloadErr, blobTextResponse) { + assert.equal(downloadErr, null); + assert.equal(blobTextResponse, blobText); + + done(); + }); + }); + }); + }); + }); }); it('responseEmits', function (done) { diff --git a/test/services/file/fileservice-directory-tests.js b/test/services/file/fileservice-directory-tests.js index bfd41867..b7a649d3 100644 --- a/test/services/file/fileservice-directory-tests.js +++ b/test/services/file/fileservice-directory-tests.js @@ -263,7 +263,7 @@ describe('FileDirectory', function () { directories.push.apply(directories, result.entries.directories); var token = result.continuationToken; if(token) { - listFilesAndDirectoriesWithoutPrefix(shareName, directoryName, token, callback); + listFilesAndDirectories(shareName, directoryName, token, callback); } else { callback(); @@ -343,6 +343,36 @@ describe('FileDirectory', function () { }); }); }); + + it('multipleLevelsDirectory', function (done) { + fileService.createFile(shareName, directoryName, fileName1, 0, function (fileErr1) { + assert.equal(fileErr1, null); + + var nextDirectory = directoryName + "/next"; + var dotdotDirectory = nextDirectory + "/.."; + + listFilesAndDirectories(shareName, dotdotDirectory, null, function() { + assert.equal(directories.length, 0); + assert.equal(files.length, 1); + assert.equal(files[0].name, fileName1); + + files = []; + directories = []; + + fileService.createDirectory(shareName, nextDirectory, function(dirErr2) { + assert.equal(dirErr2, null); + + listFilesAndDirectories(shareName, dotdotDirectory, null, function() { + assert.equal(directories.length, 1); + assert.equal(directories[0].name, "next"); + assert.equal(files.length, 1); + assert.equal(files[0].name, fileName1); + done(); + }); + }); + }); + }); + }); }); }); diff --git a/test/services/file/fileservice-file-tests.js b/test/services/file/fileservice-file-tests.js index 75a7f5f4..e401cef6 100644 --- a/test/services/file/fileservice-file-tests.js +++ b/test/services/file/fileservice-file-tests.js @@ -73,6 +73,45 @@ describe('File', function () { done(); }); + describe('getUrl', function() { + var share = 'share'; + var directory = 'directory'; + var file = 'file' + it('Directory and file', function(done) { + var url = fileService.getUrl(share, directory, file, true); + var host = fileService.host.primaryHost; + assert.strictEqual(url, host + share + '/' + directory + '/' + file); + + url = fileService.getUrl(share, directory, file, false); + host = fileService.host.secondaryHost; + assert.strictEqual(url, host + share + '/' + directory + '/' + file); + + done(); + }); + + it('No file', function(done) { + var url = fileService.getUrl(share, directory, null, true); + var host = fileService.host.primaryHost; + assert.strictEqual(url, host + share + '/' + directory); + url = fileService.getUrl(share, directory, '', true); + assert.strictEqual(url, host + share + '/' + directory); + + done(); + }); + + it('No directory', function(done) { + var url = fileService.getUrl(share, '', null, true); + var host = fileService.host.primaryHost; + assert.strictEqual(url, host + share); + + var url = fileService.getUrl(share, '', file, true); + var host = fileService.host.primaryHost; + assert.strictEqual(url, host + share + '/' + file); + + done(); + }); + }); + describe('doesFileExist', function () { it('should work', function (done) { fileService.doesFileExist(shareName, directoryName, fileName, function (existsError, exists) {