From dfd2c413702a0e73542bb916a2826893f608b7f1 Mon Sep 17 00:00:00 2001 From: Stephen Sawchuk Date: Fri, 13 Nov 2015 19:42:08 -0500 Subject: [PATCH] Implement Service & Service Object for Resource --- lib/resource/index.js | 91 +++++------- lib/resource/project.js | 259 +++++++++++++++++---------------- test/resource/index.js | 110 ++++++-------- test/resource/project.js | 300 +++++++-------------------------------- 4 files changed, 267 insertions(+), 493 deletions(-) diff --git a/lib/resource/index.js b/lib/resource/index.js index ec0f06cc2eb..75a14fc6217 100644 --- a/lib/resource/index.js +++ b/lib/resource/index.js @@ -22,6 +22,7 @@ var extend = require('extend'); var is = require('is'); +var nodeutil = require('util'); /** * @type {module:resource/project} @@ -30,31 +31,22 @@ var is = require('is'); var Project = require('./project.js'); /** - * @type {module:common/streamrouter} + * @type {module:common/service} * @private */ -var streamRouter = require('../common/stream-router.js'); +var Service = require('../common/service.js'); /** - * @type {module:common/util} - * @private - */ -var util = require('../common/util.js'); - -/** - * @const {string} + * @type {module:common/streamrouter} * @private */ -var BASE_URL = 'https://cloudresourcemanager.googleapis.com/v1beta1/projects'; +var streamRouter = require('../common/stream-router.js'); /** - * Required scopes for Google Cloud Resource Manager API. - * @const {array} + * @type {module:common/util} * @private */ -var SCOPES = [ - 'https://www.googleapis.com/auth/cloud-platform' -]; +var util = require('../common/util.js'); /** * [The Cloud Resource Manager](https://cloud.google.com/resource-manager/) @@ -88,24 +80,28 @@ function Resource(options) { return new Resource(options); } - this.defaultProjectId_ = options.projectId; + var config = { + baseUrl: 'https://cloudresourcemanager.googleapis.com/v1beta1', + scopes: ['https://www.googleapis.com/auth/cloud-platform'], + projectIdRequired: false + }; - this.makeAuthenticatedRequest_ = util.makeAuthenticatedRequestFactory({ - credentials: options.credentials, - keyFile: options.keyFilename, - scopes: SCOPES, - email: options.email - }); + Service.call(this, config, options); + + this.defaultProjectId_ = options.projectId; } +nodeutil.inherits(Resource, Service); + /** * Create a project. * + * **This method only works if you are authenticated as yourself, e.g. using the + * gcloud SDK.** + * * @resource [Projects Overview]{@link https://cloud.google.com/compute/docs/networking#networks} * @resource [projects: create API Documentation]{@link https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/create} * - * @private - * * @param {string} name - Name of the project. * @param {object=} options - See a * [Project resource](https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects#Project). @@ -130,11 +126,13 @@ Resource.prototype.createProject = function(id, options, callback) { options = {}; } - var body = extend({}, options, { - projectId: id - }); - - this.makeReq_('POST', '/', null, body, function(err, resp) { + this.request({ + method: 'POST', + uri: '/projects', + json: extend({}, options, { + projectId: id + }) + }, function(err, resp) { if (err) { callback(err, null, resp); return; @@ -219,7 +217,10 @@ Resource.prototype.getProjects = function(options, callback) { options = options || {}; - this.makeReq_('GET', '/', options, null, function(err, resp) { + this.request({ + uri: '/projects', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -244,8 +245,8 @@ Resource.prototype.getProjects = function(options, callback) { }; /** - * Create a Project object to reference an existing project. See - * {module:resoucemanager/createProject} to create a project. + * Create a Project object. See {module:resoucemanager/createProject} to create + * a project. * * @throws {Error} If an ID is not provided. * @@ -265,32 +266,6 @@ Resource.prototype.project = function(id) { return new Project(this, id); }; -/** - * Make a new request object from the provided arguments and wrap the callback - * to intercept non-successful responses. - * - * @private - * - * @param {string} method - Action. - * @param {string} path - Request path. - * @param {*} query - Request query object. - * @param {*} body - Request body contents. - * @param {function} callback - The callback function. - */ -Resource.prototype.makeReq_ = function(method, path, query, body, callback) { - var reqOpts = { - method: method, - qs: query, - uri: BASE_URL + path - }; - - if (body) { - reqOpts.json = body; - } - - this.makeAuthenticatedRequest_(reqOpts, callback); -}; - /*! Developer Documentation * * These methods can be used with either a callback or as a readable object diff --git a/lib/resource/project.js b/lib/resource/project.js index d52dfdc6547..4bfeb5e3d66 100644 --- a/lib/resource/project.js +++ b/lib/resource/project.js @@ -20,6 +20,14 @@ 'use strict'; +var nodeutil = require('util'); + +/** + * @type {module:common/serviceObject} + * @private + */ +var ServiceObject = require('../common/service-object.js'); + /** * @type {module:common/util} * @private @@ -59,73 +67,140 @@ var util = require('../common/util.js'); * var project = resource.project(); */ function Project(resource, id) { - this.resource = resource; - this.id = id; - this.metadata = {}; -} - -/** - * Delete the project. - * - * @resource [projects: delete API Documentation]{@link https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/delete} - * - * @private - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * project.delete(function(err, apiResponse) { - * if (!err) { - * // The project was deleted! - * } - * }); - */ -Project.prototype.delete = function(callback) { - callback = callback || util.noop; - - this.makeReq_('DELETE', '', null, null, function(err, resp) { - callback(err, resp); + var methods = { + /** + * Create a project. + * + * @param {object=} config - See {module:resource#createProject}. + * + * @example + * project.create(function(err, zone, apiResponse) { + * if (!err) { + * // The zone was created successfully. + * } + * }); + */ + create: true, + + /** + * Delete the project. + * + * **This method only works if you are authenticated as yourself, e.g. using + * the gcloud SDK.** + * + * @resource [projects: delete API Documentation]{@link https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/delete} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * project.delete(function(err, apiResponse) { + * if (!err) { + * // The project was deleted! + * } + * }); + */ + delete: true, + + /** + * Check if the project exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the project exists or not. + * + * @example + * project.exists(function(err, exists) {}); + */ + exists: true, + + /** + * Get a project if it exists. + * + * You may optionally use this to "get or create" an object by providing an + * object with `autoCreate` set to `true`. Any extra configuration that is + * normally required for the `create` method must be contained within this + * object as well. + * + * @param {options=} options - Configuration object. + * @param {boolean} options.autoCreate - Automatically create the object if + * it does not exist. Default: `false` + * + * @example + * project.get(function(err, project, apiResponse) { + * // `project.metadata` has been populated. + * }); + */ + get: true, + + /** + * Get the metadata for the project. + * + * @resource [projects: get API Documentation]{@link https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/get} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {?object} callback.metadata - Metadata of the project from the + * API. + * @param {object} callback.apiResponse - Raw API response. + * + * @example + * project.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: true, + + /** + * Set the project's metadata. + * + * **This method only works if you are authenticated as yourself, e.g. using + * the gcloud SDK.** + * + * @resource [projects: update API Documentation]{@link https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/update} + * @resource [Project Resource]{@link https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects#Project} + * + * @param {object} metadata - See a + * [Project resource](https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects#Project). + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * var metadata = { + * name: 'New name' + * }; + * + * project.setMetadata(metadata, function(err, apiResponse) { + * if (!err) { + * // The project has been successfully updated. + * } + * }); + */ + setMetadata: true + }; + + ServiceObject.call(this, { + parent: resource, + baseUrl: '/projects', + id: id, + createMethod: resource.createProject.bind(resource), + methods: methods }); -}; - -/** - * Get the metadata for the project. - * - * @resource [projects: get API Documentation]{@link https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/get} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request. - * @param {?object} callback.metadata - Metadata of the project from the API. - * @param {object} callback.apiResponse - Raw API response. - * - * @example - * project.getMetadata(function(err, metadata, apiResponse) {}); - */ -Project.prototype.getMetadata = function(callback) { - var self = this; - - callback = callback || util.noop; - - this.makeReq_('GET', '', null, null, function(err, resp) { - if (err) { - callback(err, null, resp); - return; - } +} - self.metadata = resp; - - callback(null, self.metadata, resp); - }); -}; +nodeutil.inherits(Project, ServiceObject); /** * Restore a project. * - * @resource [projects: undelete API Documentation]{@link https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/undelete} + * **This method only works if you are authenticated as yourself, e.g. using the + * gcloud SDK.** * - * @private + * @resource [projects: undelete API Documentation]{@link https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/undelete} * * @param {function=} callback - The callback function. * @param {?error} callback.err - An error returned while making this request. @@ -141,68 +216,12 @@ Project.prototype.getMetadata = function(callback) { Project.prototype.restore = function(callback) { callback = callback || util.noop; - this.makeReq_('POST', ':undelete', null, null, function(err, resp) { + this.request({ + method: 'POST', + uri: ':undelete' + }, function(err, resp) { callback(err, resp); }); }; -/** - * Set the project's metadata. - * - * @resource [projects: update API Documentation]{@link https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/update} - * @resource [Project Resource]{@link https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects#Project} - * - * @private - * - * @param {object} metadata - See a - * [Project resource](https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects#Project). - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * var metadata = { - * name: 'New name' - * }; - * - * project.setMetadata(metadata, function(err, apiResponse) { - * if (!err) { - * // The project has been successfully updated. - * } - * }); - */ -Project.prototype.setMetadata = function(metadata, callback) { - var self = this; - - callback = callback || util.noop; - - this.makeReq_('PUT', '', null, metadata, function(err, resp) { - if (err) { - callback(err, resp); - return; - } - - self.metadata = resp; - - callback(null, resp); - }); -}; - -/** - * Make a new request object from the provided arguments and wrap the callback - * to intercept non-successful responses. - * - * @private - * - * @param {string} method - Action. - * @param {string} path - Request path. - * @param {*} query - Request query object. - * @param {*} body - Request body contents. - * @param {function} callback - The callback function. - */ -Project.prototype.makeReq_ = function(method, path, query, body, callback) { - path = '/' + this.id + path; - this.resource.makeReq_(method, path, query, body, callback); -}; - module.exports = Project; diff --git a/test/resource/index.js b/test/resource/index.js index 36d778febec..f31f7d3690e 100644 --- a/test/resource/index.js +++ b/test/resource/index.js @@ -20,6 +20,9 @@ var arrify = require('arrify'); var assert = require('assert'); var extend = require('extend'); var mockery = require('mockery'); +var nodeutil = require('util'); + +var Service = require('../../lib/common/service.js'); var util = require('../../lib/common/util.js'); function FakeProject() { @@ -51,6 +54,13 @@ var fakeUtil = extend({}, util, { } }); +function FakeService() { + this.calledWith_ = arguments; + Service.apply(this, arguments); +} + +nodeutil.inherits(FakeService, Service); + describe('Resource', function() { var PROJECT_ID = 'test-project-id'; @@ -58,9 +68,11 @@ describe('Resource', function() { var resource; before(function() { + mockery.registerMock('../common/service.js', FakeService); mockery.registerMock('../common/stream-router.js', fakeStreamRouter); mockery.registerMock('../common/util.js', fakeUtil); mockery.registerMock('./project.js', FakeProject); + mockery.enable({ useCleanCache: true, warnOnUnregistered: false @@ -106,32 +118,21 @@ describe('Resource', function() { fakeUtil.normalizeArguments = normalizeArguments; }); - it('should create an authenticated request function', function(done) { - var options = { - projectId: 'projectId', - credentials: 'credentials', - email: 'email', - keyFilename: 'keyFile' - }; + it('should localize the projectId', function() { + assert.equal(resource.defaultProjectId_, PROJECT_ID); + }); - makeAuthenticatedRequestFactoryOverride = function(options_) { - assert.deepEqual(options_, { - credentials: options.credentials, - email: options.email, - keyFile: options.keyFilename, - scopes: [ - 'https://www.googleapis.com/auth/cloud-platform' - ] - }); - return done; - }; + it('should inherit from Service', function() { + assert(resource instanceof Service); - var resource = new Resource(options); - resource.makeAuthenticatedRequest_(); - }); + var calledWith = resource.calledWith_[0]; - it('should localize the projectId', function() { - assert.equal(resource.defaultProjectId_, PROJECT_ID); + var baseUrl = 'https://cloudresourcemanager.googleapis.com/v1beta1'; + assert.strictEqual(calledWith.baseUrl, baseUrl); + assert.deepEqual(calledWith.scopes, [ + 'https://www.googleapis.com/auth/cloud-platform' + ]); + assert.strictEqual(resource.projectIdRequired, false); }); }); @@ -143,8 +144,8 @@ describe('Resource', function() { it('should not require any options', function(done) { var expectedBody = { projectId: NEW_PROJECT_ID }; - resource.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body, expectedBody); + resource.request = function(reqOpts) { + assert.deepEqual(reqOpts.json, expectedBody); done(); }; @@ -152,11 +153,10 @@ describe('Resource', function() { }); it('should make the correct API request', function(done) { - resource.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/'); - assert.strictEqual(query, null); - assert.deepEqual(body, EXPECTED_BODY); + resource.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/projects'); + assert.deepEqual(reqOpts.json, EXPECTED_BODY); done(); }; @@ -169,7 +169,7 @@ describe('Resource', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - resource.makeReq_ = function(method, path, query, body, callback) { + resource.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -188,7 +188,7 @@ describe('Resource', function() { var apiResponse = { projectId: NEW_PROJECT_ID }; beforeEach(function() { - resource.makeReq_ = function(method, path, query, body, callback) { + resource.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -215,8 +215,8 @@ describe('Resource', function() { describe('getProjects', function() { it('should accept only a callback', function(done) { - resource.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + resource.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -226,11 +226,9 @@ describe('Resource', function() { it('should make the correct API request', function(done) { var query = { a: 'b', c: 'd' }; - resource.makeReq_ = function(method, path, query_, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/'); - assert.strictEqual(query_, query); - assert.strictEqual(body, null); + resource.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/projects'); + assert.strictEqual(reqOpts.qs, query); done(); }; @@ -243,7 +241,7 @@ describe('Resource', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - resource.makeReq_ = function(method, path, query, body, callback) { + resource.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -267,7 +265,7 @@ describe('Resource', function() { }; beforeEach(function() { - resource.makeReq_ = function(method, path, query, body, callback) { + resource.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -281,7 +279,7 @@ describe('Resource', function() { pageToken: nextPageToken }; - resource.makeReq_ = function(method, path, query, body, callback) { + resource.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -338,32 +336,4 @@ describe('Resource', function() { }, /A project ID is required/); }); }); - - describe('makeReq_', function() { - it('should make the correct request', function(done) { - var base = 'https://cloudresourcemanager.googleapis.com/v1beta1/projects'; - - var method = 'POST'; - var path = '/test'; - var query = { - a: 'b', - c: 'd' - }; - var body = { - a: 'b', - c: 'd' - }; - - resource.makeAuthenticatedRequest_ = function(reqOpts, callback) { - assert.strictEqual(reqOpts.method, method); - - assert.strictEqual(reqOpts.uri, base + path); - assert.strictEqual(reqOpts.qs, query); - assert.strictEqual(reqOpts.json, body); - callback(); - }; - - resource.makeReq_(method, path, query, body, done); - }); - }); }); diff --git a/test/resource/project.js b/test/resource/project.js index 10a65c28548..e28e9955e41 100644 --- a/test/resource/project.js +++ b/test/resource/project.js @@ -17,168 +17,75 @@ 'use strict'; var assert = require('assert'); +var extend = require('extend'); +var mockery = require('mockery'); +var nodeutil = require('util'); -var Project = require('../../lib/resource/project.js'); +var ServiceObject = require('../../lib/common/service-object.js'); +var util = require('../../lib/common/util.js'); -describe('Project', function() { - var RESOURCE = {}; - var ID = 'project-id'; +function FakeServiceObject() { + this.calledWith_ = arguments; + ServiceObject.apply(this, arguments); +} + +nodeutil.inherits(FakeServiceObject, ServiceObject); +describe('Project', function() { + var Project; var project; - beforeEach(function() { - project = new Project(RESOURCE, ID); - }); + var RESOURCE = { + createProject: util.noop + }; + var ID = 'project-id'; - describe('instantiation', function() { - it('should localize the resource', function() { - assert.strictEqual(project.resource, RESOURCE); - }); + before(function() { + mockery.registerMock('../common/service-object.js', FakeServiceObject); - it('should localize the ID', function() { - assert.strictEqual(project.id, ID); + mockery.enable({ + useCleanCache: true, + warnOnUnregistered: false }); - it('should default metadata to an empty object', function() { - assert.deepEqual(project.metadata, {}); - }); + Project = require('../../lib/resource/project.js'); }); - describe('delete', function() { - it('should make the correct API request', function(done) { - project.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'DELETE'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - done(); - }; - - project.delete(assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - project.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); - - it('should return an error if the request fails', function(done) { - project.delete(function(err, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - project.delete(); - }); - }); - }); - - describe('success', function() { - var apiResponse = { - projectId: ID - }; - - beforeEach(function() { - project.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; - }); - - it('should execute callback with error and API response', function(done) { - project.delete(function(err, apiResponse_) { - assert.ifError(err); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - project.delete(); - }); - }); - }); + after(function() { + mockery.deregisterAll(); + mockery.disable(); }); - describe('getMetadata', function() { - it('should make the correct API request', function(done) { - project.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - - done(); - }; - - project.getMetadata(assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - project.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); - - it('should execute callback with error and API response', function(done) { - project.getMetadata(function(err, metadata, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(metadata, null); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - project.getMetadata(); - }); - }); - }); - - describe('success', function() { - var apiResponse = { a: 'b', c: 'd' }; + beforeEach(function() { + project = new Project(RESOURCE, ID); + }); - beforeEach(function() { - project.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; + describe('instantiation', function() { + it('should inherit from ServiceObject', function(done) { + var resourceInstance = extend({}, RESOURCE, { + createProject: { + bind: function(context) { + assert.strictEqual(context, resourceInstance); + done(); + } + } }); - it('should update the metadata to the API response', function(done) { - project.getMetadata(function(err) { - assert.ifError(err); - assert.strictEqual(project.metadata, apiResponse); - done(); - }); - }); + var project = new Project(resourceInstance, ID); + assert(project instanceof ServiceObject); - it('should exec callback with metadata and API response', function(done) { - project.getMetadata(function(err, metadata, apiResponse_) { - assert.ifError(err); - assert.strictEqual(metadata, apiResponse); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); + var calledWith = project.calledWith_[0]; - it('should not require a callback', function() { - assert.doesNotThrow(function() { - project.getMetadata(); - }); + assert.strictEqual(calledWith.parent, resourceInstance); + assert.strictEqual(calledWith.baseUrl, '/projects'); + assert.strictEqual(calledWith.id, ID); + assert.deepEqual(calledWith.methods, { + create: true, + delete: true, + exists: true, + get: true, + getMetadata: true, + setMetadata: true }); }); }); @@ -188,17 +95,15 @@ describe('Project', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - project.makeReq_ = function(method, path, query, body, callback) { + project.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); it('should make the correct API request', function(done) { - project.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, ':undelete'); - assert.strictEqual(query, null); - assert.strictEqual(body, null); + project.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, ':undelete'); done(); }; @@ -220,99 +125,4 @@ describe('Project', function() { }); }); }); - - describe('setMetadata', function() { - var METADATA = { a: 'b', c: 'd' }; - - it('should make the correct API request', function(done) { - project.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'PUT'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, METADATA); - - done(); - }; - - project.setMetadata(METADATA, assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - project.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); - - it('should return an error if the request fails', function(done) { - project.setMetadata(METADATA, function(err, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - project.setMetadata(METADATA); - }); - }); - }); - - describe('success', function() { - var apiResponse = { - projectId: ID - }; - - beforeEach(function() { - project.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; - }); - - it('should execute callback with API response', function(done) { - project.setMetadata(METADATA, function(err, apiResponse_) { - assert.ifError(err); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - project.setMetadata(METADATA); - }); - }); - }); - }); - - describe('makeReq_', function() { - it('should make the correct request to Resource', function(done) { - var expectedPathPrefix = '/' + project.id; - - var method = 'POST'; - var path = '/test'; - var query = { - a: 'b', - c: 'd' - }; - var body = { - a: 'b', - c: 'd' - }; - - project.resource.makeReq_ = function(method_, path_, query_, body_, cb) { - assert.strictEqual(method_, method); - assert.strictEqual(path_, expectedPathPrefix + path); - assert.strictEqual(query_, query); - assert.strictEqual(body_, body); - cb(); - }; - - project.makeReq_(method, path, query, body, done); - }); - }); });