Skip to content
This repository has been archived by the owner on Apr 23, 2021. It is now read-only.

Tests sendwebpush #11

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ node_js:
- 'stable'

install:
- npm install -g mocha
- npm install
26 changes: 26 additions & 0 deletions gulp-tasks/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
Copyright 2014 Google Inc. All Rights Reserved.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well switch to 2016.


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.
*/
/* eslint-env node */

'use strict';

const gulp = require('gulp');
const mocha = require('gulp-mocha');

gulp.task('test:manual', function() {
return gulp.src('./test/*.js', {read: false})
.pipe(mocha());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you'd want to add something like

.on('error', function(error) {
  console.error(error);
  process.exit(1);
});

to your Gulp stream to fail with a non-zero status if gulp-mocha fires an error event. That will, in turn, cause the npm run test to fail, which is what you'd want.

});
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
"eslint-config-google": "^0.3.0",
"gulp": "^3.9.0",
"gulp-eslint": "^1.1.1",
"gulp-mocha": "^2.2.0",
"proxyquire": "^1.7.4",
"require-dir": "^0.3.0",
"sinon": "^1.17.3"
},
"scripts": {
"test": "gulp lint && mocha"
"test": "gulp lint && gulp test:manual"
},
"keywords": [
"web",
Expand Down
27 changes: 19 additions & 8 deletions src/push.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,23 @@ function addAuthToken(pattern, token) {

/**
* Sends a message using the Web Push protocol
* @param {String} message The message to send
* @param {Object} subscription The subscription details for the client we
* are sending to
* @param {String} message The message to send
* @return {Promise} A promise that resolves if the push was sent successfully
* with status and body.
*/
function sendWebPush(message, subscription) {
function sendWebPush(subscription, message) {
if (
!subscription || !subscription.endpoint ||
!message || typeof message !== 'string') {
throw new Error('sendWebPush() expects a subscription endpoint with ' +
'an endpoint parameter and a string send with the push message.');
}

let endpoint = subscription.endpoint;
const authToken = getAuthToken(endpoint);

// If the endpoint is GCM then we temporarily need to rewrite it, as not all
// GCM servers support the Web Push protocol. This should go away in the
// future.
endpoint = endpoint.replace(GCM_URL, TEMP_GCM_URL);

const payload = encrypt(message, subscription);
const headers = {
'Encryption': createHeaderField('salt', payload.salt),
Expand All @@ -95,8 +97,16 @@ function sendWebPush(message, subscription) {

if (authToken) {
headers.Authorization = 'key=' + authToken;
} else if (endpoint.indexOf(GCM_URL) !== -1) {
throw new Error('GCM requires an Auth Token. Please add one using the' +
'addAuthToken() method.');
}

// If the endpoint is GCM then we temporarily need to rewrite it, as not all
// GCM servers support the Web Push protocol. This should go away in the
// future.
endpoint = endpoint.replace(GCM_URL, TEMP_GCM_URL);

return new Promise(function(resolve, reject) {
request.post(endpoint, {
body: payload.ciphertext,
Expand All @@ -106,7 +116,8 @@ function sendWebPush(message, subscription) {
reject(error);
} else {
resolve({
status: `${response.statusCode} ${response.statusMessage}`,
statusCode: response.statusCode,
statusMessage: response.statusMessage,
body: body
});
}
Expand Down
180 changes: 179 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ const EXAMPLE_SERVER_KEYS = {
public: 'BOg5KfYiBdDDRF12Ri17y3v+POPr8X0nVP2jDjowPVI/DMKU1aQ3OLdPH1iaakvR9/PHq6tNCzJH35v/JUz2crY=',
private: 'uDNsfsz91y2ywQeOHljVoiUg3j5RGrDVAswRqjP3v90='
};

const EXAMPLE_SALT = 'AAAAAAAAAAAAAAAAAAAAAA==';

const EXAMPLE_INPUT = 'Hello, World.';
Expand Down Expand Up @@ -59,6 +58,11 @@ const SUBSCRIPTION_NO_KEYS = {
endpoint: 'https://example-endpoint.com/example/1234'
};

const GCM_SUBSCRIPTION_EXAMPLE = {
original: 'https://android.googleapis.com/gcm/send/AAAAAAAAAAA:AAA91AAAA2_A7AAAAAAAAAAAAAAAAAAAAAAAAAAA9AAAAA9AAA_AAAA8AAAAAA5-AAAAAA2AAAA_AAAAA4A51A_A3AAA1AAAAAAAAAAAAAAA3AAAAAAAAA6AA2AAAAAAAA80AAAAAA',
webpush: 'https://gcm-http.googleapis.com/gcm/AAAAAAAAAAA:AAA91AAAA2_A7AAAAAAAAAAAAAAAAAAAAAAAAAAA9AAAAA9AAA_AAAA8AAAAAA5-AAAAAA2AAAA_AAAAA4A51A_A3AAA1AAAAAAAAAAAAAAA3AAAAAAAAA6AA2AAAAAAAA80AAAAAA'
};

const SALT_LENGTH = 16;
const SERVER_PUBLIC_KEY_LENGTH = 65;

Expand Down Expand Up @@ -204,4 +208,178 @@ describe('Test the Libraries Top Level API', function() {
).to.throw('Payload is too large. The max number of bytes is 4080, input is 5000 bytes.');
});
});

describe('Test sendWebPush() method', function() {
it('should throw an error when no input provided', function() {
const library = require('../src/index.js');

expect(
() => library.sendWebPush()
).to.throw('sendWebPush() expects a subscription endpoint with ' +
'an endpoint parameter and a string send with the push message.');
});

it('should throw an error when the subscription object has no endpoint', function() {
const library = require('../src/index.js');

expect(
() => library.sendWebPush({})
).to.throw('sendWebPush() expects a subscription endpoint with ' +
'an endpoint parameter and a string send with the push message.');
});

it('should throw an error when a subscription is passed in with no payload data', function() {
const library = require('../src/index.js');

expect(
() => library.sendWebPush({
endpoint: 'http://fakendpoint'
})
).to.throw('sendWebPush() expects a subscription endpoint with ' +
'an endpoint parameter and a string send with the push message.');
});

it('should throw an error when a subscription is passed in with array as payload data', function() {
const library = require('../src/index.js');

expect(
() => library.sendWebPush({
endpoint: 'http://fakendpoint'
}, [
{
hello: 'world'
},
'This is a test',
Promise.resolve('Promise Resolve'),
Promise.reject('Promise Reject')
])
).to.throw('sendWebPush() expects a subscription endpoint with ' +
'an endpoint parameter and a string send with the push message.');
});

it('should throw an error when a subscription with no encryption details is passed in with string as payload data', function() {
const library = require('../src/index.js');

expect(
() => library.sendWebPush({
endpoint: 'http://fakendpoint'
}, 'Hello, World!')
).to.throw('Subscription has no encryption details.');
});

it('should attempt a web push protocol request', function() {
const requestReplacement = {
post: (endpoint, data, cb) => {
endpoint.should.equal(VALID_SUBSCRIPTION.endpoint);

Buffer.isBuffer(data.body).should.equal(true);
data.headers.Encryption.should.have.length(27);
data.headers['Crypto-Key'].should.have.length(90);

cb(
null,
{
statusCode: 200,
statusMessage: 'Status message'
},
'Response body'
);
}
};
const pushProxy = proxyquire('../src/push.js', {
'request': requestReplacement
});
const library = proxyquire('../src/index.js', {
'./push': pushProxy
});
return library.sendWebPush(VALID_SUBSCRIPTION, 'Hello, World!')
.then(response => {
response.statusCode.should.equal(200);
response.statusMessage.should.equal('Status message');
response.body.should.equal('Response body');
});
});

it('should attempt a web push protocol request for GCM', function() {
const API_KEY = 'AAAA';
const gcmSubscription = {
endpoint: GCM_SUBSCRIPTION_EXAMPLE.original,
keys: VALID_SUBSCRIPTION.keys
};
const requestReplacement = {
post: (endpoint, data, cb) => {
endpoint.should.equal(GCM_SUBSCRIPTION_EXAMPLE.webpush);

Buffer.isBuffer(data.body).should.equal(true);
data.headers.Encryption.should.have.length(27);
data.headers['Crypto-Key'].should.have.length(90);
data.headers.Authorization.should.equal('key=' + API_KEY);

cb(
null,
{
statusCode: 200,
statusMessage: 'Status message'
},
'Response body'
);
}
};

const pushProxy = proxyquire('../src/push.js', {
'request': requestReplacement
});
const library = proxyquire('../src/index.js', {
'./push': pushProxy
});
library.addAuthToken('https://android.googleapis.com/gcm/send', API_KEY);
return library.sendWebPush(gcmSubscription, 'Hello, World!')
.then(response => {
response.statusCode.should.equal(200);
response.statusMessage.should.equal('Status message');
response.body.should.equal('Response body');
});
});

it('should throw an error when not providing an AuthToken for a GCM subscription', function() {
const gcmSubscription = {
endpoint: GCM_SUBSCRIPTION_EXAMPLE.original,
keys: VALID_SUBSCRIPTION.keys
};

const library = require('../src/index.js');
expect(
() => library.sendWebPush(gcmSubscription, 'Hello, World!')
).to.throw('GCM requires an Auth Token. Please add one using theaddAuthToken() method.');
});

it('should handle errors from request', function() {
const EXAMPLE_ERROR = 'Example Error';

const requestReplacement = {
post: (endpoint, data, cb) => {
endpoint.should.equal(VALID_SUBSCRIPTION.endpoint);

Buffer.isBuffer(data.body).should.equal(true);
data.headers.Encryption.should.have.length(27);
data.headers['Crypto-Key'].should.have.length(90);

cb(EXAMPLE_ERROR);
}
};
const pushProxy = proxyquire('../src/push.js', {
'request': requestReplacement
});
const library = proxyquire('../src/index.js', {
'./push': pushProxy
});
return library.sendWebPush(VALID_SUBSCRIPTION, 'Hello, World!')
.then(() => {
throw new Error('The promise was expected to reject.');
})
.catch(err => {
err.should.equal(EXAMPLE_ERROR);
});
});
});
});