diff --git a/functions/datastore/README.md b/functions/datastore/README.md
new file mode 100644
index 0000000000..a3715be0de
--- /dev/null
+++ b/functions/datastore/README.md
@@ -0,0 +1,54 @@
+
+
+# Google Cloud Functions Cloud Datastore sample
+
+This recipe shows you how to read and write an entity in Datastore from a Cloud Function.
+
+View the [source code][code].
+
+[code]: index.js
+
+## Deploy and Test
+
+1. Follow the [Cloud Functions quickstart guide](https://cloud.google.com/functions/quickstart) to setup Cloud Functions for your project.
+
+1. Clone this repository:
+
+ git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
+ cd nodejs-docs-samples/functions/datastore
+
+1. Create a Cloud Storage Bucket to stage our deployment:
+
+ gsutil mb gs://
+
+1. Ensure the Cloud Datastore API is enabled:
+
+ [Click here to enable the Cloud Datastore API](https://console.cloud.google.com/flows/enableapi?apiid=datastore.googleapis.com&redirect=https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/master/functions/datastore)
+
+1. Deploy the "ds-get" function with an HTTP trigger:
+
+ gcloud alpha functions deploy ds-get --bucket --trigger-http --entry-point get
+
+1. Deploy the "ds-set" function with an HTTP trigger:
+
+ gcloud alpha functions deploy ds-set --bucket --trigger-http --entry-point set
+
+1. Deploy the "ds-del" function with an HTTP trigger:
+
+ gcloud alpha functions deploy ds-del --bucket --trigger-http --entry-point del
+
+1. Call the "ds-set" function to create a new entity:
+
+ gcloud alpha functions call ds-set --data '{"kind":"gcf-test","key":"foobar","value":{"message": "Hello World!"}}'
+
+1. Call the "ds-get" function to read the newly created entity:
+
+ gcloud alpha functions call ds-get --data '{"kind":"gcf-test","key":"foobar"}'
+
+1. Call the "ds-del" function to delete the entity:
+
+ gcloud alpha functions call ds-del --data '{"kind":"gcf-test","key":"foobar"}'
+
+1. Call the "ds-get" function again to verify it was deleted:
+
+ gcloud alpha functions call ds-get --data '{"kind":"gcf-test","key":"foobar"}'
diff --git a/functions/datastore/index.js b/functions/datastore/index.js
new file mode 100644
index 0000000000..91df305df6
--- /dev/null
+++ b/functions/datastore/index.js
@@ -0,0 +1,154 @@
+// 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 gcloud = require('gcloud');
+
+// Create a datastore client.
+var datastore = gcloud.datastore();
+
+/**
+ * Gets a Datastore key from the kind/key pair in the request.
+ *
+ * @param {Object} requestData Cloud Function request data.
+ * @param {string} requestData.key Datastore key string.
+ * @param {string} requestData.kind Datastore kind.
+ * @returns {Object} Datastore key object.
+ */
+function getKeyFromRequestData (requestData) {
+ if (!requestData.key) {
+ throw new Error('Key not provided. Make sure you have a "key" property ' +
+ 'in your request');
+ }
+
+ if (!requestData.kind) {
+ throw new Error('Kind not provided. Make sure you have a "kind" property ' +
+ 'in your request');
+ }
+
+ return datastore.key([requestData.kind, requestData.key]);
+}
+
+/**
+ * Creates and/or updates a record.
+ *
+ * @example
+ * gcloud alpha functions call ds-set --data '{"kind":"gcf-test","key":"foobar","value":{"message": "Hello World!"}}'
+ *
+ * @param {Object} context Cloud Function context.
+ * @param {Function} context.success Success callback.
+ * @param {Function} context.failure Failure callback.
+ * @param {Object} data Request data, in this case an object provided by the user.
+ * @param {string} data.kind The Datastore kind of the data to save, e.g. "user".
+ * @param {string} data.key Key at which to save the data, e.g. 5075192766267392.
+ * @param {Object} data.value Value to save to Cloud Datastore, e.g. {"name":"John"}
+ */
+function set (context, data) {
+ try {
+ // The value contains a JSON document representing the entity we want to save
+ if (!data.value) {
+ throw new Error('Value not provided. Make sure you have a "value" ' +
+ 'property in your request');
+ }
+
+ var key = getKeyFromRequestData(data);
+
+ return datastore.save({
+ key: key,
+ data: data.value
+ }, function (err) {
+ if (err) {
+ console.error(err);
+ return context.failure(err);
+ }
+
+ return context.success('Entity saved');
+ });
+ } catch (err) {
+ console.error(err);
+ return context.failure(err.message);
+ }
+}
+
+/**
+ * Retrieves a record.
+ *
+ * @example
+ * gcloud alpha functions call ds-get --data '{"kind":"gcf-test","key":"foobar"}'
+ *
+ * @param {Object} context Cloud Function context.
+ * @param {Function} context.success Success callback.
+ * @param {Function} context.failure Failure callback.
+ * @param {Object} data Request data, in this case an object provided by the user.
+ * @param {string} data.kind The Datastore kind of the data to retrieve, e.g. "user".
+ * @param {string} data.key Key at which to retrieve the data, e.g. 5075192766267392.
+ */
+function get (context, data) {
+ try {
+ var key = getKeyFromRequestData(data);
+
+ return datastore.get(key, function (err, entity) {
+ if (err) {
+ console.error(err);
+ return context.failure(err);
+ }
+
+ // The get operation will not fail for a non-existent entity, it just
+ // returns null.
+ if (!entity) {
+ return context.failure('No entity found for key ' + key.path);
+ }
+
+ return context.success(entity);
+ });
+ } catch (err) {
+ console.error(err);
+ return context.failure(err.message);
+ }
+}
+
+/**
+ * Deletes a record.
+ *
+ * @example
+ * gcloud alpha functions call ds-del --data '{"kind":"gcf-test","key":"foobar"}'
+ *
+ * @param {Object} context Cloud Function context.
+ * @param {Function} context.success Success callback.
+ * @param {Function} context.failure Failure callback.
+ * @param {Object} data Request data, in this case an object provided by the user.
+ * @param {string} data.kind The Datastore kind of the data to delete, e.g. "user".
+ * @param {string} data.key Key at which to delete data, e.g. 5075192766267392.
+ */
+function del (context, data) {
+ try {
+ var key = getKeyFromRequestData(data);
+
+ return datastore.delete(key, function (err) {
+ if (err) {
+ console.error(err);
+ return context.failure(err);
+ }
+
+ return context.success('Entity deleted');
+ });
+ } catch (err) {
+ console.error(err);
+ return context.failure(err.message);
+ }
+}
+
+exports.set = set;
+exports.get = get;
+exports.del = del;
diff --git a/functions/datastore/package.json b/functions/datastore/package.json
new file mode 100644
index 0000000000..62b1f2f0e5
--- /dev/null
+++ b/functions/datastore/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "nodejs-docs-samples-functions-datastore",
+ "description": "Node.js samples found on https://cloud.google.com",
+ "version": "0.0.1",
+ "private": true,
+ "license": "Apache Version 2.0",
+ "author": "Google Inc.",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
+ },
+ "dependencies": {
+ "gcloud": "^0.35.0"
+ }
+}
diff --git a/functions/gcs/README.md b/functions/gcs/README.md
new file mode 100644
index 0000000000..5b6150fba3
--- /dev/null
+++ b/functions/gcs/README.md
@@ -0,0 +1,38 @@
+
+
+# Google Cloud Functions Cloud Storage sample
+
+This recipe demonstrates how to load a file from Cloud Storage.
+
+View the [source code][code].
+
+[code]: index.js
+
+## Deploy and Test
+
+1. Follow the [Cloud Functions quickstart guide](https://cloud.google.com/functions/quickstart) to setup Cloud Functions for your project.
+
+1. Clone this repository:
+
+ git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
+ cd nodejs-docs-samples/functions/gcs
+
+1. Create a Cloud Storage Bucket to stage our deployment:
+
+ gsutil mb gs://
+
+1. Upload the sample file to the bucket:
+
+ gsutil cp sample.txt gs://
+
+1. Deploy the "wordCount" function with an HTTP trigger:
+
+ gcloud alpha functions deploy wordCount --bucket --trigger-http --entry-point map
+
+1. Call the "wordCount" function using the sample file:
+
+ gcloud alpha functions call wordCount --data '{"bucket":"","file":"sample.txt"}'
+
+ You should see something like this in your console
+
+ The file sample.txt has 114 words
diff --git a/functions/gcs/index.js b/functions/gcs/index.js
new file mode 100644
index 0000000000..3b3517bb61
--- /dev/null
+++ b/functions/gcs/index.js
@@ -0,0 +1,69 @@
+// 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 gcloud = require('gcloud');
+var readline = require('readline');
+
+function getFileStream (bucketName, fileName) {
+ if (!bucketName) {
+ throw new Error('Bucket not provided. Make sure you have a ' +
+ '"bucket" property in your request');
+ }
+ if (!fileName) {
+ throw new Error('Filename not provided. Make sure you have a ' +
+ '"file" property in your request');
+ }
+
+ // Create a gcs client.
+ var gcs = gcloud.storage();
+ var bucket = gcs.bucket(bucketName);
+ return bucket.file(fileName).createReadStream();
+}
+
+/**
+ * Reads file and responds with the number of words in the file.
+ *
+ * @example
+ * gcloud alpha functions call wordCount --data '{"bucket":"","file":"sample.txt"}'
+ *
+ * @param {Object} context Cloud Function context.
+ * @param {Function} context.success Success callback.
+ * @param {Function} context.failure Failure callback.
+ * @param {Object} data Request data, in this case an object provided by the user.
+ * @param {Object} data.bucket Name of a Cloud Storage bucket.
+ * @param {Object} data.file Name of a file in the Cloud Storage bucket.
+ */
+function wordCount (context, data) {
+ try {
+ var count = 0;
+
+ // Use the linebyline module to read the stream line by line.
+ var lineReader = readline.createInterface({
+ input: getFileStream(data.bucket, data.file)
+ });
+
+ lineReader.on('line', function (line) {
+ count += line.trim().split(/\s+/).length;
+ });
+
+ lineReader.on('close', function () {
+ context.success('The file ' + data.file + ' has ' + count + ' words');
+ });
+ } catch (err) {
+ context.failure(err.message);
+ }
+}
+
+exports.wordCount = wordCount;
diff --git a/functions/gcs/package.json b/functions/gcs/package.json
new file mode 100644
index 0000000000..e2c8c45c19
--- /dev/null
+++ b/functions/gcs/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "nodejs-docs-samples-functions-cloud-storage",
+ "description": "Node.js samples found on https://cloud.google.com",
+ "version": "0.0.1",
+ "private": true,
+ "license": "Apache Version 2.0",
+ "author": "Google Inc.",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
+ },
+ "dependencies": {
+ "gcloud": "^0.35.0"
+ }
+}
diff --git a/functions/gcs/sample.txt b/functions/gcs/sample.txt
new file mode 100644
index 0000000000..fa128797ee
--- /dev/null
+++ b/functions/gcs/sample.txt
@@ -0,0 +1,14 @@
+Shall I compare thee to a summer's day?
+Thou art more lovely and more temperate:
+Rough winds do shake the darling buds of May,
+And summer's lease hath all too short a date:
+Sometime too hot the eye of heaven shines,
+And often is his gold complexion dimm'd;
+And every fair from fair sometime declines,
+By chance, or nature's changing course, untrimm'd;
+But thy eternal summer shall not fade
+Nor lose possession of that fair thou ow'st;
+Nor shall Death brag thou wander'st in his shade,
+When in eternal lines to time thou grow'st;
+So long as men can breathe or eyes can see,
+So long lives this, and this gives life to thee.
diff --git a/functions/log/index.js b/functions/log/index.js
index 573699750a..35ed2a25ef 100644
--- a/functions/log/index.js
+++ b/functions/log/index.js
@@ -19,19 +19,3 @@ exports.helloworld = function (context, data) {
context.success();
};
// [END log]
-
-exports.log = exports.helloworld;
-
-// [START walkthrough_pubsub]
-exports.helloworld = function (context, data) {
- console.log('My GCF Function: ' + data.message);
- context.success();
-};
-// [END walkthrough_pubsub]
-
-// [START walkthrough_http]
-exports.hellohttp = function (context, data) {
- // Use the success argument to send data back to the caller
- context.success('My GCF Function: ' + data.message);
-};
-// [END walkthrough_http]
diff --git a/functions/log2/README.md b/functions/log2/README.md
new file mode 100644
index 0000000000..1e44dd8df1
--- /dev/null
+++ b/functions/log2/README.md
@@ -0,0 +1,24 @@
+
+
+# Google Cloud Functions message sample #2
+
+This sample shows writing to logs in a Cloud Function.
+
+View the [documentation][docs] or the [source code][code].
+
+[docs]: https://cloud.google.com/functions/walkthroughs
+[code]: index.js
+
+## Deploy
+
+This example deploys the function with an HTTP trigger.
+
+ gcloud alpha functions deploy helloworld --bucket --trigger-http
+
+## Test
+
+ gcloud alpha functions call helloworld
+
+You can also use `curl` to trigger the function:
+
+ curl -X POST https://..cloudfunctions.net/helloworld
diff --git a/functions/log2/index.js b/functions/log2/index.js
new file mode 100644
index 0000000000..a1f7b20439
--- /dev/null
+++ b/functions/log2/index.js
@@ -0,0 +1,28 @@
+// 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 walkthrough_pubsub]
+exports.helloworld = function (context, data) {
+ console.log('My GCF Function: ' + data.message);
+ context.success();
+};
+// [END walkthrough_pubsub]
+
+// [START walkthrough_http]
+exports.hellohttp = function (context, data) {
+ // Use the success argument to send data back to the caller
+ context.success('My GCF Function: ' + data.message);
+};
+// [END walkthrough_http]
diff --git a/functions/pubsub/README.md b/functions/pubsub/README.md
new file mode 100644
index 0000000000..74020076b5
--- /dev/null
+++ b/functions/pubsub/README.md
@@ -0,0 +1,52 @@
+
+
+# Google Cloud Functions Pub/Sub sample
+
+This recipe shows you how to publish messages to a Cloud Pub/Sub topic from a
+Cloud Function.
+
+View the [source code][code].
+
+[code]: index.js
+
+## Deploy and Test
+
+1. Follow the [Cloud Functions quickstart guide](https://cloud.google.com/functions/quickstart)
+to setup Cloud Functions for your project.
+
+1. Clone this repository:
+
+ git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
+ cd nodejs-docs-samples/functions/pubsub
+
+1. Create a Cloud Pub/Sub topic (if you already have one you want to use, you
+can skip this step):
+
+ gcloud alpha pubsub topics create
+
+1. Create a Cloud Storage Bucket to stage our deployment:
+
+ gsutil mb gs://
+
+1. Deploy the "publish" function with an HTTP trigger
+
+ gcloud alpha functions deploy publish --bucket --trigger-http
+
+1. Deploy the "subscribe" function with the Pub/Sub topic as a trigger
+
+ gcloud alpha functions deploy subscribe --bucket --trigger-topic
+
+1. Call the "publish" function:
+
+ gcloud alpha functions call publish --data '{"topic":"","message":"Hello World!"}'
+
+1. Check the logs for the "subscribe" function:
+
+ gcloud alpha functions get-logs subscribe
+
+You should see something like this in your console
+```
+D ... User function triggered, starting execution
+I ... Hello World!
+D ... Execution took 1 ms, user function completed successfully
+```
diff --git a/functions/pubsub/index.js b/functions/pubsub/index.js
new file mode 100644
index 0000000000..da73061ed3
--- /dev/null
+++ b/functions/pubsub/index.js
@@ -0,0 +1,86 @@
+// 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 gcloud = require('gcloud');
+
+// Create a pubsub client.
+var pubsub = gcloud.pubsub();
+
+/**
+ * Publishes a message to a Cloud Pub/Sub Topic.
+ *
+ * @example
+ * gcloud alpha functions call publish --data '{"topic":"","message":"Hello World!"}'
+ *
+ * @param {Object} context Cloud Function context.
+ * @param {Function} context.success Success callback.
+ * @param {Function} context.failure Failure callback.
+ * @param {Object} data Request data, in this case an object provided by the user.
+ * @param {string} data.topic Topic name on which to publish.
+ * @param {string} data.message Message to publish.
+ */
+function publish (context, data) {
+ try {
+ if (!data.topic) {
+ throw new Error('Topic not provided. Make sure you have a ' +
+ '"topic" property in your request');
+ }
+ if (!data.message) {
+ throw new Error('Message not provided. Make sure you have a ' +
+ '"message" property in your request');
+ }
+
+ console.log('Publishing message to topic ' + data.topic);
+
+ // The Pub/Sub topic must already exist.
+ var topic = pubsub.topic(data.topic);
+
+ // Pub/Sub messages must be valid JSON objects.
+ return topic.publish({
+ data: {
+ message: data.message
+ }
+ }, function (err) {
+ if (err) {
+ console.error(err);
+ return context.failure(err);
+ }
+ return context.success('Message published');
+ });
+ } catch (err) {
+ console.error(err);
+ return context.failure(err.message);
+ }
+}
+
+/**
+ * Triggered from a message on a Pub/Sub topic.
+ *
+ * @param {Object} context Cloud Function context.
+ * @param {Function} context.success Success callback.
+ * @param {Function} context.failure Failure callback.
+ * @param {Object} data Request data, in this case an object provided by the Pub/Sub trigger.
+ * @param {Object} data.message Message that was published via Pub/Sub.
+ */
+function subscribe (context, data) {
+ // We're just going to log the message to prove that it worked!
+ console.log(data.message);
+
+ // Don't forget to call success!
+ context.success();
+}
+
+exports.publish = publish;
+exports.subscribe = subscribe;
diff --git a/functions/pubsub/package.json b/functions/pubsub/package.json
new file mode 100644
index 0000000000..1f489c2950
--- /dev/null
+++ b/functions/pubsub/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "nodejs-docs-samples-functions-pubsub",
+ "description": "Node.js samples found on https://cloud.google.com",
+ "version": "0.0.1",
+ "private": true,
+ "license": "Apache Version 2.0",
+ "author": "Google Inc.",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
+ },
+ "dependencies": {
+ "gcloud": "^0.35.0"
+ }
+}
diff --git a/package.json b/package.json
index 1357ca8e84..d1542c8ccc 100644
--- a/package.json
+++ b/package.json
@@ -81,6 +81,7 @@
"proxyquire": "^1.7.9",
"request": "^2.72.0",
"semistandard": "^8.0.0",
+ "sinon": "^1.17.4",
"supertest": "^1.2.0"
}
}
diff --git a/test/functions/datastore.test.js b/test/functions/datastore.test.js
new file mode 100644
index 0000000000..4436fe3483
--- /dev/null
+++ b/test/functions/datastore.test.js
@@ -0,0 +1,312 @@
+// 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 test = require('ava');
+var sinon = require('sinon');
+var proxyquire = require('proxyquire').noCallThru();
+
+var KEY = 'key';
+var KIND = 'user';
+
+function getSample () {
+ var datastore = {
+ delete: sinon.stub().callsArg(1),
+ get: sinon.stub().callsArg(1),
+ key: sinon.stub().returns({
+ kind: KIND,
+ path: KEY
+ }),
+ save: sinon.stub().callsArg(1)
+ };
+ var gcloud = {
+ datastore: sinon.stub().returns(datastore)
+ };
+ return {
+ sample: proxyquire('../../functions/datastore', {
+ gcloud: gcloud
+ }),
+ mocks: {
+ gcloud: gcloud,
+ datastore: datastore
+ }
+ };
+}
+
+function getMockContext () {
+ return {
+ success: sinon.stub(),
+ failure: sinon.stub()
+ };
+}
+
+test('set: Set fails without a value', function (t) {
+ var expectedMsg = 'Value not provided. Make sure you have a "value" ' +
+ 'property in your request';
+ var context = getMockContext();
+
+ getSample().sample.set(context, {});
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+});
+
+test('set: Set fails without a key', function (t) {
+ var expectedMsg = 'Key not provided. Make sure you have a "key" ' +
+ 'property in your request';
+ var context = getMockContext();
+
+ getSample().sample.set(context, {
+ value: {}
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+});
+
+test('set: Set fails without a kind', function (t) {
+ var expectedMsg = 'Kind not provided. Make sure you have a "kind" ' +
+ 'property in your request';
+ var context = getMockContext();
+
+ getSample().sample.set(context, {
+ value: {},
+ key: KEY
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+});
+
+test('set: Handles save error', function (t) {
+ var expectedMsg = 'test error';
+ var context = getMockContext();
+ var datastoreSample = getSample();
+
+ datastoreSample.mocks.datastore.save = sinon.stub().callsArgWith(
+ 1,
+ expectedMsg
+ );
+
+ datastoreSample.sample.set(context, {
+ value: {},
+ key: KEY,
+ kind: KIND
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+ t.is(datastoreSample.mocks.datastore.save.calledOnce, true);
+});
+
+test('set: Set saves an entity', function (t) {
+ var expectedMsg = 'Entity saved';
+ var context = getMockContext();
+ var datastoreSample = getSample();
+
+ var data = {
+ value: {
+ name: 'John'
+ },
+ key: KEY,
+ kind: KIND
+ };
+
+ datastoreSample.sample.set(context, data);
+
+ t.is(context.success.calledOnce, true);
+ t.is(context.success.firstCall.args[0], expectedMsg);
+ t.is(context.failure.called, false);
+ t.is(datastoreSample.mocks.datastore.key.calledOnce, true);
+ t.deepEqual(
+ datastoreSample.mocks.datastore.key.firstCall.args[0],
+ [data.kind, data.key]
+ );
+ t.is(datastoreSample.mocks.datastore.save.calledOnce, true);
+ t.deepEqual(datastoreSample.mocks.datastore.save.firstCall.args[0], {
+ key: {
+ kind: data.kind,
+ path: data.key
+ },
+ data: data.value
+ });
+});
+
+test('get: Get fails without a key', function (t) {
+ var expectedMsg = 'Key not provided. Make sure you have a "key" ' +
+ 'property in your request';
+ var context = getMockContext();
+
+ getSample().sample.get(context, {});
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+});
+
+test('get: Get fails without a kind', function (t) {
+ var expectedMsg = 'Kind not provided. Make sure you have a "kind" ' +
+ 'property in your request';
+ var context = getMockContext();
+
+ getSample().sample.get(context, {
+ key: KEY
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+});
+
+test('get: Handles get error', function (t) {
+ var expectedMsg = 'test error';
+ var context = getMockContext();
+ var datastoreSample = getSample();
+
+ datastoreSample.mocks.datastore.get = sinon.stub().callsArgWith(
+ 1,
+ expectedMsg
+ );
+
+ datastoreSample.sample.get(context, {
+ key: KEY,
+ kind: KIND
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+ t.is(datastoreSample.mocks.datastore.get.calledOnce, true);
+});
+
+test('get: Fails when entity does not exist', function (t) {
+ var expectedMsg = 'No entity found for key key';
+ var context = getMockContext();
+ var datastoreSample = getSample();
+
+ datastoreSample.sample.get(context, {
+ key: KEY,
+ kind: KIND
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+ t.is(datastoreSample.mocks.datastore.get.calledOnce, true);
+});
+
+test('get: Finds an entity', function (t) {
+ var expectedResult = {
+ name: 'John'
+ };
+ var context = getMockContext();
+ var datastoreSample = getSample();
+
+ datastoreSample.mocks.datastore.get = sinon.stub().callsArgWith(
+ 1,
+ null,
+ expectedResult
+ );
+ datastoreSample.sample.get(context, {
+ key: KEY,
+ kind: KIND
+ });
+
+ t.is(context.success.calledOnce, true);
+ t.is(context.success.firstCall.args[0], expectedResult);
+ t.is(context.failure.called, false);
+ t.is(datastoreSample.mocks.datastore.get.calledOnce, true);
+ t.deepEqual(
+ datastoreSample.mocks.datastore.get.firstCall.args[0],
+ {
+ path: KEY,
+ kind: KIND
+ }
+ );
+});
+
+test('del: Delete fails without a key', function (t) {
+ var expectedMsg = 'Key not provided. Make sure you have a "key" ' +
+ 'property in your request';
+ var context = getMockContext();
+
+ getSample().sample.del(context, {});
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+});
+
+test('del: Delete fails without a kind', function (t) {
+ var expectedMsg = 'Kind not provided. Make sure you have a "kind" ' +
+ 'property in your request';
+ var context = getMockContext();
+
+ getSample().sample.del(context, {
+ key: KEY
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+});
+
+test('del: Handles delete error', function (t) {
+ var expectedMsg = 'Kind not provided. Make sure you have a "kind" ' +
+ 'property in your request';
+ var context = getMockContext();
+ var datastoreSample = getSample();
+
+ datastoreSample.mocks.datastore.delete = sinon.stub().callsArgWith(
+ 1,
+ expectedMsg
+ );
+
+ datastoreSample.sample.del(context, {
+ key: KEY,
+ kind: KIND
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+ t.is(datastoreSample.mocks.datastore.delete.calledOnce, true);
+});
+
+test('del: Deletes an entity', function (t) {
+ var expectedMsg = 'Entity deleted';
+ var context = getMockContext();
+ var datastoreSample = getSample();
+
+ datastoreSample.sample.del(context, {
+ key: KEY,
+ kind: KIND
+ });
+
+ t.is(context.success.calledOnce, true);
+ t.is(context.success.firstCall.args[0], expectedMsg);
+ t.is(context.failure.called, false);
+ t.is(datastoreSample.mocks.datastore.delete.calledOnce, true);
+ t.deepEqual(
+ datastoreSample.mocks.datastore.delete.firstCall.args[0],
+ {
+ path: KEY,
+ kind: KIND
+ }
+ );
+});
diff --git a/test/functions/gcs.test.js b/test/functions/gcs.test.js
new file mode 100644
index 0000000000..704001f9f3
--- /dev/null
+++ b/test/functions/gcs.test.js
@@ -0,0 +1,109 @@
+// 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 fs = require('fs');
+var path = require('path');
+var test = require('ava');
+var sinon = require('sinon');
+var proxyquire = require('proxyquire').noCallThru();
+
+function getSample () {
+ var file = {
+ createReadStream: function () {
+ var filepath = path.join(__dirname, '../../functions/gcs/sample.txt');
+ return fs.createReadStream(filepath, { encoding: 'utf8' });
+ }
+ };
+ var bucket = {
+ file: sinon.stub().returns(file)
+ };
+ var storage = {
+ bucket: sinon.stub().returns(bucket)
+ };
+ var gcloud = {
+ storage: sinon.stub().returns(storage)
+ };
+ return {
+ sample: proxyquire('../../functions/gcs', {
+ gcloud: gcloud
+ }),
+ mocks: {
+ gcloud: gcloud,
+ storage: storage,
+ bucket: bucket,
+ file: file
+ }
+ };
+}
+
+function getMockContext () {
+ return {
+ success: sinon.stub(),
+ failure: sinon.stub()
+ };
+}
+
+test('Fails without a bucket', function (t) {
+ var expectedMsg = 'Bucket not provided. Make sure you have a "bucket" ' +
+ 'property in your request';
+ var context = getMockContext();
+
+ getSample().sample.wordCount(context, {
+ file: 'file'
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+});
+
+test('Fails without a file', function (t) {
+ var expectedMsg = 'Filename not provided. Make sure you have a "file" ' +
+ 'property in your request';
+ var context = getMockContext();
+
+ getSample().sample.wordCount(context, {
+ bucket: 'bucket'
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+});
+
+test.cb('Reads the file line by line', function (t) {
+ var expectedMsg = 'The file sample.txt has 114 words';
+ var data = {
+ bucket: 'bucket',
+ file: 'sample.txt'
+ };
+ var context = {
+ success: function (message) {
+ t.is(message, expectedMsg);
+ t.end();
+ },
+ failure: function () {
+ t.end('Should have succeeded!');
+ }
+ };
+
+ var gcsSample = getSample();
+ gcsSample.sample.wordCount(context, data);
+
+ t.is(gcsSample.mocks.storage.bucket.calledOnce, true);
+ t.is(gcsSample.mocks.storage.bucket.firstCall.args[0], data.bucket);
+ t.is(gcsSample.mocks.bucket.file.calledOnce, true);
+ t.is(gcsSample.mocks.bucket.file.firstCall.args[0], data.file);
+});
diff --git a/test/functions/log.test.js b/test/functions/log.test.js
index a72a0be532..cb17ec710b 100644
--- a/test/functions/log.test.js
+++ b/test/functions/log.test.js
@@ -14,60 +14,21 @@
'use strict';
var test = require('ava');
+var sinon = require('sinon');
var logSample = require('../../functions/log');
-test.cb('should write to log', function (t) {
- var logMessage = 'I am a log entry!';
- var messageWasPrinted = false;
-
- console.log = function (data) {
- if (data === logMessage) {
- messageWasPrinted = true;
- }
- };
-
- logSample.log({
- success: function (result) {
- t.is(result, undefined);
- if (messageWasPrinted) {
- t.end();
- } else {
- t.end('message was not printed!');
- }
- }
- });
-});
-test.cb('should write to log 2', function (t) {
- var logMessage = 'My GCF Function: foo';
- var messageWasPrinted = false;
-
- console.log = function (data) {
- if (data === logMessage) {
- messageWasPrinted = true;
- }
- };
+test('should write to log', function (t) {
+ var expectedMsg = 'I am a log entry!';
+ sinon.spy(console, 'log');
logSample.helloworld({
success: function (result) {
t.is(result, undefined);
- if (messageWasPrinted) {
- t.end();
- } else {
- t.end('message was not printed!');
- }
- }
- }, {
- message: 'foo'
- });
-});
-test.cb('should write to log 3', function (t) {
- var logMessage = 'My GCF Function: foo';
- logSample.hellohttp({
- success: function (result) {
- t.is(result, logMessage);
- t.end();
- }
- }, {
- message: 'foo'
+ t.is(console.log.calledOnce, true);
+ t.is(console.log.calledWith(expectedMsg), true);
+ },
+ failure: t.fail
});
+
+ console.log.restore();
});
diff --git a/test/functions/log2.test.js b/test/functions/log2.test.js
new file mode 100644
index 0000000000..4457758e01
--- /dev/null
+++ b/test/functions/log2.test.js
@@ -0,0 +1,47 @@
+// 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 test = require('ava');
+var sinon = require('sinon');
+var log2Sample = require('../../functions/log2');
+
+test('should write to log 2', function (t) {
+ var expectedMsg = 'My GCF Function: foo';
+ sinon.spy(console, 'log');
+
+ log2Sample.helloworld({
+ success: function (result) {
+ t.is(result, undefined);
+ t.is(console.log.calledOnce, true);
+ t.is(console.log.calledWith(expectedMsg), true);
+ },
+ failure: t.fail
+ }, {
+ message: 'foo'
+ });
+
+ console.log.restore();
+});
+test('should write to log 3', function (t) {
+ var logMessage = 'My GCF Function: foo';
+ log2Sample.hellohttp({
+ success: function (result) {
+ t.is(result, logMessage);
+ },
+ failure: t.fail
+ }, {
+ message: 'foo'
+ });
+});
diff --git a/test/functions/pubsub.test.js b/test/functions/pubsub.test.js
new file mode 100644
index 0000000000..90d32a7c51
--- /dev/null
+++ b/test/functions/pubsub.test.js
@@ -0,0 +1,146 @@
+// 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 test = require('ava');
+var sinon = require('sinon');
+var proxyquire = require('proxyquire').noCallThru();
+
+function getSample () {
+ var topic = {
+ publish: sinon.stub().callsArg(1)
+ };
+ var pubsub = {
+ topic: sinon.stub().returns(topic)
+ };
+ var gcloud = {
+ pubsub: sinon.stub().returns(pubsub)
+ };
+ return {
+ sample: proxyquire('../../functions/pubsub', {
+ gcloud: gcloud
+ }),
+ mocks: {
+ gcloud: gcloud,
+ pubsub: pubsub,
+ topic: topic
+ }
+ };
+}
+
+function getMockContext () {
+ return {
+ success: sinon.stub(),
+ failure: sinon.stub()
+ };
+}
+
+test('Publish fails without a topic', function (t) {
+ var expectedMsg = 'Topic not provided. Make sure you have a "topic" ' +
+ 'property in your request';
+ var context = getMockContext();
+
+ getSample().sample.publish(context, {
+ message: 'message'
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+});
+
+test('Publish fails without a message', function (t) {
+ var expectedMsg = 'Message not provided. Make sure you have a "message" ' +
+ 'property in your request';
+ var context = getMockContext();
+
+ getSample().sample.publish(context, {
+ topic: 'topic'
+ });
+
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(context.success.called, false);
+});
+
+test('Publishes the message to the topic and calls success', function (t) {
+ var expectedMsg = 'Message published';
+ var data = {
+ topic: 'topic',
+ message: 'message'
+ };
+ var context = getMockContext();
+
+ var pubsubSample = getSample();
+ pubsubSample.sample.publish(context, data);
+
+ t.is(context.success.calledOnce, true);
+ t.is(context.success.firstCall.args[0], expectedMsg);
+ t.is(context.failure.called, false);
+ t.is(pubsubSample.mocks.pubsub.topic.calledOnce, true);
+ t.deepEqual(pubsubSample.mocks.pubsub.topic.firstCall.args[0], data.topic);
+ t.is(pubsubSample.mocks.topic.publish.calledOnce, true);
+ t.deepEqual(pubsubSample.mocks.topic.publish.firstCall.args[0], {
+ data: {
+ message: data.message
+ }
+ });
+});
+
+test('Fails to publish the message and calls failure', function (t) {
+ var expectedMsg = 'error';
+ var data = {
+ topic: 'topic',
+ message: 'message'
+ };
+ var context = getMockContext();
+
+ var pubsubSample = getSample();
+ pubsubSample.mocks.topic.publish = sinon.stub().callsArgWith(1, expectedMsg);
+
+ pubsubSample.sample.publish(context, data);
+
+ t.is(context.success.called, false);
+ t.is(context.failure.calledOnce, true);
+ t.is(context.failure.firstCall.args[0], expectedMsg);
+ t.is(pubsubSample.mocks.pubsub.topic.calledOnce, true);
+ t.deepEqual(pubsubSample.mocks.pubsub.topic.firstCall.args[0], data.topic);
+ t.is(pubsubSample.mocks.topic.publish.calledOnce, true);
+ t.deepEqual(pubsubSample.mocks.topic.publish.firstCall.args[0], {
+ data: {
+ message: data.message
+ }
+ });
+});
+
+test('Subscribes to a message', function (t) {
+ var expectedMsg = 'message';
+ var data = {
+ topic: 'topic',
+ message: expectedMsg
+ };
+ var context = getMockContext();
+
+ var pubsubSample = getSample();
+ sinon.spy(console, 'log');
+
+ pubsubSample.sample.subscribe(context, data);
+
+ t.is(console.log.calledOnce, true);
+ t.is(console.log.firstCall.args[0], expectedMsg);
+ t.is(context.success.calledOnce, true);
+ t.is(context.failure.called, false);
+
+ console.log.restore();
+});