Upgrade APNS to use HTTP/2

- uses universal certificate
- removes tests logs
- standardized returned promises from APNS and GCM to something usable in _PushStatus
This commit is contained in:
Florent Vilmart
2016-03-13 18:15:15 -04:00
parent a392c088d8
commit dad50d12f5
13 changed files with 316 additions and 463 deletions

View File

@@ -1,3 +1,4 @@
'use strict';
var APNS = require('../src/APNS');
describe('APNS', () => {
@@ -9,17 +10,13 @@ describe('APNS', () => {
production: true,
bundleId: 'bundleId'
}
var apns = new APNS(args);
var apns = APNS(args);
expect(apns.conns.length).toBe(1);
var apnsConnection = apns.conns[0];
expect(apnsConnection.index).toBe(0);
expect(apnsConnection.bundleId).toBe(args.bundleId);
// TODO: Remove this checking onec we inject APNS
var prodApnsOptions = apnsConnection.options;
expect(prodApnsOptions.cert).toBe(args.cert);
expect(prodApnsOptions.key).toBe(args.key);
expect(prodApnsOptions.production).toBe(args.production);
var apnsConfiguration = apns.getConfiguration();
expect(apnsConfiguration.bundleId).toBe(args.bundleId);
expect(apnsConfiguration.cert).toBe(args.cert);
expect(apnsConfiguration.key).toBe(args.key);
expect(apnsConfiguration.production).toBe(args.production);
done();
});
@@ -39,24 +36,18 @@ describe('APNS', () => {
}
]
var apns = new APNS(args);
expect(apns.conns.length).toBe(2);
var devApnsConnection = apns.conns[1];
expect(devApnsConnection.index).toBe(1);
var devApnsOptions = devApnsConnection.options;
expect(devApnsOptions.cert).toBe(args[0].cert);
expect(devApnsOptions.key).toBe(args[0].key);
expect(devApnsOptions.production).toBe(args[0].production);
expect(devApnsConnection.bundleId).toBe(args[0].bundleId);
var apns = APNS(args);
var devApnsConfiguration = apns.getConfiguration('bundleId');
expect(devApnsConfiguration.cert).toBe(args[0].cert);
expect(devApnsConfiguration.key).toBe(args[0].key);
expect(devApnsConfiguration.production).toBe(args[0].production);
expect(devApnsConfiguration.bundleId).toBe(args[0].bundleId);
var prodApnsConnection = apns.conns[0];
expect(prodApnsConnection.index).toBe(0);
// TODO: Remove this checking onec we inject APNS
var prodApnsOptions = prodApnsConnection.options;
expect(prodApnsOptions.cert).toBe(args[1].cert);
expect(prodApnsOptions.key).toBe(args[1].key);
expect(prodApnsOptions.production).toBe(args[1].production);
expect(prodApnsOptions.bundleId).toBe(args[1].bundleId);
var prodApnsConfiguration = apns.getConfiguration('bundleIdAgain');
expect(prodApnsConfiguration.cert).toBe(args[1].cert);
expect(prodApnsConfiguration.key).toBe(args[1].key);
expect(prodApnsConfiguration.production).toBe(args[1].production);
expect(prodApnsConfiguration.bundleId).toBe(args[1].bundleId);
done();
});
@@ -73,56 +64,14 @@ describe('APNS', () => {
};
var expirationTime = 1454571491354
var notification = APNS.generateNotification(data, expirationTime);
expect(notification.alert).toEqual(data.alert);
expect(notification.badge).toEqual(data.badge);
expect(notification.sound).toEqual(data.sound);
expect(notification.contentAvailable).toEqual(1);
expect(notification.category).toEqual(data.category);
expect(notification.payload).toEqual({
'key': 'value',
'keyAgain': 'valueAgain'
});
expect(notification.expiry).toEqual(expirationTime);
done();
});
it('can choose conns for device without appIdentifier', (done) => {
// Mock conns
var conns = [
{
bundleId: 'bundleId'
},
{
bundleId: 'bundleIdAgain'
}
];
// Mock device
var device = {};
var qualifiedConns = APNS.chooseConns(conns, device);
expect(qualifiedConns).toEqual([0, 1]);
done();
});
it('can choose conns for device with valid appIdentifier', (done) => {
// Mock conns
var conns = [
{
bundleId: 'bundleId'
},
{
bundleId: 'bundleIdAgain'
}
];
// Mock device
var device = {
appIdentifier: 'bundleId'
};
var qualifiedConns = APNS.chooseConns(conns, device);
expect(qualifiedConns).toEqual([0]);
var notification = APNS.generateNotification(data);
expect(notification.aps.alert).toEqual(data.alert);
expect(notification.aps.badge).toEqual(data.badge);
expect(notification.aps.sound).toEqual(data.sound);
expect(notification.aps['content-available']).toEqual(1);
expect(notification.aps.category).toEqual(data.category);
expect(notification.key).toEqual('value');
expect(notification.keyAgain).toEqual('valueAgain');
done();
});
@@ -130,7 +79,7 @@ describe('APNS', () => {
// Mock conns
var conns = [
{
bundleId: 'bundleId'
bundleId: 'bundleId',
},
{
bundleId: 'bundleIdAgain'
@@ -140,143 +89,18 @@ describe('APNS', () => {
var device = {
appIdentifier: 'invalid'
};
var qualifiedConns = APNS.chooseConns(conns, device);
expect(qualifiedConns).toEqual([]);
done();
});
it('can handle transmission error when notification is not in cache or device is missing', (done) => {
// Mock conns
var conns = [];
var errorCode = 1;
var notification = undefined;
var device = {};
APNS.handleTransmissionError(conns, errorCode, notification, device);
var notification = {};
var device = undefined;
APNS.handleTransmissionError(conns, errorCode, notification, device);
done();
});
it('can handle transmission error when there are other qualified conns', (done) => {
// Mock conns
var conns = [
{
pushNotification: jasmine.createSpy('pushNotification'),
bundleId: 'bundleId1'
},
{
pushNotification: jasmine.createSpy('pushNotification'),
bundleId: 'bundleId1'
},
{
pushNotification: jasmine.createSpy('pushNotification'),
bundleId: 'bundleId2'
},
];
var errorCode = 1;
var notification = {};
var apnDevice = {
connIndex: 0,
appIdentifier: 'bundleId1'
};
APNS.handleTransmissionError(conns, errorCode, notification, apnDevice);
expect(conns[0].pushNotification).not.toHaveBeenCalled();
expect(conns[1].pushNotification).toHaveBeenCalled();
expect(conns[2].pushNotification).not.toHaveBeenCalled();
done();
});
it('can handle transmission error when there is no other qualified conns', (done) => {
// Mock conns
var conns = [
{
pushNotification: jasmine.createSpy('pushNotification'),
bundleId: 'bundleId1'
},
{
pushNotification: jasmine.createSpy('pushNotification'),
bundleId: 'bundleId1'
},
{
pushNotification: jasmine.createSpy('pushNotification'),
bundleId: 'bundleId1'
},
{
pushNotification: jasmine.createSpy('pushNotification'),
bundleId: 'bundleId2'
},
{
pushNotification: jasmine.createSpy('pushNotification'),
bundleId: 'bundleId1'
}
];
var errorCode = 1;
var notification = {};
var apnDevice = {
connIndex: 2,
appIdentifier: 'bundleId1'
};
APNS.handleTransmissionError(conns, errorCode, notification, apnDevice);
expect(conns[0].pushNotification).not.toHaveBeenCalled();
expect(conns[1].pushNotification).not.toHaveBeenCalled();
expect(conns[2].pushNotification).not.toHaveBeenCalled();
expect(conns[3].pushNotification).not.toHaveBeenCalled();
expect(conns[4].pushNotification).toHaveBeenCalled();
done();
});
it('can handle transmission error when device has no appIdentifier', (done) => {
// Mock conns
var conns = [
{
pushNotification: jasmine.createSpy('pushNotification'),
bundleId: 'bundleId1'
},
{
pushNotification: jasmine.createSpy('pushNotification'),
bundleId: 'bundleId2'
},
{
pushNotification: jasmine.createSpy('pushNotification'),
bundleId: 'bundleId3'
},
];
var errorCode = 1;
var notification = {};
var apnDevice = {
connIndex: 1,
};
APNS.handleTransmissionError(conns, errorCode, notification, apnDevice);
expect(conns[0].pushNotification).not.toHaveBeenCalled();
expect(conns[1].pushNotification).not.toHaveBeenCalled();
expect(conns[2].pushNotification).toHaveBeenCalled();
let apns = APNS(conns);
var config = apns.getConfiguration(device.appIdentifier);
expect(config).toBeUndefined();
done();
});
it('can send APNS notification', (done) => {
var args = {
cert: 'prodCert.pem',
key: 'prodKey.pem',
production: true,
bundleId: 'bundleId'
}
var apns = new APNS(args);
var conn = {
pushNotification: jasmine.createSpy('send'),
bundleId: 'bundleId'
};
apns.conns = [ conn ];
var apns = APNS(args);
// Mock data
var expirationTime = 1454571491354
var data = {
@@ -293,15 +117,18 @@ describe('APNS', () => {
}
];
var promise = apns.send(data, devices);
expect(conn.pushNotification).toHaveBeenCalled();
var args = conn.pushNotification.calls.first().args;
var notification = args[0];
expect(notification.alert).toEqual(data.data.alert);
expect(notification.expiry).toEqual(data['expiration_time']);
var apnDevice = args[1]
expect(apnDevice.connIndex).toEqual(0);
expect(apnDevice.appIdentifier).toEqual('bundleId');
done();
apns.send(data, devices).then((results) => {
let isArray = Array.isArray(results);
expect(isArray).toBe(true);
expect(results.length).toBe(1);
// No provided certificates
expect(results[0].status).toBe(403);
expect(results[0].device).toEqual(devices[0]);
expect(results[0].transmitted).toBe(false);
done();
}, (err) => {
fail('should not fail');
done();
});
});
});

View File

@@ -23,17 +23,15 @@ describe('GCM', () => {
var data = {
'alert': 'alert'
};
var pushId = 1;
var timeStamp = 1454538822113;
var timeStampISOStr = new Date(timeStamp).toISOString();
var payload = GCM.generateGCMPayload(data, pushId, timeStamp);
var payload = GCM.generateGCMPayload(data, timeStamp);
expect(payload.priority).toEqual('normal');
expect(payload.timeToLive).toEqual(undefined);
var dataFromPayload = payload.data;
expect(dataFromPayload.time).toEqual(timeStampISOStr);
expect(dataFromPayload['push_id']).toEqual(pushId);
var dataFromUser = JSON.parse(dataFromPayload.data);
expect(dataFromUser).toEqual(data);
done();
@@ -44,18 +42,16 @@ describe('GCM', () => {
var data = {
'alert': 'alert'
};
var pushId = 1;
var timeStamp = 1454538822113;
var timeStampISOStr = new Date(timeStamp).toISOString();
var expirationTime = 1454538922113
var payload = GCM.generateGCMPayload(data, pushId, timeStamp, expirationTime);
var payload = GCM.generateGCMPayload(data, timeStamp, expirationTime);
expect(payload.priority).toEqual('normal');
expect(payload.timeToLive).toEqual(Math.floor((expirationTime - timeStamp) / 1000));
var dataFromPayload = payload.data;
expect(dataFromPayload.time).toEqual(timeStampISOStr);
expect(dataFromPayload['push_id']).toEqual(pushId);
var dataFromUser = JSON.parse(dataFromPayload.data);
expect(dataFromUser).toEqual(data);
done();
@@ -66,18 +62,16 @@ describe('GCM', () => {
var data = {
'alert': 'alert'
};
var pushId = 1;
var timeStamp = 1454538822113;
var timeStampISOStr = new Date(timeStamp).toISOString();
var expirationTime = 1454538822112;
var payload = GCM.generateGCMPayload(data, pushId, timeStamp, expirationTime);
var payload = GCM.generateGCMPayload(data, timeStamp, expirationTime);
expect(payload.priority).toEqual('normal');
expect(payload.timeToLive).toEqual(0);
var dataFromPayload = payload.data;
expect(dataFromPayload.time).toEqual(timeStampISOStr);
expect(dataFromPayload['push_id']).toEqual(pushId);
var dataFromUser = JSON.parse(dataFromPayload.data);
expect(dataFromUser).toEqual(data);
done();
@@ -88,19 +82,17 @@ describe('GCM', () => {
var data = {
'alert': 'alert'
};
var pushId = 1;
var timeStamp = 1454538822113;
var timeStampISOStr = new Date(timeStamp).toISOString();
var expirationTime = 2454538822113;
var payload = GCM.generateGCMPayload(data, pushId, timeStamp, expirationTime);
var payload = GCM.generateGCMPayload(data, timeStamp, expirationTime);
expect(payload.priority).toEqual('normal');
// Four week in second
expect(payload.timeToLive).toEqual(4 * 7 * 24 * 60 * 60);
var dataFromPayload = payload.data;
expect(dataFromPayload.time).toEqual(timeStampISOStr);
expect(dataFromPayload['push_id']).toEqual(pushId);
var dataFromUser = JSON.parse(dataFromPayload.data);
expect(dataFromUser).toEqual(data);
done();
@@ -139,6 +131,46 @@ describe('GCM', () => {
done();
});
it('can send GCM request', (done) => {
var gcm = new GCM({
apiKey: 'apiKey'
});
// Mock data
var expirationTime = 2454538822113;
var data = {
'expiration_time': expirationTime,
'data': {
'alert': 'alert'
}
}
// Mock devices
var devices = [
{
deviceToken: 'token'
},
{
deviceToken: 'token2'
},
{
deviceToken: 'token3'
},
{
deviceToken: 'token4'
}
];
gcm.send(data, devices).then((response) => {
expect(Array.isArray(response)).toBe(true);
expect(response.length).toEqual(devices.length);
expect(response.length).toEqual(4);
response.forEach((res, index) => {
expect(res.transmitted).toEqual(false);
expect(res.device).toEqual(devices[index]);
})
done();
})
});
it('can slice devices', (done) => {
// Mock devices
var devices = [makeDevice(1), makeDevice(2), makeDevice(3), makeDevice(4)];

View File

@@ -3,18 +3,20 @@ describe('Parse.Push', () => {
var pushAdapter = {
send: function(body, installations) {
var badge = body.data.badge;
installations.forEach((installation) => {
let promises = installations.map((installation) => {
if (installation.deviceType == "ios") {
expect(installation.badge).toEqual(badge);
expect(installation.originalBadge+1).toEqual(installation.badge);
} else {
expect(installation.badge).toBeUndefined();
}
return Promise.resolve({
err: null,
deviceType: installation.deviceType,
result: true
})
});
return Promise.resolve({
body: body,
installations: installations
});
return Promise.all(promises)
},
getValidPushTypes: function() {
return ["ios", "android"];
@@ -56,4 +58,4 @@ describe('Parse.Push', () => {
done();
});
});
});
});

View File

@@ -1153,7 +1153,6 @@ describe('Parse.ACL', () => {
var query = new Parse.Query("TestClassMasterACL");
return query.find();
}).then((results) => {
console.log(JSON.stringify(results[0]));
ok(!results.length, 'Should not have returned object with secure ACL.');
done();
});

View File

@@ -29,7 +29,9 @@ describe('ParsePushAdapter', () => {
var parsePushAdapter = new ParsePushAdapter(pushConfig);
// Check ios
var iosSender = parsePushAdapter.senderMap['ios'];
expect(iosSender instanceof APNS).toBe(true);
expect(iosSender).not.toBe(undefined);
expect(typeof iosSender.send).toEqual('function');
expect(typeof iosSender.getConfiguration).toEqual('function');
// Check android
var androidSender = parsePushAdapter.senderMap['android'];
expect(androidSender instanceof GCM).toBe(true);

View File

@@ -143,7 +143,7 @@ describe('PushController', () => {
}
})
return Promise.resolve({
error: null
error: null,
payload: body,
})
},