From cb88bd09d65fc541cab09c93b4be0574fe3e924e Mon Sep 17 00:00:00 2001 From: Jason Dobry Date: Tue, 9 Aug 2016 21:36:43 -0700 Subject: [PATCH] * Refactored Pub/Sub samples into nice command-line programs, like the new Cloud Storage samples * Brought Pub/Sub samples code coverage up to 98.66% --- bigquery/system-test/dataset_size.test.js | 2 +- bigquery/system-test/getting_started.test.js | 2 +- .../system-test/load_data_from_csv.test.js | 2 +- .../system-test/load_data_from_gcs.test.js | 2 +- computeengine/system-test/vms.test.js | 2 +- computeengine/system-test/vms_api.test.js | 2 +- datastore/system-test/concepts.test.js | 4 +- datastore/system-test/tasks.test.js | 6 +- language/system-test/analyze.test.js | 6 +- logging/system-test/export.test.js | 2 +- logging/system-test/list.test.js | 2 +- logging/system-test/write.test.js | 2 +- monitoring/create_custom_metric.js | 4 +- .../system-test/create_custom_metric.test.js | 5 +- monitoring/system-test/list_resources.test.js | 2 +- prediction/system-test/hostedmodels.test.js | 2 +- pubsub/README.md | 87 +++- pubsub/iam.js | 248 +++++++----- pubsub/package.json | 3 +- pubsub/subscription.js | 331 --------------- pubsub/subscriptions.js | 206 ++++++++++ pubsub/system-test/iam.test.js | 97 +++-- pubsub/system-test/subscription.test.js | 49 --- pubsub/system-test/subscriptions.test.js | 103 +++++ pubsub/system-test/topics.test.js | 72 ++++ pubsub/test/iam.test.js | 379 ++++++++++++------ pubsub/test/subscription.test.js | 205 ---------- pubsub/test/subscriptions.test.js | 246 ++++++++++++ pubsub/test/topics.test.js | 219 ++++++++++ pubsub/topics.js | 181 +++++++++ speech/system-test/recognize.test.js | 2 +- .../system-test/recognize_streaming.test.js | 2 +- vision/system-test/faceDetection.test.js | 2 +- vision/system-test/labelDetection.test.js | 2 +- vision/system-test/landmarkDetection.test.js | 2 +- vision/system-test/textDetection.test.js | 4 +- 36 files changed, 1603 insertions(+), 884 deletions(-) delete mode 100644 pubsub/subscription.js create mode 100644 pubsub/subscriptions.js delete mode 100644 pubsub/system-test/subscription.test.js create mode 100644 pubsub/system-test/subscriptions.test.js create mode 100644 pubsub/system-test/topics.test.js delete mode 100644 pubsub/test/subscription.test.js create mode 100644 pubsub/test/subscriptions.test.js create mode 100644 pubsub/test/topics.test.js create mode 100644 pubsub/topics.js diff --git a/bigquery/system-test/dataset_size.test.js b/bigquery/system-test/dataset_size.test.js index b3e3d21879..c0fb9df434 100644 --- a/bigquery/system-test/dataset_size.test.js +++ b/bigquery/system-test/dataset_size.test.js @@ -21,7 +21,7 @@ describe('bigquery:dataset_size', function () { 'bigquery-public-data', 'hacker_news', function (err, size) { - assert(!err); + assert.ifError(err); assert.equal(typeof size, 'string'); assert(size.indexOf(' GB') === size.length - 3); done(); diff --git a/bigquery/system-test/getting_started.test.js b/bigquery/system-test/getting_started.test.js index 301e8f57d6..142a0ae02e 100644 --- a/bigquery/system-test/getting_started.test.js +++ b/bigquery/system-test/getting_started.test.js @@ -19,7 +19,7 @@ describe('bigquery:getting_started', function () { it('should run a query', function (done) { gettingStartedExample.main( function (err, rows) { - assert(!err); + assert.ifError(err); assert(Array.isArray(rows)); assert.equal(rows.length, 10); done(); diff --git a/bigquery/system-test/load_data_from_csv.test.js b/bigquery/system-test/load_data_from_csv.test.js index 1949f843a0..b30906582c 100644 --- a/bigquery/system-test/load_data_from_csv.test.js +++ b/bigquery/system-test/load_data_from_csv.test.js @@ -39,7 +39,7 @@ describe('bigquery:load_data_from_csv', function () { done(err); }); } else { - assert(!err); + assert.ifError(err); // metadata assert.equal(results[1].status.state, 'DONE'); done(); diff --git a/bigquery/system-test/load_data_from_gcs.test.js b/bigquery/system-test/load_data_from_gcs.test.js index 00452bae3c..ac6b0416db 100644 --- a/bigquery/system-test/load_data_from_gcs.test.js +++ b/bigquery/system-test/load_data_from_gcs.test.js @@ -40,7 +40,7 @@ describe('bigquery:load_data_from_gcs', function () { done(err); }); } else { - assert(!err); + assert.ifError(err); // metadata assert.equal(results[1].status.state, 'DONE'); done(); diff --git a/computeengine/system-test/vms.test.js b/computeengine/system-test/vms.test.js index 52f4b2b804..4d1888b163 100644 --- a/computeengine/system-test/vms.test.js +++ b/computeengine/system-test/vms.test.js @@ -18,7 +18,7 @@ var vmsExample = require('../vms'); describe('computeengine:vms', function () { it('should retrieve vms', function (done) { vmsExample.main(function (err, result) { - assert(!err); + assert.ifError(err); assert(result); assert(Array.isArray(result)); done(); diff --git a/computeengine/system-test/vms_api.test.js b/computeengine/system-test/vms_api.test.js index cf89dc85a5..d7749bb569 100644 --- a/computeengine/system-test/vms_api.test.js +++ b/computeengine/system-test/vms_api.test.js @@ -18,7 +18,7 @@ var vmsExample = require('../vms_api'); describe('computeengine:vms_api', function () { it('should retrieve vms', function (done) { vmsExample.main(function (err, result) { - assert(!err); + assert.ifError(err); assert(result); done(); }); diff --git a/datastore/system-test/concepts.test.js b/datastore/system-test/concepts.test.js index d5764df7b8..8bf310f674 100644 --- a/datastore/system-test/concepts.test.js +++ b/datastore/system-test/concepts.test.js @@ -131,7 +131,7 @@ describe('datastore:concepts', function () { it('performs a projection query', function (done) { entity.testProperties(function (err, tasks) { console.log(err, tasks); - assert(!err); + assert.ifError(err); setTimeout(function () { query.testRunQueryProjection(done); }, 1000); @@ -203,7 +203,7 @@ describe('datastore:concepts', function () { it.skip('allows manual pagination through results', function (done) { entity.testBatchUpsert(function (err) { - assert(!err); + assert.ifError(err); setTimeout(function () { query.testCursorPaging(done); }, 1000); diff --git a/datastore/system-test/tasks.test.js b/datastore/system-test/tasks.test.js index 33d660749b..f87da87449 100644 --- a/datastore/system-test/tasks.test.js +++ b/datastore/system-test/tasks.test.js @@ -32,7 +32,7 @@ describe('datastore:tasks', function () { it('should mark a task as done', function (done) { getTaskId(function (err, taskId) { - assert(!err); + assert.ifError(err); tasks.updateEntity(taskId, done); }); }); @@ -43,14 +43,14 @@ describe('datastore:tasks', function () { it('should delete a task', function (done) { getTaskId(function (err, taskId) { - assert(!err); + assert.ifError(err); tasks.deleteEntity(taskId, done); }); }); it('should format tasks', function (done) { tasks.retrieveEntities(function (err, _tasks) { - assert(!err); + assert.ifError(err); assert.doesNotThrow(function () { tasks.formatTasks(_tasks); }); diff --git a/language/system-test/analyze.test.js b/language/system-test/analyze.test.js index 16b500f989..813590fa35 100644 --- a/language/system-test/analyze.test.js +++ b/language/system-test/analyze.test.js @@ -21,7 +21,7 @@ describe('language:analyze', function () { 'sentiment', 'This gazinga pin is bad and it should feel bad', function (err, result) { - assert(!err); + assert.ifError(err); assert(result); assert(result.documentSentiment); assert(result.documentSentiment.polarity < 0); @@ -34,7 +34,7 @@ describe('language:analyze', function () { 'entities', 'Mark Twain is the author of a book called Tom Sawyer', function (err, result) { - assert(!err); + assert.ifError(err); assert(result); assert(result.entities && result.entities.length); assert(result.entities[0].name === 'Mark Twain'); @@ -51,7 +51,7 @@ describe('language:analyze', function () { 'buy some better butter - better than this bitter butter - it will ' + 'make my batter better."', function (err, result) { - assert(!err); + assert.ifError(err); assert(result); assert(result.sentences && result.sentences.length); assert(result.tokens && result.tokens.length > 5); diff --git a/logging/system-test/export.test.js b/logging/system-test/export.test.js index 222d18e90c..3d6b65d7bc 100644 --- a/logging/system-test/export.test.js +++ b/logging/system-test/export.test.js @@ -18,7 +18,7 @@ var exportExample = require('../export'); describe('logging:export', function () { it('should list sinks', function (done) { exportExample.main(function (err, sinks) { - assert(!err); + assert.ifError(err); assert(sinks, 'should have received sinks'); assert(Array.isArray(sinks), 'sinks should be an array'); done(); diff --git a/logging/system-test/list.test.js b/logging/system-test/list.test.js index 701bd0f4cb..907f020318 100644 --- a/logging/system-test/list.test.js +++ b/logging/system-test/list.test.js @@ -18,7 +18,7 @@ var listExample = require('../list'); describe('logging:list', function () { it('should list entries', function (done) { listExample.main(undefined, function (err, entries, nextQuery, apiResponse) { - assert(!err); + assert.ifError(err); assert(entries, 'should have received entries'); assert(Array.isArray(entries), 'entries should be an array'); assert(nextQuery, 'should have received nextQuery'); diff --git a/logging/system-test/write.test.js b/logging/system-test/write.test.js index fe5663b076..519cde8bac 100644 --- a/logging/system-test/write.test.js +++ b/logging/system-test/write.test.js @@ -21,7 +21,7 @@ describe('logging:write', function () { if (err && err.code === 404) { return done(); } - assert(!err); + assert.ifError(err); assert(results.length === 2, 'should have two results'); done(); }); diff --git a/monitoring/create_custom_metric.js b/monitoring/create_custom_metric.js index a2951b74de..68b80a572b 100644 --- a/monitoring/create_custom_metric.js +++ b/monitoring/create_custom_metric.js @@ -250,12 +250,12 @@ exports.main = function (projectId, name, cb) { function (cb) { setTimeout(function () { customMetrics.writeTimeSeriesForCustomMetric(authClient, cb); - }, 5000); + }, 10000); }, function (cb) { setTimeout(function () { customMetrics.listTimeSeries(authClient, cb); - }, 5000); + }, 10000); }, function (cb) { customMetrics.deleteMetric(authClient, cb); diff --git a/monitoring/system-test/create_custom_metric.test.js b/monitoring/system-test/create_custom_metric.test.js index 0e2cad29b4..5fcfbc64db 100644 --- a/monitoring/system-test/create_custom_metric.test.js +++ b/monitoring/system-test/create_custom_metric.test.js @@ -26,10 +26,7 @@ describe('monitoring:create_custom_metric', function () { process.env.GCLOUD_PROJECT, Math.random().toString(36).substring(7), function (err, results) { - assert(!err); - // console.log('---------------------------------------------'); - // console.log(JSON.stringify(results, null, 2)); - // console.log('---------------------------------------------'); + assert.ifError(err); assert(results.length === 4); // Result of creating metric assert(typeof results[0].name === 'string'); diff --git a/monitoring/system-test/list_resources.test.js b/monitoring/system-test/list_resources.test.js index 8660828a73..49e64ad97b 100644 --- a/monitoring/system-test/list_resources.test.js +++ b/monitoring/system-test/list_resources.test.js @@ -20,7 +20,7 @@ describe('monitoring:list_resources', function () { listResourcesExample.main( process.env.GCLOUD_PROJECT, function (err, results) { - assert(!err); + assert.ifError(err); assert(results.length === 3); // Monitored resources assert(Array.isArray(results[0].resourceDescriptors)); diff --git a/prediction/system-test/hostedmodels.test.js b/prediction/system-test/hostedmodels.test.js index 63b9a6d262..e4cad834a7 100644 --- a/prediction/system-test/hostedmodels.test.js +++ b/prediction/system-test/hostedmodels.test.js @@ -18,7 +18,7 @@ var hostedmodels = require('../../prediction/hostedmodels'); describe('prediction:hostedmodels', function () { it('should predict', function (done) { hostedmodels.main('good night', function (err, result) { - assert(!err); + assert.ifError(err); assert(result); assert(console.log.calledWith('Sentiment for "good night": positive')); done(); diff --git a/pubsub/README.md b/pubsub/README.md index a5a6f5778f..05b0f80373 100644 --- a/pubsub/README.md +++ b/pubsub/README.md @@ -11,7 +11,8 @@ allows you to send and receive messages between independent applications. * [Setup](#setup) * [Samples](#samples) - * [Subscription](#subscription) + * [Topics](#topics) + * [Subscriptions](#subscriptions) * [IAM](#iam) ## Setup @@ -26,24 +27,90 @@ allows you to send and receive messages between independent applications. ## Samples -### Subscription +### Topics -View the [documentation][subscription_docs] or the [source code][subscription_code]. +View the [documentation][topics_docs] or the [source code][topics_code]. -__Run the sample:__ +__Usage:__ `node topics --help` - node subscription +``` +Usage: node topics COMMAND [ARGS...] -[subscription_docs]: https://cloud.google.com/pubsub/subscriber -[subscription_code]: subscription.js +Commands: + + create TOPIC_NAME + delete TOPIC_NAME + publish TOPIC_NAME MESSAGE + list + +Examples: + + node topics create my-topic + node topics list + node topics publish my-topic '{"data":"Hello world!"}' + node topics delete my-topic +``` + +[topics_docs]: https://cloud.google.com/pubsub/publisher +[topics_code]: topics.js + +### Subscriptions + +View the [documentation][subscriptions_docs] or the [source code][subscriptions_code]. + +__Usage:__ `node subscriptions --help` + +``` +Usage: node subscriptions COMMAND [ARGS...] + +Commands: + + create TOPIC_NAME SUBSCRIPTION_NAME + delete SUBSCRIPTION_NAME + pull SUBSCRIPTION_NAME + list [TOPIC_NAME] + +Examples: + + node subscriptions create my-topic my-subscription + node subscriptions delete my-subscription + node subscriptions pull my-subscription + node subscriptions list + node subscriptions list my-topic +``` + +[subscriptions_docs]: https://cloud.google.com/pubsub/subscriber +[subscriptions_code]: subscriptions.js ### IAM View the [documentation][iam_docs] or the [source code][iam_code]. -__Run the sample:__ +__Usage:__ `node iam --help` + +``` +Usage: node iam RESOURCE COMMAND [ARGS...] + +Resources: + + topics + subscriptions + +Commands: + + get NAME + set NAME + test NAME + +Examples: - node iam + node iam topics get my-topic + node iam topics set my-topic + node iam topics test my-topic + node iam subscriptions get my-subscription + node iam subscriptions set my-subscription + node iam subscriptions test my-subscription +``` [iam_docs]: https://cloud.google.com/pubsub/access_control -[iam_code]: subscription.js +[iam_code]: iam.js diff --git a/pubsub/iam.js b/pubsub/iam.js index 6111702137..752d28c129 100644 --- a/pubsub/iam.js +++ b/pubsub/iam.js @@ -13,69 +13,88 @@ 'use strict'; -var async = require('async'); -var subscriptionSample = require('./subscription'); -var createTopicExample = subscriptionSample.createTopicExample; -var deleteTopicExample = subscriptionSample.deleteTopicExample; -var subscribeExample = subscriptionSample.subscribeExample; -var deleteSubscriptionExample = subscriptionSample.deleteSubscriptionExample; -var pubsub = subscriptionSample.pubsub; +// [START auth] +// By default, gcloud will authenticate using the service account file specified +// by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use the +// project specified by the GCLOUD_PROJECT environment variable. See +// https://googlecloudplatform.github.io/gcloud-node/#/docs/guides/authentication +var gcloud = require('gcloud'); + +// Instantiate a pubsub client +var pubsub = gcloud.pubsub(); +// [END auth] // [START get_topic_policy] /** - * @param {string} topicName Name of the topic whose policy is to be retrieved. - * @param {Function} callback Callback function. + * Retrieve a topic's IAM policy. + * + * @param {string} topicName The name of the topic. + * @param {function} callback The callback function. */ -function getTopicPolicyExample (topicName, callback) { +function getTopicPolicy (topicName, callback) { + if (!topicName) { + return callback(new Error('"topicName" is required!')); + } + // Grab a reference to an existing topic var topic = pubsub.topic(topicName); // Retrieve the IAM policy for the topic - topic.iam.getPolicy(function (err, policy, apiResponse) { + topic.iam.getPolicy(function (err, policy) { if (err) { return callback(err); } - // Received the policy - console.log(policy); // { etag: 'ACAB' } - callback(null, policy, apiResponse); + console.log('Got topic policy:', policy); + return callback(null, policy); }); } // [END get_topic_policy] // [START get_subscription_policy] /** - * @param {string} subscriptionName Name of the subscription whose policy is to - * be retrieved. - * @param {Function} callback Callback function. + * Retrieve a subcription's IAM policy. + * + * @param {string} subscriptionName The name of the subscription. + * @param {function} callback The callback function. */ -function getSubscriptionPolicyExample (subscriptionName, callback) { +function getSubscriptionPolicy (subscriptionName, callback) { + if (!subscriptionName) { + return callback(new Error('"subscriptionName" is required!')); + } + // Grab a reference to an existing subscription var subscription = pubsub.subscription(subscriptionName); // Retrieve the IAM policy for the subscription - subscription.iam.getPolicy(function (err, policy, apiResponse) { + subscription.iam.getPolicy(function (err, policy) { if (err) { return callback(err); } - console.log(policy); // { etag: 'ACAB' } - callback(null, policy, apiResponse); + console.log('Got subscription policy:', policy); + return callback(null, policy); }); } // [END get_subscription_policy] // [START set_topic_policy] /** - * @param {string} topicName Name of the topic whose policy is to be updated. - * @param {Function} callback Callback function. + * Set a topic's IAM policy. + * + * @param {string} topicName The name of the topic. + * @param {function} callback The callback function. */ -function setTopicPolicyExample (topicName, callback) { +function setTopicPolicy (topicName, callback) { + if (!topicName) { + return callback(new Error('"topicName" is required!')); + } + // Grab a reference to an existing topic var topic = pubsub.topic(topicName); // Policy update - var myPolicy = { + var newPolicy = { bindings: [ { role: 'roles/pubsub.publisher', @@ -84,14 +103,14 @@ function setTopicPolicyExample (topicName, callback) { ] }; - // Retrieve the IAM policy for the provided topic - topic.iam.setPolicy(myPolicy, function (err, policy, apiResponse) { + // Set the IAM policy for the specified topic + topic.iam.setPolicy(newPolicy, function (err, policy) { if (err) { return callback(err); } - console.log('Updated policy for ' + topicName); - callback(null, policy, apiResponse); + console.log('Updated policy for topic: %s', topicName); + return callback(null, policy); }); } // [END set_topic_policy] @@ -100,14 +119,18 @@ function setTopicPolicyExample (topicName, callback) { /** * @param {string} subscriptionName Name of the subscription whose policy is to * be updated. - * @param {Function} callback Callback function. + * @param {function} callback The callback function. */ -function setSubscriptionPolicyExample (subscriptionName, callback) { +function setSubscriptionPolicy (subscriptionName, callback) { + if (!subscriptionName) { + return callback(new Error('"subscriptionName" is required!')); + } + // Grab a reference to an existing subscription var subscription = pubsub.subscription(subscriptionName); // Policy update - var myPolicy = { + var newPolicy = { bindings: [ { role: 'roles/pubsub.subscriber', @@ -116,114 +139,145 @@ function setSubscriptionPolicyExample (subscriptionName, callback) { ] }; - // Retrieve the IAM policy for the provided subscription - subscription.iam.setPolicy(myPolicy, function (err, policy, apiResponse) { + // Set the IAM policy for the specified subscription + subscription.iam.setPolicy(newPolicy, function (err, policy) { if (err) { return callback(err); } - console.log('Updated policy for ' + subscriptionName); - callback(null, policy, apiResponse); + console.log('Updated policy for subscription: %s', subscriptionName); + return callback(null, policy); }); } // [END set_subscription_policy] // [START test_topic_permissions] /** - * @param {string} topicName Name of the topic whose policy is to be tested. - * @param {Function} callback Callback function. + * Test a topic's IAM permissions. + * + * @param {string} topicName The name of the topic. + * @param {function} callback The callback function. */ -function testTopicPermissionsExample (topicName, callback) { +function testTopicPermissions (topicName, callback) { + if (!topicName) { + return callback(new Error('"topicName" is required!')); + } + // Grab a reference to an existing topic var topic = pubsub.topic(topicName); - var tests = [ + var permissionsToTest = [ 'pubsub.topics.attachSubscription', 'pubsub.topics.publish', 'pubsub.topics.update' ]; - // Retrieve the IAM policy for the provided topic - topic.iam.testPermissions(tests, function (err, permissions, apiResponse) { + // Test the IAM policy for the specified topic + topic.iam.testPermissions(permissionsToTest, function (err, permissions) { if (err) { return callback(err); } - console.log('Got permissions for ' + topicName); - callback(null, permissions, apiResponse); + console.log('Tested permissions for topic: %s', topicName); + return callback(null, permissions); }); } // [END test_topic_permissions] // [START test_subscription_permissions] /** - * @param {string} subscriptionName Name of the subscription whose policy is to - * be tested. - * @param {Function} callback Callback function. + * Test a subcription's IAM permissions. + * + * @param {string} subscriptionName The name of the subscription. + * @param {function} callback The callback function. */ -function testSubscriptionPermissionsExample (subscriptionName, callback) { +function testSubscriptionPermissions (subscriptionName, callback) { + if (!subscriptionName) { + return callback(new Error('"subscriptionName" is required!')); + } + // Grab a reference to an existing subscription var subscription = pubsub.subscription(subscriptionName); - var tests = [ + var permissionsToTest = [ 'pubsub.subscriptions.consume', 'pubsub.subscriptions.update' ]; - // Retrieve the IAM policy for the provided subscription - subscription.iam.testPermissions( - tests, - function (err, permissions, apiResponse) { - if (err) { - return callback(err); - } - - console.log('Got permissions for ' + subscriptionName); - callback(null, permissions, apiResponse); + // Test the IAM policy for the specified subscription + subscription.iam.testPermissions(permissionsToTest, function (err, permissions) { + if (err) { + return callback(err); } - ); + + console.log('Tested permissions for subscription: %s', subscriptionName); + return callback(null, permissions); + }); } // [END test_subscription_permissions] -exports.getTopicPolicyExample = getTopicPolicyExample; -exports.getSubscriptionPolicyExample = getSubscriptionPolicyExample; -exports.setTopicPolicyExample = setTopicPolicyExample; -exports.setSubscriptionPolicyExample = setSubscriptionPolicyExample; -exports.testTopicPermissionsExample = testTopicPermissionsExample; -exports.testSubscriptionPermissionsExample = testSubscriptionPermissionsExample; - -// Run the examples -exports.main = function (cb) { - var topicName = 'messageCenter2'; - var subscriptionName = 'newMessages2'; - async.series([ - function (cb) { - createTopicExample(topicName, cb); - }, - function (cb) { - getTopicPolicyExample(topicName, cb); - }, - function (cb) { - testTopicPermissionsExample(topicName, cb); - }, - function (cb) { - subscribeExample(topicName, subscriptionName, cb); - }, - function (cb) { - getSubscriptionPolicyExample(subscriptionName, cb); - }, - function (cb) { - testSubscriptionPermissionsExample(subscriptionName, cb); - }, - function (cb) { - deleteSubscriptionExample(subscriptionName, cb); - }, - function (cb) { - deleteTopicExample(topicName, cb); +// [START usage] +function printUsage () { + console.log('Usage: node iam RESOURCE COMMAND [ARGS...]'); + console.log('\nResources:\n'); + console.log('\ttopics'); + console.log('\tsubscriptions'); + console.log('\nCommands:\n'); + console.log('\tget NAME'); + console.log('\tset NAME'); + console.log('\ttest NAME'); + console.log('\nExamples:\n'); + console.log('\tnode iam topics get my-topic'); + console.log('\tnode iam topics set my-topic'); + console.log('\tnode iam topics test my-topic'); + console.log('\tnode iam subscriptions get my-subscription'); + console.log('\tnode iam subscriptions set my-subscription'); + console.log('\tnode iam subscriptions test my-subscription'); +} +// [END usage] + +// The command-line program +var program = { + getTopicPolicy: getTopicPolicy, + getSubscriptionPolicy: getSubscriptionPolicy, + setTopicPolicy: setTopicPolicy, + setSubscriptionPolicy: setSubscriptionPolicy, + testTopicPermissions: testTopicPermissions, + testSubscriptionPermissions: testSubscriptionPermissions, + printUsage: printUsage, + + // Executed when this program is run from the command-line + main: function (args, cb) { + var resource = args.shift(); + var command = args.shift(); + if (resource === 'topics') { + if (command === 'get') { + this.getTopicPolicy(args[0], cb); + } else if (command === 'set') { + this.setTopicPolicy(args[0], cb); + } else if (command === 'test') { + this.testTopicPermissions(args[0], cb); + } else { + this.printUsage(); + } + } else if (resource === 'subscriptions') { + if (command === 'get') { + this.getSubscriptionPolicy(args[0], cb); + } else if (command === 'set') { + this.setSubscriptionPolicy(args[0], cb); + } else if (command === 'test') { + this.testSubscriptionPermissions(args[0], cb); + } else { + this.printUsage(); + } + } else { + this.printUsage(); } - ], cb || console.log); + } }; if (module === require.main) { - exports.main(); + program.main(process.argv.slice(2), console.log); } + +module.exports = program; diff --git a/pubsub/package.json b/pubsub/package.json index abb35d2ab3..b9c1983be4 100644 --- a/pubsub/package.json +++ b/pubsub/package.json @@ -12,6 +12,7 @@ "gcloud": "^0.37.0" }, "devDependencies": { - "mocha": "^2.5.3" + "mocha": "^3.0.2", + "node-uuid": "^1.4.7" } } diff --git a/pubsub/subscription.js b/pubsub/subscription.js deleted file mode 100644 index bd5e4e6da2..0000000000 --- a/pubsub/subscription.js +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2016, Google, Inc. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -'use strict'; - -var async = require('async'); - -// [START auth] -// By default, gcloud will authenticate using the service account file specified -// by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use the -// project specified by the GCLOUD_PROJECT environment variable. See -// https://googlecloudplatform.github.io/gcloud-node/#/docs/guides/authentication -var gcloud = require('gcloud'); - -// Get a reference to the pubsub component -var pubsub = gcloud.pubsub(); -// [END auth] - -// [START create_topic] -/** - * @param {string} topicName Name for the new topic. - * @param {Function} callback Callback function. - */ -function createTopicExample (topicName, callback) { - var topic = pubsub.topic(topicName); - - // Get the topic if it exists. Create it if it does not exist. - topic.get({ - autoCreate: true - }, function (err, topic, apiResponse) { - if (err) { - return callback(err); - } - - // Created the topic - console.log('Created topic ' + topicName); - callback(null, topic, apiResponse); - }); -} -// [END create_topic] - -// [START delete_topic] -/** - * @param {string} topicName Name of the topic to delete. - * @param {Function} callback Callback function. - */ -function deleteTopicExample (topicName, callback) { - var topic = pubsub.topic(topicName); - - // Delete the topic - topic.delete(function (err, apiResponse) { - if (err) { - return callback(err); - } - - // Deleted the topic - console.log('Deleted topic ' + topicName); - callback(null, apiResponse); - }); -} -// [END delete_topic] - -// [START delete_subscription] -/** - * @param {string} subscriptionName Name of the subscription to delete. - * @param {Function} callback Callback function. - */ -function deleteSubscriptionExample (subscriptionName, callback) { - var subscription = pubsub.subscription(subscriptionName); - - // Delete the subscription - subscription.delete(function (err, apiResponse) { - if (err) { - return callback(err); - } - - // Deleted the subscription - console.log('Deleted subscription ' + subscriptionName); - callback(null, apiResponse); - }); -} -// [END delete_subscription] - -// [START publish] -/** - * @param {string} topicName Name of the topic to which to publish. - * @param {Function} callback Callback function. - */ -function publishExample (topicName, callback) { - // Grab a reference to an existing topic - var topic = pubsub.topic(topicName); - - // Publish a message to the topic - topic.publish({ - data: 'Hello, world!' - }, function (err, messageIds, apiResponse) { - if (err) { - return callback(err); - } - - console.log('Published ' + messageIds.length + ' messages'); - callback(null, messageIds, apiResponse); - }); -} -// [END publish] - -// [START list_topics] -/** - * @param {string} [pageToken] Page to retrieve. - * @param {Function} callback Callback function. - */ -function getAllTopicsExample (pageToken, callback) { - if (typeof pageToken === 'function') { - callback = pageToken; - pageToken = undefined; - } - var options = {}; - if (pageToken) { - options.pageToken = pageToken; - } - - // Grab paginated topics - pubsub.getTopics(options, function (err, topics, nextQuery) { - // Quit on error - if (err) { - return callback(err); - } - - // There is another page of topics - if (nextQuery) { - // Grab the remaining pages of topics recursively - return getAllTopicsExample(nextQuery.token, function (err, _topics) { - if (err) { - return callback(err); - } - if (_topics) { - topics = topics.concat(_topics); - } - callback(null, topics); - }); - } - // Last page of topics - return callback(null, topics); - }); -} -// [END list_topics] - -// [START get_all_subscriptions] -/** - * @param {string} [pageToken] Page to retrieve. - * @param {Function} callback Callback function. - */ -function getAllSubscriptionsExample (pageToken, callback) { - if (typeof pageToken === 'function') { - callback = pageToken; - pageToken = undefined; - } - var options = {}; - if (pageToken) { - options.pageToken = pageToken; - } - // Grab paginated subscriptions - pubsub.getSubscriptions( - options, - function (err, subscriptions, nextQuery) { - // Quit on error - if (err) { - return callback(err); - } - - // There is another page of subscriptions - if (nextQuery) { - // Grab the remaining pages of subscriptions recursively - return getAllSubscriptionsExample( - nextQuery.token, - function (err, _subscriptions) { - if (err) { - return callback(err); - } - if (_subscriptions) { - subscriptions = subscriptions.concat(_subscriptions); - } - callback(null, subscriptions); - } - ); - } - // Last page of subscriptions - return callback(null, subscriptions); - } - ); -} -// [END get_all_subscriptions] - -// [START create_subscription] -/** - * @param {string} topicName Name of the topic for the new subscription. - * @param {string} subscriptionName Name for the new subscription. - * @param {Function} callback Callback function. - */ -function subscribeExample (topicName, subscriptionName, callback) { - var options = { - reuseExisting: true - }; - pubsub.subscribe( - topicName, - subscriptionName, - options, - function (err, subscription, apiResponse) { - if (err) { - return callback(err); - } - - // Got the subscription - console.log('Subscribed to ' + topicName); - callback(null, subscription, apiResponse); - } - ); -} -// [END create_subscription] - -// [START handle_message] -function handleMessageExample (message) { - console.log('received message: ' + message.data); -} -// [END handle_message] - -// [START pull_messages] -/** - * - */ -function pullMessagesExample (topicName, subscriptionName, callback) { - // Use the "async" library to handle a chain of asynchronous functions - async.waterfall([ - function (cb) { - // Create a topic - createTopicExample(topicName, cb); - }, - function (topic, apiResponse, cb) { - // Create a subscription - subscribeExample(topicName, subscriptionName, cb); - }, - function (subscription, apiResponse, cb) { - var options = { - // Limit the amount of messages pulled. - maxResults: 100, - // If set, the system will respond immediately. Otherwise, wait until - // new messages are available. Returns if timeout is reached. - returnImmediately: false - }; - // Pull any messages on the subscription - subscription.pull(options, cb); - }, - function (messages, apiResponse, cb) { - // Do something for each message - messages.forEach(handleMessageExample); - - // Acknowledge messages - var subscription = pubsub.subscription(subscriptionName); - subscription.ack(messages.map(function (message) { - return message.ackId; - }), function (err) { - if (err) { - return cb(err); - } - cb(null, messages); - }); - } - ], function (err, messages) { - if (err) { - return callback(err); - } - - console.log('Pulled ' + messages.length + ' messages'); - callback(null, messages); - }); -} -// [END pull_messages] - -exports.createTopicExample = createTopicExample; -exports.deleteTopicExample = deleteTopicExample; -exports.subscribeExample = subscribeExample; -exports.deleteSubscriptionExample = deleteSubscriptionExample; -exports.publishExample = publishExample; -exports.getAllTopicsExample = getAllTopicsExample; -exports.getAllSubscriptionsExample = getAllSubscriptionsExample; -exports.subscribeExample = subscribeExample; -exports.pubsub = pubsub; -exports.main = function (cb) { - var topicName = 'messageCenter'; - var subscriptionName = 'newMessages'; - async.series([ - function (cb) { - createTopicExample(topicName, cb); - }, - function (cb) { - subscribeExample(topicName, subscriptionName, cb); - }, - function (cb) { - getAllTopicsExample(cb); - }, - function (cb) { - getAllSubscriptionsExample(cb); - }, - function (cb) { - publishExample(topicName, cb); - }, - function (cb) { - pullMessagesExample(topicName, subscriptionName, cb); - }, - function (cb) { - deleteSubscriptionExample(subscriptionName, cb); - }, - function (cb) { - deleteTopicExample(topicName, cb); - } - ], cb || console.log); -}; - -if (module === require.main) { - exports.main(); -} diff --git a/pubsub/subscriptions.js b/pubsub/subscriptions.js new file mode 100644 index 0000000000..6cea5910dc --- /dev/null +++ b/pubsub/subscriptions.js @@ -0,0 +1,206 @@ +// Copyright 2016, Google, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START auth] +// By default, gcloud will authenticate using the service account file specified +// by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use the +// project specified by the GCLOUD_PROJECT environment variable. See +// https://googlecloudplatform.github.io/gcloud-node/#/docs/guides/authentication +var gcloud = require('gcloud'); + +// Instantiate a pubsub client +var pubsub = gcloud.pubsub(); +// [END auth] + +// [START create_subscription] +/** + * Create a new subscription. + * + * @param {string} topicName Name of the topic for the new subscription. + * @param {string} subscriptionName Name for the new subscription. + * @param {Function} callback Callback function. + */ +function createSubscription (topicName, subscriptionName, callback) { + if (!topicName) { + return callback(new Error('"topicName" is required!')); + } else if (!subscriptionName) { + return callback(new Error('"subscriptionName" is required!')); + } + + var options = { + reuseExisting: true + }; + pubsub.subscribe(topicName, subscriptionName, options, function (err, subscription) { + if (err) { + return callback(err); + } + + console.log('Created subscription %s to topic %s', subscriptionName, topicName); + return callback(null, subscription); + }); +} +// [END create_subscription] + +// [START delete_subscription] +/** + * Delete a subscription. + * + * @param {string} subscriptionName Name of the subscription to delete. + * @param {Function} callback The callback function. + */ +function deleteSubscription (subscriptionName, callback) { + if (!subscriptionName) { + return callback(new Error('"subscriptionName" is required!')); + } + + var subscription = pubsub.subscription(subscriptionName); + + // Delete the subscription + subscription.delete(function (err) { + if (err) { + return callback(err); + } + + console.log('Deleted subscription: %s', subscriptionName); + return callback(null); + }); +} +// [END delete_subscription] + +// [START get_all_subscriptions] +/** + * List all subscriptions for the specified topic, or list all subcriptions for + * all topics. + * + * @param {string} [topicName] The name of the topic. If omitted, list all + * subscriptions. + * @param {Function} callback The callback function. + */ +function listSubscriptions (topicName, callback) { + var options = {}; + if (typeof topicName === 'string') { + // Optionally find subscriptions for a specific topic + options.topic = pubsub.topic(topicName); + } + pubsub.getSubscriptions(options, function (err, subscriptions) { + if (err) { + return callback(err); + } + + console.log('Found %d subscriptions!', subscriptions.length); + return callback(null, subscriptions); + }); +} +// [END get_all_subscriptions] + +// [START handle_message] +function handleMessage (message) { + console.log('received message: ' + message.data); +} +// [END handle_message] + +// [START pull_messages] +/** + * Pull messages from a topic's subscription. + * + * @param {string} subscriptionName The name of the subscription. + * @param {function} callback The callback function. + */ +function pullMessages (subscriptionName, callback) { + if (!subscriptionName) { + return callback(new Error('"subscriptionName" is required!')); + } + + var subscription = pubsub.subscription(subscriptionName); + var options = { + // Limit the amount of messages pulled. + maxResults: 100, + // If set, the system will respond immediately. Otherwise, wait until + // new messages are available. Returns if timeout is reached. + returnImmediately: false + }; + // Pull any messages on the subscription + subscription.pull(options, function (err, messages) { + if (err) { + return callback(err); + } + // Do something for each message + messages.forEach(handleMessage); + + console.log('Pulled %d messages!', messages.length); + + // Acknowledge messages + var subscription = pubsub.subscription(subscriptionName); + subscription.ack(messages.map(function (message) { + return message.ackId; + }), function (err) { + if (err) { + return callback(err); + } + + console.log('Acked %d messages!', messages.length); + return callback(null, messages); + }); + }); +} +// [END pull_messages] + +// [START usage] +function printUsage () { + console.log('Usage: node subscriptions COMMAND [ARGS...]'); + console.log('\nCommands:\n'); + console.log('\tcreate TOPIC_NAME SUBSCRIPTION_NAME'); + console.log('\tdelete SUBSCRIPTION_NAME'); + console.log('\tpull SUBSCRIPTION_NAME'); + console.log('\tlist [TOPIC_NAME]'); + console.log('\nExamples:\n'); + console.log('\tnode subscriptions create my-topic my-subscription'); + console.log('\tnode subscriptions delete my-subscription'); + console.log('\tnode subscriptions pull my-subscription'); + console.log('\tnode subscriptions list'); + console.log('\tnode subscriptions list my-topic'); +} +// [END usage] + +// The command-line program +var program = { + create: createSubscription, + delete: deleteSubscription, + pull: pullMessages, + list: listSubscriptions, + printUsage: printUsage, + + // Executed when this program is run from the command-line + main: function (args, cb) { + var command = args.shift(); + if (command === 'create') { + this.create(args[0], args[1], cb); + } else if (command === 'delete') { + this.delete(args[0], cb); + } else if (command === 'pull') { + this.pull(args[0], cb); + } else if (command === 'list') { + this.list(args[0], cb); + } else { + this.printUsage(); + } + } +}; + +if (module === require.main) { + program.main(process.argv.slice(2), console.log); +} + +module.exports = program; diff --git a/pubsub/system-test/iam.test.js b/pubsub/system-test/iam.test.js index aee3de942f..e99f448d7f 100644 --- a/pubsub/system-test/iam.test.js +++ b/pubsub/system-test/iam.test.js @@ -1,4 +1,4 @@ -// Copyright 2016, Google, Inc. +// Copyright 2015-2016, Google, Inc. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -13,36 +13,75 @@ 'use strict'; -var proxyquire = require('proxyquire'); +var uuid = require('node-uuid'); +var gcloud = require('gcloud'); +var pubsub = gcloud.pubsub(); +var program = require('../iam'); +var topicName = 'nodejs-docs-samples-test-' + uuid.v4(); +var subscriptionName = 'nodejs-docs-samples-test-sub-' + uuid.v4(); describe('pubsub:iam', function () { - it('should run the sample', function (done) { - proxyquire('../iam', {}).main(function (err, results) { - assert.ifError(err); - assert(results.length === 8); - // Got topic and apiResponse - assert(results[0].length === 2); - // Got policy and apiResponse - assert(results[1].length === 2); - // Got permissions and apiResponse - assert(results[2].length === 2); - // Got subscription and apiResponse - assert(results[3].length === 2); - // Got policy and apiResponse - assert(results[4].length === 2); - // Got permissions and apiResponse - assert(results[5].length === 2); - // Got empty apiResponse - assert.deepEqual(results[6], {}); - // Got empty apiResponse - assert.deepEqual(results[7], {}); - assert(console.log.calledWith('Created topic messageCenter2')); - assert(console.log.calledWith('Got permissions for messageCenter2')); - assert(console.log.calledWith('Subscribed to messageCenter2')); - assert(console.log.calledWith('Got permissions for newMessages2')); - assert(console.log.calledWith('Deleted subscription newMessages2')); - assert(console.log.calledWith('Deleted topic messageCenter2')); - done(); + before(function (done) { + pubsub.topic(topicName).get({ + autoCreate: true + }, function (err) { + if (err) { + return done(err); + } + var options = { + reuseExisting: true + }; + pubsub.subscribe(topicName, subscriptionName, options, done); + }); + }); + + after(function (done) { + pubsub.subscription(subscriptionName).delete(function () { + pubsub.topic(topicName).delete(done); + }); + }); + + describe('getTopicPolicy', function () { + it('should get a topic\'s policy', function (done) { + program.getTopicPolicy(topicName, function (err, policy) { + assert.ifError(err); + assert(policy); + assert(console.log.calledWith('Got topic policy:', policy)); + done(); + }); + }); + }); + + describe('getSubscriptionPolicy', function () { + it('should get a subscriptions\'s policy', function (done) { + program.getSubscriptionPolicy(subscriptionName, function (err, policy) { + assert.ifError(err); + assert(policy); + assert(console.log.calledWith('Got subscription policy:', policy)); + done(); + }); + }); + }); + + describe('testTopicPermissions', function () { + it('should test a topic\'s permissions', function (done) { + program.testTopicPermissions(topicName, function (err, permissions) { + assert.ifError(err); + assert(permissions); + assert(console.log.calledWith('Tested permissions for topic: %s', topicName)); + done(); + }); + }); + }); + + describe('testSubscriptionPermissions', function () { + it('should test a subscriptions\'s permissions', function (done) { + program.testSubscriptionPermissions(subscriptionName, function (err, permissions) { + assert.ifError(err); + assert(permissions); + assert(console.log.calledWith('Tested permissions for subscription: %s', subscriptionName)); + done(); + }); }); }); }); diff --git a/pubsub/system-test/subscription.test.js b/pubsub/system-test/subscription.test.js deleted file mode 100644 index 0531ccf80b..0000000000 --- a/pubsub/system-test/subscription.test.js +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2016, Google, Inc. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -'use strict'; - -var proxyquire = require('proxyquire'); - -describe('pubsub:subscription', function () { - it('should run the sample', function (done) { - proxyquire('../subscription', {}).main(function (err, results) { - assert.ifError(err); - assert(results.length === 8); - // Got topic and apiResponse - assert(results[0].length === 2); - // Got subscription and apiResponse - assert(results[1].length === 2); - // Got array of topics - assert(Array.isArray(results[2])); - // Got array of subscriptions - assert(Array.isArray(results[3])); - // Got messageIds and apiResponse - assert(results[4].length === 2); - // Got array of messages - assert(Array.isArray(results[5])); - // Got empty apiResponse - assert.deepEqual(results[6], {}); - // Got empty apiResponse - assert.deepEqual(results[7], {}); - assert(console.log.calledWith('Created topic messageCenter')); - assert(console.log.calledWith('Subscribed to messageCenter')); - assert(console.log.calledWith('Published 1 messages')); - assert(console.log.calledWith('received message: Hello, world!')); - assert(console.log.calledWith('Pulled 1 messages')); - assert(console.log.calledWith('Deleted subscription newMessages')); - assert(console.log.calledWith('Deleted topic messageCenter')); - done(); - }); - }); -}); diff --git a/pubsub/system-test/subscriptions.test.js b/pubsub/system-test/subscriptions.test.js new file mode 100644 index 0000000000..7f601b2b7c --- /dev/null +++ b/pubsub/system-test/subscriptions.test.js @@ -0,0 +1,103 @@ +// Copyright 2015-2016, Google, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +var uuid = require('node-uuid'); +var gcloud = require('gcloud'); +var pubsub = gcloud.pubsub(); +var program = require('../subscriptions'); +var topicName = 'nodejs-docs-samples-test-' + uuid.v4(); +var subscriptionName = 'nodejs-docs-samples-test-sub-' + uuid.v4(); +var projectId = process.env.GCLOUD_PROJECT; +var name = 'projects/' + projectId + '/subscriptions/' + subscriptionName; + +describe('pubsub:subscriptions', function () { + before(function (done) { + pubsub.topic(topicName).get({ + autoCreate: true + }, done); + }); + + after(function (done) { + pubsub.topic(topicName).delete(done); + }); + + describe('create', function () { + it('should create a subscription', function (done) { + program.create(topicName, subscriptionName, function (err, subscription) { + assert.ifError(err); + assert.equal(subscription.name, name); + assert(console.log.calledWith('Created subscription %s to topic %s', subscriptionName, topicName)); + done(); + }); + }); + }); + + describe('list', function () { + it('should list subscriptions', function (done) { + program.list(topicName, function (err, subscriptions) { + assert.ifError(err); + assert(Array.isArray(subscriptions)); + assert(subscriptions.length > 0); + var recentlyCreatedSubscriptions = subscriptions.filter(function (subscription) { + return subscription.name === name; + }); + assert.equal(recentlyCreatedSubscriptions.length, 1, 'list has newly created subscription'); + assert(console.log.calledWith('Found %d subscriptions!', subscriptions.length)); + + program.list(undefined, function (err, allSubscriptions) { + assert.ifError(err); + assert(Array.isArray(allSubscriptions)); + assert(allSubscriptions.length > 0); + var recentlyCreatedAllSubscriptions = allSubscriptions.filter(function (subscription) { + return subscription.name === name; + }); + assert.equal(recentlyCreatedAllSubscriptions.length, 1, 'list has newly created subscription'); + assert(console.log.calledWith('Found %d subscriptions!', allSubscriptions.length)); + done(); + }); + }); + }); + }); + + describe('pull', function () { + var expected = 'Hello World!'; + + before(function (done) { + pubsub.topic(topicName).publish({ data: expected }, done); + }); + + it('should pull messages', function (done) { + program.pull(subscriptionName, function (err, messages) { + assert.ifError(err); + assert(Array.isArray(messages)); + assert(messages.length > 0); + assert(console.log.calledWith('Pulled %d messages!', messages.length)); + assert(console.log.calledWith('Acked %d messages!', messages.length)); + assert.equal(messages[0].data, expected); + done(); + }); + }); + }); + + describe('delete', function () { + it('should delete a subscription', function (done) { + program.delete(subscriptionName, function (err) { + assert.ifError(err); + assert(console.log.calledWith('Deleted subscription: %s', subscriptionName)); + done(); + }); + }); + }); +}); diff --git a/pubsub/system-test/topics.test.js b/pubsub/system-test/topics.test.js new file mode 100644 index 0000000000..6cfd9c8b36 --- /dev/null +++ b/pubsub/system-test/topics.test.js @@ -0,0 +1,72 @@ +// Copyright 2015-2016, Google, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +var uuid = require('node-uuid'); +var program = require('../topics'); +var topicName = 'nodejs-docs-samples-test-' + uuid.v4(); +var projectId = process.env.GCLOUD_PROJECT; +var name = 'projects/' + projectId + '/topics/' + topicName; + +describe('pubsub:topics', function () { + describe('create', function () { + it('should create a topic', function (done) { + program.create(topicName, function (err, topic) { + assert.ifError(err); + assert.equal(topic.name, name); + assert(console.log.calledWith('Created topic: %s', topicName)); + done(); + }); + }); + }); + + describe('list', function () { + it('should list topics', function (done) { + program.list(function (err, topics) { + assert.ifError(err); + assert(Array.isArray(topics)); + assert(topics.length > 0); + var recentlyCreatedTopics = topics.filter(function (topic) { + return topic.name === name; + }); + assert.equal(recentlyCreatedTopics.length, 1, 'list has newly created topic'); + assert(console.log.calledWith('Found %d topics!', topics.length)); + done(); + }); + }); + }); + + describe('publish', function () { + it('should publish a message', function (done) { + var json = '{"data":"Hello World!"}'; + program.publish(topicName, json, function (err, messageIds) { + assert.ifError(err); + assert(Array.isArray(messageIds)); + assert(messageIds.length > 0); + assert(console.log.calledWith('Published %d messages!', messageIds.length)); + done(); + }); + }); + }); + + describe('delete', function () { + it('should delete a topic', function (done) { + program.delete(topicName, function (err) { + assert.ifError(err); + assert(console.log.calledWith('Deleted topic: %s', topicName)); + done(); + }); + }); + }); +}); diff --git a/pubsub/test/iam.test.js b/pubsub/test/iam.test.js index 63d91f4461..4047db9e96 100644 --- a/pubsub/test/iam.test.js +++ b/pubsub/test/iam.test.js @@ -14,159 +14,278 @@ 'use strict'; var proxyquire = require('proxyquire').noCallThru(); +var topicName = 'foo'; +var subscriptionName = 'bar'; +var policyMock = 'policy'; +var permissionsMock = 'permissions'; + +function getSample () { + var subscriptionMock = { + iam: { + getPolicy: sinon.stub().callsArgWith(0, null, policyMock), + setPolicy: sinon.stub().callsArgWith(1, null, policyMock), + testPermissions: sinon.stub().callsArgWith(1, null, permissionsMock) + } + }; + var topicMock = { + iam: { + getPolicy: sinon.stub().callsArgWith(0, null, policyMock), + setPolicy: sinon.stub().callsArgWith(1, null, policyMock), + testPermissions: sinon.stub().callsArgWith(1, null, permissionsMock) + } + }; + var pubsubMock = { + topic: sinon.stub().returns(topicMock), + subscription: sinon.stub().returns(subscriptionMock) + }; + var gcloudMock = { + pubsub: sinon.stub().returns(pubsubMock) + }; + return { + program: proxyquire('../iam', { + gcloud: gcloudMock + }), + mocks: { + gcloud: gcloudMock, + pubsub: pubsubMock, + topic: topicMock, + subscription: subscriptionMock + } + }; +} describe('pubsub:iam', function () { - describe('getTopicPolicyExample', function () { - it('handles error', function () { - var topic = { - iam: { - getPolicy: sinon.stub().callsArgWith(0, 'error') - } - }; - proxyquire('../iam', { - './subscription': { - pubsub: { - topic: sinon.stub().returns(topic) - } - } - }).getTopicPolicyExample('test-topic', function (err) { - assert(err === 'error'); + describe('getTopicPolicy', function () { + it('should get a topic\'s policy', function () { + var sample = getSample(); + sample.program.getTopicPolicy(topicName, function (err, policy) { + assert.ifError(err); + assert.equal(policy, policyMock); + assert(console.log.calledWith('Got topic policy:', policyMock)); + }); + }); + it('should require topicName', function () { + var sample = getSample(); + sample.program.getTopicPolicy(undefined, function (err, policy) { + assert(err); + assert(err.message = '"topicName" is required!'); + assert.equal(policy, undefined); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.topic.iam.getPolicy.callsArgWith(0, new Error(error)); + sample.program.getTopicPolicy(topicName, function (err) { + assert(err); + assert(err.message === 'error'); }); }); }); - describe('getSubscriptionPolicyExample', function () { - it('handles error', function () { - var subscription = { - iam: { - getPolicy: sinon.stub().callsArgWith(0, 'error') - } - }; - proxyquire('../iam', { - './subscription': { - pubsub: { - subscription: sinon.stub().returns(subscription) - } - } - }).getSubscriptionPolicyExample('test-subscription', function (err) { - assert(err === 'error'); + describe('getSubscriptionPolicy', function () { + it('should get a subscription\'s policy', function () { + var sample = getSample(); + sample.program.getSubscriptionPolicy(subscriptionName, function (err, policy) { + assert.ifError(err); + assert.equal(policy, policyMock); + assert(console.log.calledWith('Got subscription policy:', policyMock)); + }); + }); + it('should require subscriptionName', function () { + var sample = getSample(); + sample.program.getSubscriptionPolicy(undefined, function (err, policy) { + assert(err); + assert(err.message = '"subscriptionName" is required!'); + assert.equal(policy, undefined); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.subscription.iam.getPolicy.callsArgWith(0, new Error(error)); + sample.program.getSubscriptionPolicy(subscriptionName, function (err) { + assert(err); + assert(err.message === 'error'); }); }); }); - describe('setTopicPolicyExample', function () { - it('sets topic policy', function () { - var policy = {}; - var apiResponse = {}; - var topic = { - iam: { - setPolicy: sinon.stub().callsArgWith(1, null, policy, apiResponse) - } - }; - proxyquire('../iam', { - './subscription': { - pubsub: { - topic: sinon.stub().returns(topic) - } - } - }).setTopicPolicyExample('test-topic', function (err, policy, apiResponse) { - assert(!err); - assert(console.log.calledWith('Updated policy for test-topic')); - }); - }); - - it('handles error', function () { - var topic = { - iam: { - setPolicy: sinon.stub().callsArgWith(1, 'error') - } - }; - proxyquire('../iam', { - './subscription': { - pubsub: { - topic: sinon.stub().returns(topic) - } - } - }).setTopicPolicyExample('test-topic', function (err) { - assert(err === 'error'); + describe('setTopicPolicy', function () { + it('should set a topic\'s policy', function () { + var sample = getSample(); + sample.program.setTopicPolicy(topicName, function (err, policy) { + assert.ifError(err); + assert.equal(policy, policyMock); + assert(console.log.calledWith('Updated policy for topic: %s', topicName)); + }); + }); + it('should require topicName', function () { + var sample = getSample(); + sample.program.setTopicPolicy(undefined, function (err, policy) { + assert(err); + assert(err.message = '"topicName" is required!'); + assert.equal(policy, undefined); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.topic.iam.setPolicy.callsArgWith(1, new Error(error)); + sample.program.setTopicPolicy(topicName, function (err) { + assert(err); + assert(err.message === 'error'); }); }); }); - describe('setSubscriptionPolicyExample', function () { - it('sets subscription policy', function () { - var policy = {}; - var apiResponse = {}; - var subscription = { - iam: { - setPolicy: sinon.stub().callsArgWith(1, null, policy, apiResponse) - } - }; - proxyquire('../iam', { - './subscription': { - pubsub: { - subscription: sinon.stub().returns(subscription) - } - } - }).setSubscriptionPolicyExample('test-subscription', function (err, policy, apiResponse) { - assert(!err); - assert(console.log.calledWith('Updated policy for test-subscription')); - }); - }); - - it('handles error', function () { - var subscription = { - iam: { - setPolicy: sinon.stub().callsArgWith(1, 'error') - } - }; - proxyquire('../iam', { - './subscription': { - pubsub: { - subscription: sinon.stub().returns(subscription) - } - } - }).setSubscriptionPolicyExample('test-subscription', function (err) { - assert(err === 'error'); + describe('setSubscriptionPolicy', function () { + it('should set a subscription\'s policy', function () { + var sample = getSample(); + sample.program.setSubscriptionPolicy(subscriptionName, function (err, policy) { + assert.ifError(err); + assert.equal(policy, policyMock); + assert(console.log.calledWith('Updated policy for subscription: %s', subscriptionName)); + }); + }); + it('should require subscriptionName', function () { + var sample = getSample(); + sample.program.setSubscriptionPolicy(undefined, function (err, policy) { + assert(err); + assert(err.message = '"subscriptionName" is required!'); + assert.equal(policy, undefined); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.subscription.iam.setPolicy.callsArgWith(1, new Error(error)); + sample.program.setSubscriptionPolicy(subscriptionName, function (err) { + assert(err); + assert(err.message === 'error'); }); }); }); - describe('testTopicPermissionsExample', function () { - it('handles error', function () { - var topic = { - iam: { - testPermissions: sinon.stub().callsArgWith(1, 'error') - } - }; - proxyquire('../iam', { - './subscription': { - pubsub: { - topic: sinon.stub().returns(topic) - } - } - }).testTopicPermissionsExample('test-topic', function (err) { - assert(err === 'error'); + describe('testTopicPermissions', function () { + it('should test a topic\'s permissions', function () { + var sample = getSample(); + sample.program.testTopicPermissions(topicName, function (err, permissions) { + assert.ifError(err); + assert.equal(permissions, permissionsMock); + assert(console.log.calledWith('Tested permissions for topic: %s', topicName)); + }); + }); + it('should require topicName', function () { + var sample = getSample(); + sample.program.testTopicPermissions(undefined, function (err, policy) { + assert(err); + assert(err.message = '"topicName" is required!'); + assert.equal(policy, undefined); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.topic.iam.testPermissions.callsArgWith(1, new Error(error)); + sample.program.testTopicPermissions(topicName, function (err, permissions) { + assert(err); + assert(err.message === 'error'); + assert.equal(permissions, undefined); }); }); }); - describe('testSubscriptionPermissionsExample', function () { - it('handles error', function () { - var subscription = { - iam: { - testPermissions: sinon.stub().callsArgWith(1, 'error') - } - }; - proxyquire('../iam', { - './subscription': { - pubsub: { - subscription: sinon.stub().returns(subscription) - } - } - }).testSubscriptionPermissionsExample('test-subscription', function (err) { - assert(err === 'error'); + describe('testSubscriptionPermissions', function () { + it('should tests a subscription\'s permissions', function () { + var sample = getSample(); + sample.program.testSubscriptionPermissions(subscriptionName, function (err, permissions) { + assert.ifError(err); + assert.equal(permissions, permissionsMock); + assert(console.log.calledWith('Tested permissions for subscription: %s', subscriptionName)); }); }); + it('should require subscriptionName', function () { + var sample = getSample(); + sample.program.testSubscriptionPermissions(undefined, function (err, policy) { + assert(err); + assert(err.message = '"subscriptionName" is required!'); + assert.equal(policy, undefined); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.subscription.iam.testPermissions.callsArgWith(1, new Error(error)); + sample.program.testSubscriptionPermissions(subscriptionName, function (err, permissions) { + assert(err); + assert(err.message === 'error'); + assert.equal(permissions, undefined); + }); + }); + }); + + describe('printUsage', function () { + it('should print usage', function () { + var program = getSample().program; + + program.printUsage(); + + assert(console.log.calledWith('Usage: node iam RESOURCE COMMAND [ARGS...]')); + assert(console.log.calledWith('\nResources:\n')); + assert(console.log.calledWith('\ttopics')); + assert(console.log.calledWith('\tsubscriptions')); + assert(console.log.calledWith('\nCommands:\n')); + assert(console.log.calledWith('\tget NAME')); + assert(console.log.calledWith('\tset NAME')); + assert(console.log.calledWith('\ttest NAME')); + assert(console.log.calledWith('\nExamples:\n')); + assert(console.log.calledWith('\tnode iam topics get my-topic')); + assert(console.log.calledWith('\tnode iam topics set my-topic')); + assert(console.log.calledWith('\tnode iam topics test my-topic')); + assert(console.log.calledWith('\tnode iam subscriptions get my-subscription')); + assert(console.log.calledWith('\tnode iam subscriptions set my-subscription')); + assert(console.log.calledWith('\tnode iam subscriptions test my-subscription')); + }); + }); + + describe('main', function () { + it('should call the right commands', function () { + var program = getSample().program; + + sinon.stub(program, 'getTopicPolicy'); + program.main(['topics', 'get']); + assert(program.getTopicPolicy.calledOnce); + + sinon.stub(program, 'getSubscriptionPolicy'); + program.main(['subscriptions', 'get']); + assert(program.getSubscriptionPolicy.calledOnce); + + sinon.stub(program, 'setTopicPolicy'); + program.main(['topics', 'set']); + assert(program.setTopicPolicy.calledOnce); + + sinon.stub(program, 'setSubscriptionPolicy'); + program.main(['subscriptions', 'set']); + assert(program.setSubscriptionPolicy.calledOnce); + + sinon.stub(program, 'testTopicPermissions'); + program.main(['topics', 'test']); + assert(program.testTopicPermissions.calledOnce); + + sinon.stub(program, 'testSubscriptionPermissions'); + program.main(['subscriptions', 'test']); + assert(program.testSubscriptionPermissions.calledOnce); + + sinon.stub(program, 'printUsage'); + program.main(['--help']); + assert(program.printUsage.calledOnce); + + program.main(['topics', '--help']); + assert(program.printUsage.calledTwice); + + program.main(['subscriptions', '--help']); + assert(program.printUsage.calledThrice); + }); }); }); diff --git a/pubsub/test/subscription.test.js b/pubsub/test/subscription.test.js deleted file mode 100644 index 005cab2652..0000000000 --- a/pubsub/test/subscription.test.js +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2016, Google, Inc. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -'use strict'; - -var proxyquire = require('proxyquire'); - -describe('pubsub:subscription', function () { - describe('createTopicExample', function () { - it('handles error', function () { - var topic = { - get: sinon.stub().callsArgWith(1, 'error') - }; - var pubsub = { - topic: sinon.stub().returns(topic) - }; - proxyquire('../subscription', { - gcloud: { - pubsub: sinon.stub().returns(pubsub) - } - }).createTopicExample('test-topic', function (err) { - assert(err === 'error'); - }); - }); - }); - - describe('deleteTopicExample', function () { - it('handles error', function () { - var topic = { - delete: sinon.stub().callsArgWith(0, 'error') - }; - var pubsub = { - topic: sinon.stub().returns(topic) - }; - proxyquire('../subscription', { - gcloud: { - pubsub: sinon.stub().returns(pubsub) - } - }).deleteTopicExample('test-topic', function (err) { - assert(err === 'error'); - }); - }); - }); - - describe('deleteSubscriptionExample', function () { - it('handles error', function () { - var subscription = { - delete: sinon.stub().callsArgWith(0, 'error') - }; - var pubsub = { - subscription: sinon.stub().returns(subscription) - }; - proxyquire('../subscription', { - gcloud: { - pubsub: sinon.stub().returns(pubsub) - } - }).deleteSubscriptionExample('test-subscription', function (err) { - assert(err === 'error'); - }); - }); - }); - - describe('publishExample', function () { - it('handles error', function () { - var topic = { - publish: sinon.stub().callsArgWith(1, 'error') - }; - var pubsub = { - topic: sinon.stub().returns(topic) - }; - proxyquire('../subscription', { - gcloud: { - pubsub: sinon.stub().returns(pubsub) - } - }).publishExample('test-topic', function (err) { - assert(err === 'error'); - }); - }); - }); - - describe('getAllTopicsExample', function () { - it('handles error', function () { - var pubsub = { - getTopics: sinon.stub().callsArgWith(1, 'error') - }; - proxyquire('../subscription', { - gcloud: { - pubsub: sinon.stub().returns(pubsub) - } - }).getAllTopicsExample(function (err) { - assert(err === 'error'); - }); - }); - - it('recurses', function () { - var pubsub = { - getTopics: sinon.stub().callsArgWith(1, 'error') - }; - pubsub.getTopics.onFirstCall().callsArgWith(1, null, [], { - token: '1234' - }); - pubsub.getTopics.onSecondCall().callsArgWith(1, null, []); - proxyquire('../subscription', { - gcloud: { - pubsub: sinon.stub().returns(pubsub) - } - }).getAllTopicsExample(function (err, topics) { - assert(!err); - assert.deepEqual(topics, []); - }); - }); - - it('handles deep error', function () { - var pubsub = { - getTopics: sinon.stub().callsArgWith(1, 'error') - }; - pubsub.getTopics.onFirstCall().callsArgWith(1, null, [], { - token: '1234' - }); - pubsub.getTopics.onSecondCall().callsArgWith(1, 'error'); - proxyquire('../subscription', { - gcloud: { - pubsub: sinon.stub().returns(pubsub) - } - }).getAllTopicsExample(function (err) { - assert(err === 'error'); - }); - }); - }); - - describe('getAllSubscriptionsExample', function () { - it('handles error', function () { - var pubsub = { - getSubscriptions: sinon.stub().callsArgWith(1, 'error') - }; - proxyquire('../subscription', { - gcloud: { - pubsub: sinon.stub().returns(pubsub) - } - }).getAllSubscriptionsExample(function (err) { - assert(err === 'error'); - }); - }); - - it('recurses', function () { - var pubsub = { - getSubscriptions: sinon.stub().callsArgWith(1, 'error') - }; - pubsub.getSubscriptions.onFirstCall().callsArgWith(1, null, [], { - token: '1234' - }); - pubsub.getSubscriptions.onSecondCall().callsArgWith(1, null, []); - proxyquire('../subscription', { - gcloud: { - pubsub: sinon.stub().returns(pubsub) - } - }).getAllSubscriptionsExample(function (err, subscriptions) { - assert(!err); - assert.deepEqual(subscriptions, []); - }); - }); - - it('handles deep error', function () { - var pubsub = { - getSubscriptions: sinon.stub().callsArgWith(1, 'error') - }; - pubsub.getSubscriptions.onFirstCall().callsArgWith(1, null, [], { - token: '1234' - }); - pubsub.getSubscriptions.onSecondCall().callsArgWith(1, 'error'); - proxyquire('../subscription', { - gcloud: { - pubsub: sinon.stub().returns(pubsub) - } - }).getAllSubscriptionsExample(function (err) { - assert(err === 'error'); - }); - }); - }); - - describe('subscribeExample', function () { - it('handles error', function () { - var pubsub = { - subscribe: sinon.stub().callsArgWith(3, 'error') - }; - proxyquire('../subscription', { - gcloud: { - pubsub: sinon.stub().returns(pubsub) - } - }).subscribeExample('test-topic', 'test-subscription', function (err) { - assert(err === 'error'); - }); - }); - }); -}); diff --git a/pubsub/test/subscriptions.test.js b/pubsub/test/subscriptions.test.js new file mode 100644 index 0000000000..fd71607696 --- /dev/null +++ b/pubsub/test/subscriptions.test.js @@ -0,0 +1,246 @@ +// Copyright 2016, Google, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +var proxyquire = require('proxyquire').noCallThru(); +var topicName = 'foo'; +var subscriptionName = 'bar'; + +function getSample () { + var messagesMock = [ + { + data: 'Hello World!' + } + ]; + var subscriptionMock = { + get: sinon.stub(), + delete: sinon.stub().callsArgWith(0, null), + pull: sinon.stub().callsArgWith(1, null, messagesMock), + ack: sinon.stub().callsArgWith(1, null) + }; + subscriptionMock.get.callsArgWith(1, null, subscriptionMock); + var topicMock = { + get: sinon.stub(), + subscription: sinon.stub().returns(subscriptionMock) + }; + topicMock.get.callsArgWith(1, null, topicMock); + var subscriptionsMock = [ + { + name: subscriptionName + } + ]; + + var pubsubMock = { + topic: sinon.stub().returns(topicMock), + subscription: sinon.stub().returns(subscriptionMock), + subscribe: sinon.stub().callsArgWith(3, null, subscriptionMock), + getSubscriptions: sinon.stub().callsArgWith(1, null, subscriptionsMock) + }; + var gcloudMock = { + pubsub: sinon.stub().returns(pubsubMock) + }; + return { + program: proxyquire('../subscriptions', { + gcloud: gcloudMock + }), + mocks: { + gcloud: gcloudMock, + pubsub: pubsubMock, + topic: topicMock, + subscription: subscriptionMock, + subscriptions: subscriptionsMock, + messages: messagesMock + } + }; +} + +describe('pubsub:subscriptions', function () { + describe('create', function () { + it('should create a subscription', function () { + var sample = getSample(); + sample.program.create(topicName, subscriptionName, function (err, subscription) { + assert.ifError(err); + assert.strictEqual(subscription, sample.mocks.subscription); + assert(console.log.calledWith('Created subscription %s to topic %s', subscriptionName, topicName)); + }); + }); + it('should require topicName', function () { + var sample = getSample(); + sample.program.create(undefined, undefined, function (err, subscription) { + assert(err); + assert(err.message = '"topicName" is required!'); + assert.equal(subscription, undefined); + }); + }); + it('should require subscriptionName', function () { + var sample = getSample(); + sample.program.create(topicName, undefined, function (err, subscription) { + assert(err); + assert(err.message = '"subscriptionName" is required!'); + assert.equal(subscription, undefined); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.pubsub.subscribe.callsArgWith(3, new Error(error)); + sample.program.create(topicName, subscriptionName, function (err) { + assert(err); + assert(err.message === 'error'); + }); + }); + }); + + describe('delete', function () { + it('should delete a subscription', function () { + var sample = getSample(); + sample.program.delete(subscriptionName, function (err) { + assert.ifError(err); + assert(console.log.calledWith('Deleted subscription: %s', subscriptionName)); + }); + }); + it('should require subscriptionName', function () { + var sample = getSample(); + sample.program.delete(undefined, function (err, subscription) { + assert(err); + assert(err.message = '"subscriptionName" is required!'); + assert.equal(subscription, undefined); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.subscription.delete.callsArgWith(0, new Error(error)); + sample.program.delete(subscriptionName, function (err) { + assert(err); + assert(err.message === 'error'); + }); + }); + }); + + describe('list', function () { + it('should list all subscriptions', function () { + var sample = getSample(); + sample.program.list(undefined, function (err, subscriptions) { + assert.ifError(err); + assert.strictEqual(subscriptions, sample.mocks.subscriptions); + assert(console.log.calledWith('Found %d subscriptions!', subscriptions.length)); + }); + }); + it('should list all subscriptions of a topic', function () { + var sample = getSample(); + sample.program.list(topicName, function (err, subscriptions) { + assert.ifError(err); + assert.strictEqual(subscriptions, sample.mocks.subscriptions); + assert(console.log.calledWith('Found %d subscriptions!', subscriptions.length)); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.pubsub.getSubscriptions.callsArgWith(1, new Error(error)); + sample.program.list(undefined, function (err, subscriptions) { + assert(err); + assert(err.message === 'error'); + assert.equal(subscriptions, undefined); + }); + }); + }); + + describe('pull', function () { + it('should pull messages', function () { + var sample = getSample(); + sample.program.pull(subscriptionName, function (err, messages) { + assert.ifError(err); + assert.strictEqual(messages, sample.mocks.messages); + assert(console.log.calledWith('Pulled %d messages!', messages.length)); + assert(console.log.calledWith('Acked %d messages!', messages.length)); + }); + }); + it('should require subscriptionName', function () { + var sample = getSample(); + sample.program.pull(undefined, function (err, subscription) { + assert(err); + assert(err.message = '"subscriptionName" is required!'); + assert.equal(subscription, undefined); + }); + }); + it('should handle pull error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.subscription.pull.callsArgWith(1, new Error(error)); + sample.program.pull(subscriptionName, function (err, messages) { + assert(err); + assert(err.message === 'error'); + assert.equal(messages, undefined); + }); + }); + it('should handle ack error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.subscription.ack.callsArgWith(1, new Error(error)); + sample.program.pull(subscriptionName, function (err) { + assert(err); + assert(err.message === 'error'); + }); + }); + }); + + describe('printUsage', function () { + it('should print usage', function () { + var program = getSample().program; + + program.printUsage(); + + assert(console.log.calledWith('Usage: node subscriptions COMMAND [ARGS...]')); + assert(console.log.calledWith('\nCommands:\n')); + assert(console.log.calledWith('\tcreate TOPIC_NAME SUBSCRIPTION_NAME')); + assert(console.log.calledWith('\tdelete SUBSCRIPTION_NAME')); + assert(console.log.calledWith('\tpull SUBSCRIPTION_NAME')); + assert(console.log.calledWith('\tlist [TOPIC_NAME]')); + assert(console.log.calledWith('\nExamples:\n')); + assert(console.log.calledWith('\tnode subscriptions create my-topic my-subscription')); + assert(console.log.calledWith('\tnode subscriptions delete my-subscription')); + assert(console.log.calledWith('\tnode subscriptions pull my-subscription')); + assert(console.log.calledWith('\tnode subscriptions list')); + assert(console.log.calledWith('\tnode subscriptions list my-topic')); + }); + }); + + describe('main', function () { + it('should call the right commands', function () { + var program = getSample().program; + + sinon.stub(program, 'create'); + program.main(['create']); + assert(program.create.calledOnce); + + sinon.stub(program, 'delete'); + program.main(['delete']); + assert(program.delete.calledOnce); + + sinon.stub(program, 'list'); + program.main(['list']); + assert(program.list.calledOnce); + + sinon.stub(program, 'pull'); + program.main(['pull']); + assert(program.pull.calledOnce); + + sinon.stub(program, 'printUsage'); + program.main(['--help']); + assert(program.printUsage.calledOnce); + }); + }); +}); diff --git a/pubsub/test/topics.test.js b/pubsub/test/topics.test.js new file mode 100644 index 0000000000..2a8e21495f --- /dev/null +++ b/pubsub/test/topics.test.js @@ -0,0 +1,219 @@ +// Copyright 2016, Google, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +var proxyquire = require('proxyquire').noCallThru(); +var topicName = 'foo'; + +function getSample () { + var topicMock = { + get: sinon.stub(), + publish: sinon.stub().callsArgWith(1, null, [1]), + delete: sinon.stub().callsArgWith(0, null) + }; + topicMock.get.callsArgWith(1, null, topicMock); + var topicsMock = [ + { + name: topicName + } + ]; + + var pubsubMock = { + topic: sinon.stub().returns(topicMock), + getTopics: sinon.stub().callsArgWith(0, null, topicsMock) + }; + var gcloudMock = { + pubsub: sinon.stub().returns(pubsubMock) + }; + return { + program: proxyquire('../topics', { + gcloud: gcloudMock + }), + mocks: { + gcloud: gcloudMock, + pubsub: pubsubMock, + topics: topicsMock, + topic: topicMock + } + }; +} + +describe('pubsub:topics', function () { + describe('create', function () { + it('should create a topic', function () { + var sample = getSample(); + sample.program.create(topicName, function (err, topic) { + assert.ifError(err); + assert.strictEqual(topic, sample.mocks.topic); + assert(console.log.calledWith('Created topic: %s', topicName)); + }); + }); + it('should require name', function () { + var sample = getSample(); + sample.program.create(undefined, function (err, topic) { + assert(err); + assert(err.message = '"name" is required!'); + assert.equal(topic, undefined); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.topic.get.callsArgWith(1, new Error(error)); + sample.program.create(topicName, function (err) { + assert(err); + assert(err.message === 'error'); + }); + }); + }); + + describe('delete', function () { + it('should delete a topic', function () { + var sample = getSample(); + sample.program.delete(topicName, function (err) { + assert.ifError(err); + assert(console.log.calledWith('Deleted topic: %s', topicName)); + }); + }); + it('should require name', function () { + var sample = getSample(); + sample.program.delete(undefined, function (err, topic) { + assert(err); + assert(err.message = '"name" is required!'); + assert.equal(topic, undefined); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.topic.delete.callsArgWith(0, new Error(error)); + sample.program.delete(topicName, function (err) { + assert(err); + assert(err.message === 'error'); + }); + }); + }); + + describe('publish', function () { + it('should publish a message', function () { + var sample = getSample(); + sample.program.publish(topicName, '{"data":"hello world"}', function (err, messageIds) { + assert.ifError(err); + assert.deepEqual(messageIds, [1]); + assert(console.log.calledWith('Published %d messages!', messageIds.length)); + }); + }); + it('should require name', function () { + var sample = getSample(); + sample.program.publish(undefined, undefined, function (err, messageIds) { + assert(err); + assert(err.message = '"name" is required!'); + assert.equal(messageIds, undefined); + }); + }); + it('should require message', function () { + var sample = getSample(); + sample.program.publish(topicName, undefined, function (err, messageIds) { + assert(err); + assert(err.message = '"message" is required!'); + assert.equal(messageIds, undefined); + }); + }); + it('should require a valid JSON string', function () { + var sample = getSample(); + sample.program.publish(topicName, 'asdf', function (err, messageIds) { + assert(err); + assert(err.message = '"message" must be a valid JSON string!'); + assert.equal(messageIds, undefined); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.topic.publish.callsArgWith(1, new Error(error)); + sample.program.publish(topicName, '{"data":"hello world"}', function (err, messageIds) { + assert(err); + assert(err.message === 'error'); + assert.equal(messageIds, undefined); + }); + }); + }); + + describe('list', function () { + it('should list topics', function () { + var sample = getSample(); + sample.program.list(function (err, topics) { + assert.ifError(err); + assert.strictEqual(topics, sample.mocks.topics); + assert(console.log.calledWith('Found %d topics!', topics.length)); + }); + }); + it('should handle error', function () { + var sample = getSample(); + var error = 'error'; + sample.mocks.pubsub.getTopics.callsArgWith(0, new Error(error)); + sample.program.list(function (err, topics) { + assert(err); + assert(err.message === 'error'); + assert.equal(topics, undefined); + }); + }); + }); + + describe('printUsage', function () { + it('should print usage', function () { + var program = getSample().program; + + program.printUsage(); + + assert(console.log.calledWith('Usage: node topics COMMAND [ARGS...]')); + assert(console.log.calledWith('\nCommands:\n')); + assert(console.log.calledWith('\tcreate TOPIC_NAME')); + assert(console.log.calledWith('\tdelete TOPIC_NAME')); + assert(console.log.calledWith('\tpublish TOPIC_NAME MESSAGE')); + assert(console.log.calledWith('\tlist')); + assert(console.log.calledWith('\nExamples:\n')); + assert(console.log.calledWith('\tnode topics create my-topic')); + assert(console.log.calledWith('\tnode topics list')); + assert(console.log.calledWith('\tnode topics publish my-topic \'{"data":"Hello world!"}\'')); + assert(console.log.calledWith('\tnode topics delete my-topic')); + }); + }); + + describe('main', function () { + it('should call the right commands', function () { + var program = getSample().program; + + sinon.stub(program, 'create'); + program.main(['create']); + assert(program.create.calledOnce); + + sinon.stub(program, 'delete'); + program.main(['delete']); + assert(program.delete.calledOnce); + + sinon.stub(program, 'list'); + program.main(['list']); + assert(program.list.calledOnce); + + sinon.stub(program, 'publish'); + program.main(['publish']); + assert(program.publish.calledOnce); + + sinon.stub(program, 'printUsage'); + program.main(['--help']); + assert(program.printUsage.calledOnce); + }); + }); +}); diff --git a/pubsub/topics.js b/pubsub/topics.js new file mode 100644 index 0000000000..12ace89397 --- /dev/null +++ b/pubsub/topics.js @@ -0,0 +1,181 @@ +// Copyright 2016, Google, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START auth] +// By default, gcloud will authenticate using the service account file specified +// by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use the +// project specified by the GCLOUD_PROJECT environment variable. See +// https://googlecloudplatform.github.io/gcloud-node/#/docs/guides/authentication +var gcloud = require('gcloud'); + +// Instantiate a pubsub client +var pubsub = gcloud.pubsub(); +// [END auth] + +// [START create_topic] +/** + * Create a new topic. + * + * @param {string} topicName Name for the new topic. + * @param {Function} callback The callback function. + */ +function createTopic (topicName, callback) { + if (!topicName) { + return callback(new Error('"topicName" is required!')); + } + + var topic = pubsub.topic(topicName); + + // Get the topic if it exists. Create it if it does not exist. + topic.get({ + autoCreate: true + }, function (err, topic) { + if (err) { + return callback(err); + } + + // Created the topic + console.log('Created topic: %s', topicName); + return callback(null, topic); + }); +} +// [END create_topic] + +// [START delete_topic] +/** + * Delete a topic. + * + * @param {string} topicName Name of the topic to delete. + * @param {Function} callback Callback function. + */ +function deleteTopic (topicName, callback) { + if (!topicName) { + return callback(new Error('"topicName" is required!')); + } + + // Grab a reference to an existing topic + var topic = pubsub.topic(topicName); + + // Delete the topic + topic.delete(function (err) { + if (err) { + return callback(err); + } + + // Deleted the topic + console.log('Deleted topic: %s', topicName); + return callback(null); + }); +} +// [END delete_topic] + +// [START publish] +/** + * Publish a message to a topic. + * + * @param {string} topicName Name of the topic to which to publish. + * @param {Function} callback Callback function. + */ +function publishMessage (topicName, message, callback) { + if (!topicName) { + return callback(new Error('"topicName" is required!')); + } else if (!message) { + return callback(new Error('"message" is required!')); + } + try { + message = JSON.parse(message); + } catch (err) { + return callback(new Error('"message" must be a valid JSON string!')); + } + + // Grab a reference to an existing topic + var topic = pubsub.topic(topicName); + + // Publish a message to the topic + topic.publish(message, function (err, messageIds) { + if (err) { + return callback(err); + } + + console.log('Published %d messages!', messageIds.length); + return callback(null, messageIds); + }); +} +// [END publish] + +// [START list_topics] +/** + * List all topics in the current project. + * + * @param {Function} callback The callback function. + */ +function listTopics (callback) { + pubsub.getTopics(function (err, topics) { + if (err) { + return callback(err); + } + + console.log('Found %d topics!', topics.length); + return callback(null, topics); + }); +} +// [END list_topics] + +// [START usage] +function printUsage () { + console.log('Usage: node topics COMMAND [ARGS...]'); + console.log('\nCommands:\n'); + console.log('\tcreate TOPIC_NAME'); + console.log('\tdelete TOPIC_NAME'); + console.log('\tpublish TOPIC_NAME MESSAGE'); + console.log('\tlist'); + console.log('\nExamples:\n'); + console.log('\tnode topics create my-topic'); + console.log('\tnode topics list'); + console.log('\tnode topics publish my-topic \'{"data":"Hello world!"}\''); + console.log('\tnode topics delete my-topic'); +} +// [END usage] + +// The command-line program +var program = { + create: createTopic, + delete: deleteTopic, + publish: publishMessage, + list: listTopics, + printUsage: printUsage, + + // Executed when this program is run from the command-line + main: function (args, cb) { + var command = args.shift(); + if (command === 'create') { + this.create(args[0], cb); + } else if (command === 'delete') { + this.delete(args[0], cb); + } else if (command === 'publish') { + this.publish(args[0], args[1], cb); + } else if (command === 'list') { + this.list(cb); + } else { + this.printUsage(); + } + } +}; + +if (module === require.main) { + program.main(process.argv.slice(2), console.log); +} + +module.exports = program; diff --git a/speech/system-test/recognize.test.js b/speech/system-test/recognize.test.js index 7da66d5031..870caeebfa 100644 --- a/speech/system-test/recognize.test.js +++ b/speech/system-test/recognize.test.js @@ -21,7 +21,7 @@ describe('speech:recognize', function () { recognizeExample.main( path.join(__dirname, '../resources/audio.raw'), function (err, result) { - assert(!err); + assert.ifError(err); assert(result); assert(Array.isArray(result.results)); assert(result.results.length === 1); diff --git a/speech/system-test/recognize_streaming.test.js b/speech/system-test/recognize_streaming.test.js index 0d008d0c1c..b155cf426e 100644 --- a/speech/system-test/recognize_streaming.test.js +++ b/speech/system-test/recognize_streaming.test.js @@ -22,7 +22,7 @@ describe('speech:recognize_streaming', function () { path.join(__dirname, '../resources/audio.raw'), process.env.SPEECH_API_HOST || 'speech.googleapis.com', function (err, results) { - assert(!err); + assert.ifError(err); assert(results); assert(results.length === 3); assert(results[0].results); diff --git a/vision/system-test/faceDetection.test.js b/vision/system-test/faceDetection.test.js index b19659e22e..4b4ae23c9b 100644 --- a/vision/system-test/faceDetection.test.js +++ b/vision/system-test/faceDetection.test.js @@ -55,7 +55,7 @@ describe('vision:faceDetection', function () { outputFile, MockCanvas, function (err, faces) { - assert(!err); + assert.ifError(err); assert(faces.length === 1); var image = fs.readFileSync(outputFile); assert(image.toString('utf8') === 'testfoobar'); diff --git a/vision/system-test/labelDetection.test.js b/vision/system-test/labelDetection.test.js index 0446196f72..2afc6d4a79 100644 --- a/vision/system-test/labelDetection.test.js +++ b/vision/system-test/labelDetection.test.js @@ -22,7 +22,7 @@ describe('vision:labelDetection', function () { labelDetectionSample.main( inputFile, function (err, labels) { - assert(!err); + assert.ifError(err); assert(labels.length > 0); assert(console.log.calledWith('Found label: cat for ' + inputFile)); done(); diff --git a/vision/system-test/landmarkDetection.test.js b/vision/system-test/landmarkDetection.test.js index 028e98097a..f701cb3fed 100644 --- a/vision/system-test/landmarkDetection.test.js +++ b/vision/system-test/landmarkDetection.test.js @@ -19,7 +19,7 @@ var inputFile = 'https://cloud-samples-tests.storage.googleapis.com/vision/water describe('vision:landmarkDetection', function () { it('should detect landmarks', function (done) { landmarkDetectionSample.main(inputFile, function (err, landmarks) { - assert(!err); + assert.ifError(err); assert(landmarks.length > 0); assert(console.log.calledWith('Found landmark: Taitung, Famous Places "up the water flow" marker for ' + inputFile)); done(); diff --git a/vision/system-test/textDetection.test.js b/vision/system-test/textDetection.test.js index 0b38faf861..e4dcc2c413 100644 --- a/vision/system-test/textDetection.test.js +++ b/vision/system-test/textDetection.test.js @@ -20,10 +20,10 @@ var textDetectionSample = require('../textDetection'); describe('vision:textDetection', function () { it('should detect texts', function (done) { textDetectionSample.main(inputDir, function (err, textResponse) { - assert(!err); + assert.ifError(err); assert(Object.keys(textResponse).length > 0); textDetectionSample.lookup(['the', 'sunbeams', 'in'], function (err, hits) { - assert(!err); + assert.ifError(err); assert(hits.length > 0); assert(hits[0].length > 0); done();