`;
- utils.getRequest(config)
- .get(`/books/${id}`)
- .expect(200)
- .expect((response) => {
- assert.equal(response.text.includes(expected), true);
- })
- .end(done);
- });
-
- it(`should delete a book`, (done) => {
- const expected = `Redirecting to /books`;
- utils.getRequest(config)
- .get(`/books/${id}/delete`)
- .expect(302)
- .expect((response) => {
- id = undefined;
- assert.equal(response.text.includes(expected), true);
- })
- .end(done);
- });
-
- // clean up if necessary
- after((done) => {
- if (id) {
- utils.getRequest(config)
- .delete(`/api/books/${id}`)
- .expect(200)
- .end(done);
- } else {
- done();
- }
- });
- });
-
- after(() => {
- require(`../config`).set(`DATA_BACKEND`, ORIG_DATA_BACKEND);
- });
- });
-};
diff --git a/2-structured-data/test/datastore.test.js b/2-structured-data/test/datastore.test.js
new file mode 100644
index 0000000000..a4faa673b6
--- /dev/null
+++ b/2-structured-data/test/datastore.test.js
@@ -0,0 +1,23 @@
+// Copyright 2017, 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';
+
+const test = require(`ava`);
+
+if (require(`../config`).get(`DATA_BACKEND`) === `datastore` || process.env.TEST_DATASTORE) {
+ require(`./_api-tests`)(`datastore`);
+ require(`./_crud-tests`)(`datastore`);
+} else {
+ test(`Skipping Cloud Datastore tests...`, (t) => t.pass());
+}
diff --git a/2-structured-data/test/index.js b/2-structured-data/test/index.js
deleted file mode 100644
index 7c479e29f6..0000000000
--- a/2-structured-data/test/index.js
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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';
-
-const config = require(`./config`);
-const utils = require(`nodejs-repo-tools`);
-
-describe(`${config.test}/`, () => {
- if (!process.env.E2E_TESTS) {
- it(`should install dependencies`, (done) => {
- utils.testInstallation(config, done);
- }).timeout(120 * 1000);
- }
- require(`./app.test`);
- describe(`books/`, () => {
- const appConfig = require(`../config`);
- const DATA_BACKEND = appConfig.get(`DATA_BACKEND`);
- if (DATA_BACKEND === `datastore` || process.env.TEST_DATASTORE) {
- require(`./api.test`)(`datastore`);
- require(`./crud.test`)(`datastore`);
- }
- if (DATA_BACKEND === `cloudsql` || process.env.TEST_CLOUDSQL) {
- require(`./api.test`)(`cloudsql`);
- require(`./crud.test`)(`cloudsql`);
- }
- if (DATA_BACKEND === `mongodb` || process.env.TEST_MONGODB) {
- require(`./api.test`)(`mongodb`);
- require(`./crud.test`)(`mongodb`);
- }
- });
-});
diff --git a/2-structured-data/test/mongodb.test.js b/2-structured-data/test/mongodb.test.js
new file mode 100644
index 0000000000..c7b49311b0
--- /dev/null
+++ b/2-structured-data/test/mongodb.test.js
@@ -0,0 +1,23 @@
+// Copyright 2017, 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';
+
+const test = require(`ava`);
+
+if (require(`../config`).get(`DATA_BACKEND`) === `mongodb` || process.env.TEST_MONGODB) {
+ require(`./_api-tests`)(`mongodb`);
+ require(`./_crud-tests`)(`mongodb`);
+} else {
+ test(`Skipping MongoDB tests...`, (t) => t.pass());
+}
diff --git a/2-structured-data/views/base.jade b/2-structured-data/views/base.jade
index 7a93bf7156..13a8d6ca29 100644
--- a/2-structured-data/views/base.jade
+++ b/2-structured-data/views/base.jade
@@ -1,4 +1,4 @@
-//- Copyright 2015-2016, Google, Inc.
+//- Copyright 2017, 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
diff --git a/2-structured-data/views/books/form.jade b/2-structured-data/views/books/form.jade
index 24fcc7c0bc..5d0dd03bf2 100644
--- a/2-structured-data/views/books/form.jade
+++ b/2-structured-data/views/books/form.jade
@@ -1,4 +1,4 @@
-//- Copyright 2015-2016, Google, Inc.
+//- Copyright 2017, 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
diff --git a/2-structured-data/views/books/list.jade b/2-structured-data/views/books/list.jade
index 3ecd5b5f05..7fe931779a 100644
--- a/2-structured-data/views/books/list.jade
+++ b/2-structured-data/views/books/list.jade
@@ -1,4 +1,4 @@
-//- Copyright 2015-2016, Google, Inc.
+//- Copyright 2017, 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
diff --git a/2-structured-data/views/books/view.jade b/2-structured-data/views/books/view.jade
index 760730fed6..ad390ee04c 100644
--- a/2-structured-data/views/books/view.jade
+++ b/2-structured-data/views/books/view.jade
@@ -1,4 +1,4 @@
-//- Copyright 2015-2016, Google, Inc.
+//- Copyright 2017, 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
diff --git a/3-binary-data/README.md b/3-binary-data/README.md
index 4c5d333077..d02baf4d22 100644
--- a/3-binary-data/README.md
+++ b/3-binary-data/README.md
@@ -5,3 +5,96 @@ tutorial. Please refer to the tutorial for instructions on configuring, running,
and deploying this sample.
[step-3]: https://cloud.google.com/nodejs/getting-started/using-cloud-storage
+
+# Simple instructions
+
+1. Install [Node.js](https://nodejs.org/en/).
+
+ * Optional: Install [Yarn](https://yarnpkg.com/).
+
+1. Install [git](https://git-scm.com/).
+1. Create a [Google Cloud Platform project](https://console.cloud.google.com).
+1. Install the [Google Cloud SDK](https://cloud.google.com/sdk/).
+
+ * After downloading the SDK, initialize it:
+
+ gcloud init
+
+1. Acquire local credentials for authenticating with Google Cloud Platform
+ services:
+
+ gcloud beta auth application-default login
+
+1. Clone the repository:
+
+ git clone https://github.com/GoogleCloudPlatform/nodejs-getting-started.git
+
+1. Change directory:
+
+ cd nodejs-getting-started/3-binary-data
+
+1. Create a `config.json` file (copied from the `config-default.json` file):
+
+ cp config-default.json config.json
+
+ * Set `GCLOUD_PROJECT` in `config.json` to your Google Cloud Platform
+ project ID.
+ * Set `DATA_BACKEND` in `config.json` to one of `"datastore"`, `"cloudsql"`,
+ or `"mongodb"`.
+ * Set `CLOUD_BUCKET` in `config.json` to the name of your Google Cloud
+ Storage bucket.
+
+1. Install dependencies using NPM or Yarn:
+
+ * Using NPM:
+
+ npm install
+
+ * Using Yarn:
+
+ yarn install
+
+1. Configure the backing store:
+
+ * If `DATA_BACKEND` is set to `"cloudsql"`:
+
+ 1. Create a Cloud SQL instance, and download and start the Cloud SQL
+ proxy:
+
+ Instructions for doing so: https://cloud.google.com/nodejs/getting-started/using-cloud-sql#creating_a_cloud_sql_instance
+ 1. Set `MYSQL_USER` in `config.json`, e.g. `"my-cloudsql-username"`.
+ 1. Set `MYSQL_PASSWORD` in `config.json`, e.g. `"my-cloudsql-password"`.
+ 1. Set `INSTANCE_CONNECTION_NAME` in `config.json`, e.g. `"YOUR_PROJECT_ID:YOUR_REGION:YOUR_INSTANCE_ID"`.
+ 1. Run the script to setup the table:
+
+ * Using NPM:
+
+ npm run init-cloudsql
+
+ * Using Yarn:
+
+ yarn run init-cloudsql
+
+ * If `DATA_BACKEND` is set to `"mongodb"`:
+
+ 1. Set `MONGO_URL` in `config.json`, e.g. `"mongodb://username:password@123.45.67.890:27017"`.
+
+1. Start the app using NPM or Yarn:
+
+ * Using NPM:
+
+ npm start
+
+ * Using Yarn:
+
+ yarn start
+
+1. View the app at [http://localhost:8080](http://localhost:8080).
+
+1. Stop the app by pressing `Ctrl+C`.
+
+1. Deploy the app:
+
+ gcloud app deploy
+
+1. View the deployed app at [https://YOUR_PROJECT_ID.appspot.com](https://YOUR_PROJECT_ID.appspot.com).
diff --git a/3-binary-data/app.js b/3-binary-data/app.js
index 0865e03fd1..b8fd1c86f8 100644
--- a/3-binary-data/app.js
+++ b/3-binary-data/app.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
diff --git a/3-binary-data/app.yaml b/3-binary-data/app.yaml
index 7b396d0773..15bb61da94 100644
--- a/3-binary-data/app.yaml
+++ b/3-binary-data/app.yaml
@@ -1,4 +1,4 @@
-# Copyright 2015-2016, Google, Inc.
+# Copyright 2017, 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
diff --git a/3-binary-data/books/api.js b/3-binary-data/books/api.js
index a5afadb6e5..98b65fc587 100644
--- a/3-binary-data/books/api.js
+++ b/3-binary-data/books/api.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -15,10 +15,9 @@
const express = require('express');
const bodyParser = require('body-parser');
-const config = require('../config');
function getModel () {
- return require(`./model-${config.get('DATA_BACKEND')}`);
+ return require(`./model-${require('../config').get('DATA_BACKEND')}`);
}
const router = express.Router();
diff --git a/3-binary-data/books/crud.js b/3-binary-data/books/crud.js
index bb2019e298..debf0db525 100644
--- a/3-binary-data/books/crud.js
+++ b/3-binary-data/books/crud.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -14,15 +14,18 @@
'use strict';
const express = require('express');
-const config = require('../config');
+const bodyParser = require('body-parser');
const images = require('../lib/images');
function getModel () {
- return require(`./model-${config.get('DATA_BACKEND')}`);
+ return require(`./model-${require('../config').get('DATA_BACKEND')}`);
}
const router = express.Router();
+// Automatically parse request body as form data
+router.use(bodyParser.urlencoded({ extended: false }));
+
// Set Content-Type for all responses for these routes
router.use((req, res, next) => {
res.set('Content-Type', 'text/html');
diff --git a/3-binary-data/books/model-cloudsql.js b/3-binary-data/books/model-cloudsql.js
index 001c7f35d6..b78f8015e4 100644
--- a/3-binary-data/books/model-cloudsql.js
+++ b/3-binary-data/books/model-cloudsql.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -17,23 +17,20 @@ const extend = require('lodash').assign;
const mysql = require('mysql');
const config = require('../config');
-function getConnection () {
- const options = {
- user: config.get('MYSQL_USER'),
- password: config.get('MYSQL_PASSWORD'),
- database: 'bookshelf'
- };
-
- if (config.get('INSTANCE_CONNECTION_NAME') && config.get('NODE_ENV') === 'production') {
- options.socketPath = `/cloudsql/${config.get('INSTANCE_CONNECTION_NAME')}`;
- }
+const options = {
+ user: config.get('MYSQL_USER'),
+ password: config.get('MYSQL_PASSWORD'),
+ database: 'bookshelf'
+};
- return mysql.createConnection(options);
+if (config.get('INSTANCE_CONNECTION_NAME') && config.get('NODE_ENV') === 'production') {
+ options.socketPath = `/cloudsql/${config.get('INSTANCE_CONNECTION_NAME')}`;
}
+const connection = mysql.createConnection(options);
+
function list (limit, token, cb) {
token = token ? parseInt(token, 10) : 0;
- const connection = getConnection();
connection.query(
'SELECT * FROM `books` LIMIT ? OFFSET ?', [limit, token],
(err, results) => {
@@ -45,11 +42,9 @@ function list (limit, token, cb) {
cb(null, results, hasMore);
}
);
- connection.end();
}
function create (data, cb) {
- const connection = getConnection();
connection.query('INSERT INTO `books` SET ?', data, (err, res) => {
if (err) {
cb(err);
@@ -57,11 +52,9 @@ function create (data, cb) {
}
read(res.insertId, cb);
});
- connection.end();
}
function read (id, cb) {
- const connection = getConnection();
connection.query(
'SELECT * FROM `books` WHERE `id` = ?', id, (err, results) => {
if (err) {
@@ -77,11 +70,9 @@ function read (id, cb) {
}
cb(null, results[0]);
});
- connection.end();
}
function update (id, data, cb) {
- const connection = getConnection();
connection.query(
'UPDATE `books` SET ? WHERE `id` = ?', [data, id], (err) => {
if (err) {
@@ -90,13 +81,10 @@ function update (id, data, cb) {
}
read(id, cb);
});
- connection.end();
}
function _delete (id, cb) {
- const connection = getConnection();
connection.query('DELETE FROM `books` WHERE `id` = ?', id, cb);
- connection.end();
}
module.exports = {
diff --git a/3-binary-data/books/model-datastore.js b/3-binary-data/books/model-datastore.js
index a8bbacf854..5473fd9f20 100644
--- a/3-binary-data/books/model-datastore.js
+++ b/3-binary-data/books/model-datastore.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -38,8 +38,8 @@ const kind = 'Book';
// property: value
// }
function fromDatastore (obj) {
- obj.data.id = obj.key.id;
- return obj.data;
+ obj.id = obj[Datastore.KEY].id;
+ return obj;
}
// Translates from the application's format to the datastore's
diff --git a/3-binary-data/books/model-mongodb.js b/3-binary-data/books/model-mongodb.js
index 0c22846b36..c5c9986bed 100644
--- a/3-binary-data/books/model-mongodb.js
+++ b/3-binary-data/books/model-mongodb.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
diff --git a/3-binary-data/config.js b/3-binary-data/config.js
index 44c5a53f7a..fd8048186d 100644
--- a/3-binary-data/config.js
+++ b/3-binary-data/config.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -26,10 +26,12 @@ nconf
'CLOUD_BUCKET',
'DATA_BACKEND',
'GCLOUD_PROJECT',
+ 'INSTANCE_CONNECTION_NAME',
'MONGO_URL',
'MONGO_COLLECTION',
'MYSQL_USER',
'MYSQL_PASSWORD',
+ 'NODE_ENV',
'PORT'
])
// 3. Config file
@@ -56,7 +58,6 @@ nconf
MYSQL_USER: '',
MYSQL_PASSWORD: '',
- // Port the HTTP server
PORT: 8080
});
diff --git a/3-binary-data/lib/images.js b/3-binary-data/lib/images.js
index d16c00e56f..879058c6fa 100644
--- a/3-binary-data/lib/images.js
+++ b/3-binary-data/lib/images.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
diff --git a/3-binary-data/package.json b/3-binary-data/package.json
index 63e32dbaaf..8456c56e53 100644
--- a/3-binary-data/package.json
+++ b/3-binary-data/package.json
@@ -1,14 +1,13 @@
{
"name": "nodejs-getting-started",
"version": "1.0.0",
- "description": "End to end sample for running Node.JS applications on Google Cloud Platform",
+ "description": "End to end sample for running Node.js applications on Google Cloud Platform",
"repository": "https://github.com/GoogleCloudPlatform/nodejs-getting-started",
"private": true,
"scripts": {
"start": "node app.js",
- "monitor": "nodemon app.js",
- "deploy": "gcloud app deploy app.yaml",
- "test": "mocha test/index.js -t 30000",
+ "test": "ava -t 30s --tap test/*.test.js | tap-dot",
+ "cover": "nyc --cache npm test; nyc report --reporter=html",
"init-cloudsql": "node books/model-cloudsql.js"
},
"author": "Google Inc.",
@@ -24,9 +23,13 @@
{
"name": "Jason Dobry",
"email": "jdobry@google.com"
+ },
+ {
+ "name": "Ace Nassri",
+ "email": "anassri@google.com"
}
],
- "license": "Apache Version 2.0",
+ "license": "Apache-2.0",
"semistandard": {
"globals": [
"after",
@@ -38,24 +41,24 @@
]
},
"dependencies": {
- "@google-cloud/storage": "^0.1.1",
- "body-parser": "^1.15.2",
- "express": "^4.14.0",
- "@google-cloud/datastore": "^0.1.1",
- "jade": "^1.11.0",
- "kerberos": "^0.0.21",
- "lodash": "^4.14.2",
- "mongodb": "^2.2.5",
- "multer": "^1.2.0",
- "mysql": "^2.11.1",
- "nconf": "^0.8.4",
- "prompt": "^1.0.0"
+ "@google-cloud/datastore": "~1.0.0",
+ "@google-cloud/storage": "~1.1.0",
+ "body-parser": "~1.17.1",
+ "express": "~4.15.2",
+ "jade": "~1.11.0",
+ "lodash": "~4.17.4",
+ "mongodb": "~2.2.25",
+ "multer": "~1.3.0",
+ "mysql": "~2.13.0",
+ "nconf": "~0.8.4",
+ "prompt": "~1.0.0"
},
"devDependencies": {
- "mocha": "^3.0.2",
+ "ava": "~0.19.1",
"nodejs-repo-tools": "git+https://git@github.com/GoogleCloudPlatform/nodejs-repo-tools.git",
- "proxyquire": "^1.7.10",
- "sinon": "^1.17.5"
+ "proxyquire": "~1.7.11",
+ "sinon": "~2.1.0",
+ "tap-dot": "~1.0.5"
},
"engines": {
"node": ">=4.3.2"
diff --git a/3-binary-data/test/_api-tests.js b/3-binary-data/test/_api-tests.js
new file mode 100644
index 0000000000..a6f3ad270b
--- /dev/null
+++ b/3-binary-data/test/_api-tests.js
@@ -0,0 +1,69 @@
+// Copyright 2017, 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';
+
+const getRequest = require(`nodejs-repo-tools`).getRequest;
+const test = require(`ava`);
+
+module.exports = (DATA_BACKEND) => {
+ let originalDataBackend, id, testConfig, appConfig;
+
+ test.before(() => {
+ testConfig = require(`./_test-config`);
+ appConfig = require(`../config`);
+ originalDataBackend = appConfig.get(`DATA_BACKEND`);
+ appConfig.set(`DATA_BACKEND`, DATA_BACKEND);
+ });
+
+ test.serial.cb(`should create a book`, (t) => {
+ getRequest(testConfig)
+ .post(`/api/books`)
+ .send({ title: `beep` })
+ .expect(200)
+ .expect((response) => {
+ id = response.body.id;
+ t.truthy(response.body.id);
+ t.is(response.body.title, `beep`);
+ })
+ .end(t.end);
+ });
+
+ test.serial.cb(`should list books`, (t) => {
+ // Give Datastore time to become consistent
+ setTimeout(() => {
+ getRequest(testConfig)
+ .get(`/api/books`)
+ .expect(200)
+ .expect((response) => {
+ t.true(Array.isArray(response.body.items));
+ t.true(response.body.items.length >= 1);
+ })
+ .end(t.end);
+ }, 1000);
+ });
+
+ test.serial.cb(`should delete a book`, (t) => {
+ getRequest(testConfig)
+ .delete(`/api/books/${id}/`)
+ // .expect(200)
+ .expect((response) => {
+ t.is(response.text, `OK`);
+ })
+ .end(t.end);
+ });
+
+ test.always.after(() => {
+ appConfig.set(`DATA_BACKEND`, originalDataBackend);
+ });
+};
diff --git a/3-binary-data/test/_crud-tests.js b/3-binary-data/test/_crud-tests.js
new file mode 100644
index 0000000000..a3da03c520
--- /dev/null
+++ b/3-binary-data/test/_crud-tests.js
@@ -0,0 +1,193 @@
+// Copyright 2017, 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';
+
+const getRequest = require(`nodejs-repo-tools`).getRequest;
+const test = require(`ava`);
+
+module.exports = (DATA_BACKEND) => {
+ let originalDataBackend, id, testConfig, appConfig;
+
+ test.before(() => {
+ testConfig = require(`./_test-config`);
+ appConfig = require(`../config`);
+ originalDataBackend = appConfig.get(`DATA_BACKEND`);
+ appConfig.set(`DATA_BACKEND`, DATA_BACKEND);
+ });
+
+ // setup a book
+ test.serial.cb(`should create a book`, (t) => {
+ getRequest(testConfig)
+ .post(`/api/books`)
+ .send({ title: `my book` })
+ .expect(200)
+ .expect((response) => {
+ id = response.body.id;
+ t.truthy(response.body.id);
+ t.is(response.body.title, `my book`);
+ })
+ .end(t.end);
+ });
+
+ test.serial.cb(`should show a list of books`, (t) => {
+ // Give Datastore time to become consistent
+ setTimeout(() => {
+ const expected = /
`;
- utils.getRequest(config)
- .get(`/books/${id}`)
- .expect(200)
- .expect((response) => {
- assert.equal(response.text.includes(expected), true);
- })
- .end(done);
- });
-
- it(`should delete a book`, (done) => {
- const expected = `Redirecting to /books`;
- utils.getRequest(config)
- .get(`/books/${id}/delete`)
- .expect(302)
- .expect((response) => {
- id = undefined;
- assert.equal(response.text.includes(expected), true);
- })
- .end(done);
- });
-
- // clean up if necessary
- after((done) => {
- if (id) {
- utils.getRequest(config)
- .delete(`/api/books/${id}`)
- .expect(200)
- .end(done);
- } else {
- done();
- }
- });
- });
-
- after(() => {
- require(`../config`).set(`DATA_BACKEND`, ORIG_DATA_BACKEND);
- });
- });
-};
diff --git a/3-binary-data/test/datastore.test.js b/3-binary-data/test/datastore.test.js
new file mode 100644
index 0000000000..a4faa673b6
--- /dev/null
+++ b/3-binary-data/test/datastore.test.js
@@ -0,0 +1,23 @@
+// Copyright 2017, 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';
+
+const test = require(`ava`);
+
+if (require(`../config`).get(`DATA_BACKEND`) === `datastore` || process.env.TEST_DATASTORE) {
+ require(`./_api-tests`)(`datastore`);
+ require(`./_crud-tests`)(`datastore`);
+} else {
+ test(`Skipping Cloud Datastore tests...`, (t) => t.pass());
+}
diff --git a/3-binary-data/test/index.js b/3-binary-data/test/index.js
deleted file mode 100644
index 7c479e29f6..0000000000
--- a/3-binary-data/test/index.js
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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';
-
-const config = require(`./config`);
-const utils = require(`nodejs-repo-tools`);
-
-describe(`${config.test}/`, () => {
- if (!process.env.E2E_TESTS) {
- it(`should install dependencies`, (done) => {
- utils.testInstallation(config, done);
- }).timeout(120 * 1000);
- }
- require(`./app.test`);
- describe(`books/`, () => {
- const appConfig = require(`../config`);
- const DATA_BACKEND = appConfig.get(`DATA_BACKEND`);
- if (DATA_BACKEND === `datastore` || process.env.TEST_DATASTORE) {
- require(`./api.test`)(`datastore`);
- require(`./crud.test`)(`datastore`);
- }
- if (DATA_BACKEND === `cloudsql` || process.env.TEST_CLOUDSQL) {
- require(`./api.test`)(`cloudsql`);
- require(`./crud.test`)(`cloudsql`);
- }
- if (DATA_BACKEND === `mongodb` || process.env.TEST_MONGODB) {
- require(`./api.test`)(`mongodb`);
- require(`./crud.test`)(`mongodb`);
- }
- });
-});
diff --git a/3-binary-data/test/mongodb.test.js b/3-binary-data/test/mongodb.test.js
new file mode 100644
index 0000000000..c7b49311b0
--- /dev/null
+++ b/3-binary-data/test/mongodb.test.js
@@ -0,0 +1,23 @@
+// Copyright 2017, 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';
+
+const test = require(`ava`);
+
+if (require(`../config`).get(`DATA_BACKEND`) === `mongodb` || process.env.TEST_MONGODB) {
+ require(`./_api-tests`)(`mongodb`);
+ require(`./_crud-tests`)(`mongodb`);
+} else {
+ test(`Skipping MongoDB tests...`, (t) => t.pass());
+}
diff --git a/3-binary-data/views/base.jade b/3-binary-data/views/base.jade
index 7a93bf7156..13a8d6ca29 100644
--- a/3-binary-data/views/base.jade
+++ b/3-binary-data/views/base.jade
@@ -1,4 +1,4 @@
-//- Copyright 2015-2016, Google, Inc.
+//- Copyright 2017, 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
diff --git a/3-binary-data/views/books/form.jade b/3-binary-data/views/books/form.jade
index 36a6308a9d..9b76d983bf 100644
--- a/3-binary-data/views/books/form.jade
+++ b/3-binary-data/views/books/form.jade
@@ -1,4 +1,4 @@
-//- Copyright 2015-2016, Google, Inc.
+//- Copyright 2017, 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
diff --git a/3-binary-data/views/books/list.jade b/3-binary-data/views/books/list.jade
index dd453386ba..f7e901c3b3 100644
--- a/3-binary-data/views/books/list.jade
+++ b/3-binary-data/views/books/list.jade
@@ -1,4 +1,4 @@
-//- Copyright 2015-2016, Google, Inc.
+//- Copyright 2017, 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
diff --git a/3-binary-data/views/books/view.jade b/3-binary-data/views/books/view.jade
index 55e7a4532c..ec14edb6c1 100644
--- a/3-binary-data/views/books/view.jade
+++ b/3-binary-data/views/books/view.jade
@@ -1,4 +1,4 @@
-//- Copyright 2015-2016, Google, Inc.
+//- Copyright 2017, 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
diff --git a/4-auth/README.md b/4-auth/README.md
index 4346802b98..8d46f501de 100644
--- a/4-auth/README.md
+++ b/4-auth/README.md
@@ -5,3 +5,98 @@ tutorial. Please refer to the tutorial for instructions on configuring, running,
and deploying this sample.
[step-4]: https://cloud.google.com/nodejs/getting-started/authenticate-users
+
+# Simple instructions
+
+1. Install [Node.js](https://nodejs.org/en/).
+
+ * Optional: Install [Yarn](https://yarnpkg.com/).
+
+1. Install [git](https://git-scm.com/).
+1. Create a [Google Cloud Platform project](https://console.cloud.google.com).
+1. Install the [Google Cloud SDK](https://cloud.google.com/sdk/).
+
+ * After downloading the SDK, initialize it:
+
+ gcloud init
+
+1. Acquire local credentials for authenticating with Google Cloud Platform
+ services:
+
+ gcloud beta auth application-default login
+
+1. Clone the repository:
+
+ git clone https://github.com/GoogleCloudPlatform/nodejs-getting-started.git
+
+1. Change directory:
+
+ cd nodejs-getting-started/4-auth
+
+1. Create a `config.json` file (copied from the `config-default.json` file):
+
+ cp config-default.json config.json
+
+ * Set `GCLOUD_PROJECT` in `config.json` to your Google Cloud Platform
+ project ID.
+ * Set `DATA_BACKEND` in `config.json` to one of `"datastore"`, `"cloudsql"`,
+ or `"mongodb"`.
+ * Set `CLOUD_BUCKET` in `config.json` to the name of your Google Cloud
+ Storage bucket.
+ * Set `OAUTH2_CLIENT_ID` in `config.json`.
+ * Set `OAUTH2_CLIENT_SECRET` in `config.json`.
+
+1. Install dependencies using NPM or Yarn:
+
+ * Using NPM:
+
+ npm install
+
+ * Using Yarn:
+
+ yarn install
+
+1. Configure the backing store:
+
+ * If `DATA_BACKEND` is set to `"cloudsql"`:
+
+ 1. Create a Cloud SQL instance, and download and start the Cloud SQL
+ proxy:
+
+ Instructions for doing so: https://cloud.google.com/nodejs/getting-started/using-cloud-sql#creating_a_cloud_sql_instance
+ 1. Set `MYSQL_USER` in `config.json`, e.g. `"my-cloudsql-username"`.
+ 1. Set `MYSQL_PASSWORD` in `config.json`, e.g. `"my-cloudsql-password"`.
+ 1. Set `INSTANCE_CONNECTION_NAME` in `config.json`, e.g. `"YOUR_PROJECT_ID:YOUR_REGION:YOUR_INSTANCE_ID"`.
+ 1. Run the script to setup the table:
+
+ * Using NPM:
+
+ npm run init-cloudsql
+
+ * Using Yarn:
+
+ yarn run init-cloudsql
+
+ * If `DATA_BACKEND` is set to `"mongodb"`:
+
+ 1. Set `MONGO_URL` in `config.json`, e.g. `"mongodb://username:password@123.45.67.890:27017"`.
+
+1. Start the app using NPM or Yarn:
+
+ * Using NPM:
+
+ npm start
+
+ * Using Yarn:
+
+ yarn start
+
+1. View the app at [http://localhost:8080](http://localhost:8080).
+
+1. Stop the app by pressing `Ctrl+C`.
+
+1. Deploy the app:
+
+ gcloud app deploy
+
+1. View the deployed app at [https://YOUR_PROJECT_ID.appspot.com](https://YOUR_PROJECT_ID.appspot.com).
diff --git a/4-auth/app.js b/4-auth/app.js
index 8a5b4bed41..67ccc689d0 100644
--- a/4-auth/app.js
+++ b/4-auth/app.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -38,7 +38,7 @@ const sessionConfig = {
// In production use the App Engine Memcache instance to store session data,
// otherwise fallback to the default MemoryStore in development.
-if (config.get('NODE_ENV') === 'production') {
+if (config.get('NODE_ENV') === 'production' && config.get('MEMCACHE_URL')) {
sessionConfig.store = new MemcachedStore({
hosts: [config.get('MEMCACHE_URL')]
});
diff --git a/4-auth/app.yaml b/4-auth/app.yaml
index 7b396d0773..054a32e739 100644
--- a/4-auth/app.yaml
+++ b/4-auth/app.yaml
@@ -1,4 +1,4 @@
-# Copyright 2015-2016, Google, Inc.
+# Copyright 2017, 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,7 +13,3 @@
#
runtime: nodejs
env: flex
-
-# Temporary setting to keep gcloud from uploading node_modules
-skip_files:
- - ^node_modules$
diff --git a/4-auth/books/api.js b/4-auth/books/api.js
index f23ad7ffe9..98b65fc587 100644
--- a/4-auth/books/api.js
+++ b/4-auth/books/api.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -15,10 +15,9 @@
const express = require('express');
const bodyParser = require('body-parser');
-const config = require('../config');
function getModel () {
- return require(`./model-${config.get('DATA_BACKEND')}`);
+ return require(`./model-${require('../config').get('DATA_BACKEND')}`);
}
const router = express.Router();
@@ -107,7 +106,7 @@ router.delete('/:book', (req, res, next) => {
/**
* Errors on "/api/books/*" routes.
*/
-router.use(() => (err, req, res, next) => {
+router.use((err, req, res, next) => {
// Format error and forward to generic error handler for logging and
// responding to the request
err.response = {
diff --git a/4-auth/books/crud.js b/4-auth/books/crud.js
index 22f6439938..447358e344 100644
--- a/4-auth/books/crud.js
+++ b/4-auth/books/crud.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -14,12 +14,11 @@
'use strict';
const express = require('express');
-const config = require('../config');
const images = require('../lib/images');
const oauth2 = require('../lib/oauth2');
function getModel () {
- return require(`./model-${config.get('DATA_BACKEND')}`);
+ return require(`./model-${require('../config').get('DATA_BACKEND')}`);
}
const router = express.Router();
diff --git a/4-auth/books/model-cloudsql.js b/4-auth/books/model-cloudsql.js
index 228d6ad8f8..d3a0d36a52 100644
--- a/4-auth/books/model-cloudsql.js
+++ b/4-auth/books/model-cloudsql.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -17,23 +17,20 @@ const extend = require('lodash').assign;
const mysql = require('mysql');
const config = require('../config');
-function getConnection () {
- const options = {
- user: config.get('MYSQL_USER'),
- password: config.get('MYSQL_PASSWORD'),
- database: 'bookshelf'
- };
-
- if (config.get('INSTANCE_CONNECTION_NAME') && config.get('NODE_ENV') === 'production') {
- options.socketPath = `/cloudsql/${config.get('INSTANCE_CONNECTION_NAME')}`;
- }
+const options = {
+ user: config.get('MYSQL_USER'),
+ password: config.get('MYSQL_PASSWORD'),
+ database: 'bookshelf'
+};
- return mysql.createConnection(options);
+if (config.get('INSTANCE_CONNECTION_NAME') && config.get('NODE_ENV') === 'production') {
+ options.socketPath = `/cloudsql/${config.get('INSTANCE_CONNECTION_NAME')}`;
}
+const connection = mysql.createConnection(options);
+
function list (limit, token, cb) {
token = token ? parseInt(token, 10) : 0;
- const connection = getConnection();
connection.query(
'SELECT * FROM `books` LIMIT ? OFFSET ?', [limit, token],
(err, results) => {
@@ -45,13 +42,11 @@ function list (limit, token, cb) {
cb(null, results, hasMore);
}
);
- connection.end();
}
// [START listby]
function listBy (userId, limit, token, cb) {
token = token ? parseInt(token, 10) : 0;
- const connection = getConnection();
connection.query(
'SELECT * FROM `books` WHERE `createdById` = ? LIMIT ? OFFSET ?',
[userId, limit, token],
@@ -63,12 +58,10 @@ function listBy (userId, limit, token, cb) {
const hasMore = results.length === limit ? token + results.length : false;
cb(null, results, hasMore);
});
- connection.end();
}
// [END listby]
function create (data, cb) {
- const connection = getConnection();
connection.query('INSERT INTO `books` SET ?', data, (err, res) => {
if (err) {
cb(err);
@@ -76,11 +69,9 @@ function create (data, cb) {
}
read(res.insertId, cb);
});
- connection.end();
}
function read (id, cb) {
- const connection = getConnection();
connection.query(
'SELECT * FROM `books` WHERE `id` = ?', id, (err, results) => {
if (err) {
@@ -96,11 +87,9 @@ function read (id, cb) {
}
cb(null, results[0]);
});
- connection.end();
}
function update (id, data, cb) {
- const connection = getConnection();
connection.query(
'UPDATE `books` SET ? WHERE `id` = ?', [data, id], (err) => {
if (err) {
@@ -109,13 +98,10 @@ function update (id, data, cb) {
}
read(id, cb);
});
- connection.end();
}
function _delete (id, cb) {
- const connection = getConnection();
connection.query('DELETE FROM `books` WHERE `id` = ?', id, cb);
- connection.end();
}
module.exports = {
diff --git a/4-auth/books/model-datastore.js b/4-auth/books/model-datastore.js
index f49cb5c5fa..5f42d49e77 100644
--- a/4-auth/books/model-datastore.js
+++ b/4-auth/books/model-datastore.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -38,8 +38,8 @@ const kind = 'Book';
// property: value
// }
function fromDatastore (obj) {
- obj.data.id = obj.key.id;
- return obj.data;
+ obj.id = obj[Datastore.KEY].id;
+ return obj;
}
// Translates from the application's format to the datastore's
diff --git a/4-auth/books/model-mongodb.js b/4-auth/books/model-mongodb.js
index c1a83c800a..cdbc9548ff 100644
--- a/4-auth/books/model-mongodb.js
+++ b/4-auth/books/model-mongodb.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
diff --git a/4-auth/config.js b/4-auth/config.js
index 558e3892e0..eae5e6fe23 100644
--- a/4-auth/config.js
+++ b/4-auth/config.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -26,6 +26,7 @@ nconf
'CLOUD_BUCKET',
'DATA_BACKEND',
'GCLOUD_PROJECT',
+ 'MEMCACHE_URL',
'MONGO_URL',
'MONGO_COLLECTION',
'MYSQL_USER',
@@ -68,9 +69,9 @@ nconf
OAUTH2_CLIENT_SECRET: '',
OAUTH2_CALLBACK: 'http://localhost:8080/auth/google/callback',
- // Port the HTTP server
PORT: 8080,
+ // Set this a secret string of your choosing
SECRET: 'keyboardcat'
});
diff --git a/4-auth/lib/images.js b/4-auth/lib/images.js
index 8207b41b0f..a8391e09bf 100644
--- a/4-auth/lib/images.js
+++ b/4-auth/lib/images.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
diff --git a/4-auth/lib/oauth2.js b/4-auth/lib/oauth2.js
index a4620ab228..fa63e72703 100644
--- a/4-auth/lib/oauth2.js
+++ b/4-auth/lib/oauth2.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
diff --git a/4-auth/package.json b/4-auth/package.json
index 9edb4229c0..8816525336 100644
--- a/4-auth/package.json
+++ b/4-auth/package.json
@@ -1,14 +1,13 @@
{
"name": "nodejs-getting-started",
"version": "1.0.0",
- "description": "End to end sample for running Node.JS applications on Google Cloud Platform",
+ "description": "End to end sample for running Node.js applications on Google Cloud Platform",
"repository": "https://github.com/GoogleCloudPlatform/nodejs-getting-started",
"private": true,
"scripts": {
"start": "node app.js",
- "monitor": "nodemon app.js",
- "deploy": "gcloud app deploy app.yaml",
- "test": "mocha test/index.js -t 30000",
+ "test": "ava -t 30s --tap test/*.test.js | tap-dot",
+ "cover": "nyc --cache npm test; nyc report --reporter=html",
"init-cloudsql": "node books/model-cloudsql.js"
},
"author": "Google Inc.",
@@ -24,9 +23,13 @@
{
"name": "Jason Dobry",
"email": "jdobry@google.com"
+ },
+ {
+ "name": "Ace Nassri",
+ "email": "anassri@google.com"
}
],
- "license": "Apache Version 2.0",
+ "license": "Apache-2.0",
"semistandard": {
"globals": [
"after",
@@ -38,29 +41,28 @@
]
},
"dependencies": {
- "@google-cloud/storage": "^0.1.1",
- "body-parser": "^1.15.2",
- "connect-memcached": "^0.2.0",
- "express": "^4.14.0",
- "express-session": "^1.14.0",
- "@google-cloud/datastore": "^0.1.1",
- "jade": "^1.11.0",
- "kerberos": "^0.0.21",
- "lodash": "^4.14.2",
- "mongodb": "^2.2.5",
- "multer": "^1.2.0",
- "mysql": "^2.11.1",
- "nconf": "^0.8.4",
- "passport": "^0.3.2",
- "passport-google-oauth20": "^1.0.0",
- "prompt": "^1.0.0"
+ "@google-cloud/datastore": "~1.0.0",
+ "@google-cloud/storage": "~1.1.0",
+ "body-parser": "~1.17.1",
+ "connect-memcached": "~0.2.0",
+ "express": "~4.15.2",
+ "express-session": "~1.15.2",
+ "jade": "~1.11.0",
+ "lodash": "~4.17.4",
+ "mongodb": "~2.2.25",
+ "multer": "~1.3.0",
+ "mysql": "~2.13.0",
+ "nconf": "~0.8.4",
+ "passport": "~0.3.2",
+ "passport-google-oauth20": "~1.0.0",
+ "prompt": "~1.0.0"
},
"devDependencies": {
- "mocha": "^3.0.2",
+ "ava": "~0.19.1",
"nodejs-repo-tools": "git+https://git@github.com/GoogleCloudPlatform/nodejs-repo-tools.git",
- "proxyquire": "^1.7.10",
- "sinon": "^1.17.5",
- "supertest": "^2.0.0"
+ "proxyquire": "~1.7.11",
+ "sinon": "~2.1.0",
+ "tap-dot": "~1.0.5"
},
"engines": {
"node": ">=4.3.2"
diff --git a/4-auth/test/_api-tests.js b/4-auth/test/_api-tests.js
new file mode 100644
index 0000000000..a6f3ad270b
--- /dev/null
+++ b/4-auth/test/_api-tests.js
@@ -0,0 +1,69 @@
+// Copyright 2017, 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';
+
+const getRequest = require(`nodejs-repo-tools`).getRequest;
+const test = require(`ava`);
+
+module.exports = (DATA_BACKEND) => {
+ let originalDataBackend, id, testConfig, appConfig;
+
+ test.before(() => {
+ testConfig = require(`./_test-config`);
+ appConfig = require(`../config`);
+ originalDataBackend = appConfig.get(`DATA_BACKEND`);
+ appConfig.set(`DATA_BACKEND`, DATA_BACKEND);
+ });
+
+ test.serial.cb(`should create a book`, (t) => {
+ getRequest(testConfig)
+ .post(`/api/books`)
+ .send({ title: `beep` })
+ .expect(200)
+ .expect((response) => {
+ id = response.body.id;
+ t.truthy(response.body.id);
+ t.is(response.body.title, `beep`);
+ })
+ .end(t.end);
+ });
+
+ test.serial.cb(`should list books`, (t) => {
+ // Give Datastore time to become consistent
+ setTimeout(() => {
+ getRequest(testConfig)
+ .get(`/api/books`)
+ .expect(200)
+ .expect((response) => {
+ t.true(Array.isArray(response.body.items));
+ t.true(response.body.items.length >= 1);
+ })
+ .end(t.end);
+ }, 1000);
+ });
+
+ test.serial.cb(`should delete a book`, (t) => {
+ getRequest(testConfig)
+ .delete(`/api/books/${id}/`)
+ // .expect(200)
+ .expect((response) => {
+ t.is(response.text, `OK`);
+ })
+ .end(t.end);
+ });
+
+ test.always.after(() => {
+ appConfig.set(`DATA_BACKEND`, originalDataBackend);
+ });
+};
diff --git a/4-auth/test/_crud-tests.js b/4-auth/test/_crud-tests.js
new file mode 100644
index 0000000000..403f3c6e2d
--- /dev/null
+++ b/4-auth/test/_crud-tests.js
@@ -0,0 +1,193 @@
+// Copyright 2017, 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';
+
+const getRequest = require(`nodejs-repo-tools`).getRequest;
+const test = require(`ava`);
+
+module.exports = (DATA_BACKEND) => {
+ let originalDataBackend, id, testConfig, appConfig;
+
+ test.before(() => {
+ testConfig = require(`./_test-config`);
+ appConfig = require(`../config`);
+ originalDataBackend = appConfig.get(`DATA_BACKEND`);
+ appConfig.set(`DATA_BACKEND`, DATA_BACKEND);
+ });
+
+ // setup a book
+ test.serial.cb(`should create a book`, (t) => {
+ getRequest(testConfig)
+ .post(`/api/books`)
+ .send({ title: `my book` })
+ .expect(200)
+ .expect((response) => {
+ id = response.body.id;
+ t.truthy(response.body.id);
+ t.is(response.body.title, `my book`);
+ })
+ .end(t.end);
+ });
+
+ test.serial.cb(`should show a list of books`, (t) => {
+ // Give Datastore time to become consistent
+ setTimeout(() => {
+ const expected = /
my other book <\/small><\/h4>/;
+ getRequest(testConfig)
+ .get(`/books/${id}`)
+ .expect(200)
+ .expect((response) => {
+ t.regex(response.text, expected);
+ })
+ .end(t.end);
+ });
+
+ test.serial.cb(`should delete a book`, (t) => {
+ const expected = /Redirecting to \/books/;
+ getRequest(testConfig)
+ .get(`/books/${id}/delete`)
+ .expect(302)
+ .expect((response) => {
+ id = undefined;
+ t.regex(response.text, expected);
+ })
+ .end(t.end);
+ });
+
+ // clean up
+ test.always.after.cb((t) => {
+ appConfig.set(`DATA_BACKEND`, originalDataBackend);
+
+ if (id) {
+ getRequest(testConfig)
+ .delete(`/api/books/${id}`)
+ .expect(200)
+ .end(t.end);
+ } else {
+ t.end();
+ }
+ });
+};
diff --git a/6-pubsub/test/config.js b/6-pubsub/test/_test-config.js
similarity index 77%
rename from 6-pubsub/test/config.js
rename to 6-pubsub/test/_test-config.js
index 4a06dc597d..06990ba3cd 100644
--- a/6-pubsub/test/config.js
+++ b/6-pubsub/test/_test-config.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -16,15 +16,19 @@
const path = require(`path`);
const test = `6-pubsub`;
+const PORT = 8086;
+
module.exports = {
test: test,
cwd: path.resolve(path.join(__dirname, `../`)),
cmd: `node`,
args: [`app.js`],
msg: `Bookshelf`,
- port: 8086,
+ port: PORT,
+ url: `http://localhost:${PORT}`,
env: {
- SUBSCRIPTION_NAME: `${test}-shared-worker-subscription`,
- TOPIC_NAME: `${test}-book-process-queue`
+ PORT: PORT,
+ SUBSCRIPTION_NAME: `shared-worker-subscription-${test}`,
+ TOPIC_NAME: `book-process-queue-${test}`
}
};
diff --git a/6-pubsub/test/config.worker.js b/6-pubsub/test/_test-config.worker.js
similarity index 96%
rename from 6-pubsub/test/config.worker.js
rename to 6-pubsub/test/_test-config.worker.js
index 1fe5180f77..b878287eb7 100644
--- a/6-pubsub/test/config.worker.js
+++ b/6-pubsub/test/_test-config.worker.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
diff --git a/6-pubsub/test/api.test.js b/6-pubsub/test/api.test.js
deleted file mode 100644
index 151759d8a8..0000000000
--- a/6-pubsub/test/api.test.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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';
-
-const assert = require(`assert`);
-const config = require(`./config`);
-const utils = require(`nodejs-repo-tools`);
-
-module.exports = (DATA_BACKEND) => {
- describe(`api.js`, () => {
- let ORIG_DATA_BACKEND;
- let id;
-
- before(() => {
- const appConfig = require(`../config`);
- ORIG_DATA_BACKEND = appConfig.get(`DATA_BACKEND`);
- appConfig.set(`DATA_BACKEND`, DATA_BACKEND);
- });
-
- it(`should create a book`, (done) => {
- utils.getRequest(config)
- .post(`/api/books`)
- .send({ title: `beep` })
- .expect(200)
- .expect((response) => {
- id = response.body.id;
- assert.ok(response.body.id);
- assert.equal(response.body.title, `beep`);
- })
- .end(done);
- });
-
- it(`should list books`, (done) => {
- // Give Datastore time to become consistent
- setTimeout(() => {
- utils.getRequest(config)
- .get(`/api/books`)
- .expect(200)
- .expect((response) => {
- assert.ok(Array.isArray(response.body.items));
- assert.ok(response.body.items.length >= 1);
- })
- .end(done);
- }, 1000);
- });
-
- it(`should delete a book`, (done) => {
- utils.getRequest(config)
- .delete(`/api/books/${id}`)
- .expect(200)
- .expect((response) => {
- assert.equal(response.text, `OK`);
- })
- .end(done);
- });
-
- after(() => {
- require(`../config`).set(`DATA_BACKEND`, ORIG_DATA_BACKEND);
- });
- });
-};
diff --git a/6-pubsub/test/app.test.js b/6-pubsub/test/app.test.js
index ccbe12661e..d1948edc8c 100644
--- a/6-pubsub/test/app.test.js
+++ b/6-pubsub/test/app.test.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -11,96 +11,82 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-`use strict`;
+'use strict';
-const assert = require(`assert`);
-const config = require(`./config`);
+const testConfig = require(`./_test-config`);
const proxyquire = require(`proxyquire`).noPreserveCache();
const sinon = require(`sinon`);
+const test = require(`ava`);
const utils = require(`nodejs-repo-tools`);
-describe(`app.js`, () => {
- if (!process.env.E2E_TESTS) {
- it(`should run`, (done) => {
- utils.testLocalApp(config, done);
- });
- }
-
- it(`should redirect / to /books`, (done) => {
- utils.getRequest(config)
- .get(`/`)
- .expect(302)
- .expect((response) => {
- assert.equal(response.text.includes(`Redirecting to /books`), true);
- })
- .end(done);
+if (!process.env.E2E_TESTS) {
+ test.cb(`should run`, (t) => {
+ utils.testLocalApp(testConfig, t.end);
});
+}
+
+test.cb(`should redirect / to /books`, (t) => {
+ utils.getRequest(testConfig)
+ .get(`/`)
+ .expect(302)
+ .expect((response) => {
+ t.regex(response.text, /Redirecting to \/books/);
+ })
+ .end(t.end);
+});
- it(`should check config`, () => {
- const nconfMock = {
- argv: sinon.stub().returnsThis(),
- env: sinon.stub().returnsThis(),
- file: sinon.stub().returnsThis(),
- defaults: sinon.stub().returnsThis(),
- get: function (setting) {
- return this[setting];
- }
- };
-
- function getMsg (setting) {
- return `You must set ${setting} as an environment variable or in config.json!`;
+test(`should check config`, (t) => {
+ const nconfMock = {
+ argv: sinon.stub().returnsThis(),
+ env: sinon.stub().returnsThis(),
+ file: sinon.stub().returnsThis(),
+ defaults: sinon.stub().returnsThis(),
+ get: function (setting) {
+ return this[setting];
}
+ };
- nconfMock.DATA_BACKEND = `datastore`;
-
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`GCLOUD_PROJECT`));
-
- nconfMock.GCLOUD_PROJECT = `project`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`CLOUD_BUCKET`));
-
- nconfMock.CLOUD_BUCKET = `bucket`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`OAUTH2_CLIENT_ID`));
-
- nconfMock.OAUTH2_CLIENT_ID = `foo`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`OAUTH2_CLIENT_SECRET`));
-
- nconfMock.OAUTH2_CLIENT_SECRET = `bar`;
- assert.doesNotThrow(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- });
-
- nconfMock.DATA_BACKEND = `cloudsql`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MYSQL_USER`));
- nconfMock.MYSQL_USER = `user`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MYSQL_PASSWORD`));
- nconfMock.MYSQL_PASSWORD = `password`;
- assert.doesNotThrow(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- });
-
- nconfMock.DATA_BACKEND = `mongodb`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MONGO_URL`));
- nconfMock.MONGO_URL = `url`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MONGO_COLLECTION`));
- nconfMock.MONGO_COLLECTION = `collection`;
- assert.doesNotThrow(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- });
- });
+ function getMsg (setting) {
+ return `You must set ${setting} as an environment variable or in config.json!`;
+ }
+
+ const testFunc = () => {
+ proxyquire(`../config`, { nconf: nconfMock });
+ };
+
+ nconfMock.DATA_BACKEND = `datastore`;
+
+ t.throws(testFunc, Error, getMsg(`GCLOUD_PROJECT`));
+ nconfMock.GCLOUD_PROJECT = `project`;
+
+ t.throws(testFunc, Error, getMsg(`CLOUD_BUCKET`));
+ nconfMock.CLOUD_BUCKET = `bucket`;
+
+ t.throws(testFunc, Error, getMsg(`OAUTH2_CLIENT_ID`));
+ nconfMock.OAUTH2_CLIENT_ID = `foo`;
+
+ t.throws(testFunc, Error, getMsg(`OAUTH2_CLIENT_SECRET`));
+ nconfMock.OAUTH2_CLIENT_SECRET = `bar`;
+
+ t.notThrows(testFunc);
+
+ nconfMock.DATA_BACKEND = `cloudsql`;
+
+ t.throws(testFunc, Error, getMsg(`MYSQL_USER`));
+ nconfMock.MYSQL_USER = `user`;
+
+ t.throws(testFunc, Error, getMsg(`MYSQL_PASSWORD`));
+ nconfMock.MYSQL_PASSWORD = `password`;
+
+ t.notThrows(testFunc);
+
+ nconfMock.DATA_BACKEND = `mongodb`;
+
+ t.throws(testFunc, Error, getMsg(`MONGO_URL`));
+ nconfMock.MONGO_URL = `url`;
+
+ t.throws(testFunc, Error, getMsg(`MONGO_COLLECTION`));
+ nconfMock.MONGO_COLLECTION = `collection`;
+
+ t.notThrows(testFunc);
});
diff --git a/6-pubsub/test/background.test.js b/6-pubsub/test/background.test.js
index 4027a5f2d4..bc63b26ac7 100644
--- a/6-pubsub/test/background.test.js
+++ b/6-pubsub/test/background.test.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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,415 +13,384 @@
'use strict';
-const assert = require(`assert`);
-const sinon = require(`sinon`);
const proxyquire = require(`proxyquire`).noPreserveCache();
+const sinon = require(`sinon`);
+const test = require(`ava`);
+
let background;
const mocks = {};
-describe(`background.js`, () => {
- beforeEach(() => {
- // Mock dependencies used by background.js
- mocks.config = {
- GCLOUD_PROJECT: process.env.GCLOUD_PROJECT,
- SUBSCRIPTION_NAME: `shared-worker-subscription`,
- TOPIC_NAME: `book-process-queue`
- };
- mocks.config.get = function (key) {
- return this[key];
- };
- mocks.subscription = {
- on: sinon.stub()
- };
- mocks.topic = {
- subscribe: sinon.stub().callsArgWith(2, null, mocks.subscription),
- publish: sinon.stub().callsArg(1)
- };
- mocks.pubsub = {
- createTopic: sinon.stub().callsArgWith(1, null, mocks.topic),
- topic: sinon.stub().returns(mocks.topic)
- };
- mocks.Pubsub = sinon.stub().returns(mocks.pubsub);
- mocks.logging = {
- info: sinon.stub(),
- error: sinon.stub()
- };
- // Load background.js with provided mocks
- background = proxyquire(`../lib/background`, {
- '@google-cloud/pubsub': mocks.Pubsub,
- '../config': mocks.config,
- './logging': mocks.logging
- });
+test.beforeEach((t) => {
+ // Mock dependencies used by background.js
+ mocks.config = {
+ GCLOUD_PROJECT: process.env.GCLOUD_PROJECT,
+ SUBSCRIPTION_NAME: `shared-worker-subscription`,
+ TOPIC_NAME: `book-process-queue`
+ };
+ mocks.config.get = function (key) {
+ return this[key];
+ };
+ mocks.subscription = {
+ on: sinon.stub()
+ };
+ mocks.topic = {
+ subscribe: sinon.stub().callsArgWith(2, null, mocks.subscription),
+ publish: sinon.stub().callsArg(1)
+ };
+ mocks.pubsub = {
+ createTopic: sinon.stub().callsArgWith(1, null, mocks.topic),
+ topic: sinon.stub().returns(mocks.topic)
+ };
+ mocks.Pubsub = sinon.stub().returns(mocks.pubsub);
+ mocks.logging = {
+ info: sinon.stub(),
+ error: sinon.stub()
+ };
+ // Load background.js with provided mocks
+ background = proxyquire(`../lib/background`, {
+ '@google-cloud/pubsub': mocks.Pubsub,
+ '../config': mocks.config,
+ './logging': mocks.logging
+ });
+
+ t.truthy(
+ mocks.Pubsub.calledOnce,
+ `Pubsub() should have been called once`
+ );
+});
- assert.ok(
- mocks.Pubsub.calledOnce,
- `Pubsub() should have been called once`
+test.serial.cb(`should subscribe and log message`, (t) => {
+ // Setup
+ const testMessage = `test message`;
+
+ // Run target functionality
+ background.subscribe((err, message) => {
+ // Assertions
+ t.truthy(
+ err === null,
+ `err should be null`
+ );
+ t.is(message, testMessage, `should have message`);
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right args`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.subscribe.calledOnce,
+ `topic.subscribe should have been called once`
+ );
+ t.is(
+ mocks.topic.subscribe.firstCall.args[0],
+ `shared-worker-subscription`,
+ `topic.subscribe() should have been called with the right arguments`
);
+ t.deepEqual(
+ mocks.topic.subscribe.firstCall.args[1],
+ {
+ autoAck: true
+ },
+ `topic.subscribe() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.subscription.on.calledTwice,
+ `subscription.on should have been called twice`
+ );
+ t.is(
+ mocks.subscription.on.firstCall.args[0],
+ `message`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.truthy(
+ typeof mocks.subscription.on.firstCall.args[1] === `function`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.subscription.on.secondCall.args[0],
+ `error`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.truthy(
+ typeof mocks.subscription.on.secondCall.args[1] === `function`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.end();
});
- describe(`subscribe()`, () => {
- it(`should subscribe and log message`, (done) => {
- // Setup
- const testMessage = `test message`;
+ // Trigger a message
+ setTimeout(() => {
+ mocks.subscription.on.firstCall.args[1]({
+ data: testMessage
+ });
+ }, 10);
+});
- // Run target functionality
- background.subscribe((err, message) => {
- // Assertions
- assert.ok(
- err === null,
- `err should be null`
- );
- assert.equal(message, testMessage, `should have message`);
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right args`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.subscribe.calledOnce,
- `topic.subscribe should have been called once`
- );
- assert.equal(
- mocks.topic.subscribe.firstCall.args[0],
- `shared-worker-subscription`,
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.deepEqual(
- mocks.topic.subscribe.firstCall.args[1],
- {
- autoAck: true,
- reuseExisting: true
- },
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.ok(
- mocks.subscription.on.calledTwice,
- `subscription.on should have been called twice`
- );
- assert.equal(
- mocks.subscription.on.firstCall.args[0],
- `message`,
- `subscription.on() should have been called with the right arguments`
- );
- assert.ok(
- typeof mocks.subscription.on.firstCall.args[1] === `function`,
- `subscription.on() should have been called with the right arguments`
- );
- assert.equal(
- mocks.subscription.on.secondCall.args[0],
- `error`,
- `subscription.on() should have been called with the right arguments`
- );
- assert.ok(
- typeof mocks.subscription.on.secondCall.args[1] === `function`,
- `subscription.on() should have been called with the right arguments`
- );
- done();
- });
+test.serial.cb(`should return topic error, if any`, (t) => {
+ // Setup
+ const testErrorMsg = `test error`;
+ mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
- // Trigger a message
- setTimeout(() => {
- mocks.subscription.on.firstCall.args[1]({
- data: testMessage
- });
- }, 10);
- });
- it(`should return topic error, if any`, (done) => {
- // Setup
- const testErrorMsg = `test error`;
- mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
+ // Run target functionality
+ background.subscribe((data) => {
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right args`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.is(data, testErrorMsg);
+ t.is(
+ mocks.topic.subscribe.callCount,
+ 0,
+ `topic.subscribe() should NOT have been called`
+ );
+ t.is(
+ mocks.subscription.on.callCount,
+ 0,
+ `subscription.on() should NOT have been called`
+ );
+ t.end();
+ });
+});
- // Run target functionality
- background.subscribe((data) => {
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right args`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.equal(data, testErrorMsg);
- assert.equal(
- mocks.topic.subscribe.callCount,
- 0,
- `topic.subscribe() should NOT have been called`
- );
- assert.equal(
- mocks.subscription.on.callCount,
- 0,
- `subscription.on() should NOT have been called`
- );
- done();
- });
- });
- it(`should return subscription error, if any`, (done) => {
- // Setup
- const testErrorMsg = `test error`;
- mocks.topic.subscribe = sinon.stub().callsArgWith(2, testErrorMsg);
+test.serial.cb(`should return subscription error, if any`, (t) => {
+ // Setup
+ const testErrorMsg = `test error`;
+ mocks.topic.subscribe = sinon.stub().callsArgWith(2, testErrorMsg);
- // Run target functionality
- background.subscribe((data) => {
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right args`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.subscribe.calledOnce,
- `topic.subscribe should have been called once`
- );
- assert.equal(
- mocks.topic.subscribe.firstCall.args[0],
- `shared-worker-subscription`,
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.deepEqual(
- mocks.topic.subscribe.firstCall.args[1],
- {
- autoAck: true,
- reuseExisting: true
- },
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.equal(data, testErrorMsg);
- assert.equal(
- mocks.subscription.on.callCount,
- 0,
- `subscription.on() should NOT have been called`
- );
- assert.equal(
- mocks.logging.info.callCount,
- 0,
- `logging.info() should NOT have been called`
- );
- done();
- });
- });
+ // Run target functionality
+ background.subscribe((data) => {
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right args`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.subscribe.calledOnce,
+ `topic.subscribe should have been called once`
+ );
+ t.is(
+ mocks.topic.subscribe.firstCall.args[0],
+ `shared-worker-subscription`,
+ `topic.subscribe() should have been called with the right arguments`
+ );
+ t.deepEqual(
+ mocks.topic.subscribe.firstCall.args[1],
+ {
+ autoAck: true
+ },
+ `topic.subscribe() should have been called with the right arguments`
+ );
+ t.is(data, testErrorMsg);
+ t.is(
+ mocks.subscription.on.callCount,
+ 0,
+ `subscription.on() should NOT have been called`
+ );
+ t.is(
+ mocks.logging.info.callCount,
+ 0,
+ `logging.info() should NOT have been called`
+ );
+ t.end();
});
+});
- describe(`queueBook()`, () => {
- it(`should queue a book and log message`, () => {
- // Setup
- const testBookId = 1;
+test.serial(`should queue a book and log message`, (t) => {
+ // Setup
+ const testBookId = 1;
- // Run target functionality
- background.queueBook(testBookId);
+ // Run target functionality
+ background.queueBook(testBookId);
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.publish,
- `topic.publish() should have been called once`
- );
- assert.deepEqual(
- mocks.topic.publish.firstCall.args[0],
- {
- data: {
- action: `processBook`,
- bookId: testBookId
- }
- },
- `topic.publish() should have been called with the right arguments`
- );
- assert.ok(
- mocks.logging.info.calledOnce,
- `logging.info() should have been called`
- );
- assert.equal(
- mocks.logging.info.firstCall.args[0],
- `Book ${testBookId} queued for background processing`,
- `logging.info() should have been called with the right arguments`
- );
- });
- it(`should queue a book and log message even if topic exists`, () => {
- // Setup
- const testBookId = 1;
- mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, {
- code: 409
- });
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.publish,
+ `topic.publish() should have been called once`
+ );
+ t.deepEqual(
+ mocks.topic.publish.firstCall.args[0],
+ {
+ data: {
+ action: `processBook`,
+ bookId: testBookId
+ }
+ },
+ `topic.publish() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.logging.info.calledOnce,
+ `logging.info() should have been called`
+ );
+ t.is(
+ mocks.logging.info.firstCall.args[0],
+ `Book ${testBookId} queued for background processing`,
+ `logging.info() should have been called with the right arguments`
+ );
+});
- // Run target functionality
- background.queueBook(testBookId);
+test.serial(`should queue a book and log message even if topic exists`, (t) => {
+ // Setup
+ const testBookId = 1;
+ mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, {
+ code: 409
+ });
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.ok(
- mocks.pubsub.topic.calledOnce,
- `pubsub.topic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.topic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.topic() should have been called with the right arguments`
- );
- assert.ok(
- mocks.topic.publish,
- `topic.publish() should have been called once`
- );
- assert.deepEqual(
- mocks.topic.publish.firstCall.args[0],
- {
- data: {
- action: `processBook`,
- bookId: testBookId
- }
- },
- `topic.publish() should have been called with the right arguments`
- );
- assert.ok(
- mocks.logging.info.calledOnce,
- `logging.info() should have been called`
- );
- assert.equal(
- mocks.logging.info.firstCall.args[0],
- `Book ${testBookId} queued for background processing`,
- `logging.info() should have been called with the right arguments`
- );
- });
- it(`should log error if cannot get topic`, () => {
- // Setup
- const testBookId = 1;
- const testErrorMsg = `test error`;
- mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
+ // Run target functionality
+ background.queueBook(testBookId);
- // Run target functionality
- background.queueBook(testBookId);
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.pubsub.topic.calledOnce,
+ `pubsub.topic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.topic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.topic() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.topic.publish,
+ `topic.publish() should have been called once`
+ );
+ t.deepEqual(
+ mocks.topic.publish.firstCall.args[0],
+ {
+ data: {
+ action: `processBook`,
+ bookId: testBookId
+ }
+ },
+ `topic.publish() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.logging.info.calledOnce,
+ `logging.info() should have been called`
+ );
+ t.is(
+ mocks.logging.info.firstCall.args[0],
+ `Book ${testBookId} queued for background processing`,
+ `logging.info() should have been called with the right arguments`
+ );
+});
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.equal(
- mocks.topic.publish.callCount,
- 0,
- `topic.publish() should NOT have been called`
- );
- assert.equal(
- mocks.logging.info.callCount,
- 0,
- `logging.info() should NOT have been called`
- );
- assert.ok(
- mocks.logging.error.calledOnce,
- `logging.error() should have been called`
- );
- assert.deepEqual(
- mocks.logging.error.firstCall.args,
- [`Error occurred while getting pubsub topic`, testErrorMsg],
- `logging.error() should have been called with the right arguments`
- );
- });
- it(`should log error if cannot publish message`, () => {
- // Setup
- const testBookId = 1;
- const testErrorMsg = `test error`;
- mocks.topic.publish = sinon.stub().callsArgWith(1, testErrorMsg);
+test.serial(`should log error if cannot get topic`, (t) => {
+ // Setup
+ const testBookId = 1;
+ const testErrorMsg = `test error`;
+ mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
- // Run target functionality
- background.queueBook(testBookId);
+ // Run target functionality
+ background.queueBook(testBookId);
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.publish,
- `topic.publish() should have been called once`
- );
- assert.deepEqual(
- mocks.topic.publish.firstCall.args[0],
- {
- data: {
- action: `processBook`,
- bookId: testBookId
- }
- },
- `topic.publish() should have been called with the right arguments`
- );
- assert.equal(
- mocks.logging.info.callCount,
- 0,
- `logging.info() should NOT have been called`
- );
- assert.ok(
- mocks.logging.error.calledOnce,
- `logging.error() should have been called`
- );
- assert.deepEqual(
- mocks.logging.error.firstCall.args,
- [`Error occurred while queuing background task`, testErrorMsg],
- `logging.error() should have been called with the right arguments`
- );
- });
- });
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.is(
+ mocks.topic.publish.callCount,
+ 0,
+ `topic.publish() should NOT have been called`
+ );
+ t.is(
+ mocks.logging.info.callCount,
+ 0,
+ `logging.info() should NOT have been called`
+ );
+ t.truthy(
+ mocks.logging.error.calledOnce,
+ `logging.error() should have been called`
+ );
+});
+
+test.serial(`should log error if cannot publish message`, (t) => {
+ // Setup
+ const testBookId = 1;
+ const testErrorMsg = `test error`;
+ mocks.topic.publish = sinon.stub().callsArgWith(1, testErrorMsg);
+
+ // Run target functionality
+ background.queueBook(testBookId);
+
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.publish,
+ `topic.publish() should have been called once`
+ );
});
diff --git a/6-pubsub/test/cloudsql.test.js b/6-pubsub/test/cloudsql.test.js
new file mode 100644
index 0000000000..ff2ba5fd52
--- /dev/null
+++ b/6-pubsub/test/cloudsql.test.js
@@ -0,0 +1,23 @@
+// Copyright 2017, 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';
+
+const test = require(`ava`);
+
+if (require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || process.env.TEST_CLOUDSQL) {
+ require(`./_api-tests`)(`cloudsql`);
+ require(`./_crud-tests`)(`cloudsql`);
+} else {
+ test(`Skipping Cloud SQL tests...`, (t) => t.pass());
+}
diff --git a/6-pubsub/test/crud.test.js b/6-pubsub/test/crud.test.js
deleted file mode 100644
index 0f07993e9c..0000000000
--- a/6-pubsub/test/crud.test.js
+++ /dev/null
@@ -1,207 +0,0 @@
-// 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';
-
-const assert = require(`assert`);
-const config = require(`./config`);
-const utils = require(`nodejs-repo-tools`);
-
-module.exports = (DATA_BACKEND) => {
- describe(`crud.js`, () => {
- let ORIG_DATA_BACKEND;
-
- before(() => {
- const appConfig = require(`../config`);
- ORIG_DATA_BACKEND = appConfig.get(`DATA_BACKEND`);
- appConfig.set(`DATA_BACKEND`, DATA_BACKEND);
- });
-
- describe(`/books`, () => {
- let id;
-
- // setup a book
- before((done) => {
- utils.getRequest(config)
- .post(`/api/books`)
- .send({ title: `my book` })
- .expect(200)
- .expect((response) => {
- id = response.body.id;
- assert.ok(response.body.id);
- assert.equal(response.body.title, `my book`);
- })
- .end(done);
- });
-
- it(`should show a list of books`, (done) => {
- // Give Datastore time to become consistent
- setTimeout(() => {
- const expected = `
my other book <\/small><\/h4>/;
+ getRequest(testConfig)
+ .get(`/books/${id}`)
+ .expect(200)
+ .expect((response) => {
+ t.regex(response.text, expected);
+ })
+ .end(t.end);
+ });
+
+ test.serial.cb(`should delete a book`, (t) => {
+ const expected = /Redirecting to \/books/;
+ getRequest(testConfig)
+ .get(`/books/${id}/delete`)
+ .expect(302)
+ .expect((response) => {
+ id = undefined;
+ t.regex(response.text, expected);
+ })
+ .end(t.end);
+ });
+
+ // clean up
+ test.always.after.cb((t) => {
+ appConfig.set(`DATA_BACKEND`, originalDataBackend);
+
+ if (id) {
+ getRequest(testConfig)
+ .delete(`/api/books/${id}`)
+ .expect(200)
+ .end(t.end);
+ } else {
+ t.end();
+ }
+ });
+};
diff --git a/7-gce/test/config.js b/7-gce/test/_test-config.js
similarity index 77%
rename from 7-gce/test/config.js
rename to 7-gce/test/_test-config.js
index cb95c60fb4..9cfe02b284 100644
--- a/7-gce/test/config.js
+++ b/7-gce/test/_test-config.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -16,15 +16,19 @@
const path = require(`path`);
const test = `7-gce`;
+const PORT = 8087;
+
module.exports = {
test: test,
cwd: path.resolve(path.join(__dirname, `../`)),
cmd: `node`,
args: [`app.js`],
msg: `Bookshelf`,
- port: 8087,
+ port: PORT,
+ url: `http://localhost:${PORT}`,
env: {
- SUBSCRIPTION_NAME: `${test}-shared-worker-subscription`,
- TOPIC_NAME: `${test}-book-process-queue`
+ PORT: PORT,
+ SUBSCRIPTION_NAME: `shared-worker-subscription-${test}`,
+ TOPIC_NAME: `book-process-queue-${test}`
}
};
diff --git a/7-gce/test/config.worker.js b/7-gce/test/_test-config.worker.js
similarity index 96%
rename from 7-gce/test/config.worker.js
rename to 7-gce/test/_test-config.worker.js
index 2658ba2fc7..63e5f09e3a 100644
--- a/7-gce/test/config.worker.js
+++ b/7-gce/test/_test-config.worker.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
diff --git a/7-gce/test/api.test.js b/7-gce/test/api.test.js
deleted file mode 100644
index 151759d8a8..0000000000
--- a/7-gce/test/api.test.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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';
-
-const assert = require(`assert`);
-const config = require(`./config`);
-const utils = require(`nodejs-repo-tools`);
-
-module.exports = (DATA_BACKEND) => {
- describe(`api.js`, () => {
- let ORIG_DATA_BACKEND;
- let id;
-
- before(() => {
- const appConfig = require(`../config`);
- ORIG_DATA_BACKEND = appConfig.get(`DATA_BACKEND`);
- appConfig.set(`DATA_BACKEND`, DATA_BACKEND);
- });
-
- it(`should create a book`, (done) => {
- utils.getRequest(config)
- .post(`/api/books`)
- .send({ title: `beep` })
- .expect(200)
- .expect((response) => {
- id = response.body.id;
- assert.ok(response.body.id);
- assert.equal(response.body.title, `beep`);
- })
- .end(done);
- });
-
- it(`should list books`, (done) => {
- // Give Datastore time to become consistent
- setTimeout(() => {
- utils.getRequest(config)
- .get(`/api/books`)
- .expect(200)
- .expect((response) => {
- assert.ok(Array.isArray(response.body.items));
- assert.ok(response.body.items.length >= 1);
- })
- .end(done);
- }, 1000);
- });
-
- it(`should delete a book`, (done) => {
- utils.getRequest(config)
- .delete(`/api/books/${id}`)
- .expect(200)
- .expect((response) => {
- assert.equal(response.text, `OK`);
- })
- .end(done);
- });
-
- after(() => {
- require(`../config`).set(`DATA_BACKEND`, ORIG_DATA_BACKEND);
- });
- });
-};
diff --git a/7-gce/test/app.test.js b/7-gce/test/app.test.js
index ccbe12661e..d1948edc8c 100644
--- a/7-gce/test/app.test.js
+++ b/7-gce/test/app.test.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -11,96 +11,82 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-`use strict`;
+'use strict';
-const assert = require(`assert`);
-const config = require(`./config`);
+const testConfig = require(`./_test-config`);
const proxyquire = require(`proxyquire`).noPreserveCache();
const sinon = require(`sinon`);
+const test = require(`ava`);
const utils = require(`nodejs-repo-tools`);
-describe(`app.js`, () => {
- if (!process.env.E2E_TESTS) {
- it(`should run`, (done) => {
- utils.testLocalApp(config, done);
- });
- }
-
- it(`should redirect / to /books`, (done) => {
- utils.getRequest(config)
- .get(`/`)
- .expect(302)
- .expect((response) => {
- assert.equal(response.text.includes(`Redirecting to /books`), true);
- })
- .end(done);
+if (!process.env.E2E_TESTS) {
+ test.cb(`should run`, (t) => {
+ utils.testLocalApp(testConfig, t.end);
});
+}
+
+test.cb(`should redirect / to /books`, (t) => {
+ utils.getRequest(testConfig)
+ .get(`/`)
+ .expect(302)
+ .expect((response) => {
+ t.regex(response.text, /Redirecting to \/books/);
+ })
+ .end(t.end);
+});
- it(`should check config`, () => {
- const nconfMock = {
- argv: sinon.stub().returnsThis(),
- env: sinon.stub().returnsThis(),
- file: sinon.stub().returnsThis(),
- defaults: sinon.stub().returnsThis(),
- get: function (setting) {
- return this[setting];
- }
- };
-
- function getMsg (setting) {
- return `You must set ${setting} as an environment variable or in config.json!`;
+test(`should check config`, (t) => {
+ const nconfMock = {
+ argv: sinon.stub().returnsThis(),
+ env: sinon.stub().returnsThis(),
+ file: sinon.stub().returnsThis(),
+ defaults: sinon.stub().returnsThis(),
+ get: function (setting) {
+ return this[setting];
}
+ };
- nconfMock.DATA_BACKEND = `datastore`;
-
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`GCLOUD_PROJECT`));
-
- nconfMock.GCLOUD_PROJECT = `project`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`CLOUD_BUCKET`));
-
- nconfMock.CLOUD_BUCKET = `bucket`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`OAUTH2_CLIENT_ID`));
-
- nconfMock.OAUTH2_CLIENT_ID = `foo`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`OAUTH2_CLIENT_SECRET`));
-
- nconfMock.OAUTH2_CLIENT_SECRET = `bar`;
- assert.doesNotThrow(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- });
-
- nconfMock.DATA_BACKEND = `cloudsql`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MYSQL_USER`));
- nconfMock.MYSQL_USER = `user`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MYSQL_PASSWORD`));
- nconfMock.MYSQL_PASSWORD = `password`;
- assert.doesNotThrow(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- });
-
- nconfMock.DATA_BACKEND = `mongodb`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MONGO_URL`));
- nconfMock.MONGO_URL = `url`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MONGO_COLLECTION`));
- nconfMock.MONGO_COLLECTION = `collection`;
- assert.doesNotThrow(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- });
- });
+ function getMsg (setting) {
+ return `You must set ${setting} as an environment variable or in config.json!`;
+ }
+
+ const testFunc = () => {
+ proxyquire(`../config`, { nconf: nconfMock });
+ };
+
+ nconfMock.DATA_BACKEND = `datastore`;
+
+ t.throws(testFunc, Error, getMsg(`GCLOUD_PROJECT`));
+ nconfMock.GCLOUD_PROJECT = `project`;
+
+ t.throws(testFunc, Error, getMsg(`CLOUD_BUCKET`));
+ nconfMock.CLOUD_BUCKET = `bucket`;
+
+ t.throws(testFunc, Error, getMsg(`OAUTH2_CLIENT_ID`));
+ nconfMock.OAUTH2_CLIENT_ID = `foo`;
+
+ t.throws(testFunc, Error, getMsg(`OAUTH2_CLIENT_SECRET`));
+ nconfMock.OAUTH2_CLIENT_SECRET = `bar`;
+
+ t.notThrows(testFunc);
+
+ nconfMock.DATA_BACKEND = `cloudsql`;
+
+ t.throws(testFunc, Error, getMsg(`MYSQL_USER`));
+ nconfMock.MYSQL_USER = `user`;
+
+ t.throws(testFunc, Error, getMsg(`MYSQL_PASSWORD`));
+ nconfMock.MYSQL_PASSWORD = `password`;
+
+ t.notThrows(testFunc);
+
+ nconfMock.DATA_BACKEND = `mongodb`;
+
+ t.throws(testFunc, Error, getMsg(`MONGO_URL`));
+ nconfMock.MONGO_URL = `url`;
+
+ t.throws(testFunc, Error, getMsg(`MONGO_COLLECTION`));
+ nconfMock.MONGO_COLLECTION = `collection`;
+
+ t.notThrows(testFunc);
});
diff --git a/7-gce/test/background.test.js b/7-gce/test/background.test.js
index 4027a5f2d4..bc63b26ac7 100644
--- a/7-gce/test/background.test.js
+++ b/7-gce/test/background.test.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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,415 +13,384 @@
'use strict';
-const assert = require(`assert`);
-const sinon = require(`sinon`);
const proxyquire = require(`proxyquire`).noPreserveCache();
+const sinon = require(`sinon`);
+const test = require(`ava`);
+
let background;
const mocks = {};
-describe(`background.js`, () => {
- beforeEach(() => {
- // Mock dependencies used by background.js
- mocks.config = {
- GCLOUD_PROJECT: process.env.GCLOUD_PROJECT,
- SUBSCRIPTION_NAME: `shared-worker-subscription`,
- TOPIC_NAME: `book-process-queue`
- };
- mocks.config.get = function (key) {
- return this[key];
- };
- mocks.subscription = {
- on: sinon.stub()
- };
- mocks.topic = {
- subscribe: sinon.stub().callsArgWith(2, null, mocks.subscription),
- publish: sinon.stub().callsArg(1)
- };
- mocks.pubsub = {
- createTopic: sinon.stub().callsArgWith(1, null, mocks.topic),
- topic: sinon.stub().returns(mocks.topic)
- };
- mocks.Pubsub = sinon.stub().returns(mocks.pubsub);
- mocks.logging = {
- info: sinon.stub(),
- error: sinon.stub()
- };
- // Load background.js with provided mocks
- background = proxyquire(`../lib/background`, {
- '@google-cloud/pubsub': mocks.Pubsub,
- '../config': mocks.config,
- './logging': mocks.logging
- });
+test.beforeEach((t) => {
+ // Mock dependencies used by background.js
+ mocks.config = {
+ GCLOUD_PROJECT: process.env.GCLOUD_PROJECT,
+ SUBSCRIPTION_NAME: `shared-worker-subscription`,
+ TOPIC_NAME: `book-process-queue`
+ };
+ mocks.config.get = function (key) {
+ return this[key];
+ };
+ mocks.subscription = {
+ on: sinon.stub()
+ };
+ mocks.topic = {
+ subscribe: sinon.stub().callsArgWith(2, null, mocks.subscription),
+ publish: sinon.stub().callsArg(1)
+ };
+ mocks.pubsub = {
+ createTopic: sinon.stub().callsArgWith(1, null, mocks.topic),
+ topic: sinon.stub().returns(mocks.topic)
+ };
+ mocks.Pubsub = sinon.stub().returns(mocks.pubsub);
+ mocks.logging = {
+ info: sinon.stub(),
+ error: sinon.stub()
+ };
+ // Load background.js with provided mocks
+ background = proxyquire(`../lib/background`, {
+ '@google-cloud/pubsub': mocks.Pubsub,
+ '../config': mocks.config,
+ './logging': mocks.logging
+ });
+
+ t.truthy(
+ mocks.Pubsub.calledOnce,
+ `Pubsub() should have been called once`
+ );
+});
- assert.ok(
- mocks.Pubsub.calledOnce,
- `Pubsub() should have been called once`
+test.serial.cb(`should subscribe and log message`, (t) => {
+ // Setup
+ const testMessage = `test message`;
+
+ // Run target functionality
+ background.subscribe((err, message) => {
+ // Assertions
+ t.truthy(
+ err === null,
+ `err should be null`
+ );
+ t.is(message, testMessage, `should have message`);
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right args`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.subscribe.calledOnce,
+ `topic.subscribe should have been called once`
+ );
+ t.is(
+ mocks.topic.subscribe.firstCall.args[0],
+ `shared-worker-subscription`,
+ `topic.subscribe() should have been called with the right arguments`
);
+ t.deepEqual(
+ mocks.topic.subscribe.firstCall.args[1],
+ {
+ autoAck: true
+ },
+ `topic.subscribe() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.subscription.on.calledTwice,
+ `subscription.on should have been called twice`
+ );
+ t.is(
+ mocks.subscription.on.firstCall.args[0],
+ `message`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.truthy(
+ typeof mocks.subscription.on.firstCall.args[1] === `function`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.subscription.on.secondCall.args[0],
+ `error`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.truthy(
+ typeof mocks.subscription.on.secondCall.args[1] === `function`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.end();
});
- describe(`subscribe()`, () => {
- it(`should subscribe and log message`, (done) => {
- // Setup
- const testMessage = `test message`;
+ // Trigger a message
+ setTimeout(() => {
+ mocks.subscription.on.firstCall.args[1]({
+ data: testMessage
+ });
+ }, 10);
+});
- // Run target functionality
- background.subscribe((err, message) => {
- // Assertions
- assert.ok(
- err === null,
- `err should be null`
- );
- assert.equal(message, testMessage, `should have message`);
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right args`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.subscribe.calledOnce,
- `topic.subscribe should have been called once`
- );
- assert.equal(
- mocks.topic.subscribe.firstCall.args[0],
- `shared-worker-subscription`,
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.deepEqual(
- mocks.topic.subscribe.firstCall.args[1],
- {
- autoAck: true,
- reuseExisting: true
- },
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.ok(
- mocks.subscription.on.calledTwice,
- `subscription.on should have been called twice`
- );
- assert.equal(
- mocks.subscription.on.firstCall.args[0],
- `message`,
- `subscription.on() should have been called with the right arguments`
- );
- assert.ok(
- typeof mocks.subscription.on.firstCall.args[1] === `function`,
- `subscription.on() should have been called with the right arguments`
- );
- assert.equal(
- mocks.subscription.on.secondCall.args[0],
- `error`,
- `subscription.on() should have been called with the right arguments`
- );
- assert.ok(
- typeof mocks.subscription.on.secondCall.args[1] === `function`,
- `subscription.on() should have been called with the right arguments`
- );
- done();
- });
+test.serial.cb(`should return topic error, if any`, (t) => {
+ // Setup
+ const testErrorMsg = `test error`;
+ mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
- // Trigger a message
- setTimeout(() => {
- mocks.subscription.on.firstCall.args[1]({
- data: testMessage
- });
- }, 10);
- });
- it(`should return topic error, if any`, (done) => {
- // Setup
- const testErrorMsg = `test error`;
- mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
+ // Run target functionality
+ background.subscribe((data) => {
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right args`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.is(data, testErrorMsg);
+ t.is(
+ mocks.topic.subscribe.callCount,
+ 0,
+ `topic.subscribe() should NOT have been called`
+ );
+ t.is(
+ mocks.subscription.on.callCount,
+ 0,
+ `subscription.on() should NOT have been called`
+ );
+ t.end();
+ });
+});
- // Run target functionality
- background.subscribe((data) => {
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right args`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.equal(data, testErrorMsg);
- assert.equal(
- mocks.topic.subscribe.callCount,
- 0,
- `topic.subscribe() should NOT have been called`
- );
- assert.equal(
- mocks.subscription.on.callCount,
- 0,
- `subscription.on() should NOT have been called`
- );
- done();
- });
- });
- it(`should return subscription error, if any`, (done) => {
- // Setup
- const testErrorMsg = `test error`;
- mocks.topic.subscribe = sinon.stub().callsArgWith(2, testErrorMsg);
+test.serial.cb(`should return subscription error, if any`, (t) => {
+ // Setup
+ const testErrorMsg = `test error`;
+ mocks.topic.subscribe = sinon.stub().callsArgWith(2, testErrorMsg);
- // Run target functionality
- background.subscribe((data) => {
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right args`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.subscribe.calledOnce,
- `topic.subscribe should have been called once`
- );
- assert.equal(
- mocks.topic.subscribe.firstCall.args[0],
- `shared-worker-subscription`,
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.deepEqual(
- mocks.topic.subscribe.firstCall.args[1],
- {
- autoAck: true,
- reuseExisting: true
- },
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.equal(data, testErrorMsg);
- assert.equal(
- mocks.subscription.on.callCount,
- 0,
- `subscription.on() should NOT have been called`
- );
- assert.equal(
- mocks.logging.info.callCount,
- 0,
- `logging.info() should NOT have been called`
- );
- done();
- });
- });
+ // Run target functionality
+ background.subscribe((data) => {
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right args`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.subscribe.calledOnce,
+ `topic.subscribe should have been called once`
+ );
+ t.is(
+ mocks.topic.subscribe.firstCall.args[0],
+ `shared-worker-subscription`,
+ `topic.subscribe() should have been called with the right arguments`
+ );
+ t.deepEqual(
+ mocks.topic.subscribe.firstCall.args[1],
+ {
+ autoAck: true
+ },
+ `topic.subscribe() should have been called with the right arguments`
+ );
+ t.is(data, testErrorMsg);
+ t.is(
+ mocks.subscription.on.callCount,
+ 0,
+ `subscription.on() should NOT have been called`
+ );
+ t.is(
+ mocks.logging.info.callCount,
+ 0,
+ `logging.info() should NOT have been called`
+ );
+ t.end();
});
+});
- describe(`queueBook()`, () => {
- it(`should queue a book and log message`, () => {
- // Setup
- const testBookId = 1;
+test.serial(`should queue a book and log message`, (t) => {
+ // Setup
+ const testBookId = 1;
- // Run target functionality
- background.queueBook(testBookId);
+ // Run target functionality
+ background.queueBook(testBookId);
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.publish,
- `topic.publish() should have been called once`
- );
- assert.deepEqual(
- mocks.topic.publish.firstCall.args[0],
- {
- data: {
- action: `processBook`,
- bookId: testBookId
- }
- },
- `topic.publish() should have been called with the right arguments`
- );
- assert.ok(
- mocks.logging.info.calledOnce,
- `logging.info() should have been called`
- );
- assert.equal(
- mocks.logging.info.firstCall.args[0],
- `Book ${testBookId} queued for background processing`,
- `logging.info() should have been called with the right arguments`
- );
- });
- it(`should queue a book and log message even if topic exists`, () => {
- // Setup
- const testBookId = 1;
- mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, {
- code: 409
- });
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.publish,
+ `topic.publish() should have been called once`
+ );
+ t.deepEqual(
+ mocks.topic.publish.firstCall.args[0],
+ {
+ data: {
+ action: `processBook`,
+ bookId: testBookId
+ }
+ },
+ `topic.publish() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.logging.info.calledOnce,
+ `logging.info() should have been called`
+ );
+ t.is(
+ mocks.logging.info.firstCall.args[0],
+ `Book ${testBookId} queued for background processing`,
+ `logging.info() should have been called with the right arguments`
+ );
+});
- // Run target functionality
- background.queueBook(testBookId);
+test.serial(`should queue a book and log message even if topic exists`, (t) => {
+ // Setup
+ const testBookId = 1;
+ mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, {
+ code: 409
+ });
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.ok(
- mocks.pubsub.topic.calledOnce,
- `pubsub.topic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.topic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.topic() should have been called with the right arguments`
- );
- assert.ok(
- mocks.topic.publish,
- `topic.publish() should have been called once`
- );
- assert.deepEqual(
- mocks.topic.publish.firstCall.args[0],
- {
- data: {
- action: `processBook`,
- bookId: testBookId
- }
- },
- `topic.publish() should have been called with the right arguments`
- );
- assert.ok(
- mocks.logging.info.calledOnce,
- `logging.info() should have been called`
- );
- assert.equal(
- mocks.logging.info.firstCall.args[0],
- `Book ${testBookId} queued for background processing`,
- `logging.info() should have been called with the right arguments`
- );
- });
- it(`should log error if cannot get topic`, () => {
- // Setup
- const testBookId = 1;
- const testErrorMsg = `test error`;
- mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
+ // Run target functionality
+ background.queueBook(testBookId);
- // Run target functionality
- background.queueBook(testBookId);
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.pubsub.topic.calledOnce,
+ `pubsub.topic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.topic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.topic() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.topic.publish,
+ `topic.publish() should have been called once`
+ );
+ t.deepEqual(
+ mocks.topic.publish.firstCall.args[0],
+ {
+ data: {
+ action: `processBook`,
+ bookId: testBookId
+ }
+ },
+ `topic.publish() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.logging.info.calledOnce,
+ `logging.info() should have been called`
+ );
+ t.is(
+ mocks.logging.info.firstCall.args[0],
+ `Book ${testBookId} queued for background processing`,
+ `logging.info() should have been called with the right arguments`
+ );
+});
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.equal(
- mocks.topic.publish.callCount,
- 0,
- `topic.publish() should NOT have been called`
- );
- assert.equal(
- mocks.logging.info.callCount,
- 0,
- `logging.info() should NOT have been called`
- );
- assert.ok(
- mocks.logging.error.calledOnce,
- `logging.error() should have been called`
- );
- assert.deepEqual(
- mocks.logging.error.firstCall.args,
- [`Error occurred while getting pubsub topic`, testErrorMsg],
- `logging.error() should have been called with the right arguments`
- );
- });
- it(`should log error if cannot publish message`, () => {
- // Setup
- const testBookId = 1;
- const testErrorMsg = `test error`;
- mocks.topic.publish = sinon.stub().callsArgWith(1, testErrorMsg);
+test.serial(`should log error if cannot get topic`, (t) => {
+ // Setup
+ const testBookId = 1;
+ const testErrorMsg = `test error`;
+ mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
- // Run target functionality
- background.queueBook(testBookId);
+ // Run target functionality
+ background.queueBook(testBookId);
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.publish,
- `topic.publish() should have been called once`
- );
- assert.deepEqual(
- mocks.topic.publish.firstCall.args[0],
- {
- data: {
- action: `processBook`,
- bookId: testBookId
- }
- },
- `topic.publish() should have been called with the right arguments`
- );
- assert.equal(
- mocks.logging.info.callCount,
- 0,
- `logging.info() should NOT have been called`
- );
- assert.ok(
- mocks.logging.error.calledOnce,
- `logging.error() should have been called`
- );
- assert.deepEqual(
- mocks.logging.error.firstCall.args,
- [`Error occurred while queuing background task`, testErrorMsg],
- `logging.error() should have been called with the right arguments`
- );
- });
- });
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.is(
+ mocks.topic.publish.callCount,
+ 0,
+ `topic.publish() should NOT have been called`
+ );
+ t.is(
+ mocks.logging.info.callCount,
+ 0,
+ `logging.info() should NOT have been called`
+ );
+ t.truthy(
+ mocks.logging.error.calledOnce,
+ `logging.error() should have been called`
+ );
+});
+
+test.serial(`should log error if cannot publish message`, (t) => {
+ // Setup
+ const testBookId = 1;
+ const testErrorMsg = `test error`;
+ mocks.topic.publish = sinon.stub().callsArgWith(1, testErrorMsg);
+
+ // Run target functionality
+ background.queueBook(testBookId);
+
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.publish,
+ `topic.publish() should have been called once`
+ );
});
diff --git a/7-gce/test/cloudsql.test.js b/7-gce/test/cloudsql.test.js
new file mode 100644
index 0000000000..ff2ba5fd52
--- /dev/null
+++ b/7-gce/test/cloudsql.test.js
@@ -0,0 +1,23 @@
+// Copyright 2017, 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';
+
+const test = require(`ava`);
+
+if (require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || process.env.TEST_CLOUDSQL) {
+ require(`./_api-tests`)(`cloudsql`);
+ require(`./_crud-tests`)(`cloudsql`);
+} else {
+ test(`Skipping Cloud SQL tests...`, (t) => t.pass());
+}
diff --git a/7-gce/test/crud.test.js b/7-gce/test/crud.test.js
deleted file mode 100644
index 0f07993e9c..0000000000
--- a/7-gce/test/crud.test.js
+++ /dev/null
@@ -1,207 +0,0 @@
-// 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';
-
-const assert = require(`assert`);
-const config = require(`./config`);
-const utils = require(`nodejs-repo-tools`);
-
-module.exports = (DATA_BACKEND) => {
- describe(`crud.js`, () => {
- let ORIG_DATA_BACKEND;
-
- before(() => {
- const appConfig = require(`../config`);
- ORIG_DATA_BACKEND = appConfig.get(`DATA_BACKEND`);
- appConfig.set(`DATA_BACKEND`, DATA_BACKEND);
- });
-
- describe(`/books`, () => {
- let id;
-
- // setup a book
- before((done) => {
- utils.getRequest(config)
- .post(`/api/books`)
- .send({ title: `my book` })
- .expect(200)
- .expect((response) => {
- id = response.body.id;
- assert.ok(response.body.id);
- assert.equal(response.body.title, `my book`);
- })
- .end(done);
- });
-
- it(`should show a list of books`, (done) => {
- // Give Datastore time to become consistent
- setTimeout(() => {
- const expected = `
my other book <\/small><\/h4>/;
+ getRequest(testConfig)
+ .get(`/books/${id}`)
+ .expect(200)
+ .expect((response) => {
+ t.regex(response.text, expected);
+ })
+ .end(t.end);
+ });
+
+ test.serial.cb(`should delete a book`, (t) => {
+ const expected = /Redirecting to \/books/;
+ getRequest(testConfig)
+ .get(`/books/${id}/delete`)
+ .expect(302)
+ .expect((response) => {
+ id = undefined;
+ t.regex(response.text, expected);
+ })
+ .end(t.end);
+ });
+
+ // clean up
+ test.always.after.cb((t) => {
+ appConfig.set(`DATA_BACKEND`, originalDataBackend);
+
+ if (id) {
+ getRequest(testConfig)
+ .delete(`/api/books/${id}`)
+ .expect(200)
+ .end(t.end);
+ } else {
+ t.end();
+ }
+ });
+};
diff --git a/optional-container-engine/test/config.js b/optional-container-engine/test/_test-config.js
similarity index 75%
rename from optional-container-engine/test/config.js
rename to optional-container-engine/test/_test-config.js
index 6b235cd304..8ceeb3e988 100644
--- a/optional-container-engine/test/config.js
+++ b/optional-container-engine/test/_test-config.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
@@ -14,7 +14,9 @@
'use strict';
const path = require(`path`);
-const test = `optional-container-engine`;
+const test = `optional`;
+
+const PORT = 8088;
module.exports = {
test: test,
@@ -22,9 +24,11 @@ module.exports = {
cmd: `node`,
args: [`app.js`],
msg: `Bookshelf`,
- port: 8088,
+ port: PORT,
+ url: `http://localhost:${PORT}`,
env: {
- SUBSCRIPTION_NAME: `${test}-shared-worker-subscription`,
- TOPIC_NAME: `${test}-book-process-queue`
+ PORT: PORT,
+ SUBSCRIPTION_NAME: `shared-worker-subscription-${test}`,
+ TOPIC_NAME: `book-process-queue-${test}`
}
};
diff --git a/optional-container-engine/test/config.worker.js b/optional-container-engine/test/_test-config.worker.js
similarity index 96%
rename from optional-container-engine/test/config.worker.js
rename to optional-container-engine/test/_test-config.worker.js
index 67655a5e67..9610d6b919 100644
--- a/optional-container-engine/test/config.worker.js
+++ b/optional-container-engine/test/_test-config.worker.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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
diff --git a/optional-container-engine/test/api.test.js b/optional-container-engine/test/api.test.js
deleted file mode 100644
index 151759d8a8..0000000000
--- a/optional-container-engine/test/api.test.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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';
-
-const assert = require(`assert`);
-const config = require(`./config`);
-const utils = require(`nodejs-repo-tools`);
-
-module.exports = (DATA_BACKEND) => {
- describe(`api.js`, () => {
- let ORIG_DATA_BACKEND;
- let id;
-
- before(() => {
- const appConfig = require(`../config`);
- ORIG_DATA_BACKEND = appConfig.get(`DATA_BACKEND`);
- appConfig.set(`DATA_BACKEND`, DATA_BACKEND);
- });
-
- it(`should create a book`, (done) => {
- utils.getRequest(config)
- .post(`/api/books`)
- .send({ title: `beep` })
- .expect(200)
- .expect((response) => {
- id = response.body.id;
- assert.ok(response.body.id);
- assert.equal(response.body.title, `beep`);
- })
- .end(done);
- });
-
- it(`should list books`, (done) => {
- // Give Datastore time to become consistent
- setTimeout(() => {
- utils.getRequest(config)
- .get(`/api/books`)
- .expect(200)
- .expect((response) => {
- assert.ok(Array.isArray(response.body.items));
- assert.ok(response.body.items.length >= 1);
- })
- .end(done);
- }, 1000);
- });
-
- it(`should delete a book`, (done) => {
- utils.getRequest(config)
- .delete(`/api/books/${id}`)
- .expect(200)
- .expect((response) => {
- assert.equal(response.text, `OK`);
- })
- .end(done);
- });
-
- after(() => {
- require(`../config`).set(`DATA_BACKEND`, ORIG_DATA_BACKEND);
- });
- });
-};
diff --git a/optional-container-engine/test/app.test.js b/optional-container-engine/test/app.test.js
index 3178f25eec..d1948edc8c 100644
--- a/optional-container-engine/test/app.test.js
+++ b/optional-container-engine/test/app.test.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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,94 +13,80 @@
'use strict';
-const assert = require(`assert`);
-const config = require(`./config`);
+const testConfig = require(`./_test-config`);
const proxyquire = require(`proxyquire`).noPreserveCache();
const sinon = require(`sinon`);
+const test = require(`ava`);
const utils = require(`nodejs-repo-tools`);
-describe(`app.js`, () => {
- if (!process.env.E2E_TESTS) {
- it(`should run`, (done) => {
- utils.testLocalApp(config, done);
- });
- }
-
- it(`should redirect / to /books`, (done) => {
- utils.getRequest(config)
- .get(`/`)
- .expect(302)
- .expect((response) => {
- assert.equal(response.text.includes(`Redirecting to /books`), true);
- })
- .end(done);
+if (!process.env.E2E_TESTS) {
+ test.cb(`should run`, (t) => {
+ utils.testLocalApp(testConfig, t.end);
});
+}
+
+test.cb(`should redirect / to /books`, (t) => {
+ utils.getRequest(testConfig)
+ .get(`/`)
+ .expect(302)
+ .expect((response) => {
+ t.regex(response.text, /Redirecting to \/books/);
+ })
+ .end(t.end);
+});
- it(`should check config`, () => {
- const nconfMock = {
- argv: sinon.stub().returnsThis(),
- env: sinon.stub().returnsThis(),
- file: sinon.stub().returnsThis(),
- defaults: sinon.stub().returnsThis(),
- get: function (setting) {
- return this[setting];
- }
- };
-
- function getMsg (setting) {
- return `You must set ${setting} as an environment variable or in config.json!`;
+test(`should check config`, (t) => {
+ const nconfMock = {
+ argv: sinon.stub().returnsThis(),
+ env: sinon.stub().returnsThis(),
+ file: sinon.stub().returnsThis(),
+ defaults: sinon.stub().returnsThis(),
+ get: function (setting) {
+ return this[setting];
}
+ };
- nconfMock.DATA_BACKEND = `datastore`;
-
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`GCLOUD_PROJECT`));
-
- nconfMock.GCLOUD_PROJECT = `project`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`CLOUD_BUCKET`));
-
- nconfMock.CLOUD_BUCKET = `bucket`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`OAUTH2_CLIENT_ID`));
-
- nconfMock.OAUTH2_CLIENT_ID = `foo`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`OAUTH2_CLIENT_SECRET`));
-
- nconfMock.OAUTH2_CLIENT_SECRET = `bar`;
- assert.doesNotThrow(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- });
-
- nconfMock.DATA_BACKEND = `cloudsql`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MYSQL_USER`));
- nconfMock.MYSQL_USER = `user`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MYSQL_PASSWORD`));
- nconfMock.MYSQL_PASSWORD = `password`;
- assert.doesNotThrow(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- });
-
- nconfMock.DATA_BACKEND = `mongodb`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MONGO_URL`));
- nconfMock.MONGO_URL = `url`;
- assert.throws(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- }, Error, getMsg(`MONGO_COLLECTION`));
- nconfMock.MONGO_COLLECTION = `collection`;
- assert.doesNotThrow(() => {
- proxyquire(`../config`, { nconf: nconfMock });
- });
- });
+ function getMsg (setting) {
+ return `You must set ${setting} as an environment variable or in config.json!`;
+ }
+
+ const testFunc = () => {
+ proxyquire(`../config`, { nconf: nconfMock });
+ };
+
+ nconfMock.DATA_BACKEND = `datastore`;
+
+ t.throws(testFunc, Error, getMsg(`GCLOUD_PROJECT`));
+ nconfMock.GCLOUD_PROJECT = `project`;
+
+ t.throws(testFunc, Error, getMsg(`CLOUD_BUCKET`));
+ nconfMock.CLOUD_BUCKET = `bucket`;
+
+ t.throws(testFunc, Error, getMsg(`OAUTH2_CLIENT_ID`));
+ nconfMock.OAUTH2_CLIENT_ID = `foo`;
+
+ t.throws(testFunc, Error, getMsg(`OAUTH2_CLIENT_SECRET`));
+ nconfMock.OAUTH2_CLIENT_SECRET = `bar`;
+
+ t.notThrows(testFunc);
+
+ nconfMock.DATA_BACKEND = `cloudsql`;
+
+ t.throws(testFunc, Error, getMsg(`MYSQL_USER`));
+ nconfMock.MYSQL_USER = `user`;
+
+ t.throws(testFunc, Error, getMsg(`MYSQL_PASSWORD`));
+ nconfMock.MYSQL_PASSWORD = `password`;
+
+ t.notThrows(testFunc);
+
+ nconfMock.DATA_BACKEND = `mongodb`;
+
+ t.throws(testFunc, Error, getMsg(`MONGO_URL`));
+ nconfMock.MONGO_URL = `url`;
+
+ t.throws(testFunc, Error, getMsg(`MONGO_COLLECTION`));
+ nconfMock.MONGO_COLLECTION = `collection`;
+
+ t.notThrows(testFunc);
});
diff --git a/optional-container-engine/test/background.test.js b/optional-container-engine/test/background.test.js
index 4027a5f2d4..bc63b26ac7 100644
--- a/optional-container-engine/test/background.test.js
+++ b/optional-container-engine/test/background.test.js
@@ -1,4 +1,4 @@
-// Copyright 2015-2016, Google, Inc.
+// Copyright 2017, 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,415 +13,384 @@
'use strict';
-const assert = require(`assert`);
-const sinon = require(`sinon`);
const proxyquire = require(`proxyquire`).noPreserveCache();
+const sinon = require(`sinon`);
+const test = require(`ava`);
+
let background;
const mocks = {};
-describe(`background.js`, () => {
- beforeEach(() => {
- // Mock dependencies used by background.js
- mocks.config = {
- GCLOUD_PROJECT: process.env.GCLOUD_PROJECT,
- SUBSCRIPTION_NAME: `shared-worker-subscription`,
- TOPIC_NAME: `book-process-queue`
- };
- mocks.config.get = function (key) {
- return this[key];
- };
- mocks.subscription = {
- on: sinon.stub()
- };
- mocks.topic = {
- subscribe: sinon.stub().callsArgWith(2, null, mocks.subscription),
- publish: sinon.stub().callsArg(1)
- };
- mocks.pubsub = {
- createTopic: sinon.stub().callsArgWith(1, null, mocks.topic),
- topic: sinon.stub().returns(mocks.topic)
- };
- mocks.Pubsub = sinon.stub().returns(mocks.pubsub);
- mocks.logging = {
- info: sinon.stub(),
- error: sinon.stub()
- };
- // Load background.js with provided mocks
- background = proxyquire(`../lib/background`, {
- '@google-cloud/pubsub': mocks.Pubsub,
- '../config': mocks.config,
- './logging': mocks.logging
- });
+test.beforeEach((t) => {
+ // Mock dependencies used by background.js
+ mocks.config = {
+ GCLOUD_PROJECT: process.env.GCLOUD_PROJECT,
+ SUBSCRIPTION_NAME: `shared-worker-subscription`,
+ TOPIC_NAME: `book-process-queue`
+ };
+ mocks.config.get = function (key) {
+ return this[key];
+ };
+ mocks.subscription = {
+ on: sinon.stub()
+ };
+ mocks.topic = {
+ subscribe: sinon.stub().callsArgWith(2, null, mocks.subscription),
+ publish: sinon.stub().callsArg(1)
+ };
+ mocks.pubsub = {
+ createTopic: sinon.stub().callsArgWith(1, null, mocks.topic),
+ topic: sinon.stub().returns(mocks.topic)
+ };
+ mocks.Pubsub = sinon.stub().returns(mocks.pubsub);
+ mocks.logging = {
+ info: sinon.stub(),
+ error: sinon.stub()
+ };
+ // Load background.js with provided mocks
+ background = proxyquire(`../lib/background`, {
+ '@google-cloud/pubsub': mocks.Pubsub,
+ '../config': mocks.config,
+ './logging': mocks.logging
+ });
+
+ t.truthy(
+ mocks.Pubsub.calledOnce,
+ `Pubsub() should have been called once`
+ );
+});
- assert.ok(
- mocks.Pubsub.calledOnce,
- `Pubsub() should have been called once`
+test.serial.cb(`should subscribe and log message`, (t) => {
+ // Setup
+ const testMessage = `test message`;
+
+ // Run target functionality
+ background.subscribe((err, message) => {
+ // Assertions
+ t.truthy(
+ err === null,
+ `err should be null`
+ );
+ t.is(message, testMessage, `should have message`);
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right args`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.subscribe.calledOnce,
+ `topic.subscribe should have been called once`
+ );
+ t.is(
+ mocks.topic.subscribe.firstCall.args[0],
+ `shared-worker-subscription`,
+ `topic.subscribe() should have been called with the right arguments`
);
+ t.deepEqual(
+ mocks.topic.subscribe.firstCall.args[1],
+ {
+ autoAck: true
+ },
+ `topic.subscribe() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.subscription.on.calledTwice,
+ `subscription.on should have been called twice`
+ );
+ t.is(
+ mocks.subscription.on.firstCall.args[0],
+ `message`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.truthy(
+ typeof mocks.subscription.on.firstCall.args[1] === `function`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.subscription.on.secondCall.args[0],
+ `error`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.truthy(
+ typeof mocks.subscription.on.secondCall.args[1] === `function`,
+ `subscription.on() should have been called with the right arguments`
+ );
+ t.end();
});
- describe(`subscribe()`, () => {
- it(`should subscribe and log message`, (done) => {
- // Setup
- const testMessage = `test message`;
+ // Trigger a message
+ setTimeout(() => {
+ mocks.subscription.on.firstCall.args[1]({
+ data: testMessage
+ });
+ }, 10);
+});
- // Run target functionality
- background.subscribe((err, message) => {
- // Assertions
- assert.ok(
- err === null,
- `err should be null`
- );
- assert.equal(message, testMessage, `should have message`);
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right args`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.subscribe.calledOnce,
- `topic.subscribe should have been called once`
- );
- assert.equal(
- mocks.topic.subscribe.firstCall.args[0],
- `shared-worker-subscription`,
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.deepEqual(
- mocks.topic.subscribe.firstCall.args[1],
- {
- autoAck: true,
- reuseExisting: true
- },
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.ok(
- mocks.subscription.on.calledTwice,
- `subscription.on should have been called twice`
- );
- assert.equal(
- mocks.subscription.on.firstCall.args[0],
- `message`,
- `subscription.on() should have been called with the right arguments`
- );
- assert.ok(
- typeof mocks.subscription.on.firstCall.args[1] === `function`,
- `subscription.on() should have been called with the right arguments`
- );
- assert.equal(
- mocks.subscription.on.secondCall.args[0],
- `error`,
- `subscription.on() should have been called with the right arguments`
- );
- assert.ok(
- typeof mocks.subscription.on.secondCall.args[1] === `function`,
- `subscription.on() should have been called with the right arguments`
- );
- done();
- });
+test.serial.cb(`should return topic error, if any`, (t) => {
+ // Setup
+ const testErrorMsg = `test error`;
+ mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
- // Trigger a message
- setTimeout(() => {
- mocks.subscription.on.firstCall.args[1]({
- data: testMessage
- });
- }, 10);
- });
- it(`should return topic error, if any`, (done) => {
- // Setup
- const testErrorMsg = `test error`;
- mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
+ // Run target functionality
+ background.subscribe((data) => {
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right args`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.is(data, testErrorMsg);
+ t.is(
+ mocks.topic.subscribe.callCount,
+ 0,
+ `topic.subscribe() should NOT have been called`
+ );
+ t.is(
+ mocks.subscription.on.callCount,
+ 0,
+ `subscription.on() should NOT have been called`
+ );
+ t.end();
+ });
+});
- // Run target functionality
- background.subscribe((data) => {
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right args`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.equal(data, testErrorMsg);
- assert.equal(
- mocks.topic.subscribe.callCount,
- 0,
- `topic.subscribe() should NOT have been called`
- );
- assert.equal(
- mocks.subscription.on.callCount,
- 0,
- `subscription.on() should NOT have been called`
- );
- done();
- });
- });
- it(`should return subscription error, if any`, (done) => {
- // Setup
- const testErrorMsg = `test error`;
- mocks.topic.subscribe = sinon.stub().callsArgWith(2, testErrorMsg);
+test.serial.cb(`should return subscription error, if any`, (t) => {
+ // Setup
+ const testErrorMsg = `test error`;
+ mocks.topic.subscribe = sinon.stub().callsArgWith(2, testErrorMsg);
- // Run target functionality
- background.subscribe((data) => {
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right args`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.subscribe.calledOnce,
- `topic.subscribe should have been called once`
- );
- assert.equal(
- mocks.topic.subscribe.firstCall.args[0],
- `shared-worker-subscription`,
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.deepEqual(
- mocks.topic.subscribe.firstCall.args[1],
- {
- autoAck: true,
- reuseExisting: true
- },
- `topic.subscribe() should have been called with the right arguments`
- );
- assert.equal(data, testErrorMsg);
- assert.equal(
- mocks.subscription.on.callCount,
- 0,
- `subscription.on() should NOT have been called`
- );
- assert.equal(
- mocks.logging.info.callCount,
- 0,
- `logging.info() should NOT have been called`
- );
- done();
- });
- });
+ // Run target functionality
+ background.subscribe((data) => {
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right args`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.subscribe.calledOnce,
+ `topic.subscribe should have been called once`
+ );
+ t.is(
+ mocks.topic.subscribe.firstCall.args[0],
+ `shared-worker-subscription`,
+ `topic.subscribe() should have been called with the right arguments`
+ );
+ t.deepEqual(
+ mocks.topic.subscribe.firstCall.args[1],
+ {
+ autoAck: true
+ },
+ `topic.subscribe() should have been called with the right arguments`
+ );
+ t.is(data, testErrorMsg);
+ t.is(
+ mocks.subscription.on.callCount,
+ 0,
+ `subscription.on() should NOT have been called`
+ );
+ t.is(
+ mocks.logging.info.callCount,
+ 0,
+ `logging.info() should NOT have been called`
+ );
+ t.end();
});
+});
- describe(`queueBook()`, () => {
- it(`should queue a book and log message`, () => {
- // Setup
- const testBookId = 1;
+test.serial(`should queue a book and log message`, (t) => {
+ // Setup
+ const testBookId = 1;
- // Run target functionality
- background.queueBook(testBookId);
+ // Run target functionality
+ background.queueBook(testBookId);
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.publish,
- `topic.publish() should have been called once`
- );
- assert.deepEqual(
- mocks.topic.publish.firstCall.args[0],
- {
- data: {
- action: `processBook`,
- bookId: testBookId
- }
- },
- `topic.publish() should have been called with the right arguments`
- );
- assert.ok(
- mocks.logging.info.calledOnce,
- `logging.info() should have been called`
- );
- assert.equal(
- mocks.logging.info.firstCall.args[0],
- `Book ${testBookId} queued for background processing`,
- `logging.info() should have been called with the right arguments`
- );
- });
- it(`should queue a book and log message even if topic exists`, () => {
- // Setup
- const testBookId = 1;
- mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, {
- code: 409
- });
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.publish,
+ `topic.publish() should have been called once`
+ );
+ t.deepEqual(
+ mocks.topic.publish.firstCall.args[0],
+ {
+ data: {
+ action: `processBook`,
+ bookId: testBookId
+ }
+ },
+ `topic.publish() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.logging.info.calledOnce,
+ `logging.info() should have been called`
+ );
+ t.is(
+ mocks.logging.info.firstCall.args[0],
+ `Book ${testBookId} queued for background processing`,
+ `logging.info() should have been called with the right arguments`
+ );
+});
- // Run target functionality
- background.queueBook(testBookId);
+test.serial(`should queue a book and log message even if topic exists`, (t) => {
+ // Setup
+ const testBookId = 1;
+ mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, {
+ code: 409
+ });
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.ok(
- mocks.pubsub.topic.calledOnce,
- `pubsub.topic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.topic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.topic() should have been called with the right arguments`
- );
- assert.ok(
- mocks.topic.publish,
- `topic.publish() should have been called once`
- );
- assert.deepEqual(
- mocks.topic.publish.firstCall.args[0],
- {
- data: {
- action: `processBook`,
- bookId: testBookId
- }
- },
- `topic.publish() should have been called with the right arguments`
- );
- assert.ok(
- mocks.logging.info.calledOnce,
- `logging.info() should have been called`
- );
- assert.equal(
- mocks.logging.info.firstCall.args[0],
- `Book ${testBookId} queued for background processing`,
- `logging.info() should have been called with the right arguments`
- );
- });
- it(`should log error if cannot get topic`, () => {
- // Setup
- const testBookId = 1;
- const testErrorMsg = `test error`;
- mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
+ // Run target functionality
+ background.queueBook(testBookId);
- // Run target functionality
- background.queueBook(testBookId);
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.pubsub.topic.calledOnce,
+ `pubsub.topic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.topic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.topic() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.topic.publish,
+ `topic.publish() should have been called once`
+ );
+ t.deepEqual(
+ mocks.topic.publish.firstCall.args[0],
+ {
+ data: {
+ action: `processBook`,
+ bookId: testBookId
+ }
+ },
+ `topic.publish() should have been called with the right arguments`
+ );
+ t.truthy(
+ mocks.logging.info.calledOnce,
+ `logging.info() should have been called`
+ );
+ t.is(
+ mocks.logging.info.firstCall.args[0],
+ `Book ${testBookId} queued for background processing`,
+ `logging.info() should have been called with the right arguments`
+ );
+});
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.equal(
- mocks.topic.publish.callCount,
- 0,
- `topic.publish() should NOT have been called`
- );
- assert.equal(
- mocks.logging.info.callCount,
- 0,
- `logging.info() should NOT have been called`
- );
- assert.ok(
- mocks.logging.error.calledOnce,
- `logging.error() should have been called`
- );
- assert.deepEqual(
- mocks.logging.error.firstCall.args,
- [`Error occurred while getting pubsub topic`, testErrorMsg],
- `logging.error() should have been called with the right arguments`
- );
- });
- it(`should log error if cannot publish message`, () => {
- // Setup
- const testBookId = 1;
- const testErrorMsg = `test error`;
- mocks.topic.publish = sinon.stub().callsArgWith(1, testErrorMsg);
+test.serial(`should log error if cannot get topic`, (t) => {
+ // Setup
+ const testBookId = 1;
+ const testErrorMsg = `test error`;
+ mocks.pubsub.createTopic = sinon.stub().callsArgWith(1, testErrorMsg);
- // Run target functionality
- background.queueBook(testBookId);
+ // Run target functionality
+ background.queueBook(testBookId);
- // Assertions
- assert.ok(
- mocks.pubsub.createTopic,
- `pubsub.createTopic() should have been called once`
- );
- assert.equal(
- mocks.pubsub.createTopic.firstCall.args[0],
- `book-process-queue`,
- `pubsub.createTopic() should have been called with the right arguments`
- );
- assert.equal(
- mocks.pubsub.topic.callCount,
- 0,
- `pubsub.topic() should NOT have been called`
- );
- assert.ok(
- mocks.topic.publish,
- `topic.publish() should have been called once`
- );
- assert.deepEqual(
- mocks.topic.publish.firstCall.args[0],
- {
- data: {
- action: `processBook`,
- bookId: testBookId
- }
- },
- `topic.publish() should have been called with the right arguments`
- );
- assert.equal(
- mocks.logging.info.callCount,
- 0,
- `logging.info() should NOT have been called`
- );
- assert.ok(
- mocks.logging.error.calledOnce,
- `logging.error() should have been called`
- );
- assert.deepEqual(
- mocks.logging.error.firstCall.args,
- [`Error occurred while queuing background task`, testErrorMsg],
- `logging.error() should have been called with the right arguments`
- );
- });
- });
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.is(
+ mocks.topic.publish.callCount,
+ 0,
+ `topic.publish() should NOT have been called`
+ );
+ t.is(
+ mocks.logging.info.callCount,
+ 0,
+ `logging.info() should NOT have been called`
+ );
+ t.truthy(
+ mocks.logging.error.calledOnce,
+ `logging.error() should have been called`
+ );
+});
+
+test.serial(`should log error if cannot publish message`, (t) => {
+ // Setup
+ const testBookId = 1;
+ const testErrorMsg = `test error`;
+ mocks.topic.publish = sinon.stub().callsArgWith(1, testErrorMsg);
+
+ // Run target functionality
+ background.queueBook(testBookId);
+
+ // Assertions
+ t.truthy(
+ mocks.pubsub.createTopic,
+ `pubsub.createTopic() should have been called once`
+ );
+ t.is(
+ mocks.pubsub.createTopic.firstCall.args[0],
+ `book-process-queue`,
+ `pubsub.createTopic() should have been called with the right arguments`
+ );
+ t.is(
+ mocks.pubsub.topic.callCount,
+ 0,
+ `pubsub.topic() should NOT have been called`
+ );
+ t.truthy(
+ mocks.topic.publish,
+ `topic.publish() should have been called once`
+ );
});
diff --git a/optional-container-engine/test/cloudsql.test.js b/optional-container-engine/test/cloudsql.test.js
new file mode 100644
index 0000000000..ff2ba5fd52
--- /dev/null
+++ b/optional-container-engine/test/cloudsql.test.js
@@ -0,0 +1,23 @@
+// Copyright 2017, 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';
+
+const test = require(`ava`);
+
+if (require(`../config`).get(`DATA_BACKEND`) === `cloudsql` || process.env.TEST_CLOUDSQL) {
+ require(`./_api-tests`)(`cloudsql`);
+ require(`./_crud-tests`)(`cloudsql`);
+} else {
+ test(`Skipping Cloud SQL tests...`, (t) => t.pass());
+}
diff --git a/optional-container-engine/test/crud.test.js b/optional-container-engine/test/crud.test.js
deleted file mode 100644
index 0f07993e9c..0000000000
--- a/optional-container-engine/test/crud.test.js
+++ /dev/null
@@ -1,207 +0,0 @@
-// 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';
-
-const assert = require(`assert`);
-const config = require(`./config`);
-const utils = require(`nodejs-repo-tools`);
-
-module.exports = (DATA_BACKEND) => {
- describe(`crud.js`, () => {
- let ORIG_DATA_BACKEND;
-
- before(() => {
- const appConfig = require(`../config`);
- ORIG_DATA_BACKEND = appConfig.get(`DATA_BACKEND`);
- appConfig.set(`DATA_BACKEND`, DATA_BACKEND);
- });
-
- describe(`/books`, () => {
- let id;
-
- // setup a book
- before((done) => {
- utils.getRequest(config)
- .post(`/api/books`)
- .send({ title: `my book` })
- .expect(200)
- .expect((response) => {
- id = response.body.id;
- assert.ok(response.body.id);
- assert.equal(response.body.title, `my book`);
- })
- .end(done);
- });
-
- it(`should show a list of books`, (done) => {
- // Give Datastore time to become consistent
- setTimeout(() => {
- const expected = `