diff --git a/lib/common/util.js b/lib/common/util.js index 84a4f0d73cb..f454afc3f21 100644 --- a/lib/common/util.js +++ b/lib/common/util.js @@ -124,7 +124,7 @@ util.arrayize = arrayize; */ function format(template, args) { return template.replace(/{([^}]*)}/g, function(match, key) { - return args[key] || match; + return key in args ? args[key] : match; }); } diff --git a/lib/pubsub/index.js b/lib/pubsub/index.js index 44d332ab957..00324548499 100644 --- a/lib/pubsub/index.js +++ b/lib/pubsub/index.js @@ -162,6 +162,123 @@ PubSub.prototype.createTopic = function(name, callback) { }); }; +/** + * Create a subscription to a topic. You may optionally provide an object to + * customize the subscription. + * + * Your provided callback will either be invoked with an error object, if an API + * error occurred, or a {@linkcode module:pubsub/subscription} object. + * + * @throws {Error} If a Topic instance or topic name is not provided. + * @throws {Error} If a subName is not provided. + * + * @param {module:pubsub/topic|string} - topic - The Topic to create a + * subscription to. + * @param {string} subName - The name of the subscription. + * @param {object=} options - Configuration object. + * @param {number} options.ackDeadlineSeconds - The maximum time after receiving + * a message that you must ack a message before it is redelivered. + * @param {boolean} options.autoAck - Automatically acknowledge the message once + * it's pulled. (default: false) + * @param {number} options.interval - Interval in milliseconds to check for new + * messages. (default: 10) + * @param {boolean} options.reuseExisting - If the subscription already exists, + * reuse it. The options of the existing subscription are not changed. If + * false, attempting to create a subscription that already exists will fail. + * (default: false) + * @param {function} callback - The callback function. + * + * @example + * // Without specifying any options. + * var topic = pubsub.topic('messageCenter'); + * var name = 'newMessages'; + * + * pubsub.subscribe(topic, name, function(err, subscription, apiResponse) {}); + * + * // With options. + * pubsub.subscribe(topic, name, { + * ackDeadlineSeconds: 90, + * autoAck: true, + * interval: 30 + * }, function(err, subscription, apiResponse) {}); + */ +PubSub.prototype.subscribe = function(topic, subName, options, callback) { + if (!util.is(topic, 'string') && !(topic instanceof Topic)) { + throw new Error('A Topic is required for a new subscription.'); + } + + if (!subName || !util.is(subName, 'string')) { + throw new Error('A subscription name is required for a new subscription.'); + } + + if (!callback) { + callback = options; + options = {}; + } + + options = options || {}; + + if (util.is(topic, 'string')) { + topic = this.topic(topic); + } + + var body = { + topic: topic.name + }; + + if (options.ackDeadlineSeconds) { + body.ackDeadlineSeconds = options.ackDeadlineSeconds; + } + + var subscription = this.subscription(subName, options); + + this.makeReq_('PUT', subscription.name, null, body, function(err, result) { + if (err && !(err.code === 409 && options.reuseExisting)) { + callback(err, null, result); + return; + } + + callback(null, subscription, result); + }); +}; + +/** + * Create a Subscription object in reference to an existing subscription. This + * command by itself will not run any API requests. You will receive a + * {@linkcode module:pubsub/subscription} object, which will allow you to + * interact with your subscription. + * + * @throws {Error} If a name is not provided. + * + * @param {string} name - The name of the subscription. + * @param {object=} options - Configuration object. + * @param {boolean} options.autoAck - Automatically acknowledge the message once + * it's pulled. + * @param {number} options.interval - Interval in milliseconds to check for new + * messages. + * @return {module:pubsub/subscription} + * + * @example + * var subscription = pubsub.subscription('my-existing-subscription'); + * + * // Register a listener for `message` events. + * subscription.on('message', function(message) { + * // Called every time a message is received. + * // message.id = ID used to acknowledge its receival. + * // message.data = Contents of the message. + * // message.attributes = Attributes of the message. + * }); + */ +PubSub.prototype.subscription = function(name, options) { + if (!name) { + throw new Error('The name of a subscription is required.'); + } + + options = options || {}; + options.name = name; + return new Subscription(this, options); +}; + /** * Create a Topic object to reference an existing topic. * @@ -194,9 +311,11 @@ PubSub.prototype.topic = function(name, options) { * You may optionally provide a query object as the first argument to customize * the response. * - * @param {object=} query - Query object. - * @param {string=} query.pageToken - Page token. - * @param {number=} query.pageSize - Maximum number of results to return. + * @param {object=} options - Configuration object. + * @param {string} options.topic - The name of the topic to list subscriptions + * from. + * @param {number} options.pageSize - Maximum number of results to return. + * @param {string} options.pageToken - Page token. * @param {function} callback - The callback function. * * @example @@ -205,38 +324,69 @@ PubSub.prototype.topic = function(name, options) { * // so, run `pubsub.getSubscriptions(nextQuery, callback);`. * }; * - * // Get all subscriptions. + * // Get all subscriptions for this project. * pubsub.getSubscriptions(callback); * * // Customize the query. * pubsub.getSubscriptions({ - * pageSize: 10 + * pageSize: 3 * }, callback); */ -PubSub.prototype.getSubscriptions = function(query, callback) { +PubSub.prototype.getSubscriptions = function(options, callback) { var self = this; + if (!callback) { - callback = query; - query = {}; + callback = options; + options = {}; } - var path = this.projectName + '/subscriptions'; - this.makeReq_('GET', path, query, true, function(err, result) { + options = options || {}; + + var topicName; + + if (util.is(options.topic, 'string')) { + topicName = options.topic; + } else if (options.topic instanceof Topic) { + topicName = options.topic.unformattedName; + } + + var query = {}; + + if (options.pageSize) { + query.pageSize = options.pageSize; + } + + if (options.pageToken) { + query.pageToken = options.pageToken; + } + + var apiPath = util.format('{projectPath}{topicPath}/subscriptions', { + projectPath: 'projects/' + this.projectId, + topicPath: topicName ? '/topics/' + topicName : '' + }); + + this.makeReq_('GET', apiPath, query, null, function(err, result) { if (err) { callback(err, null, null, result); return; } - var subscriptions = (result.subscriptions || []).map(function(item) { + var subscriptions = (result.subscriptions || []).map(function(sub) { return new Subscription(self, { - name: item.name + // Depending on if we're using a subscriptions.list or + // topics.subscriptions.list API endpoint, we will get back a + // Subscription resource or just the name of the subscription. + name: sub.name || sub }); }); + var nextQuery = null; + if (result.nextPageToken) { - nextQuery = query; + nextQuery = options; nextQuery.pageToken = result.nextPageToken; } + callback(null, subscriptions, nextQuery, result); }); }; diff --git a/lib/pubsub/subscription.js b/lib/pubsub/subscription.js index 1740c1c468f..ab6f6e2179c 100644 --- a/lib/pubsub/subscription.js +++ b/lib/pubsub/subscription.js @@ -117,6 +117,7 @@ function Subscription(pubsub, options) { events.EventEmitter.call(this); this.name = Subscription.formatName_(pubsub.projectId, options.name); + this.makeReq_ = pubsub.makeReq_.bind(pubsub); this.autoAck = util.is(options.autoAck, 'boolean') ? options.autoAck : false; diff --git a/lib/pubsub/topic.js b/lib/pubsub/topic.js index 9789dd70f07..54fc143a159 100644 --- a/lib/pubsub/topic.js +++ b/lib/pubsub/topic.js @@ -26,12 +26,6 @@ */ var util = require('../common/util.js'); -/** - * @type {module:pubsub/subscription} - * @private - */ -var Subscription = require('./subscription.js'); - /*! Developer Documentation * * @param {module:pubsub} pubsub - PubSub object. @@ -62,9 +56,9 @@ function Topic(pubsub, options) { this.name = Topic.formatName_(pubsub.projectId, options.name); this.projectId = pubsub.projectId; this.pubsub = pubsub; + this.unformattedName = options.name; if (options.autoCreate) { - this.unformattedName = options.name; this.origMakeReq_ = this.makeReq_; this.makeReq_ = this.autoCreateWrapper_; } @@ -222,49 +216,35 @@ Topic.prototype.delete = function(callback) { * error occurred, or an array of {@linkcode module:pubsub/subscription} * objects. * - * @param {object=} query - Query object. - * @param {string=} query.pageToken - Page token. - * @param {number=} query.pageSize - Maximum number of results to return. + * @param {object=} options - Configuration object. + * @param {number=} options.pageSize - Maximum number of results to return. + * @param {string=} options.pageToken - Page token. * @param {function} callback - The callback function. * * @example - * // Get all subscriptions for this topic. - * topic.getSubscriptions(function(err, subscriptions, nextQuery, apiResponse) { + * var callback = function(err, subscriptions, nextQuery, apiResponse) { * // If `nextQuery` is non-null, there may be more results to fetch. To do * // so, run `topic.getSubscriptions(nextQuery, callback);`. - * }); + * }; + * + * // Get all subscriptions for this topic. + * topic.getSubscriptions(callback); * * // Customize the query. * topic.getSubscriptions({ * pageSize: 3 - * }, function(err, subscriptions, nextQuery, apiResponse) {}); + * }, callback); */ -Topic.prototype.getSubscriptions = function(query, callback) { - var self = this; - if (util.is(query, 'function')) { - callback = query; - query = {}; +Topic.prototype.getSubscriptions = function(options, callback) { + if (!callback) { + callback = options; + options = {}; } - var path = this.name + '/subscriptions'; - this.makeReq_('GET', path, query, true, function(err, result) { - if (err) { - callback(err, null, null, result); - return; - } + options = options || {}; + options.topic = this; - var subscriptions = (result.subscriptions || []).map(function(name) { - return new Subscription(self, { - name: name - }); - }); - var nextQuery = null; - if (result.nextPageToken) { - nextQuery = query; - nextQuery.pageToken = result.nextPageToken; - } - callback(null, subscriptions, nextQuery, result); - }); + this.pubsub.getSubscriptions(options, callback); }; /** @@ -274,9 +254,7 @@ Topic.prototype.getSubscriptions = function(query, callback) { * Your provided callback will either be invoked with an error object, if an API * error occurred, or a {@linkcode module:pubsub/subscription} object. * - * @throws {Error} If a name is not provided. - * - * @param {string} name - The name of the subscription. + * @param {string} subName - The name of the subscription. * @param {object=} options - Configuration object. * @param {number=} options.ackDeadlineSeconds - The maximum time after * receiving a message that you must ack a message before it is redelivered. @@ -292,44 +270,17 @@ Topic.prototype.getSubscriptions = function(query, callback) { * * @example * // Without specifying any options. - * topic.subscribe('my-cool-sub', function(err, subscription, apiResponse) {}); + * topic.subscribe('newMessages', function(err, subscription, apiResponse) {}); * * // With options. - * topic.subscribe('my-cool-sub', { + * topic.subscribe('newMessages', { * ackDeadlineSeconds: 90, * autoAck: true, * interval: 30 * }, function(err, subscription, apiResponse) {}); */ -Topic.prototype.subscribe = function(name, options, callback) { - var self = this; - if (!name) { - throw new Error('A name is required for a new subscription.'); - } - if (!callback) { - callback = options; - options = {}; - } - - var body = { - topic: this.name - }; - - if (options.ackDeadlineSeconds) { - body.ackDeadlineSeconds = options.ackDeadlineSeconds; - } - - var path = Subscription.formatName_(this.projectId, name); - - this.makeReq_('PUT', path, null, body, function(err, result) { - if (options.reuseExisting && err && err.code === 409) { - callback(null, self.subscription(name, options), result); - } else if (err) { - callback(err, null, result); - } else { - callback(null, self.subscription(name, options), result); - } - }); +Topic.prototype.subscribe = function(subName, options, callback) { + this.pubsub.subscribe(this, subName, options, callback); }; /** @@ -338,8 +289,6 @@ Topic.prototype.subscribe = function(name, options, callback) { * {@linkcode module:pubsub/subscription} object, which will allow you to * interact with your subscription. * - * @throws {Error} If a name is not provided. - * * @param {string} name - Name of the subscription. * @param {object=} options - Configuration object. * @param {boolean=} options.autoAck - Automatically acknowledge the message @@ -360,12 +309,7 @@ Topic.prototype.subscribe = function(name, options, callback) { * }); */ Topic.prototype.subscription = function(name, options) { - if (!name) { - throw new Error('The name of a subscription is required.'); - } - options = options || {}; - options.name = name; - return new Subscription(this.pubsub, options); + return this.pubsub.subscription(name, options); }; module.exports = Topic; diff --git a/system-test/pubsub.js b/system-test/pubsub.js index e23a4635a14..958f7a7fd65 100644 --- a/system-test/pubsub.js +++ b/system-test/pubsub.js @@ -14,8 +14,6 @@ * limitations under the License. */ -/*global describe, it, before, after */ - 'use strict'; var assert = require('assert'); diff --git a/test/pubsub/index.js b/test/pubsub/index.js index 9ec0e5fd31c..56b24c1ae1a 100644 --- a/test/pubsub/index.js +++ b/test/pubsub/index.js @@ -14,16 +14,28 @@ * limitations under the License. */ -/*global describe, it, beforeEach, before, after */ - 'use strict'; var assert = require('assert'); var mockery = require('mockery'); var request = require('request'); -var Subscription = require('../../lib/pubsub/subscription.js'); var Topic = require('../../lib/pubsub/topic.js'); +var SubscriptionCached = require('../../lib/pubsub/subscription.js'); +var formatName_Cached = SubscriptionCached.formatName_; +var SubscriptionOverride; +var formatName_Override; + +function Subscription(a, b) { + var OverrideFn = SubscriptionOverride || SubscriptionCached; + return new OverrideFn(a, b); +} + +Subscription.formatName_ = function() { + var args = [].slice.apply(arguments); + return (formatName_Override || formatName_Cached).apply(null, args); +}; + var request_Cached = request; var request_Override; function fakeRequest() { @@ -57,6 +69,7 @@ describe('PubSub', function() { }); beforeEach(function() { + SubscriptionOverride = null; request_Override = null; pubsub = new PubSub({ projectId: PROJECT_ID }); pubsub.makeReq_ = function(method, path, q, body, callback) { @@ -186,28 +199,108 @@ describe('PubSub', function() { pubsub.getSubscriptions(done); }); - it('should pass the right parameters', function() { - pubsub.makeReq_ = function(method, path, q) { + it('should pass the correct arguments to the API', function() { + pubsub.makeReq_ = function(method, path, query) { assert.equal(method, 'GET'); assert.equal(path, 'projects/' + PROJECT_ID + '/subscriptions'); - assert.equal(q.query, undefined); + assert.equal(query.query, undefined); }; - pubsub.getSubscriptions(function() {}); + + pubsub.getSubscriptions(assert.ifError); }); - it('should return Subscription instances', function() { - pubsub.getSubscriptions(function(err, subscriptions) { - assert.ifError(err); - assert(subscriptions[0] instanceof Subscription); + describe('topics', function() { + var TOPIC; + var TOPIC_NAME = 'topic'; + var TOPIC_SUBCRIPTION_NAME = + 'projects/' + PROJECT_ID + '/topics/' + TOPIC_NAME + '/subscriptions'; + + before(function() { + TOPIC = new Topic(pubsub, { name: TOPIC_NAME }); + }); + + it('should subscribe to a topic by string', function(done) { + pubsub.makeReq_ = function(method, path) { + assert.equal(path, TOPIC_SUBCRIPTION_NAME); + done(); + }; + + pubsub.getSubscriptions({ topic: TOPIC_NAME }, assert.ifError); + }); + + it('should subscribe to a topic by Topic instance', function(done) { + pubsub.makeReq_ = function(method, path) { + assert.equal(path, TOPIC_SUBCRIPTION_NAME); + done(); + }; + + pubsub.getSubscriptions({ topic: TOPIC }, assert.ifError); + }); + }); + + it('should pass options to API request', function(done) { + var opts = { pageSize: 10, pageToken: 'abc' }; + + pubsub.makeReq_ = function(method, path, query) { + assert.equal(query.pageSize, opts.pageSize); + assert.equal(query.pageToken, opts.pageToken); + done(); + }; + + pubsub.getSubscriptions(opts, assert.ifError); + }); + + it('should pass error & response if api returns an error', function(done) { + var error = new Error('Error'); + var resp = { error: true }; + + pubsub.makeReq_ = function(method, path, q, body, callback) { + callback(error, resp); + }; + + pubsub.getSubscriptions(function(err, subs, nextQuery, apiResponse) { + assert.equal(err, error); + assert.deepEqual(apiResponse, resp); + done(); + }); + }); + + describe('returning Subscription instances', function() { + it('should handle subscriptions.list response', function(done) { + pubsub.getSubscriptions(function(err, subscriptions) { + assert.ifError(err); + assert(subscriptions[0] instanceof SubscriptionCached); + done(); + }); + }); + + it('should handle topics.subscriptions.list response', function(done) { + var subName = 'sub-name'; + var subFullName = + 'projects/' + PROJECT_ID + '/subscriptions/' + subName; + + pubsub.makeReq_ = function(method, path, query, body, callback) { + callback(null, { subscriptions: [subName] }); + }; + + pubsub.getSubscriptions(function(err, subscriptions) { + assert.ifError(err); + assert(subscriptions[0] instanceof SubscriptionCached); + assert.equal(subscriptions[0].name, subFullName); + done(); + }); }); }); it('should return a query if more results exist', function() { var token = 'next-page-token'; + pubsub.makeReq_ = function(method, path, q, body, callback) { callback(null, { nextPageToken: token }); }; + var query = { maxResults: 1 }; + pubsub.getSubscriptions(query, function(err, subscriptions, nextQuery) { assert.ifError(err); assert.strictEqual(query.maxResults, nextQuery.maxResults); @@ -215,26 +308,197 @@ describe('PubSub', function() { }); }); - it('should pass error if api returns an error', function() { - var error = new Error('Error'); + it('should pass apiResponse to callback', function(done) { + var resp = { success: true }; + pubsub.makeReq_ = function(method, path, q, body, callback) { + callback(null, resp); + }; + + pubsub.getSubscriptions(function(err, subs, nextQuery, apiResponse) { + assert.equal(resp, apiResponse); + done(); + }); + }); + }); + + describe('subscribe', function() { + var TOPIC_NAME = 'topic'; + var TOPIC = { + name: 'projects/' + PROJECT_ID + '/topics/' + TOPIC_NAME + }; + + var SUB_NAME = 'subscription'; + var SUBSCRIPTION = { + name: 'projects/' + PROJECT_ID + '/subscriptions/' + SUB_NAME + }; + + it('should throw if no Topic is provided', function() { + assert.throws(function() { + pubsub.subscribe(); + }, /A Topic is required.*/); + }); + + it('should throw if no sub name is provided', function() { + assert.throws(function() { + pubsub.subscribe('topic'); + }, /A subscription name is required.*/); + }); + + it('should not require configuration options', function(done) { + pubsub.makeReq_ = function(method, path, qs, body, callback) { + callback(); + }; + + pubsub.subscribe(TOPIC_NAME, SUB_NAME, done); + }); + + it('should cretae a topic object from a string', function(done) { + pubsub.topic = function(topicName) { + assert.equal(topicName, TOPIC_NAME); + done(); + return TOPIC; + }; + + pubsub.subscribe(TOPIC_NAME, SUB_NAME, assert.ifError); + }); + + it('should create a subscription object from a string', function(done) { + var opts = {}; + + pubsub.subscription = function(subName, options) { + assert.equal(subName, SUB_NAME); + assert.deepEqual(options, opts); + done(); + return SUBSCRIPTION; + }; + + pubsub.subscribe(TOPIC_NAME, SUB_NAME, opts, assert.ifError); + }); + + it('should pass options to a created subscription object', function(done) { + var opts = { a: 'b', c: 'd' }; + + pubsub.subscription = function(subName, options) { + assert.equal(subName, SUB_NAME); + assert.deepEqual(options, opts); + done(); + return SUBSCRIPTION; + }; + + pubsub.subscribe(TOPIC_NAME, SUB_NAME, opts, assert.ifError); + }); + + it('should send correct request', function(done) { + pubsub.makeReq_ = function(method, path, query, body) { + assert.equal(method, 'PUT'); + assert.equal(path, SUBSCRIPTION.name); + assert.equal(body.topic, TOPIC.name); + done(); + }; + + pubsub.subscribe(TOPIC_NAME, SUB_NAME, assert.ifError); + }); + + it('should re-use existing subscription if specified', function(done) { + pubsub.subscription = function() { + return SUBSCRIPTION; + }; + + pubsub.makeReq_ = function(method, path, query, body, callback) { + callback({ code: 409 }); + }; + + // Don't re-use an existing subscription (error if one exists). + pubsub.subscribe(TOPIC_NAME, SUB_NAME, function(err) { + assert.equal(err.code, 409); + }); + + // Re-use an existing subscription (ignore error if one exists). + var opts = { reuseExisting: true }; + pubsub.subscribe(TOPIC_NAME, SUB_NAME, opts, function(err, sub) { + assert.ifError(err); + assert.deepEqual(sub, SUBSCRIPTION); + + done(); + }); + }); + + it('should return an api error to the callback', function(done) { + var error = new Error('Error.'); + + pubsub.makeReq_ = function(method, path, query, body, callback) { callback(error); }; - pubsub.getSubscriptions(function(err) { + + pubsub.subscribe(TOPIC_NAME, SUB_NAME, function(err) { assert.equal(err, error); + done(); }); }); - it('should pass apiResponse to callback', function(done) { + it('should return apiResponse to the callback', function(done) { var resp = { success: true }; - pubsub.makeReq_ = function(method, path, q, body, callback) { + + pubsub.makeReq_ = function(method, path, query, body, callback) { callback(null, resp); }; - pubsub.getSubscriptions(function(err, topics, nextQuery, apiResponse) { - assert.equal(resp, apiResponse); + + pubsub.subscribe(TOPIC_NAME, SUB_NAME, function(err, sub, apiResponse) { + assert.deepEqual(resp, apiResponse); done(); }); }); + + it('should pass options to the api request', function(done) { + var opts = { ackDeadlineSeconds: 90 }; + + pubsub.makeReq_ = function(method, path, query, body) { + assert.strictEqual(body.ackDeadlineSeconds, opts.ackDeadlineSeconds); + done(); + }; + + pubsub.subscribe(TOPIC_NAME, SUB_NAME, opts, assert.ifError); + }); + }); + + describe('subscription', function() { + var SUB_NAME = 'new-sub-name'; + var CONFIG = { autoAck: true, interval: 90 }; + + it('should throw if no name is provided', function() { + assert.throws(function() { + pubsub.subscription(); + }, /The name of a subscription is required/); + }); + + it('should return a Subscription object', function() { + SubscriptionOverride = function() {}; + var subscription = pubsub.subscription(SUB_NAME, {}); + assert(subscription instanceof SubscriptionOverride); + }); + + it('should honor settings', function(done) { + SubscriptionOverride = function(pubsub, options) { + assert.deepEqual(options, CONFIG); + done(); + }; + pubsub.subscription(SUB_NAME, CONFIG); + }); + + it('should pass specified name to the Subscription', function(done) { + SubscriptionOverride = function(pubsub, options) { + assert.equal(options.name, SUB_NAME); + done(); + }; + pubsub.subscription(SUB_NAME, {}); + }); + + it('should not require options', function() { + assert.doesNotThrow(function() { + pubsub.subscription(SUB_NAME); + }); + }); }); describe('makeReq_', function() { diff --git a/test/pubsub/topic.js b/test/pubsub/topic.js index cdd90154c87..72cf6aa5b3b 100644 --- a/test/pubsub/topic.js +++ b/test/pubsub/topic.js @@ -14,31 +14,13 @@ * limitations under the License. */ -/*global describe, it, beforeEach, before, after */ - 'use strict'; var assert = require('assert'); -var mockery = require('mockery'); +var Topic = require('../../lib/pubsub/topic'); var util = require('../../lib/common/util.js'); -var SubscriptionCached = require('../../lib/pubsub/subscription.js'); -var formatName_Cached = SubscriptionCached.formatName_; -var SubscriptionOverride; -var formatName_Override; - -function Subscription(a, b) { - var OverrideFn = SubscriptionOverride || SubscriptionCached; - return new OverrideFn(a, b); -} - -Subscription.formatName_ = function() { - var args = [].slice.apply(arguments); - return (formatName_Override || formatName_Cached).apply(null, args); -}; - describe('Topic', function() { - var Topic; var PROJECT_ID = 'test-project'; var TOPIC_NAME = 'test-topic'; var pubsubMock = { @@ -47,23 +29,7 @@ describe('Topic', function() { }; var topic; - before(function() { - mockery.registerMock('./subscription.js', Subscription); - mockery.enable({ - useCleanCache: true, - warnOnUnregistered: false - }); - Topic = require('../../lib/pubsub/topic'); - }); - - after(function() { - mockery.deregisterAll(); - mockery.disable(); - }); - beforeEach(function() { - SubscriptionOverride = null; - formatName_Override = null; topic = new Topic(pubsubMock, { name: TOPIC_NAME }); }); @@ -245,176 +211,68 @@ describe('Topic', function() { }); }); - describe('subscriptions', function() { - var SUB_NAME = 'new-sub-name'; - var SUB_FULL_NAME = 'projects/' + PROJECT_ID + '/subscriptions/' + SUB_NAME; - var CONFIG = { autoAck: true, interval: 90 }; - - describe('getSubscriptions', function() { - it('should pass query', function(done) { - var query = { pageToken: 1, maxResults: 3 }; - topic.getSubscriptions = function(q) { - assert.strictEqual(q.pageToken, query.pageToken); - assert.strictEqual(q.maxResults, query.maxResults); - done(); - }; - topic.getSubscriptions(query, assert.ifError); - }); - - it('should pass callback', function(done) { - topic.getSubscriptions = function(q, callback) { - callback(); - }; - topic.getSubscriptions({}, done); - }); + describe('getSubscriptions', function() { + it('should accept just a callback', function(done) { + topic.pubsub.getSubscriptions = function(options, callback) { + assert.deepEqual(options, { topic: topic }); + callback(); + }; - it('should pass apiResponse with callback', function(done) { - var resp = { success: true }; - topic.getSubscriptions = function(q, callback) { - callback(null, [], resp); - }; - topic.getSubscriptions({}, function(err, subs, apiResponse) { - assert.deepEqual(resp, apiResponse); - done(); - }); - }); + topic.getSubscriptions(done); }); - describe('subscribe', function() { - it('should throw if no name is provided', function() { - assert.throws(function() { - topic.subscribe(); - }, /name.*required/); - }); - - it('should not require configuration options', function(done) { - topic.makeReq_ = function(method, path, qs, body, callback) { - callback(); - }; - topic.subscribe(SUB_NAME, done); - }); - - it('should format provided name', function(done) { - formatName_Override = function() { - done(); - }; - topic.subscribe(SUB_NAME, assert.ifError); - }); - - it('should send correct request', function(done) { - topic.makeReq_ = function(method, path, qs, body) { - assert.equal(method, 'PUT'); - assert.equal(path, SUB_FULL_NAME); - assert.equal(body.topic, topic.name); - done(); - }; - topic.subscribe(SUB_NAME, assert.ifError); - }); - - it('should return an api error to the callback', function(done) { - var error = new Error('Error.'); - topic.makeReq_ = function(method, path, qs, body, callback) { - callback(error); - }; - topic.subscribe(SUB_NAME, function(err) { - assert.equal(err, error); - done(); - }); - }); - - it('should return apiResponse to the callback', function(done) { - var resp = { success: true }; - topic.makeReq_ = function(method, path, qs, body, callback) { - callback(null, resp); - }; - topic.subscribe(SUB_NAME, function(err, sub, apiResponse) { - assert.deepEqual(resp, apiResponse); - done(); - }); - }); - - it('should create a new subscription', function(done) { - topic.subscription = function(name) { - assert.equal(name, SUB_NAME); - done(); - }; - topic.makeReq_ = function(method, path, qs, body, callback) { - callback(); - }; - topic.subscribe(SUB_NAME, assert.ifError); - }); - - it('should honor settings on the api request', function(done) { - var SEC = 90; - topic.makeReq_ = function(method, path, qs, body) { - assert.strictEqual(body.ackDeadlineSeconds, SEC); - done(); - }; - topic.subscribe(SUB_NAME, { ackDeadlineSeconds: SEC }, assert.ifError); - }); + it('should pass correct args to pubsub#getSubscriptions', function(done) { + var opts = { a: 'b', c: 'd' }; - it('should honor settings on the subscription object', function(done) { - topic.subscription = function(name, options) { - assert.deepEqual(options, CONFIG); - done(); - }; - topic.makeReq_ = function(method, path, qs, body, callback) { + topic.pubsub = { + getSubscriptions: function(options, callback) { + assert.deepEqual(options, opts); + assert.deepEqual(options.topic, topic); callback(); - }; - topic.subscribe(SUB_NAME, CONFIG, assert.ifError); - }); + } + }; - it('should re-use existing subscription if specified', function(done) { - topic.subscription = function() { - done(); - }; + topic.getSubscriptions(opts, done); + }); + }); - topic.makeReq_ = function(method, path, qs, body, callback) { - callback({ code: 409 }); - }; + describe('subscribe', function() { + it('should pass correct arguments to pubsub#subscribe', function(done) { + var subscriptionName = 'subName'; + var opts = {}; - topic.subscribe(SUB_NAME, function(err) { - assert.equal(err.code, 409); - }); + topic.pubsub.subscribe = function(t, subName, options, callback) { + assert.deepEqual(t, topic); + assert.equal(subName, subscriptionName); + assert.deepEqual(options, opts); + callback(); + }; - topic.subscribe(SUB_NAME, { reuseExisting: true }, assert.ifError); - }); + topic.subscribe(subscriptionName, opts, done); }); + }); - describe('subscription', function() { - it('should throw if no name is provided', function() { - assert.throws(function() { - topic.subscription(); - }, /name.*required/); - }); + describe('subscription', function() { + it('should pass correct arguments to pubsub#subscription', function(done) { + var subscriptionName = 'subName'; + var opts = {}; - it('should return a Subscription object', function() { - SubscriptionOverride = function() {}; - var subscription = topic.subscription(SUB_NAME, {}); - assert(subscription instanceof SubscriptionOverride); - }); + topic.pubsub.subscription = function(name, options) { + assert.equal(name, subscriptionName); + assert.deepEqual(options, opts); + done(); + }; - it('should honor settings', function(done) { - SubscriptionOverride = function(pubsub, options) { - assert.deepEqual(options, CONFIG); - done(); - }; - topic.subscription(SUB_NAME, CONFIG); - }); + topic.subscription(subscriptionName, opts); + }); - it('should pass specified name to the Subscription', function(done) { - SubscriptionOverride = function(pubsub, options) { - assert.equal(options.name, SUB_NAME); - done(); - }; - topic.subscription(SUB_NAME, {}); - }); + it('should return the result', function(done) { + topic.pubsub.subscription = function() { + return done; + }; - it('should not require options', function() { - assert.doesNotThrow(function() { - topic.subscription(SUB_NAME); - }); - }); + var doneFn = topic.subscription(); + doneFn(); }); }); });