Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -1,6 +1,65 @@
|
||||
var APNS = require('../APNS');
|
||||
var APNS = require('../src/APNS');
|
||||
|
||||
describe('APNS', () => {
|
||||
|
||||
it('can initialize with single cert', (done) => {
|
||||
var args = {
|
||||
cert: 'prodCert.pem',
|
||||
key: 'prodKey.pem',
|
||||
production: true,
|
||||
bundleId: 'bundleId'
|
||||
}
|
||||
var apns = new 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);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can initialize with multiple certs', (done) => {
|
||||
var args = [
|
||||
{
|
||||
cert: 'devCert.pem',
|
||||
key: 'devKey.pem',
|
||||
production: false,
|
||||
bundleId: 'bundleId'
|
||||
},
|
||||
{
|
||||
cert: 'prodCert.pem',
|
||||
key: 'prodKey.pem',
|
||||
production: true,
|
||||
bundleId: 'bundleIdAgain'
|
||||
}
|
||||
]
|
||||
|
||||
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 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);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can generate APNS notification', (done) => {
|
||||
//Mock request data
|
||||
var data = {
|
||||
@@ -29,12 +88,195 @@ describe('APNS', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('can send APNS notification', (done) => {
|
||||
var apns = new APNS();
|
||||
var sender = {
|
||||
pushNotification: jasmine.createSpy('send')
|
||||
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'
|
||||
};
|
||||
apns.sender = sender;
|
||||
|
||||
var qualifiedConns = APNS.chooseConns(conns, device);
|
||||
expect(qualifiedConns).toEqual([0]);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can choose conns for device with invalid appIdentifier', (done) => {
|
||||
// Mock conns
|
||||
var conns = [
|
||||
{
|
||||
bundleId: 'bundleId'
|
||||
},
|
||||
{
|
||||
bundleId: 'bundleIdAgain'
|
||||
}
|
||||
];
|
||||
// Mock device
|
||||
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();
|
||||
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 ];
|
||||
// Mock data
|
||||
var expirationTime = 1454571491354
|
||||
var data = {
|
||||
@@ -43,16 +285,23 @@ describe('APNS', () => {
|
||||
'alert': 'alert'
|
||||
}
|
||||
}
|
||||
// Mock registrationTokens
|
||||
var deviceTokens = ['token'];
|
||||
// Mock devices
|
||||
var devices = [
|
||||
{
|
||||
deviceToken: '112233',
|
||||
appIdentifier: 'bundleId'
|
||||
}
|
||||
];
|
||||
|
||||
var promise = apns.send(data, deviceTokens);
|
||||
expect(sender.pushNotification).toHaveBeenCalled();
|
||||
var args = sender.pushNotification.calls.first().args;
|
||||
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']);
|
||||
expect(args[1]).toEqual(deviceTokens);
|
||||
var apnDevice = args[1]
|
||||
expect(apnDevice.connIndex).toEqual(0);
|
||||
expect(apnDevice.appIdentifier).toEqual('bundleId');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var ExportAdapter = require('../ExportAdapter');
|
||||
var ExportAdapter = require('../src/ExportAdapter');
|
||||
|
||||
describe('ExportAdapter', () => {
|
||||
it('can be constructed', (done) => {
|
||||
|
||||
64
spec/FileLoggerAdapter.spec.js
Normal file
64
spec/FileLoggerAdapter.spec.js
Normal file
@@ -0,0 +1,64 @@
|
||||
var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter;
|
||||
var Parse = require('parse/node').Parse;
|
||||
var request = require('request');
|
||||
var fs = require('fs');
|
||||
|
||||
var LOGS_FOLDER = './test_logs/';
|
||||
|
||||
var deleteFolderRecursive = function(path) {
|
||||
if( fs.existsSync(path) ) {
|
||||
fs.readdirSync(path).forEach(function(file,index){
|
||||
var curPath = path + "/" + file;
|
||||
if(fs.lstatSync(curPath).isDirectory()) { // recurse
|
||||
deleteFolderRecursive(curPath);
|
||||
} else { // delete file
|
||||
fs.unlinkSync(curPath);
|
||||
}
|
||||
});
|
||||
fs.rmdirSync(path);
|
||||
}
|
||||
};
|
||||
|
||||
describe('info logs', () => {
|
||||
|
||||
afterEach((done) => {
|
||||
deleteFolderRecursive(LOGS_FOLDER);
|
||||
done();
|
||||
});
|
||||
|
||||
it("Verify INFO logs", (done) => {
|
||||
var fileLoggerAdapter = new FileLoggerAdapter({
|
||||
logsFolder: LOGS_FOLDER
|
||||
});
|
||||
fileLoggerAdapter.info('testing info logs', () => {
|
||||
fileLoggerAdapter.query({
|
||||
size: 1,
|
||||
level: 'info'
|
||||
}, (results) => {
|
||||
expect(results[0].message).toEqual('testing info logs');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('error logs', () => {
|
||||
|
||||
afterEach((done) => {
|
||||
deleteFolderRecursive(LOGS_FOLDER);
|
||||
done();
|
||||
});
|
||||
|
||||
it("Verify ERROR logs", (done) => {
|
||||
var fileLoggerAdapter = new FileLoggerAdapter();
|
||||
fileLoggerAdapter.error('testing error logs', () => {
|
||||
fileLoggerAdapter.query({
|
||||
size: 1,
|
||||
level: 'error'
|
||||
}, (results) => {
|
||||
expect(results[0].message).toEqual('testing error logs');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,23 @@
|
||||
var GCM = require('../GCM');
|
||||
var GCM = require('../src/GCM');
|
||||
|
||||
describe('GCM', () => {
|
||||
it('can initialize', (done) => {
|
||||
var args = {
|
||||
apiKey: 'apiKey'
|
||||
};
|
||||
var gcm = new GCM(args);
|
||||
expect(gcm.sender.key).toBe(args.apiKey);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can throw on initializing with invalid args', (done) => {
|
||||
var args = 123
|
||||
expect(function() {
|
||||
new GCM(args);
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can generate GCM Payload without expiration time', (done) => {
|
||||
//Mock request data
|
||||
var data = {
|
||||
@@ -90,7 +107,9 @@ describe('GCM', () => {
|
||||
});
|
||||
|
||||
it('can send GCM request', (done) => {
|
||||
var gcm = new GCM('apiKey');
|
||||
var gcm = new GCM({
|
||||
apiKey: 'apiKey'
|
||||
});
|
||||
// Mock gcm sender
|
||||
var sender = {
|
||||
send: jasmine.createSpy('send')
|
||||
@@ -104,34 +123,37 @@ describe('GCM', () => {
|
||||
'alert': 'alert'
|
||||
}
|
||||
}
|
||||
// Mock registrationTokens
|
||||
var registrationTokens = ['token'];
|
||||
// Mock devices
|
||||
var devices = [
|
||||
{
|
||||
deviceToken: 'token'
|
||||
}
|
||||
];
|
||||
|
||||
var promise = gcm.send(data, registrationTokens);
|
||||
gcm.send(data, devices);
|
||||
expect(sender.send).toHaveBeenCalled();
|
||||
var args = sender.send.calls.first().args;
|
||||
// It is too hard to verify message of gcm library, we just verify tokens and retry times
|
||||
expect(args[1].registrationTokens).toEqual(registrationTokens);
|
||||
expect(args[1].registrationTokens).toEqual(['token']);
|
||||
expect(args[2]).toEqual(5);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can throw on sending when we have too many registration tokens', (done) => {
|
||||
var gcm = new GCM('apiKey');
|
||||
// Mock gcm sender
|
||||
var sender = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
gcm.sender = sender;
|
||||
// Mock registrationTokens
|
||||
var registrationTokens = [];
|
||||
for (var i = 0; i <= 2000; i++) {
|
||||
registrationTokens.push(i.toString());
|
||||
}
|
||||
it('can slice devices', (done) => {
|
||||
// Mock devices
|
||||
var devices = [makeDevice(1), makeDevice(2), makeDevice(3), makeDevice(4)];
|
||||
|
||||
expect(function() {
|
||||
gcm.send({}, registrationTokens);
|
||||
}).toThrow();
|
||||
var chunkDevices = GCM.sliceDevices(devices, 3);
|
||||
expect(chunkDevices).toEqual([
|
||||
[makeDevice(1), makeDevice(2), makeDevice(3)],
|
||||
[makeDevice(4)]
|
||||
]);
|
||||
done();
|
||||
});
|
||||
|
||||
function makeDevice(deviceToken) {
|
||||
return {
|
||||
deviceToken: deviceToken
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
55
spec/LoggerController.spec.js
Normal file
55
spec/LoggerController.spec.js
Normal file
@@ -0,0 +1,55 @@
|
||||
var LoggerController = require('../src/Controllers/LoggerController').LoggerController;
|
||||
var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter;
|
||||
|
||||
describe('LoggerController', () => {
|
||||
it('can check valid master key of request', (done) => {
|
||||
// Make mock request
|
||||
var request = {
|
||||
auth: {
|
||||
isMaster: true
|
||||
},
|
||||
query: {}
|
||||
};
|
||||
|
||||
var loggerController = new LoggerController(new FileLoggerAdapter());
|
||||
|
||||
expect(() => {
|
||||
loggerController.handleGET(request);
|
||||
}).not.toThrow();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can check invalid construction of controller', (done) => {
|
||||
// Make mock request
|
||||
var request = {
|
||||
auth: {
|
||||
isMaster: true
|
||||
},
|
||||
query: {}
|
||||
};
|
||||
|
||||
var loggerController = new LoggerController();
|
||||
|
||||
expect(() => {
|
||||
loggerController.handleGET(request);
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can check invalid master key of request', (done) => {
|
||||
// Make mock request
|
||||
var request = {
|
||||
auth: {
|
||||
isMaster: false
|
||||
},
|
||||
query: {}
|
||||
};
|
||||
|
||||
var loggerController = new LoggerController(new FileLoggerAdapter());
|
||||
|
||||
expect(() => {
|
||||
loggerController.handleGET(request);
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -251,6 +251,9 @@ describe('Parse.ACL', () => {
|
||||
equal(results.length, 1);
|
||||
var result = results[0];
|
||||
ok(result);
|
||||
if (!result) {
|
||||
return fail();
|
||||
}
|
||||
equal(result.id, object.id);
|
||||
equal(result.getACL().getReadAccess(user), true);
|
||||
equal(result.getACL().getWriteAccess(user), true);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// A bunch of different tests are in here - it isn't very thematic.
|
||||
// It would probably be better to refactor them into different files.
|
||||
|
||||
var DatabaseAdapter = require('../DatabaseAdapter');
|
||||
var DatabaseAdapter = require('../src/DatabaseAdapter');
|
||||
var request = require('request');
|
||||
|
||||
describe('miscellaneous', function() {
|
||||
|
||||
@@ -33,6 +33,95 @@ describe('Parse.File testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('supports REST end-to-end file create, read, delete, read', done => {
|
||||
var headers = {
|
||||
'Content-Type': 'image/jpeg',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
};
|
||||
request.post({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/files/testfile.txt',
|
||||
body: 'check one two',
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var b = JSON.parse(body);
|
||||
expect(b.name).toMatch(/_testfile.txt$/);
|
||||
expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*testfile.txt$/);
|
||||
request.get(b.url, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(body).toEqual('check one two');
|
||||
request.del({
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'X-Parse-Master-Key': 'test'
|
||||
},
|
||||
url: 'http://localhost:8378/1/files/' + b.name
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(response.statusCode).toEqual(200);
|
||||
request.get({
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
},
|
||||
url: b.url
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(response.statusCode).toEqual(404);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('blocks file deletions with missing or incorrect master-key header', done => {
|
||||
var headers = {
|
||||
'Content-Type': 'image/jpeg',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
};
|
||||
request.post({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/files/thefile.jpg',
|
||||
body: 'the file body'
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var b = JSON.parse(body);
|
||||
expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*thefile.jpg$/);
|
||||
// missing X-Parse-Master-Key header
|
||||
request.del({
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
},
|
||||
url: 'http://localhost:8378/1/files/' + b.name
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var del_b = JSON.parse(body);
|
||||
expect(response.statusCode).toEqual(403);
|
||||
expect(del_b.error).toMatch(/unauthorized/);
|
||||
// incorrect X-Parse-Master-Key header
|
||||
request.del({
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'X-Parse-Master-Key': 'tryagain'
|
||||
},
|
||||
url: 'http://localhost:8378/1/files/' + b.name
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var del_b2 = JSON.parse(body);
|
||||
expect(response.statusCode).toEqual(403);
|
||||
expect(del_b2.error).toMatch(/unauthorized/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('handles other filetypes', done => {
|
||||
var headers = {
|
||||
'Content-Type': 'image/jpeg',
|
||||
|
||||
81
spec/ParseGlobalConfig.spec.js
Normal file
81
spec/ParseGlobalConfig.spec.js
Normal file
@@ -0,0 +1,81 @@
|
||||
|
||||
var request = require('request');
|
||||
var Parse = require('parse/node').Parse;
|
||||
var DatabaseAdapter = require('../src/DatabaseAdapter');
|
||||
|
||||
var database = DatabaseAdapter.getDatabaseConnection('test');
|
||||
|
||||
describe('a GlobalConfig', () => {
|
||||
beforeEach(function(done) {
|
||||
database.rawCollection('_GlobalConfig')
|
||||
.then(coll => coll.updateOne({ '_id': 1}, { $set: { params: { companies: ['US', 'DK'] } } }, { upsert: true }))
|
||||
.then(done());
|
||||
});
|
||||
|
||||
it('can be retrieved', (done) => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/config',
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-Master-Key': 'test',
|
||||
},
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(body.params.companies).toEqual(['US', 'DK']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can be updated when a master key exists', (done) => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/config',
|
||||
json: true,
|
||||
body: { params: { companies: ['US', 'DK', 'SE'] } },
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-Master-Key': 'test'
|
||||
},
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(body.result).toEqual(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fail to update if master key is missing', (done) => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/config',
|
||||
json: true,
|
||||
body: { params: { companies: [] } },
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
},
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(body.error).toEqual('unauthorized');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('failed getting config when it is missing', (done) => {
|
||||
database.rawCollection('_GlobalConfig')
|
||||
.then(coll => coll.deleteOne({ '_id': 1}, {}, {}))
|
||||
.then(_ => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/config',
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-Master-Key': 'test',
|
||||
},
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(404);
|
||||
expect(body.code).toEqual(Parse.Error.INVALID_KEY_NAME);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,12 +1,12 @@
|
||||
// These tests check the Installations functionality of the REST API.
|
||||
// Ported from installation_collection_test.go
|
||||
|
||||
var auth = require('../Auth');
|
||||
var cache = require('../cache');
|
||||
var Config = require('../Config');
|
||||
var DatabaseAdapter = require('../DatabaseAdapter');
|
||||
var auth = require('../src/Auth');
|
||||
var cache = require('../src/cache');
|
||||
var Config = require('../src/Config');
|
||||
var DatabaseAdapter = require('../src/DatabaseAdapter');
|
||||
var Parse = require('parse/node').Parse;
|
||||
var rest = require('../rest');
|
||||
var rest = require('../src/rest');
|
||||
|
||||
var config = new Config('test');
|
||||
var database = DatabaseAdapter.getDatabaseConnection('test');
|
||||
|
||||
150
spec/ParsePushAdapter.spec.js
Normal file
150
spec/ParsePushAdapter.spec.js
Normal file
@@ -0,0 +1,150 @@
|
||||
var ParsePushAdapter = require('../src/Adapters/Push/ParsePushAdapter');
|
||||
var APNS = require('../src/APNS');
|
||||
var GCM = require('../src/GCM');
|
||||
|
||||
describe('ParsePushAdapter', () => {
|
||||
it('can be initialized', (done) => {
|
||||
// Make mock config
|
||||
var pushConfig = {
|
||||
android: {
|
||||
senderId: 'senderId',
|
||||
apiKey: 'apiKey'
|
||||
},
|
||||
ios: [
|
||||
{
|
||||
cert: 'prodCert.pem',
|
||||
key: 'prodKey.pem',
|
||||
production: true,
|
||||
bundleId: 'bundleId'
|
||||
},
|
||||
{
|
||||
cert: 'devCert.pem',
|
||||
key: 'devKey.pem',
|
||||
production: false,
|
||||
bundleId: 'bundleIdAgain'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var parsePushAdapter = new ParsePushAdapter(pushConfig);
|
||||
// Check ios
|
||||
var iosSender = parsePushAdapter.senderMap['ios'];
|
||||
expect(iosSender instanceof APNS).toBe(true);
|
||||
// Check android
|
||||
var androidSender = parsePushAdapter.senderMap['android'];
|
||||
expect(androidSender instanceof GCM).toBe(true);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can throw on initializing with unsupported push type', (done) => {
|
||||
// Make mock config
|
||||
var pushConfig = {
|
||||
win: {
|
||||
senderId: 'senderId',
|
||||
apiKey: 'apiKey'
|
||||
}
|
||||
};
|
||||
|
||||
expect(function() {
|
||||
new ParsePushAdapter(pushConfig);
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can get valid push types', (done) => {
|
||||
var parsePushAdapter = new ParsePushAdapter();
|
||||
|
||||
expect(parsePushAdapter.getValidPushTypes()).toEqual(['ios', 'android']);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can classify installation', (done) => {
|
||||
// Mock installations
|
||||
var validPushTypes = ['ios', 'android'];
|
||||
var installations = [
|
||||
{
|
||||
deviceType: 'android',
|
||||
deviceToken: 'androidToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'ios',
|
||||
deviceToken: 'iosToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'win',
|
||||
deviceToken: 'winToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'android',
|
||||
deviceToken: undefined
|
||||
}
|
||||
];
|
||||
|
||||
var deviceMap = ParsePushAdapter.classifyInstallation(installations, validPushTypes);
|
||||
expect(deviceMap['android']).toEqual([makeDevice('androidToken')]);
|
||||
expect(deviceMap['ios']).toEqual([makeDevice('iosToken')]);
|
||||
expect(deviceMap['win']).toBe(undefined);
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
it('can send push notifications', (done) => {
|
||||
var parsePushAdapter = new ParsePushAdapter();
|
||||
// Mock android ios senders
|
||||
var androidSender = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
var iosSender = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
var senderMap = {
|
||||
ios: iosSender,
|
||||
android: androidSender
|
||||
};
|
||||
parsePushAdapter.senderMap = senderMap;
|
||||
// Mock installations
|
||||
var installations = [
|
||||
{
|
||||
deviceType: 'android',
|
||||
deviceToken: 'androidToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'ios',
|
||||
deviceToken: 'iosToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'win',
|
||||
deviceToken: 'winToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'android',
|
||||
deviceToken: undefined
|
||||
}
|
||||
];
|
||||
var data = {};
|
||||
|
||||
parsePushAdapter.send(data, installations);
|
||||
// Check android sender
|
||||
expect(androidSender.send).toHaveBeenCalled();
|
||||
var args = androidSender.send.calls.first().args;
|
||||
expect(args[0]).toEqual(data);
|
||||
expect(args[1]).toEqual([
|
||||
makeDevice('androidToken')
|
||||
]);
|
||||
// Check ios sender
|
||||
expect(iosSender.send).toHaveBeenCalled();
|
||||
args = iosSender.send.calls.first().args;
|
||||
expect(args[0]).toEqual(data);
|
||||
expect(args[1]).toEqual([
|
||||
makeDevice('iosToken')
|
||||
]);
|
||||
done();
|
||||
});
|
||||
|
||||
function makeDevice(deviceToken, appIdentifier) {
|
||||
return {
|
||||
deviceToken: deviceToken,
|
||||
appIdentifier: appIdentifier
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -2056,7 +2056,7 @@ describe('Parse.Query testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('query match on array value', (done) => {
|
||||
it('query match on array with single object', (done) => {
|
||||
var target = {__type: 'Pointer', className: 'TestObject', objectId: 'abc123'};
|
||||
var obj = new Parse.Object('TestObject');
|
||||
obj.set('someObjs', [target]);
|
||||
@@ -2072,4 +2072,20 @@ describe('Parse.Query testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('query match on array with multiple objects', (done) => {
|
||||
var target1 = {__type: 'Pointer', className: 'TestObject', objectId: 'abc'};
|
||||
var target2 = {__type: 'Pointer', className: 'TestObject', objectId: '123'};
|
||||
var obj= new Parse.Object('TestObject');
|
||||
obj.set('someObjs', [target1, target2]);
|
||||
obj.save().then(() => {
|
||||
var query = new Parse.Query('TestObject');
|
||||
query.equalTo('someObjs', target1);
|
||||
return query.find();
|
||||
}).then((results) => {
|
||||
expect(results.length).toEqual(1);
|
||||
done();
|
||||
}, (error) => {
|
||||
console.log(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// Tests that involve sending password reset emails.
|
||||
|
||||
var request = require('request');
|
||||
var passwordCrypto = require('../password');
|
||||
var passwordCrypto = require('../src/password');
|
||||
|
||||
describe('Parse.User testing', () => {
|
||||
it("user sign up class method", (done) => {
|
||||
@@ -64,6 +64,22 @@ describe('Parse.User testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("user login with files", (done) => {
|
||||
"use strict";
|
||||
|
||||
let file = new Parse.File("yolo.txt", [1,2,3], "text/plain");
|
||||
file.save().then((file) => {
|
||||
return Parse.User.signUp("asdf", "zxcv", { "file" : file });
|
||||
}).then(() => {
|
||||
return Parse.User.logIn("asdf", "zxcv");
|
||||
}).then((user) => {
|
||||
let fileAgain = user.get('file');
|
||||
ok(fileAgain.name());
|
||||
ok(fileAgain.url());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("become", (done) => {
|
||||
var user = null;
|
||||
var sessionToken = null;
|
||||
@@ -1576,5 +1592,27 @@ describe('Parse.User testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('ensure logout works', (done) => {
|
||||
var user = null;
|
||||
var sessionToken = null;
|
||||
|
||||
Parse.Promise.as().then(function() {
|
||||
return Parse.User.signUp('log', 'out');
|
||||
}).then((newUser) => {
|
||||
user = newUser;
|
||||
sessionToken = user.getSessionToken();
|
||||
return Parse.User.logOut();
|
||||
}).then(() => {
|
||||
user.set('foo', 'bar');
|
||||
return user.save(null, { sessionToken: sessionToken });
|
||||
}).then(() => {
|
||||
fail('Save should have failed.');
|
||||
done();
|
||||
}, (e) => {
|
||||
expect(e.code).toEqual(Parse.Error.SESSION_MISSING);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var push = require('../push');
|
||||
var PushController = require('../src/Controllers/PushController').PushController;
|
||||
|
||||
describe('push', () => {
|
||||
describe('PushController', () => {
|
||||
it('can check valid master key of request', (done) => {
|
||||
// Make mock request
|
||||
var request = {
|
||||
@@ -13,7 +13,7 @@ describe('push', () => {
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
push.validateMasterKey(request);
|
||||
PushController.validateMasterKey(request);
|
||||
}).not.toThrow();
|
||||
done();
|
||||
});
|
||||
@@ -30,7 +30,7 @@ describe('push', () => {
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
push.validateMasterKey(request);
|
||||
PushController.validateMasterKey(request);
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
@@ -43,7 +43,7 @@ describe('push', () => {
|
||||
}
|
||||
}
|
||||
|
||||
var where = push.getQueryCondition(request);
|
||||
var where = PushController.getQueryCondition(request);
|
||||
expect(where).toEqual({
|
||||
'channels': {
|
||||
'$in': ['Giants', 'Mets']
|
||||
@@ -62,7 +62,7 @@ describe('push', () => {
|
||||
}
|
||||
}
|
||||
|
||||
var where = push.getQueryCondition(request);
|
||||
var where = PushController.getQueryCondition(request);
|
||||
expect(where).toEqual({
|
||||
'injuryReports': true
|
||||
});
|
||||
@@ -77,7 +77,7 @@ describe('push', () => {
|
||||
}
|
||||
|
||||
expect(function() {
|
||||
push.getQueryCondition(request);
|
||||
PushController.getQueryCondition(request);
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
@@ -96,7 +96,7 @@ describe('push', () => {
|
||||
}
|
||||
|
||||
expect(function() {
|
||||
push.getQueryCondition(request);
|
||||
PushController.getQueryCondition(request);
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
@@ -104,10 +104,11 @@ describe('push', () => {
|
||||
it('can validate device type when no device type is set', (done) => {
|
||||
// Make query condition
|
||||
var where = {
|
||||
}
|
||||
};
|
||||
var validPushTypes = ['ios', 'android'];
|
||||
|
||||
expect(function(){
|
||||
push.validateDeviceType(where);
|
||||
PushController.validatePushType(where, validPushTypes);
|
||||
}).not.toThrow();
|
||||
done();
|
||||
});
|
||||
@@ -116,10 +117,11 @@ describe('push', () => {
|
||||
// Make query condition
|
||||
var where = {
|
||||
'deviceType': 'ios'
|
||||
}
|
||||
};
|
||||
var validPushTypes = ['ios', 'android'];
|
||||
|
||||
expect(function(){
|
||||
push.validateDeviceType(where);
|
||||
PushController.validatePushType(where, validPushTypes);
|
||||
}).not.toThrow();
|
||||
done();
|
||||
});
|
||||
@@ -130,10 +132,11 @@ describe('push', () => {
|
||||
'deviceType': {
|
||||
'$in': ['android', 'ios']
|
||||
}
|
||||
}
|
||||
};
|
||||
var validPushTypes = ['ios', 'android'];
|
||||
|
||||
expect(function(){
|
||||
push.validateDeviceType(where);
|
||||
PushController.validatePushType(where, validPushTypes);
|
||||
}).not.toThrow();
|
||||
done();
|
||||
});
|
||||
@@ -142,10 +145,11 @@ describe('push', () => {
|
||||
// Make query condition
|
||||
var where = {
|
||||
'deviceType': 'osx'
|
||||
}
|
||||
};
|
||||
var validPushTypes = ['ios', 'android'];
|
||||
|
||||
expect(function(){
|
||||
push.validateDeviceType(where);
|
||||
PushController.validatePushType(where, validPushTypes);
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
@@ -154,10 +158,11 @@ describe('push', () => {
|
||||
// Make query condition
|
||||
var where = {
|
||||
'deviceType': 'osx'
|
||||
}
|
||||
};
|
||||
var validPushTypes = ['ios', 'android'];
|
||||
|
||||
expect(function(){
|
||||
push.validateDeviceType(where)
|
||||
PushController.validatePushType(where, validPushTypes);
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
@@ -171,7 +176,7 @@ describe('push', () => {
|
||||
}
|
||||
}
|
||||
|
||||
var time = push.getExpirationTime(request);
|
||||
var time = PushController.getExpirationTime(request);
|
||||
expect(time).toEqual(new Date(timeStr).valueOf());
|
||||
done();
|
||||
});
|
||||
@@ -185,7 +190,7 @@ describe('push', () => {
|
||||
}
|
||||
}
|
||||
|
||||
var time = push.getExpirationTime(request);
|
||||
var time = PushController.getExpirationTime(request);
|
||||
expect(time).toEqual(timeNumber * 1000);
|
||||
done();
|
||||
});
|
||||
@@ -199,7 +204,7 @@ describe('push', () => {
|
||||
}
|
||||
|
||||
expect(function(){
|
||||
push.getExpirationTime(request);
|
||||
PushController.getExpirationTime(request);
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
@@ -1,10 +1,10 @@
|
||||
// These tests check the "create" functionality of the REST API.
|
||||
var auth = require('../Auth');
|
||||
var cache = require('../cache');
|
||||
var Config = require('../Config');
|
||||
var DatabaseAdapter = require('../DatabaseAdapter');
|
||||
var auth = require('../src/Auth');
|
||||
var cache = require('../src/cache');
|
||||
var Config = require('../src/Config');
|
||||
var DatabaseAdapter = require('../src/DatabaseAdapter');
|
||||
var Parse = require('parse/node').Parse;
|
||||
var rest = require('../rest');
|
||||
var rest = require('../src/rest');
|
||||
var request = require('request');
|
||||
|
||||
var config = new Config('test');
|
||||
@@ -57,6 +57,50 @@ describe('rest create', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('handles anonymous user signup', (done) => {
|
||||
var data1 = {
|
||||
authData: {
|
||||
anonymous: {
|
||||
id: '00000000-0000-0000-0000-000000000001'
|
||||
}
|
||||
}
|
||||
};
|
||||
var data2 = {
|
||||
authData: {
|
||||
anonymous: {
|
||||
id: '00000000-0000-0000-0000-000000000002'
|
||||
}
|
||||
}
|
||||
};
|
||||
var username1;
|
||||
rest.create(config, auth.nobody(config), '_User', data1)
|
||||
.then((r) => {
|
||||
expect(typeof r.response.objectId).toEqual('string');
|
||||
expect(typeof r.response.createdAt).toEqual('string');
|
||||
expect(typeof r.response.sessionToken).toEqual('string');
|
||||
return rest.create(config, auth.nobody(config), '_User', data1);
|
||||
}).then((r) => {
|
||||
expect(typeof r.response.objectId).toEqual('string');
|
||||
expect(typeof r.response.createdAt).toEqual('string');
|
||||
expect(typeof r.response.username).toEqual('string');
|
||||
expect(typeof r.response.updatedAt).toEqual('string');
|
||||
username1 = r.response.username;
|
||||
return rest.create(config, auth.nobody(config), '_User', data2);
|
||||
}).then((r) => {
|
||||
expect(typeof r.response.objectId).toEqual('string');
|
||||
expect(typeof r.response.createdAt).toEqual('string');
|
||||
expect(typeof r.response.sessionToken).toEqual('string');
|
||||
return rest.create(config, auth.nobody(config), '_User', data2);
|
||||
}).then((r) => {
|
||||
expect(typeof r.response.objectId).toEqual('string');
|
||||
expect(typeof r.response.createdAt).toEqual('string');
|
||||
expect(typeof r.response.username).toEqual('string');
|
||||
expect(typeof r.response.updatedAt).toEqual('string');
|
||||
expect(r.response.username).not.toEqual(username1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test facebook signup and login', (done) => {
|
||||
var data = {
|
||||
authData: {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// These tests check the "find" functionality of the REST API.
|
||||
var auth = require('../Auth');
|
||||
var cache = require('../cache');
|
||||
var Config = require('../Config');
|
||||
var rest = require('../rest');
|
||||
var auth = require('../src/Auth');
|
||||
var cache = require('../src/cache');
|
||||
var Config = require('../src/Config');
|
||||
var rest = require('../src/rest');
|
||||
|
||||
var config = new Config('test');
|
||||
var nobody = auth.nobody(config);
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
// These tests check that the Schema operates correctly.
|
||||
var Config = require('../Config');
|
||||
var Schema = require('../Schema');
|
||||
var Config = require('../src/Config');
|
||||
var Schema = require('../src/Schema');
|
||||
var dd = require('deep-diff');
|
||||
|
||||
var config = new Config('test');
|
||||
|
||||
var hasAllPODobject = () => {
|
||||
var obj = new Parse.Object('HasAllPOD');
|
||||
obj.set('aNumber', 5);
|
||||
obj.set('aString', 'string');
|
||||
obj.set('aBool', true);
|
||||
obj.set('aDate', new Date());
|
||||
obj.set('aObject', {k1: 'value', k2: true, k3: 5});
|
||||
obj.set('aArray', ['contents', true, 5]);
|
||||
obj.set('aGeoPoint', new Parse.GeoPoint({latitude: 0, longitude: 0}));
|
||||
obj.set('aFile', new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' }));
|
||||
var objACL = new Parse.ACL();
|
||||
objACL.setPublicWriteAccess(false);
|
||||
obj.setACL(objACL);
|
||||
return obj;
|
||||
};
|
||||
|
||||
describe('Schema', () => {
|
||||
it('can validate one object', (done) => {
|
||||
config.database.loadSchema().then((schema) => {
|
||||
@@ -252,7 +267,7 @@ describe('Schema', () => {
|
||||
it('refuses to add fields with invalid pointer types', done => {
|
||||
config.database.loadSchema()
|
||||
.then(schema => schema.addClassIfNotExists('NewClass', {
|
||||
foo: {type: 'Pointer'},
|
||||
foo: {type: 'Pointer'}
|
||||
}))
|
||||
.catch(error => {
|
||||
expect(error.code).toEqual(135);
|
||||
@@ -398,7 +413,7 @@ describe('Schema', () => {
|
||||
config.database.loadSchema()
|
||||
.then(schema => schema.addClassIfNotExists('NewClass', {
|
||||
geo1: {type: 'GeoPoint'},
|
||||
geo2: {type: 'GeoPoint'},
|
||||
geo2: {type: 'GeoPoint'}
|
||||
}))
|
||||
.catch(error => {
|
||||
expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE);
|
||||
@@ -406,4 +421,153 @@ describe('Schema', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can check if a class exists', done => {
|
||||
config.database.loadSchema()
|
||||
.then(schema => {
|
||||
return schema.addClassIfNotExists('NewClass', {})
|
||||
.then(() => {
|
||||
schema.hasClass('NewClass')
|
||||
.then(hasClass => {
|
||||
expect(hasClass).toEqual(true);
|
||||
done();
|
||||
})
|
||||
.catch(fail);
|
||||
|
||||
schema.hasClass('NonexistantClass')
|
||||
.then(hasClass => {
|
||||
expect(hasClass).toEqual(false);
|
||||
done();
|
||||
})
|
||||
.catch(fail);
|
||||
})
|
||||
.catch(error => {
|
||||
fail('Couldn\'t create class');
|
||||
fail(error);
|
||||
});
|
||||
})
|
||||
.catch(error => fail('Couldn\'t load schema'));
|
||||
});
|
||||
|
||||
it('refuses to delete fields from invalid class names', done => {
|
||||
config.database.loadSchema()
|
||||
.then(schema => schema.deleteField('fieldName', 'invalid class name'))
|
||||
.catch(error => {
|
||||
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('refuses to delete invalid fields', done => {
|
||||
config.database.loadSchema()
|
||||
.then(schema => schema.deleteField('invalid field name', 'ValidClassName'))
|
||||
.catch(error => {
|
||||
expect(error.code).toEqual(Parse.Error.INVALID_KEY_NAME);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('refuses to delete the default fields', done => {
|
||||
config.database.loadSchema()
|
||||
.then(schema => schema.deleteField('installationId', '_Installation'))
|
||||
.catch(error => {
|
||||
expect(error.code).toEqual(136);
|
||||
expect(error.error).toEqual('field installationId cannot be changed');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('refuses to delete fields from nonexistant classes', done => {
|
||||
config.database.loadSchema()
|
||||
.then(schema => schema.deleteField('field', 'NoClass'))
|
||||
.catch(error => {
|
||||
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||
expect(error.error).toEqual('class NoClass does not exist');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('refuses to delete fields that dont exist', done => {
|
||||
hasAllPODobject().save()
|
||||
.then(() => config.database.loadSchema())
|
||||
.then(schema => schema.deleteField('missingField', 'HasAllPOD'))
|
||||
.fail(error => {
|
||||
expect(error.code).toEqual(255);
|
||||
expect(error.error).toEqual('field missingField does not exist, cannot delete');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('drops related collection when deleting relation field', done => {
|
||||
var obj1 = hasAllPODobject();
|
||||
obj1.save()
|
||||
.then(savedObj1 => {
|
||||
var obj2 = new Parse.Object('HasPointersAndRelations');
|
||||
obj2.set('aPointer', savedObj1);
|
||||
var relation = obj2.relation('aRelation');
|
||||
relation.add(obj1);
|
||||
return obj2.save();
|
||||
})
|
||||
.then(() => {
|
||||
config.database.db.collection('test__Join:aRelation:HasPointersAndRelations', { strict: true }, (err, coll) => {
|
||||
expect(err).toEqual(null);
|
||||
config.database.loadSchema()
|
||||
.then(schema => schema.deleteField('aRelation', 'HasPointersAndRelations', config.database.db, 'test_'))
|
||||
.then(() => config.database.db.collection('test__Join:aRelation:HasPointersAndRelations', { strict: true }, (err, coll) => {
|
||||
expect(err).not.toEqual(null);
|
||||
done();
|
||||
}))
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
it('can delete string fields and resave as number field', done => {
|
||||
Parse.Object.disableSingleInstance();
|
||||
var obj1 = hasAllPODobject();
|
||||
var obj2 = hasAllPODobject();
|
||||
var p = Parse.Object.saveAll([obj1, obj2])
|
||||
.then(() => config.database.loadSchema())
|
||||
.then(schema => schema.deleteField('aString', 'HasAllPOD', config.database.db, 'test_'))
|
||||
.then(() => new Parse.Query('HasAllPOD').get(obj1.id))
|
||||
.then(obj1Reloaded => {
|
||||
expect(obj1Reloaded.get('aString')).toEqual(undefined);
|
||||
obj1Reloaded.set('aString', ['not a string', 'this time']);
|
||||
obj1Reloaded.save()
|
||||
.then(obj1reloadedAgain => {
|
||||
expect(obj1reloadedAgain.get('aString')).toEqual(['not a string', 'this time']);
|
||||
return new Parse.Query('HasAllPOD').get(obj2.id);
|
||||
})
|
||||
.then(obj2reloaded => {
|
||||
expect(obj2reloaded.get('aString')).toEqual(undefined);
|
||||
done();
|
||||
Parse.Object.enableSingleInstance();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
it('can delete pointer fields and resave as string', done => {
|
||||
Parse.Object.disableSingleInstance();
|
||||
var obj1 = new Parse.Object('NewClass');
|
||||
obj1.save()
|
||||
.then(() => {
|
||||
obj1.set('aPointer', obj1);
|
||||
return obj1.save();
|
||||
})
|
||||
.then(obj1 => {
|
||||
expect(obj1.get('aPointer').id).toEqual(obj1.id);
|
||||
})
|
||||
.then(() => config.database.loadSchema())
|
||||
.then(schema => schema.deleteField('aPointer', 'NewClass', config.database.db, 'test_'))
|
||||
.then(() => new Parse.Query('NewClass').get(obj1.id))
|
||||
.then(obj1 => {
|
||||
expect(obj1.get('aPointer')).toEqual(undefined);
|
||||
obj1.set('aPointer', 'Now a string');
|
||||
return obj1.save();
|
||||
})
|
||||
.then(obj1 => {
|
||||
expect(obj1.get('aPointer')).toEqual('Now a string');
|
||||
done();
|
||||
Parse.Object.enableSingleInstance();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
83
spec/cryptoUtils.spec.js
Normal file
83
spec/cryptoUtils.spec.js
Normal file
@@ -0,0 +1,83 @@
|
||||
var cryptoUtils = require('../src/cryptoUtils');
|
||||
|
||||
function givesUniqueResults(fn, iterations) {
|
||||
var results = {};
|
||||
for (var i = 0; i < iterations; i++) {
|
||||
var s = fn();
|
||||
if (results[s]) {
|
||||
return false;
|
||||
}
|
||||
results[s] = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
describe('randomString', () => {
|
||||
it('returns a string', () => {
|
||||
expect(typeof cryptoUtils.randomString(10)).toBe('string');
|
||||
});
|
||||
|
||||
it('returns result of the given length', () => {
|
||||
expect(cryptoUtils.randomString(11).length).toBe(11);
|
||||
expect(cryptoUtils.randomString(25).length).toBe(25);
|
||||
});
|
||||
|
||||
it('throws if requested length is zero', () => {
|
||||
expect(() => cryptoUtils.randomString(0)).toThrow();
|
||||
});
|
||||
|
||||
it('returns unique results', () => {
|
||||
expect(givesUniqueResults(() => cryptoUtils.randomString(10), 100)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('randomHexString', () => {
|
||||
it('returns a string', () => {
|
||||
expect(typeof cryptoUtils.randomHexString(10)).toBe('string');
|
||||
});
|
||||
|
||||
it('returns result of the given length', () => {
|
||||
expect(cryptoUtils.randomHexString(10).length).toBe(10);
|
||||
expect(cryptoUtils.randomHexString(32).length).toBe(32);
|
||||
});
|
||||
|
||||
it('throws if requested length is zero', () => {
|
||||
expect(() => cryptoUtils.randomHexString(0)).toThrow();
|
||||
});
|
||||
|
||||
it('throws if requested length is not even', () => {
|
||||
expect(() => cryptoUtils.randomHexString(11)).toThrow();
|
||||
});
|
||||
|
||||
it('returns unique results', () => {
|
||||
expect(givesUniqueResults(() => cryptoUtils.randomHexString(20), 100)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('newObjectId', () => {
|
||||
it('returns a string', () => {
|
||||
expect(typeof cryptoUtils.newObjectId()).toBe('string');
|
||||
});
|
||||
|
||||
it('returns result with at least 10 characters', () => {
|
||||
expect(cryptoUtils.newObjectId().length).toBeGreaterThan(9);
|
||||
});
|
||||
|
||||
it('returns unique results', () => {
|
||||
expect(givesUniqueResults(() => cryptoUtils.newObjectId(), 100)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('newToken', () => {
|
||||
it('returns a string', () => {
|
||||
expect(typeof cryptoUtils.newToken()).toBe('string');
|
||||
});
|
||||
|
||||
it('returns result with at least 32 characters', () => {
|
||||
expect(cryptoUtils.newToken().length).toBeGreaterThan(31);
|
||||
});
|
||||
|
||||
it('returns unique results', () => {
|
||||
expect(givesUniqueResults(() => cryptoUtils.newToken(), 100)).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000;
|
||||
|
||||
var cache = require('../cache');
|
||||
var DatabaseAdapter = require('../DatabaseAdapter');
|
||||
var cache = require('../src/cache');
|
||||
var DatabaseAdapter = require('../src/DatabaseAdapter');
|
||||
var express = require('express');
|
||||
var facebook = require('../facebook');
|
||||
var ParseServer = require('../index').ParseServer;
|
||||
var facebook = require('../src/facebook');
|
||||
var ParseServer = require('../src/index').ParseServer;
|
||||
|
||||
var databaseURI = process.env.DATABASE_URI;
|
||||
var cloudMain = process.env.CLOUD_CODE_MAIN || './cloud/main.js';
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
var Parse = require('parse/node').Parse;
|
||||
var request = require('request');
|
||||
var dd = require('deep-diff');
|
||||
|
||||
var hasAllPODobject = () => {
|
||||
var obj = new Parse.Object('HasAllPOD');
|
||||
obj.set('aNumber', 5);
|
||||
@@ -14,9 +16,9 @@ var hasAllPODobject = () => {
|
||||
objACL.setPublicWriteAccess(false);
|
||||
obj.setACL(objACL);
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
var expectedResponseForHasAllPOD = {
|
||||
var plainOldDataSchema = {
|
||||
className: 'HasAllPOD',
|
||||
fields: {
|
||||
//Default fields
|
||||
@@ -33,10 +35,10 @@ var expectedResponseForHasAllPOD = {
|
||||
aArray: {type: 'Array'},
|
||||
aGeoPoint: {type: 'GeoPoint'},
|
||||
aFile: {type: 'File'}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var expectedResponseforHasPointersAndRelations = {
|
||||
var pointersAndRelationsSchema = {
|
||||
className: 'HasPointersAndRelations',
|
||||
fields: {
|
||||
//Default fields
|
||||
@@ -56,17 +58,30 @@ var expectedResponseforHasPointersAndRelations = {
|
||||
},
|
||||
}
|
||||
|
||||
var noAuthHeaders = {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
};
|
||||
|
||||
var restKeyHeaders = {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
};
|
||||
|
||||
var masterKeyHeaders = {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-Master-Key': 'test',
|
||||
};
|
||||
|
||||
describe('schemas', () => {
|
||||
it('requires the master key to get all schemas', (done) => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/schemas',
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
},
|
||||
headers: noAuthHeaders,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(401);
|
||||
//api.parse.com uses status code 401, but due to the lack of keys
|
||||
//being necessary in parse-server, 403 makes more sense
|
||||
expect(response.statusCode).toEqual(403);
|
||||
expect(body.error).toEqual('unauthorized');
|
||||
done();
|
||||
});
|
||||
@@ -76,10 +91,7 @@ describe('schemas', () => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/schemas/SomeSchema',
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
},
|
||||
headers: restKeyHeaders,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(body.error).toEqual('unauthorized');
|
||||
@@ -87,14 +99,23 @@ describe('schemas', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('asks for the master key if you use the rest key', (done) => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/schemas',
|
||||
json: true,
|
||||
headers: restKeyHeaders,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(body.error).toEqual('master key not specified');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('responds with empty list when there are no schemas', done => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/schemas',
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-Master-Key': 'test',
|
||||
},
|
||||
headers: masterKeyHeaders,
|
||||
}, (error, response, body) => {
|
||||
expect(body.results).toEqual([]);
|
||||
done();
|
||||
@@ -113,13 +134,10 @@ describe('schemas', () => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/schemas',
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-Master-Key': 'test',
|
||||
},
|
||||
headers: masterKeyHeaders,
|
||||
}, (error, response, body) => {
|
||||
var expected = {
|
||||
results: [expectedResponseForHasAllPOD,expectedResponseforHasPointersAndRelations]
|
||||
results: [plainOldDataSchema,pointersAndRelationsSchema]
|
||||
};
|
||||
expect(body).toEqual(expected);
|
||||
done();
|
||||
@@ -133,12 +151,9 @@ describe('schemas', () => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/schemas/HasAllPOD',
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-Master-Key': 'test',
|
||||
},
|
||||
headers: masterKeyHeaders,
|
||||
}, (error, response, body) => {
|
||||
expect(body).toEqual(expectedResponseForHasAllPOD);
|
||||
expect(body).toEqual(plainOldDataSchema);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -150,10 +165,7 @@ describe('schemas', () => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/schemas/HASALLPOD',
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-Master-Key': 'test',
|
||||
},
|
||||
headers: masterKeyHeaders,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body).toEqual({
|
||||
@@ -164,4 +176,146 @@ describe('schemas', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('requires the master key to create a schema', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas',
|
||||
json: true,
|
||||
headers: noAuthHeaders,
|
||||
body: {
|
||||
className: 'MyClass',
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(403);
|
||||
expect(body.error).toEqual('unauthorized');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('asks for the master key if you use the rest key', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas',
|
||||
json: true,
|
||||
headers: restKeyHeaders,
|
||||
body: {
|
||||
className: 'MyClass',
|
||||
},
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(body.error).toEqual('master key not specified');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('sends an error if you use mismatching class names', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas/A',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
className: 'B',
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body).toEqual({
|
||||
code: Parse.Error.INVALID_CLASS_NAME,
|
||||
error: 'class name mismatch between B and A',
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('sends an error if you use no class name', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {},
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body).toEqual({
|
||||
code: 135,
|
||||
error: 'POST /schemas needs class name',
|
||||
});
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('sends an error if you try to create the same class twice', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
className: 'A',
|
||||
},
|
||||
}, (error, response, body) => {
|
||||
expect(error).toEqual(null);
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
className: 'A',
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body).toEqual({
|
||||
code: Parse.Error.INVALID_CLASS_NAME,
|
||||
error: 'class A already exists',
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('responds with all fields when you create a class', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
className: "NewClass",
|
||||
fields: {
|
||||
foo: {type: 'Number'},
|
||||
ptr: {type: 'Pointer', targetClass: 'SomeClass'}
|
||||
}
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(body).toEqual({
|
||||
className: 'NewClass',
|
||||
fields: {
|
||||
ACL: {type: 'ACL'},
|
||||
createdAt: {type: 'Date'},
|
||||
updatedAt: {type: 'Date'},
|
||||
objectId: {type: 'String'},
|
||||
foo: {type: 'Number'},
|
||||
ptr: {type: 'Pointer', targetClass: 'SomeClass'},
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('lets you specify class name in both places', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas/NewClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
className: "NewClass",
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(body).toEqual({
|
||||
className: 'NewClass',
|
||||
fields: {
|
||||
ACL: {type: 'ACL'},
|
||||
createdAt: {type: 'Date'},
|
||||
updatedAt: {type: 'Date'},
|
||||
objectId: {type: 'String'},
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"*spec.js"
|
||||
],
|
||||
"helpers": [
|
||||
"../node_modules/babel-core/register.js",
|
||||
"helper.js"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// These tests are unit tests designed to only test transform.js.
|
||||
|
||||
var transform = require('../transform');
|
||||
var transform = require('../src/transform');
|
||||
|
||||
var dummySchema = {
|
||||
data: {},
|
||||
|
||||
Reference in New Issue
Block a user