From 2e465642dfbe34b7cacbaf88a84fa3fba51e0b81 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Tue, 14 Mar 2017 11:00:43 -0400 Subject: [PATCH 1/2] Adds more tests --- .istanbul.yml | 2 ++ package.json | 2 +- spec/APNS.spec.js | 28 +++++++++++++++++++++++++++ spec/ParsePushAdapter.spec.js | 36 +++++++++++++++++++++++++++++++++++ src/index.js | 1 + 5 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 .istanbul.yml diff --git a/.istanbul.yml b/.istanbul.yml new file mode 100644 index 0000000..c0890a3 --- /dev/null +++ b/.istanbul.yml @@ -0,0 +1,2 @@ +instrumentation: + excludes: ["**/spec/**"] diff --git a/package.json b/package.json index 7ae368b..0c455f2c 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "babel-preset-es2015": "^6.6.0", "babel-preset-stage-0": "^6.22.0", "codecov": "^1.0.1", - "istanbul": "^0.4.5", + "istanbul": "1.1.0-alpha.1", "jasmine": "2.5.3", "jasmine-spec-reporter": "^3.2.0" }, diff --git a/spec/APNS.spec.js b/spec/APNS.spec.js index 69e61f9..b22645f 100644 --- a/spec/APNS.spec.js +++ b/spec/APNS.spec.js @@ -1,4 +1,5 @@ var APNS = require('../src/APNS'); +var Parse = require('parse/node'); describe('APNS', () => { @@ -23,6 +24,33 @@ describe('APNS', () => { done(); }); + it('fails to initialize with bad data', (done) => { + try { + new APNS("args"); + } catch(e) { + expect(e.code).toBe(Parse.Error.PUSH_MISCONFIGURED); + done(); + return; + } + fail('should not be reached'); + }); + + it('fails to initialize without a bundleID', (done) => { + const apnsArgs = { + production: true, + bundle: 'hello' + }; + try { + new APNS(apnsArgs); + } catch(e) { + expect(e.code).toBe(Parse.Error.PUSH_MISCONFIGURED); + done(); + return; + } + fail('should not be reached'); + done(); + }); + it('can initialize with multiple certs', (done) => { var args = [ { diff --git a/spec/ParsePushAdapter.spec.js b/spec/ParsePushAdapter.spec.js index a2af8ad..785e602 100644 --- a/spec/ParsePushAdapter.spec.js +++ b/spec/ParsePushAdapter.spec.js @@ -1,4 +1,5 @@ var ParsePushAdapter = require('../src/index').ParsePushAdapter; +var randomString = require('../src/PushAdapterUtils').randomString; var APNS = require('../src/APNS'); var GCM = require('../src/GCM'); @@ -324,6 +325,41 @@ describe('ParsePushAdapter', () => { }) }); + it('properly marks not transmitter when sender is missing', (done) => { + var pushConfig = { + android: { + senderId: 'senderId', + apiKey: 'apiKey' + } + }; + var installations = [{ + deviceType: 'ios', + deviceToken: '0d72a1baa92a2febd9a254cbd6584f750c70b2350af5fc9052d1d12584b738e6', + appIdentifier: 'invalidiosbundleId' + }, + { + deviceType: 'ios', + deviceToken: 'ff3943ed0b2090c47e5d6f07d8f202a10427941d7897fda5a6b18c6d9fd07d48', + appIdentifier: 'invalidiosbundleId' + }] + var parsePushAdapter = new ParsePushAdapter(pushConfig); + parsePushAdapter.send({data: {alert: 'some'}}, installations).then((results) => { + expect(results.length).toBe(2); + results.forEach((result) => { + expect(result.transmitted).toBe(false); + expect(typeof result.device).toBe('object'); + expect(typeof result.device.deviceType).toBe('string'); + expect(typeof result.device.deviceToken).toBe('string'); + expect(result.response.error.indexOf('Can not find sender for push type ios, ')).toBe(0); + }); + done(); + }); + }); + + it('random string throws with size <=0', () => { + expect(() => randomString(0)).toThrow(); + }); + function makeDevice(deviceToken, deviceType, appIdentifier) { return { deviceToken: deviceToken, diff --git a/src/index.js b/src/index.js index 3cfc83f..b50e3ac 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ // for ios push. import log from 'npmlog'; +/* istanbul ignore if */ if (process.env.VERBOSE || process.env.VERBOSE_PARSE_SERVER_PUSH_ADAPTER) { log.level = 'verbose'; } From f737fc44169c7167286a41d088cd5961cba1872c Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Tue, 14 Mar 2017 11:36:14 -0400 Subject: [PATCH 2/2] Adds mockAPNS connection for further testing --- spec/MockAPNConnection.js | 14 ++++++++++++++ spec/ParsePushAdapter.spec.js | 33 ++++++++++++++++++++++++++------- spec/helper.js | 16 ++++++++++++++++ src/APNS.js | 6 +++++- 4 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 spec/MockAPNConnection.js diff --git a/spec/MockAPNConnection.js b/spec/MockAPNConnection.js new file mode 100644 index 0000000..fff8ddb --- /dev/null +++ b/spec/MockAPNConnection.js @@ -0,0 +1,14 @@ +const EventEmitter = require('events'); + +module.exports = function (args) { + let emitter = new EventEmitter(); + emitter.options = args; + emitter.pushNotification = function(push, devices) { + devices.forEach((device) => { + process.nextTick(() => { + emitter.emit('transmitted', push, device); + }); + }); + }; + return emitter; +} \ No newline at end of file diff --git a/spec/ParsePushAdapter.spec.js b/spec/ParsePushAdapter.spec.js index 785e602..a38cce5 100644 --- a/spec/ParsePushAdapter.spec.js +++ b/spec/ParsePushAdapter.spec.js @@ -2,8 +2,18 @@ var ParsePushAdapter = require('../src/index').ParsePushAdapter; var randomString = require('../src/PushAdapterUtils').randomString; var APNS = require('../src/APNS'); var GCM = require('../src/GCM'); +var MockAPNConnection = require('./MockAPNConnection'); describe('ParsePushAdapter', () => { + + beforeEach(() => { + jasmine.mockLibrary('apn', 'Connection', MockAPNConnection); + }); + + afterEach(() => { + jasmine.restoreLibrary('apn', 'Connection'); + }); + it('can be initialized', (done) => { // Make mock config var pushConfig = { @@ -279,22 +289,22 @@ describe('ParsePushAdapter', () => { { deviceType: 'ios', deviceToken: '0d72a1baa92a2febd9a254cbd6584f750c70b2350af5fc9052d1d12584b738e6', - appIdentifier: 'invalidiosbundleId' + appIdentifier: 'iosbundleId' }, { deviceType: 'ios', deviceToken: 'ff3943ed0b2090c47e5d6f07d8f202a10427941d7897fda5a6b18c6d9fd07d48', - appIdentifier: 'invalidiosbundleId' + appIdentifier: 'iosbundleId' }, { deviceType: 'osx', deviceToken: '5cda62a8d88eb48d9111a6c436f2e326a053eb0cd72dfc3a0893089342602235', - appIdentifier: 'invalidosxbundleId' + appIdentifier: 'osxbundleId' }, { deviceType: 'tvos', deviceToken: '3e72a1baa92a2febd9a254cbd6584f750c70b2350af5fc9052d1d12584b738e6', - appIdentifier: 'invalidiosbundleId' // ios and tvos share the same bundleid + appIdentifier: 'iosbundleId' // ios and tvos share the same bundleid }, { deviceType: 'win', @@ -313,10 +323,19 @@ describe('ParsePushAdapter', () => { // 2x iOS, 1x android, 1x osx, 1x tvos expect(results.length).toBe(5); results.forEach((result) => { - expect(result.transmitted).toBe(false); expect(typeof result.device).toBe('object'); - expect(typeof result.device.deviceType).toBe('string'); - expect(typeof result.device.deviceToken).toBe('string'); + if (!result.device) { + fail('result should have device'); + return; + } + const device = result.device; + expect(typeof device.deviceType).toBe('string'); + expect(typeof device.deviceToken).toBe('string'); + if (device.deviceType === 'ios' || device.deviceType === 'osx') { + expect(result.transmitted).toBe(true); + } else { + expect(result.transmitted).toBe(false); + } }) done(); }).catch((err) => { diff --git a/spec/helper.js b/spec/helper.js index 8ac2da8..1b0e852 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -5,3 +5,19 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 5000 jasmine.getEnv().clearReporters(); jasmine.getEnv().addReporter(new SpecReporter()); +var libraryCache = {}; +jasmine.mockLibrary = function(library, name, mock) { + var original = require(library)[name]; + if (!libraryCache[library]) { + libraryCache[library] = {}; + } + require(library)[name] = mock; + libraryCache[library][name] = original; +} + +jasmine.restoreLibrary = function(library, name) { + if (!libraryCache[library] || !libraryCache[library][name]) { + throw 'Can not find library ' + library + ' ' + name; + } + require(library)[name] = libraryCache[library][name]; +} diff --git a/src/APNS.js b/src/APNS.js index 37f41c9..8799373 100644 --- a/src/APNS.js +++ b/src/APNS.js @@ -47,6 +47,7 @@ function APNS(args) { } // Set apns client callbacks + /* istanbul ignore next */ conn.on('connected', () => { log.verbose(LOG_PREFIX, 'APNS Connection %d Connected', conn.index); }); @@ -54,15 +55,17 @@ function APNS(args) { conn.on('transmissionError', (errCode, notification, apnDevice) => { handleTransmissionError(this.conns, errCode, notification, apnDevice); }); - + /* istanbul ignore next */ conn.on('timeout', () => { log.verbose(LOG_PREFIX, 'APNS Connection %d Timeout', conn.index); }); + /* istanbul ignore next */ conn.on('disconnected', () => { log.verbose(LOG_PREFIX, 'APNS Connection %d Disconnected', conn.index); }); + /* istanbul ignore next */ conn.on('socketError', () => { log.verbose(LOG_PREFIX, 'APNS Connection %d Socket Error', conn.index); }); @@ -121,6 +124,7 @@ APNS.prototype.send = function(data, devices) { allPromises.push(promise); } else { let apnDevice = new apn.Device(device.deviceToken); + apnDevice.deviceType = device.deviceType; apnDevice.connIndex = qualifiedConnIndexs[0]; if (device.appIdentifier) { apnDevice.appIdentifier = device.appIdentifier;