Merge branch 'master' of https://github.com/ParsePlatform/parse-server into mcdonald-gcs-adapter
Get GCSAdapter up to snuff with FilesController + FilesControllerTestFactory * 'master' of https://github.com/ParsePlatform/parse-server: (102 commits) Remove duplicated instructions Release and Changelog for 2.1.4 fixes missing coverage with sh script Fix update system schema Adds optional COVERAGE Allows to pass no where in $select clause Sanitize objectId in Fix delete schema when actual collection does not exist Fix replace query overwrite the existing query object. Fix create system class with relation/pointer Use throws syntax for errors in SchemasRouter. Completely migrate SchemasRouter to new MongoCollection API. Add tests that verify installationId in Cloud Code triggers. Propagate installationId in all Cloud Code triggers. Add test expiresAt should be a Date, not a string. Fixes #776 Fix missing 'let/var' in OneSignalPushAdapter.spec. Don't run any afterSave hooks if none are registered. Fix : remove query count limit Flatten custom operations in request.object in afterSave hooks. ...
This commit is contained in:
@@ -2,15 +2,17 @@
|
||||
var loadAdapter = require("../src/Adapters/AdapterLoader").loadAdapter;
|
||||
var FilesAdapter = require("../src/Adapters/Files/FilesAdapter").default;
|
||||
|
||||
describe("AdaptableController", ()=>{
|
||||
describe("AdapterLoader", ()=>{
|
||||
|
||||
it("should instantiate an adapter from string in object", (done) => {
|
||||
var adapterPath = require('path').resolve("./spec/MockAdapter");
|
||||
|
||||
var adapter = loadAdapter({
|
||||
adapter: adapterPath,
|
||||
key: "value",
|
||||
foo: "bar"
|
||||
options: {
|
||||
key: "value",
|
||||
foo: "bar"
|
||||
}
|
||||
});
|
||||
|
||||
expect(adapter instanceof Object).toBe(true);
|
||||
@@ -24,7 +26,6 @@ describe("AdaptableController", ()=>{
|
||||
var adapter = loadAdapter(adapterPath);
|
||||
|
||||
expect(adapter instanceof Object).toBe(true);
|
||||
expect(adapter.options).toBe(adapterPath);
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -65,4 +66,22 @@ describe("AdaptableController", ()=>{
|
||||
expect(adapter).toBe(originalAdapter);
|
||||
done();
|
||||
});
|
||||
|
||||
it("should fail loading an improperly configured adapter", (done) => {
|
||||
var Adapter = function(options) {
|
||||
if (!options.foo) {
|
||||
throw "foo is required for that adapter";
|
||||
}
|
||||
}
|
||||
var adapterOptions = {
|
||||
param: "key",
|
||||
doSomething: function() {}
|
||||
};
|
||||
|
||||
expect(() => {
|
||||
var adapter = loadAdapter(adapterOptions, Adapter);
|
||||
expect(adapter).toEqual(adapterOptions);
|
||||
}).not.toThrow("foo is required for that adapter");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
17
spec/DatabaseController.spec.js
Normal file
17
spec/DatabaseController.spec.js
Normal file
@@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
let DatabaseController = require('../src/Controllers/DatabaseController');
|
||||
let MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter');
|
||||
|
||||
describe('DatabaseController', () => {
|
||||
it('can be constructed', done => {
|
||||
let adapter = new MongoStorageAdapter('mongodb://localhost:27017/test');
|
||||
let databaseController = new DatabaseController(adapter, {
|
||||
collectionPrefix: 'test_'
|
||||
});
|
||||
databaseController.connect().then(done, error => {
|
||||
console.log('error', error.stack);
|
||||
fail();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
var ExportAdapter = require('../src/ExportAdapter');
|
||||
|
||||
describe('ExportAdapter', () => {
|
||||
it('can be constructed', (done) => {
|
||||
var database = new ExportAdapter('mongodb://localhost:27017/test',
|
||||
{
|
||||
collectionPrefix: 'test_'
|
||||
});
|
||||
database.connect().then(done, (error) => {
|
||||
console.log('error', error.stack);
|
||||
fail();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,29 +1,52 @@
|
||||
var FilesController = require('../src/Controllers/FilesController').FilesController;
|
||||
var GridStoreAdapter = require("../src/Adapters/Files/GridStoreAdapter").GridStoreAdapter;
|
||||
var S3Adapter = require("../src/Adapters/Files/S3Adapter").S3Adapter;
|
||||
var GCSAdapter = require("../src/Adapters/Files/GCSAdapter").GCSAdapter;
|
||||
var Config = require("../src/Config");
|
||||
|
||||
var FCTestFactory = require("./FilesControllerTestFactory");
|
||||
|
||||
|
||||
// Small additional tests to improve overall coverage
|
||||
describe("FilesController",()=>{
|
||||
|
||||
it("should properly expand objects", (done) => {
|
||||
var config = new Config(Parse.applicationId);
|
||||
var adapter = new GridStoreAdapter();
|
||||
var filesController = new FilesController(adapter);
|
||||
var result = filesController.expandFilesInObject(config, function(){});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
var fullFile = {
|
||||
type: '__type',
|
||||
url: "http://an.url"
|
||||
}
|
||||
|
||||
var anObject = {
|
||||
aFile: fullFile
|
||||
}
|
||||
filesController.expandFilesInObject(config, anObject);
|
||||
expect(anObject.aFile.url).toEqual("http://an.url");
|
||||
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
// Test the grid store adapter
|
||||
var gridStoreAdapter = new GridStoreAdapter('mongodb://localhost:27017/parse');
|
||||
FCTestFactory.testAdapter("GridStoreAdapter", gridStoreAdapter);
|
||||
|
||||
if (process.env.S3_ACCESS_KEY && process.env.S3_SECRET_KEY) {
|
||||
|
||||
// Test the S3 Adapter
|
||||
var s3Adapter = new S3Adapter(process.env.S3_ACCESS_KEY, process.env.S3_SECRET_KEY, 'parse.server.tests');
|
||||
|
||||
FCTestFactory.testAdapter("S3Adapter",s3Adapter);
|
||||
|
||||
// Test S3 with direct access
|
||||
var s3DirectAccessAdapter = new S3Adapter(process.env.S3_ACCESS_KEY, process.env.S3_SECRET_KEY, 'parse.server.tests', {
|
||||
directAccess: true
|
||||
});
|
||||
|
||||
FCTestFactory.testAdapter("S3AdapterDirect", s3DirectAccessAdapter);
|
||||
|
||||
} else if (!process.env.TRAVIS) {
|
||||
console.log("set S3_ACCESS_KEY and S3_SECRET_KEY to test S3Adapter")
|
||||
}
|
||||
|
||||
if (process.env.GCP_PROJECT_ID && process.env.GCP_KEYFILE_PATH && process.env.GCS_BUCKET_NAME) {
|
||||
|
||||
// Test the GCS Adapter
|
||||
var gcsAdapter = new GCSAdapter(process.env.GCP_PROJECT_ID, process.env.GCP_KEYFILE_PATH, process.env.GCS_BUCKET_NAME);
|
||||
|
||||
FCTestFactory.testAdapter("GCSAdapter", gcsAdapter);
|
||||
|
||||
// Test GCS with direct access
|
||||
var gcsDirectAccessAdapter = new GCSAdapter(process.env.GCP_PROJECT_ID, process.env.GCP_KEYFILE_PATH, process.env.GCS_BUCKET_NAME, {
|
||||
directAccess: true
|
||||
});
|
||||
|
||||
FCTestFactory.testAdapter("GCSAdapterDirect", gcsDirectAccessAdapter);
|
||||
|
||||
} else if (!process.env.TRAVIS) {
|
||||
console.log("set GCP_PROJECT_ID, GCP_KEYFILE_PATH, and GCS_BUCKET_NAME to test GCSAdapter")
|
||||
}
|
||||
});
|
||||
|
||||
73
spec/FilesControllerTestFactory.js
Normal file
73
spec/FilesControllerTestFactory.js
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
var FilesController = require('../src/Controllers/FilesController').FilesController;
|
||||
var Config = require("../src/Config");
|
||||
|
||||
var testAdapter = function(name, adapter) {
|
||||
// Small additional tests to improve overall coverage
|
||||
|
||||
var config = new Config(Parse.applicationId);
|
||||
var filesController = new FilesController(adapter);
|
||||
|
||||
describe("FilesController with "+name,()=>{
|
||||
|
||||
it("should properly expand objects", (done) => {
|
||||
|
||||
var result = filesController.expandFilesInObject(config, function(){});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
var fullFile = {
|
||||
type: '__type',
|
||||
url: "http://an.url"
|
||||
}
|
||||
|
||||
var anObject = {
|
||||
aFile: fullFile
|
||||
}
|
||||
filesController.expandFilesInObject(config, anObject);
|
||||
expect(anObject.aFile.url).toEqual("http://an.url");
|
||||
|
||||
done();
|
||||
})
|
||||
|
||||
it("should properly create, read, delete files", (done) => {
|
||||
var filename;
|
||||
filesController.createFile(config, "file.txt", "hello world").then( (result) => {
|
||||
ok(result.url);
|
||||
ok(result.name);
|
||||
filename = result.name;
|
||||
expect(result.name.match(/file.txt/)).not.toBe(null);
|
||||
return filesController.getFileData(config, filename);
|
||||
}, (err) => {
|
||||
fail("The adapter should create the file");
|
||||
console.error(err);
|
||||
done();
|
||||
}).then((result) => {
|
||||
expect(result instanceof Buffer).toBe(true);
|
||||
expect(result.toString('utf-8')).toEqual("hello world");
|
||||
return filesController.deleteFile(config, filename);
|
||||
}, (err) => {
|
||||
fail("The adapter should get the file");
|
||||
console.error(err);
|
||||
done();
|
||||
}).then((result) => {
|
||||
|
||||
filesController.getFileData(config, filename).then((res) => {
|
||||
fail("the file should be deleted");
|
||||
done();
|
||||
}, (err) => {
|
||||
done();
|
||||
});
|
||||
|
||||
}, (err) => {
|
||||
fail("The adapter should delete the file");
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
}, 5000); // longer tests
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
testAdapter: testAdapter
|
||||
}
|
||||
@@ -177,5 +177,20 @@ describe("httpRequest", () => {
|
||||
var result = httpRequest.encodeBody({"foo": "bar", "bar": "baz"}, {'X-Custom-Header': 'my-header'});
|
||||
expect(result).toEqual({"foo": "bar", "bar": "baz"});
|
||||
done();
|
||||
});
|
||||
|
||||
it("should fail gracefully", (done) => {
|
||||
httpRequest({
|
||||
url: "http://not a good url",
|
||||
success: function() {
|
||||
fail("should not succeed");
|
||||
done();
|
||||
},
|
||||
error: function(error) {
|
||||
expect(error).not.toBeUndefined();
|
||||
expect(error).not.toBeNull();
|
||||
done();
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const request = require('request');
|
||||
var LogsRouter = require('../src/Routers/LogsRouter').LogsRouter;
|
||||
var LoggerController = require('../src/Controllers/LoggerController').LoggerController;
|
||||
var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter;
|
||||
@@ -20,7 +23,7 @@ describe('LogsRouter', () => {
|
||||
var router = new LogsRouter();
|
||||
|
||||
expect(() => {
|
||||
router.handleGET(request);
|
||||
router.validateRequest(request);
|
||||
}).not.toThrow();
|
||||
done();
|
||||
});
|
||||
@@ -40,28 +43,23 @@ describe('LogsRouter', () => {
|
||||
var router = new LogsRouter();
|
||||
|
||||
expect(() => {
|
||||
router.handleGET(request);
|
||||
router.validateRequest(request);
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can check invalid master key of request', (done) => {
|
||||
// Make mock request
|
||||
var request = {
|
||||
auth: {
|
||||
isMaster: false
|
||||
},
|
||||
query: {},
|
||||
config: {
|
||||
loggerController: loggerController
|
||||
it('can check invalid master key of request', done => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/scriptlog',
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
}
|
||||
};
|
||||
|
||||
var router = new LogsRouter();
|
||||
|
||||
expect(() => {
|
||||
router.handleGET(request);
|
||||
}).toThrow();
|
||||
done();
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(403);
|
||||
expect(body.error).toEqual('unauthorized: master key is required');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
module.exports = function(options) {
|
||||
this.options = options;
|
||||
}
|
||||
return {
|
||||
options: options
|
||||
};
|
||||
};
|
||||
|
||||
5
spec/MockEmailAdapter.js
Normal file
5
spec/MockEmailAdapter.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => Promise.resolve()
|
||||
}
|
||||
10
spec/MockEmailAdapterWithOptions.js
Normal file
10
spec/MockEmailAdapterWithOptions.js
Normal file
@@ -0,0 +1,10 @@
|
||||
module.exports = options => {
|
||||
if (!options) {
|
||||
throw "Options were not provided"
|
||||
}
|
||||
return {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => Promise.resolve()
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
var OneSignalPushAdapter = require('../src/Adapters/Push/OneSignalPushAdapter');
|
||||
var classifyInstallations = require('../src/Adapters/Push/PushAdapterUtils').classifyInstallations;
|
||||
|
||||
// Make mock config
|
||||
var pushConfig = {
|
||||
oneSignalAppId:"APP ID",
|
||||
oneSignalApiKey:"API KEY"
|
||||
};
|
||||
|
||||
describe('OneSignalPushAdapter', () => {
|
||||
it('can be initialized', (done) => {
|
||||
// Make mock config
|
||||
var pushConfig = {
|
||||
oneSignalAppId:"APP ID",
|
||||
oneSignalApiKey:"API KEY"
|
||||
};
|
||||
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter(pushConfig);
|
||||
|
||||
@@ -17,9 +20,17 @@ describe('OneSignalPushAdapter', () => {
|
||||
expect(senderMap.android instanceof Function).toBe(true);
|
||||
done();
|
||||
});
|
||||
|
||||
it('cannot be initialized if options are missing', (done) => {
|
||||
|
||||
expect(() => {
|
||||
new OneSignalPushAdapter();
|
||||
}).toThrow("Trying to initialize OneSignalPushAdapter without oneSignalAppId or oneSignalApiKey");
|
||||
done();
|
||||
});
|
||||
|
||||
it('can get valid push types', (done) => {
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter();
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter(pushConfig);
|
||||
|
||||
expect(oneSignalPushAdapter.getValidPushTypes()).toEqual(['ios', 'android']);
|
||||
done();
|
||||
@@ -56,7 +67,7 @@ describe('OneSignalPushAdapter', () => {
|
||||
|
||||
|
||||
it('can send push notifications', (done) => {
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter();
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter(pushConfig);
|
||||
|
||||
// Mock android ios senders
|
||||
var androidSender = jasmine.createSpy('send')
|
||||
@@ -108,7 +119,7 @@ describe('OneSignalPushAdapter', () => {
|
||||
});
|
||||
|
||||
it("can send iOS notifications", (done) => {
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter();
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter(pushConfig);
|
||||
var sendToOneSignal = jasmine.createSpy('sendToOneSignal');
|
||||
oneSignalPushAdapter.sendToOneSignal = sendToOneSignal;
|
||||
|
||||
@@ -135,7 +146,7 @@ describe('OneSignalPushAdapter', () => {
|
||||
});
|
||||
|
||||
it("can send Android notifications", (done) => {
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter();
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter(pushConfig);
|
||||
var sendToOneSignal = jasmine.createSpy('sendToOneSignal');
|
||||
oneSignalPushAdapter.sendToOneSignal = sendToOneSignal;
|
||||
|
||||
@@ -157,10 +168,7 @@ describe('OneSignalPushAdapter', () => {
|
||||
});
|
||||
|
||||
it("can post the correct data", (done) => {
|
||||
var pushConfig = {
|
||||
oneSignalAppId:"APP ID",
|
||||
oneSignalApiKey:"API KEY"
|
||||
};
|
||||
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter(pushConfig);
|
||||
|
||||
var write = jasmine.createSpy('write');
|
||||
@@ -203,7 +211,7 @@ describe('OneSignalPushAdapter', () => {
|
||||
expect(write).toHaveBeenCalled();
|
||||
|
||||
// iOS
|
||||
args = write.calls.first().args;
|
||||
let args = write.calls.first().args;
|
||||
expect(args[0]).toEqual(JSON.stringify({
|
||||
'contents': { 'en':'Example content'},
|
||||
'content_available':true,
|
||||
@@ -212,7 +220,7 @@ describe('OneSignalPushAdapter', () => {
|
||||
'app_id':'APP ID'
|
||||
}));
|
||||
|
||||
// Android
|
||||
// Android
|
||||
args = write.calls.mostRecent().args;
|
||||
expect(args[0]).toEqual(JSON.stringify({
|
||||
'contents': { 'en':'Example content'},
|
||||
|
||||
@@ -372,6 +372,15 @@ describe('miscellaneous', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test cloud function shoud echo keys', function(done) {
|
||||
Parse.Cloud.run('echoKeys').then((result) => {
|
||||
expect(result.applicationId).toEqual(Parse.applicationId);
|
||||
expect(result.masterKey).toEqual(Parse.masterKey);
|
||||
expect(result.javascriptKey).toEqual(Parse.javascriptKey);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test rest_create_app', function(done) {
|
||||
var appId;
|
||||
@@ -683,6 +692,46 @@ describe('miscellaneous', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('afterSave flattens custom operations', done => {
|
||||
var triggerTime = 0;
|
||||
// Register a mock beforeSave hook
|
||||
Parse.Cloud.afterSave('GameScore', function(req, res) {
|
||||
let object = req.object;
|
||||
expect(object instanceof Parse.Object).toBeTruthy();
|
||||
let originalObject = req.original;
|
||||
if (triggerTime == 0) {
|
||||
// Create
|
||||
expect(object.get('yolo')).toEqual(1);
|
||||
} else if (triggerTime == 1) {
|
||||
// Update
|
||||
expect(object.get('yolo')).toEqual(2);
|
||||
// Check the originalObject
|
||||
expect(originalObject.get('yolo')).toEqual(1);
|
||||
} else {
|
||||
res.error();
|
||||
}
|
||||
triggerTime++;
|
||||
res.success();
|
||||
});
|
||||
|
||||
var obj = new Parse.Object('GameScore');
|
||||
obj.increment('yolo', 1);
|
||||
obj.save().then(() => {
|
||||
obj.increment('yolo', 1);
|
||||
return obj.save();
|
||||
}).then(() => {
|
||||
// Make sure the checking has been triggered
|
||||
expect(triggerTime).toBe(2);
|
||||
// Clear mock afterSave
|
||||
Parse.Cloud._removeHook("Triggers", "afterSave", "GameScore");
|
||||
done();
|
||||
}, error => {
|
||||
console.error(error);
|
||||
fail(error);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test cloud function error handling', (done) => {
|
||||
// Register a function which will fail
|
||||
Parse.Cloud.define('willFail', (req, res) => {
|
||||
@@ -700,6 +749,80 @@ describe('miscellaneous', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('test beforeSave/afterSave get installationId', function(done) {
|
||||
let triggerTime = 0;
|
||||
Parse.Cloud.beforeSave('GameScore', function(req, res) {
|
||||
triggerTime++;
|
||||
expect(triggerTime).toEqual(1);
|
||||
expect(req.installationId).toEqual('yolo');
|
||||
res.success();
|
||||
});
|
||||
Parse.Cloud.afterSave('GameScore', function(req) {
|
||||
triggerTime++;
|
||||
expect(triggerTime).toEqual(2);
|
||||
expect(req.installationId).toEqual('yolo');
|
||||
});
|
||||
|
||||
var headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'X-Parse-Installation-Id': 'yolo'
|
||||
};
|
||||
request.post({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/classes/GameScore',
|
||||
body: JSON.stringify({ a: 'b' })
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(triggerTime).toEqual(2);
|
||||
|
||||
Parse.Cloud._removeHook("Triggers", "beforeSave", "GameScore");
|
||||
Parse.Cloud._removeHook("Triggers", "afterSave", "GameScore");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test beforeDelete/afterDelete get installationId', function(done) {
|
||||
let triggerTime = 0;
|
||||
Parse.Cloud.beforeDelete('GameScore', function(req, res) {
|
||||
triggerTime++;
|
||||
expect(triggerTime).toEqual(1);
|
||||
expect(req.installationId).toEqual('yolo');
|
||||
res.success();
|
||||
});
|
||||
Parse.Cloud.afterDelete('GameScore', function(req) {
|
||||
triggerTime++;
|
||||
expect(triggerTime).toEqual(2);
|
||||
expect(req.installationId).toEqual('yolo');
|
||||
});
|
||||
|
||||
var headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'X-Parse-Installation-Id': 'yolo'
|
||||
};
|
||||
request.post({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/classes/GameScore',
|
||||
body: JSON.stringify({ a: 'b' })
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
request.del({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/classes/GameScore/' + JSON.parse(body).objectId
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(triggerTime).toEqual(2);
|
||||
|
||||
Parse.Cloud._removeHook("Triggers", "beforeDelete", "GameScore");
|
||||
Parse.Cloud._removeHook("Triggers", "afterDelete", "GameScore");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('test cloud function query parameters', (done) => {
|
||||
Parse.Cloud.define('echoParams', (req, res) => {
|
||||
res.success(req.params);
|
||||
@@ -844,4 +967,32 @@ describe('miscellaneous', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('dedupes an installation properly and returns updatedAt', (done) => {
|
||||
let headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
};
|
||||
let data = {
|
||||
'installationId': 'lkjsahdfkjhsdfkjhsdfkjhsdf',
|
||||
'deviceType': 'embedded'
|
||||
};
|
||||
let requestOptions = {
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/installations',
|
||||
body: JSON.stringify(data)
|
||||
};
|
||||
request.post(requestOptions, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
let b = JSON.parse(body);
|
||||
expect(typeof b.objectId).toEqual('string');
|
||||
request.post(requestOptions, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
let b = JSON.parse(body);
|
||||
expect(typeof b.updatedAt).toEqual('string');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,400 +0,0 @@
|
||||
// This is a port of the test suite:
|
||||
// hungry/js/test/parse_file_test.js
|
||||
|
||||
"use strict";
|
||||
|
||||
var request = require('request');
|
||||
var GCSAdapter = require('../src/index').GCSAdapter;
|
||||
|
||||
var str = "Hello World!";
|
||||
var data = [];
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
data.push(str.charCodeAt(i));
|
||||
}
|
||||
|
||||
// Make sure that you fill these in, otherwise the tests won't run!!!
|
||||
var GCP_PROJECT_ID = "<gcp_project_id>";
|
||||
var GCP_KEYFILE_PATH = "<path/to/keyfile>";
|
||||
var GCS_BUCKET_NAME = "<gcs_bucket_name>";
|
||||
|
||||
// Note the 'xdescribe', make sure to delete the 'x' once the above vars
|
||||
// are filled in to run the test suite
|
||||
xdescribe('Parse.File GCS testing', () => {
|
||||
describe('GCS directAccess: false', () => {
|
||||
beforeEach(function(done){
|
||||
var port = 8378;
|
||||
var GCSConfiguration = {
|
||||
databaseURI: process.env.DATABASE_URI,
|
||||
serverURL: 'http://localhost:' + port + '/1',
|
||||
appId: 'test',
|
||||
javascriptKey: 'test',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
fileKey: 'test',
|
||||
filesAdapter: new GCSAdapter(
|
||||
GCP_PROJECT_ID,
|
||||
GCP_KEYFILE_PATH,
|
||||
GCS_BUCKET_NAME,
|
||||
{
|
||||
bucketPrefix: 'private/',
|
||||
directAccess: false
|
||||
}
|
||||
)
|
||||
};
|
||||
setServerConfiguration(GCSConfiguration);
|
||||
done();
|
||||
});
|
||||
|
||||
it('works with Content-Type', done => {
|
||||
var headers = {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
};
|
||||
request.post({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/files/file.txt',
|
||||
body: 'argle bargle',
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var b = JSON.parse(body);
|
||||
expect(b.name).toMatch(/_file.txt$/);
|
||||
expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*file.txt$/);
|
||||
request.get(b.url, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(body).toEqual('argle bargle');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('works without Content-Type', done => {
|
||||
var headers = {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
};
|
||||
request.post({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/files/file.txt',
|
||||
body: 'argle bargle',
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var b = JSON.parse(body);
|
||||
expect(b.name).toMatch(/_file.txt$/);
|
||||
expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*file.txt$/);
|
||||
request.get(b.url, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(body).toEqual('argle bargle');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
};
|
||||
request.post({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/files/file.jpg',
|
||||
body: 'argle bargle',
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var b = JSON.parse(body);
|
||||
expect(b.name).toMatch(/_file.jpg$/);
|
||||
expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/.*file.jpg$/);
|
||||
request.get(b.url, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(body).toEqual('argle bargle');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GCS directAccess: true', () => {
|
||||
beforeEach(function(done){
|
||||
var port = 8378;
|
||||
var GCSConfiguration = {
|
||||
databaseURI: process.env.DATABASE_URI,
|
||||
serverURL: 'http://localhost:' + port + '/1',
|
||||
appId: 'test',
|
||||
javascriptKey: 'test',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
fileKey: 'test',
|
||||
filesAdapter: new GCSAdapter(
|
||||
GCP_PROJECT_ID,
|
||||
GCP_KEYFILE_PATH,
|
||||
GCS_BUCKET_NAME,
|
||||
{
|
||||
bucketPrefix: 'public/',
|
||||
directAccess: true
|
||||
}
|
||||
)
|
||||
};
|
||||
setServerConfiguration(GCSConfiguration);
|
||||
done();
|
||||
});
|
||||
|
||||
it('works with Content-Type', done => {
|
||||
var headers = {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
};
|
||||
request.post({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/files/file.txt',
|
||||
body: 'argle bargle',
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var b = JSON.parse(body);
|
||||
expect(b.name).toMatch(/_file.txt$/);
|
||||
var gcsRegex = new RegExp("https:\/\/" + GCS_BUCKET_NAME + ".storage.googleapis.com\/public\/.*file.txt")
|
||||
expect(b.url).toMatch(gcsRegex);
|
||||
request.get(b.url, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(body).toEqual('argle bargle');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('works without Content-Type', done => {
|
||||
var headers = {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
};
|
||||
request.post({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/files/file.txt',
|
||||
body: 'argle bargle',
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var b = JSON.parse(body);
|
||||
expect(b.name).toMatch(/_file.txt$/);
|
||||
var gcsRegex = new RegExp("https:\/\/" + GCS_BUCKET_NAME + ".storage.googleapis.com\/public\/.*file.txt")
|
||||
expect(b.url).toMatch(gcsRegex);
|
||||
request.get(b.url, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(body).toEqual('argle bargle');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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'
|
||||
};
|
||||
// Create the file
|
||||
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$/);
|
||||
var gcsRegex = new RegExp("https:\/\/" + GCS_BUCKET_NAME + ".storage.googleapis.com\/public\/.*testfile.txt")
|
||||
expect(b.url).toMatch(gcsRegex);
|
||||
// Read the file the first time
|
||||
request.get(b.url, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(body).toEqual('check one two');
|
||||
// Delete the file
|
||||
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);
|
||||
// Read the file the second time--expect it to be gone
|
||||
// Note that we're reading from the public cloud storage URL
|
||||
// This is different from the above test since it's assumed
|
||||
// users are reading from the public URL
|
||||
request.get({
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
},
|
||||
url: "https://" + GCS_BUCKET_NAME + ".storage.googleapis.com/public/.*testfile.txt"
|
||||
}, (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);
|
||||
var gcsRegex = new RegExp("https:\/\/" + GCS_BUCKET_NAME + ".storage.googleapis.com\/public\/.*thefile.jpg")
|
||||
expect(b.url).toMatch(gcsRegex);
|
||||
// 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',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
};
|
||||
request.post({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/files/file.jpg',
|
||||
body: 'argle bargle',
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var b = JSON.parse(body);
|
||||
expect(b.name).toMatch(/_file.jpg$/);
|
||||
var gcsRegex = new RegExp("https:\/\/" + GCS_BUCKET_NAME + ".storage.googleapis.com\/public\/.*file.jpg")
|
||||
expect(b.url).toMatch(gcsRegex);
|
||||
request.get(b.url, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
expect(body).toEqual('argle bargle');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,13 +2,12 @@
|
||||
|
||||
var request = require('request');
|
||||
var Parse = require('parse/node').Parse;
|
||||
var DatabaseAdapter = require('../src/DatabaseAdapter');
|
||||
|
||||
let database = DatabaseAdapter.getDatabaseConnection('test', 'test_');
|
||||
let Config = require('../src/Config');
|
||||
|
||||
describe('a GlobalConfig', () => {
|
||||
beforeEach(function(done) {
|
||||
database.rawCollection('_GlobalConfig')
|
||||
let config = new Config('test');
|
||||
config.database.rawCollection('_GlobalConfig')
|
||||
.then(coll => coll.updateOne({ '_id': 1}, { $set: { params: { companies: ['US', 'DK'] } } }, { upsert: true }))
|
||||
.then(done());
|
||||
});
|
||||
@@ -54,14 +53,15 @@ describe('a GlobalConfig', () => {
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
},
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(body.error).toEqual('unauthorized');
|
||||
expect(response.statusCode).toEqual(403);
|
||||
expect(body.error).toEqual('unauthorized: master key is required');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('failed getting config when it is missing', (done) => {
|
||||
database.rawCollection('_GlobalConfig')
|
||||
let config = new Config('test');
|
||||
config.database.rawCollection('_GlobalConfig')
|
||||
.then(coll => coll.deleteOne({ '_id': 1}, {}, {}))
|
||||
.then(_ => {
|
||||
request.get({
|
||||
|
||||
@@ -446,6 +446,52 @@ describe('Installations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('update android device token with duplicate device token', (done) => {
|
||||
var installId1 = '11111111-abcd-abcd-abcd-123456789abc';
|
||||
var installId2 = '22222222-abcd-abcd-abcd-123456789abc';
|
||||
var t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
|
||||
var input = {
|
||||
'installationId': installId1,
|
||||
'deviceToken': t,
|
||||
'deviceType': 'android'
|
||||
};
|
||||
var firstObject;
|
||||
var secondObject;
|
||||
rest.create(config, auth.nobody(config), '_Installation', input)
|
||||
.then(() => {
|
||||
input = {
|
||||
'installationId': installId2,
|
||||
'deviceType': 'android'
|
||||
};
|
||||
return rest.create(config, auth.nobody(config), '_Installation', input);
|
||||
}).then(() => {
|
||||
return database.mongoFind('_Installation',
|
||||
{installationId: installId1}, {});
|
||||
}).then((results) => {
|
||||
expect(results.length).toEqual(1);
|
||||
firstObject = results[0];
|
||||
return database.mongoFind('_Installation',
|
||||
{installationId: installId2}, {});
|
||||
}).then((results) => {
|
||||
expect(results.length).toEqual(1);
|
||||
secondObject = results[0];
|
||||
// Update second installation to conflict with first installation
|
||||
input = {
|
||||
'objectId': secondObject._id,
|
||||
'deviceToken': t
|
||||
};
|
||||
return rest.update(config, auth.nobody(config), '_Installation',
|
||||
secondObject._id, input);
|
||||
}).then(() => {
|
||||
// The first object should have been deleted
|
||||
return database.mongoFind('_Installation', {_id: firstObject._id}, {});
|
||||
}).then((results) => {
|
||||
expect(results.length).toEqual(0);
|
||||
done();
|
||||
}).catch((error) => { console.log(error); });
|
||||
});
|
||||
|
||||
|
||||
it('update ios device token with duplicate device token', (done) => {
|
||||
var installId1 = '11111111-abcd-abcd-abcd-123456789abc';
|
||||
var installId2 = '22222222-abcd-abcd-abcd-123456789abc';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use strict";
|
||||
// This is a port of the test suite:
|
||||
// hungry/js/test/parse_object_test.js
|
||||
//
|
||||
@@ -336,6 +337,34 @@ describe('Parse.Object testing', () => {
|
||||
item.save({ "foo^bar": "baz" }).then(fail, done);
|
||||
});
|
||||
|
||||
it("invalid __type", function(done) {
|
||||
var item = new Parse.Object("Item");
|
||||
var types = ['Pointer', 'File', 'Date', 'GeoPoint', 'Bytes'];
|
||||
var Error = Parse.Error;
|
||||
var tests = types.map(type => {
|
||||
var test = new Parse.Object("Item");
|
||||
test.set('foo', {
|
||||
__type: type
|
||||
});
|
||||
return test;
|
||||
});
|
||||
var next = function(index) {
|
||||
if (index < tests.length) {
|
||||
tests[index].save().then(fail, error => {
|
||||
expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE);
|
||||
next(index + 1);
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}
|
||||
item.save({
|
||||
"foo": {
|
||||
__type: "IvalidName"
|
||||
}
|
||||
}).then(fail, err => next(0));
|
||||
});
|
||||
|
||||
it("simple field deletion", function(done) {
|
||||
var simple = new Parse.Object("SimpleObject");
|
||||
simple.save({
|
||||
@@ -1763,6 +1792,55 @@ describe('Parse.Object testing', () => {
|
||||
console.error(err);
|
||||
fail("should not fail");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should have undefined includes when object is missing', (done) => {
|
||||
let obj1 = new Parse.Object("AnObject");
|
||||
let obj2 = new Parse.Object("AnObject");
|
||||
|
||||
Parse.Object.saveAll([obj1, obj2]).then(() => {
|
||||
obj1.set("obj", obj2);
|
||||
// Save the pointer, delete the pointee
|
||||
return obj1.save().then(() => { return obj2.destroy() });
|
||||
}).then(() => {
|
||||
let query = new Parse.Query("AnObject");
|
||||
query.include("obj");
|
||||
return query.find();
|
||||
}).then((res) => {
|
||||
expect(res.length).toBe(1);
|
||||
expect(res[0].get("obj")).toBe(undefined);
|
||||
let query = new Parse.Query("AnObject");
|
||||
return query.find();
|
||||
}).then((res) => {
|
||||
expect(res.length).toBe(1);
|
||||
expect(res[0].get("obj")).not.toBe(undefined);
|
||||
return res[0].get("obj").fetch();
|
||||
}).then(() => {
|
||||
fail("Should not fetch a deleted object");
|
||||
}, (err) => {
|
||||
expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND);
|
||||
done();
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
it('should have undefined includes when object is missing on deeper path', (done) => {
|
||||
let obj1 = new Parse.Object("AnObject");
|
||||
let obj2 = new Parse.Object("AnObject");
|
||||
let obj3 = new Parse.Object("AnObject");
|
||||
Parse.Object.saveAll([obj1, obj2, obj3]).then(() => {
|
||||
obj1.set("obj", obj2);
|
||||
obj2.set("obj", obj3);
|
||||
// Save the pointer, delete the pointee
|
||||
return Parse.Object.saveAll([obj1, obj2]).then(() => { return obj3.destroy() });
|
||||
}).then(() => {
|
||||
let query = new Parse.Query("AnObject");
|
||||
query.include("obj.obj");
|
||||
return query.get(obj1.id);
|
||||
}).then((res) => {
|
||||
expect(res.get("obj")).not.toBe(undefined);
|
||||
expect(res.get("obj").get("obj")).toBe(undefined);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2088,4 +2088,60 @@ describe('Parse.Query testing', () => {
|
||||
console.log(error);
|
||||
});
|
||||
});
|
||||
|
||||
// #371
|
||||
it('should properly interpret a query', (done) => {
|
||||
var query = new Parse.Query("C1");
|
||||
var auxQuery = new Parse.Query("C1");
|
||||
query.matchesKeyInQuery("A1", "A2", auxQuery);
|
||||
query.include("A3");
|
||||
query.include("A2");
|
||||
query.find().then((result) => {
|
||||
done();
|
||||
}, (err) => {
|
||||
console.error(err);
|
||||
fail("should not failt");
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('should properly interpret a query', (done) => {
|
||||
var user = new Parse.User();
|
||||
user.set("username", "foo");
|
||||
user.set("password", "bar");
|
||||
return user.save().then( (user) => {
|
||||
var objIdQuery = new Parse.Query("_User").equalTo("objectId", user.id);
|
||||
var blockedUserQuery = user.relation("blockedUsers").query();
|
||||
|
||||
var aResponseQuery = new Parse.Query("MatchRelationshipActivityResponse");
|
||||
aResponseQuery.equalTo("userA", user);
|
||||
aResponseQuery.equalTo("userAResponse", 1);
|
||||
|
||||
var bResponseQuery = new Parse.Query("MatchRelationshipActivityResponse");
|
||||
bResponseQuery.equalTo("userB", user);
|
||||
bResponseQuery.equalTo("userBResponse", 1);
|
||||
|
||||
var matchOr = Parse.Query.or(aResponseQuery, bResponseQuery);
|
||||
var matchRelationshipA = new Parse.Query("_User");
|
||||
matchRelationshipA.matchesKeyInQuery("objectId", "userAObjectId", matchOr);
|
||||
var matchRelationshipB = new Parse.Query("_User");
|
||||
matchRelationshipB.matchesKeyInQuery("objectId", "userBObjectId", matchOr);
|
||||
|
||||
|
||||
var orQuery = Parse.Query.or(objIdQuery, blockedUserQuery, matchRelationshipA, matchRelationshipB);
|
||||
var query = new Parse.Query("_User");
|
||||
query.doesNotMatchQuery("objectId", orQuery);
|
||||
return query.find();
|
||||
}).then((res) => {
|
||||
done();
|
||||
done();
|
||||
}, (err) => {
|
||||
console.error(err);
|
||||
fail("should not fail");
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -237,7 +237,7 @@ describe('Parse.Relation testing', () => {
|
||||
success: function(list) {
|
||||
equal(list.length, 1, "There should be only one result");
|
||||
equal(list[0].id, parent2.id,
|
||||
"Should have gotten back the right result");
|
||||
"Should have gotten back the right result");
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -246,6 +246,133 @@ describe('Parse.Relation testing', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("queries on relation fields with multiple ins", (done) => {
|
||||
var ChildObject = Parse.Object.extend("ChildObject");
|
||||
var childObjects = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
childObjects.push(new ChildObject({x: i}));
|
||||
}
|
||||
|
||||
Parse.Object.saveAll(childObjects).then(() => {
|
||||
var ParentObject = Parse.Object.extend("ParentObject");
|
||||
var parent = new ParentObject();
|
||||
parent.set("x", 4);
|
||||
var relation = parent.relation("child");
|
||||
relation.add(childObjects[0]);
|
||||
relation.add(childObjects[1]);
|
||||
relation.add(childObjects[2]);
|
||||
var parent2 = new ParentObject();
|
||||
parent2.set("x", 3);
|
||||
var relation2 = parent2.relation("child");
|
||||
relation2.add(childObjects[4]);
|
||||
relation2.add(childObjects[5]);
|
||||
relation2.add(childObjects[6]);
|
||||
|
||||
var otherChild2 = parent2.relation("otherChild");
|
||||
otherChild2.add(childObjects[0]);
|
||||
otherChild2.add(childObjects[1]);
|
||||
otherChild2.add(childObjects[2]);
|
||||
|
||||
var parents = [];
|
||||
parents.push(parent);
|
||||
parents.push(parent2);
|
||||
return Parse.Object.saveAll(parents);
|
||||
}).then(() => {
|
||||
var query = new Parse.Query(ParentObject);
|
||||
var objects = [];
|
||||
objects.push(childObjects[0]);
|
||||
query.containedIn("child", objects);
|
||||
query.containedIn("otherChild", [childObjects[0]]);
|
||||
return query.find();
|
||||
}).then((list) => {
|
||||
equal(list.length, 2, "There should be 2 results");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("query on pointer and relation fields with equal", (done) => {
|
||||
var ChildObject = Parse.Object.extend("ChildObject");
|
||||
var childObjects = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
childObjects.push(new ChildObject({x: i}));
|
||||
}
|
||||
|
||||
Parse.Object.saveAll(childObjects).then(() => {
|
||||
var ParentObject = Parse.Object.extend("ParentObject");
|
||||
var parent = new ParentObject();
|
||||
parent.set("x", 4);
|
||||
var relation = parent.relation("toChilds");
|
||||
relation.add(childObjects[0]);
|
||||
relation.add(childObjects[1]);
|
||||
relation.add(childObjects[2]);
|
||||
|
||||
var parent2 = new ParentObject();
|
||||
parent2.set("x", 3);
|
||||
parent2.set("toChild", childObjects[2]);
|
||||
|
||||
var parents = [];
|
||||
parents.push(parent);
|
||||
parents.push(parent2);
|
||||
parents.push(new ParentObject());
|
||||
|
||||
return Parse.Object.saveAll(parents).then(() => {
|
||||
var query = new Parse.Query(ParentObject);
|
||||
query.equalTo("objectId", parent.id);
|
||||
query.equalTo("toChilds", childObjects[2]);
|
||||
|
||||
return query.find().then((list) => {
|
||||
equal(list.length, 1, "There should be 1 result");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("or queries on pointer and relation fields", (done) => {
|
||||
var ChildObject = Parse.Object.extend("ChildObject");
|
||||
var childObjects = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
childObjects.push(new ChildObject({x: i}));
|
||||
}
|
||||
|
||||
Parse.Object.saveAll(childObjects).then(() => {
|
||||
var ParentObject = Parse.Object.extend("ParentObject");
|
||||
var parent = new ParentObject();
|
||||
parent.set("x", 4);
|
||||
var relation = parent.relation("toChilds");
|
||||
relation.add(childObjects[0]);
|
||||
relation.add(childObjects[1]);
|
||||
relation.add(childObjects[2]);
|
||||
|
||||
var parent2 = new ParentObject();
|
||||
parent2.set("x", 3);
|
||||
parent2.set("toChild", childObjects[2]);
|
||||
|
||||
var parents = [];
|
||||
parents.push(parent);
|
||||
parents.push(parent2);
|
||||
parents.push(new ParentObject());
|
||||
|
||||
return Parse.Object.saveAll(parents).then(() => {
|
||||
var query1 = new Parse.Query(ParentObject);
|
||||
query1.containedIn("toChilds", [childObjects[2]]);
|
||||
var query2 = new Parse.Query(ParentObject);
|
||||
query2.equalTo("toChild", childObjects[2]);
|
||||
var query = Parse.Query.or(query1, query2);
|
||||
return query.find().then((list) => {
|
||||
var objectIds = list.map(function(item){
|
||||
return item.id;
|
||||
});
|
||||
expect(objectIds.indexOf(parent.id)).not.toBe(-1);
|
||||
expect(objectIds.indexOf(parent2.id)).not.toBe(-1);
|
||||
equal(list.length, 2, "There should be 2 results");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("Get query on relation using un-fetched parent object", (done) => {
|
||||
// Setup data model
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
// Roles are not accessible without the master key, so they are not intended
|
||||
// for use by clients. We can manually test them using the master key.
|
||||
var Auth = require("../src/Auth").Auth;
|
||||
var Config = require("../src/Config");
|
||||
|
||||
describe('Parse Role testing', () => {
|
||||
|
||||
@@ -58,5 +60,64 @@ describe('Parse Role testing', () => {
|
||||
|
||||
});
|
||||
|
||||
it("should recursively load roles", (done) => {
|
||||
|
||||
var rolesNames = ["FooRole", "BarRole", "BazRole"];
|
||||
|
||||
var createRole = function(name, parent, user) {
|
||||
var role = new Parse.Role(name, new Parse.ACL());
|
||||
if (user) {
|
||||
var users = role.relation('users');
|
||||
users.add(user);
|
||||
}
|
||||
if (parent) {
|
||||
role.relation('roles').add(parent);
|
||||
}
|
||||
return role.save({}, { useMasterKey: true });
|
||||
}
|
||||
var roleIds = {};
|
||||
createTestUser().then( (user) => {
|
||||
|
||||
return createRole(rolesNames[0], null, null).then( (aRole) => {
|
||||
roleIds[aRole.get("name")] = aRole.id;
|
||||
return createRole(rolesNames[1], aRole, null);
|
||||
}).then( (anotherRole) => {
|
||||
roleIds[anotherRole.get("name")] = anotherRole.id;
|
||||
return createRole(rolesNames[2], anotherRole, user);
|
||||
}).then( (lastRole) => {
|
||||
roleIds[lastRole.get("name")] = lastRole.id;
|
||||
var auth = new Auth({ config: new Config("test"), isMaster: true, user: user });
|
||||
return auth._loadRoles();
|
||||
})
|
||||
}).then( (roles) => {
|
||||
expect(roles.length).toEqual(3);
|
||||
rolesNames.forEach( (name) => {
|
||||
expect(roles.indexOf('role:'+name)).not.toBe(-1);
|
||||
})
|
||||
done();
|
||||
}, function(err){
|
||||
fail("should succeed")
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("_Role object should not save without name.", (done) => {
|
||||
var role = new Parse.Role();
|
||||
role.save(null,{useMasterKey:true})
|
||||
.then((r) => {
|
||||
fail("_Role object should not save without name.");
|
||||
}, (error) => {
|
||||
expect(error.code).toEqual(111);
|
||||
role.set('name','testRole');
|
||||
role.save(null,{useMasterKey:true})
|
||||
.then((r2)=>{
|
||||
fail("_Role object should not save without ACL.");
|
||||
}, (error2) =>{
|
||||
expect(error2.code).toEqual(111);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -54,6 +54,11 @@ describe('Parse.User testing', () => {
|
||||
success: function(user) {
|
||||
Parse.User.logIn("non_existent_user", "asdf3",
|
||||
expectError(Parse.Error.OBJECT_NOT_FOUND, done));
|
||||
},
|
||||
error: function(err) {
|
||||
console.error(err);
|
||||
fail("Shit should not fail");
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1026,6 +1031,32 @@ describe('Parse.User testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("login with provider should not call beforeSave trigger", (done) => {
|
||||
var provider = getMockFacebookProvider();
|
||||
Parse.User._registerAuthenticationProvider(provider);
|
||||
Parse.User._logInWith("facebook", {
|
||||
success: function(model) {
|
||||
Parse.User.logOut();
|
||||
|
||||
Parse.Cloud.beforeSave(Parse.User, function(req, res) {
|
||||
res.error("Before save shouldn't be called on login");
|
||||
});
|
||||
|
||||
Parse.User._logInWith("facebook", {
|
||||
success: function(innerModel) {
|
||||
Parse.Cloud._removeHook('Triggers', 'beforeSave', Parse.User.className);
|
||||
done();
|
||||
},
|
||||
error: function(model, error) {
|
||||
ok(undefined, error);
|
||||
Parse.Cloud._removeHook('Triggers', 'beforeSave', Parse.User.className);
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("link with provider", (done) => {
|
||||
var provider = getMockFacebookProvider();
|
||||
Parse.User._registerAuthenticationProvider(provider);
|
||||
@@ -1678,7 +1709,7 @@ describe('Parse.User testing', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('test parse user become', (done) => {
|
||||
var sessionToken = null;
|
||||
Parse.Promise.as().then(function() {
|
||||
@@ -1732,5 +1763,22 @@ describe('Parse.User testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("session expiresAt correct format", (done) => {
|
||||
Parse.User.signUp("asdf", "zxcv", null, {
|
||||
success: function(user) {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/classes/_Session',
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-Master-Key': 'test',
|
||||
},
|
||||
}, (error, response, body) => {
|
||||
expect(body.results[0].expiresAt.__type).toEqual('Date');
|
||||
done();
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
26
spec/PromiseRouter.spec.js
Normal file
26
spec/PromiseRouter.spec.js
Normal file
@@ -0,0 +1,26 @@
|
||||
var PromiseRouter = require("../src/PromiseRouter").default;
|
||||
|
||||
describe("PromiseRouter", () => {
|
||||
|
||||
it("should properly handle rejects", (done) => {
|
||||
var router = new PromiseRouter();
|
||||
router.route("GET", "/dummy", (req)=> {
|
||||
return Promise.reject({
|
||||
error: "an error",
|
||||
code: -1
|
||||
})
|
||||
}, (req) => {
|
||||
fail("this should not be called");
|
||||
});
|
||||
|
||||
router.routes[0].handler({}).then((result) => {
|
||||
console.error(result);
|
||||
fail("this should not be called");
|
||||
done();
|
||||
}, (error)=> {
|
||||
expect(error.error).toEqual("an error");
|
||||
expect(error.code).toEqual(-1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
86
spec/PublicAPI.spec.js
Normal file
86
spec/PublicAPI.spec.js
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
var request = require('request');
|
||||
|
||||
describe("public API", () => {
|
||||
beforeEach(done => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
publicServerURL: 'http://localhost:8378/1'
|
||||
});
|
||||
done();
|
||||
})
|
||||
it("should get invalid_link.html", (done) => {
|
||||
request('http://localhost:8378/1/apps/invalid_link.html', (err, httpResponse, body) => {
|
||||
expect(httpResponse.statusCode).toBe(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should get choose_password", (done) => {
|
||||
request('http://localhost:8378/1/apps/choose_password?id=test', (err, httpResponse, body) => {
|
||||
expect(httpResponse.statusCode).toBe(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should get verify_email_success.html", (done) => {
|
||||
request('http://localhost:8378/1/apps/verify_email_success.html', (err, httpResponse, body) => {
|
||||
expect(httpResponse.statusCode).toBe(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should get password_reset_success.html", (done) => {
|
||||
request('http://localhost:8378/1/apps/password_reset_success.html', (err, httpResponse, body) => {
|
||||
expect(httpResponse.statusCode).toBe(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("public API without publicServerURL", () => {
|
||||
beforeEach(done => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
});
|
||||
done();
|
||||
})
|
||||
it("should get 404 on verify_email", (done) => {
|
||||
request('http://localhost:8378/1/apps/test/verify_email', (err, httpResponse, body) => {
|
||||
expect(httpResponse.statusCode).toBe(404);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should get 404 choose_password", (done) => {
|
||||
request('http://localhost:8378/1/apps/choose_password?id=test', (err, httpResponse, body) => {
|
||||
expect(httpResponse.statusCode).toBe(404);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should get 404 on request_password_reset", (done) => {
|
||||
request('http://localhost:8378/1/apps/test/request_password_reset', (err, httpResponse, body) => {
|
||||
expect(httpResponse.statusCode).toBe(404);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,7 @@
|
||||
var PushController = require('../src/Controllers/PushController').PushController;
|
||||
|
||||
var Config = require('../src/Config');
|
||||
|
||||
describe('PushController', () => {
|
||||
it('can check valid master key of request', (done) => {
|
||||
// Make mock request
|
||||
@@ -127,5 +129,121 @@ describe('PushController', () => {
|
||||
}).toThrow();
|
||||
done();
|
||||
});
|
||||
|
||||
it('properly increment badges', (done) => {
|
||||
|
||||
var payload = {
|
||||
alert: "Hello World!",
|
||||
badge: "Increment",
|
||||
}
|
||||
var installations = [];
|
||||
while(installations.length != 10) {
|
||||
var installation = new Parse.Object("_Installation");
|
||||
installation.set("installationId", "installation_"+installations.length);
|
||||
installation.set("deviceToken","device_token_"+installations.length)
|
||||
installation.set("badge", installations.length);
|
||||
installation.set("originalBadge", installations.length);
|
||||
installation.set("deviceType", "ios");
|
||||
installations.push(installation);
|
||||
}
|
||||
|
||||
while(installations.length != 15) {
|
||||
var installation = new Parse.Object("_Installation");
|
||||
installation.set("installationId", "installation_"+installations.length);
|
||||
installation.set("deviceToken","device_token_"+installations.length)
|
||||
installation.set("deviceType", "android");
|
||||
installations.push(installation);
|
||||
}
|
||||
|
||||
var pushAdapter = {
|
||||
send: function(body, installations) {
|
||||
var badge = body.badge;
|
||||
installations.forEach((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({
|
||||
body: body,
|
||||
installations: installations
|
||||
})
|
||||
},
|
||||
getValidPushTypes: function() {
|
||||
return ["ios", "android"];
|
||||
}
|
||||
}
|
||||
|
||||
var config = new Config(Parse.applicationId);
|
||||
var auth = {
|
||||
isMaster: true
|
||||
}
|
||||
|
||||
var pushController = new PushController(pushAdapter, Parse.applicationId);
|
||||
Parse.Object.saveAll(installations).then((installations) => {
|
||||
return pushController.sendPush(payload, {}, config, auth);
|
||||
}).then((result) => {
|
||||
done();
|
||||
}, (err) => {
|
||||
console.error(err);
|
||||
fail("should not fail");
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('properly set badges to 1', (done) => {
|
||||
|
||||
var payload = {
|
||||
alert: "Hello World!",
|
||||
badge: 1,
|
||||
}
|
||||
var installations = [];
|
||||
while(installations.length != 10) {
|
||||
var installation = new Parse.Object("_Installation");
|
||||
installation.set("installationId", "installation_"+installations.length);
|
||||
installation.set("deviceToken","device_token_"+installations.length)
|
||||
installation.set("badge", installations.length);
|
||||
installation.set("originalBadge", installations.length);
|
||||
installation.set("deviceType", "ios");
|
||||
installations.push(installation);
|
||||
}
|
||||
|
||||
var pushAdapter = {
|
||||
send: function(body, installations) {
|
||||
var badge = body.badge;
|
||||
installations.forEach((installation) => {
|
||||
expect(installation.badge).toEqual(badge);
|
||||
expect(1).toEqual(installation.badge);
|
||||
})
|
||||
return Promise.resolve({
|
||||
body: body,
|
||||
installations: installations
|
||||
})
|
||||
},
|
||||
getValidPushTypes: function() {
|
||||
return ["ios"];
|
||||
}
|
||||
}
|
||||
|
||||
var config = new Config(Parse.applicationId);
|
||||
var auth = {
|
||||
isMaster: true
|
||||
}
|
||||
|
||||
var pushController = new PushController(pushAdapter, Parse.applicationId);
|
||||
Parse.Object.saveAll(installations).then((installations) => {
|
||||
return pushController.sendPush(payload, {}, config, auth);
|
||||
}).then((result) => {
|
||||
done();
|
||||
}, (err) => {
|
||||
console.error(err);
|
||||
fail("should not fail");
|
||||
done();
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var Config = require('../src/Config');
|
||||
var Schema = require('../src/Schema');
|
||||
var dd = require('deep-diff');
|
||||
@@ -186,8 +188,8 @@ describe('Schema', () => {
|
||||
foo: {type: 'String'}
|
||||
}))
|
||||
.catch(error => {
|
||||
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME)
|
||||
expect(error.error).toEqual('class NewClass already exists');
|
||||
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||
expect(error.message).toEqual('Class NewClass already exists.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -214,7 +216,7 @@ describe('Schema', () => {
|
||||
Promise.all([p1,p2])
|
||||
.catch(error => {
|
||||
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||
expect(error.error).toEqual('class NewClass already exists');
|
||||
expect(error.message).toEqual('Class NewClass already exists.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -420,6 +422,43 @@ describe('Schema', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('creates non-custom classes which include relation field', done => {
|
||||
config.database.loadSchema()
|
||||
.then(schema => schema.addClassIfNotExists('_Role', {}))
|
||||
.then(mongoObj => {
|
||||
expect(mongoObj).toEqual({
|
||||
_id: '_Role',
|
||||
createdAt: 'string',
|
||||
updatedAt: 'string',
|
||||
objectId: 'string',
|
||||
name: 'string',
|
||||
users: 'relation<_User>',
|
||||
roles: 'relation<_Role>',
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('creates non-custom classes which include pointer field', done => {
|
||||
config.database.loadSchema()
|
||||
.then(schema => schema.addClassIfNotExists('_Session', {}))
|
||||
.then(mongoObj => {
|
||||
expect(mongoObj).toEqual({
|
||||
_id: '_Session',
|
||||
createdAt: 'string',
|
||||
updatedAt: 'string',
|
||||
objectId: 'string',
|
||||
restricted: 'boolean',
|
||||
user: '*_User',
|
||||
installationId: 'string',
|
||||
sessionToken: 'string',
|
||||
expiresAt: 'date',
|
||||
createdWith: 'object'
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('refuses to create two geopoints', done => {
|
||||
config.database.loadSchema()
|
||||
.then(schema => schema.addClassIfNotExists('NewClass', {
|
||||
@@ -483,7 +522,7 @@ describe('Schema', () => {
|
||||
.then(schema => schema.deleteField('installationId', '_Installation'))
|
||||
.catch(error => {
|
||||
expect(error.code).toEqual(136);
|
||||
expect(error.error).toEqual('field installationId cannot be changed');
|
||||
expect(error.message).toEqual('field installationId cannot be changed');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -493,7 +532,7 @@ describe('Schema', () => {
|
||||
.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');
|
||||
expect(error.message).toEqual('Class NoClass does not exist.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -504,7 +543,7 @@ describe('Schema', () => {
|
||||
.then(schema => schema.deleteField('missingField', 'HasAllPOD'))
|
||||
.fail(error => {
|
||||
expect(error.code).toEqual(255);
|
||||
expect(error.error).toEqual('field missingField does not exist, cannot delete');
|
||||
expect(error.message).toEqual('Field missingField does not exist, cannot delete.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -512,24 +551,32 @@ describe('Schema', () => {
|
||||
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();
|
||||
}))
|
||||
.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.collectionExists('_Join:aRelation:HasPointersAndRelations'))
|
||||
.then(exists => {
|
||||
if (!exists) {
|
||||
fail('Relation collection ' +
|
||||
'should exist after save.');
|
||||
}
|
||||
})
|
||||
.then(() => config.database.loadSchema())
|
||||
.then(schema => schema.deleteField('aRelation', 'HasPointersAndRelations', config.database))
|
||||
.then(() => config.database.collectionExists('_Join:aRelation:HasPointersAndRelations'))
|
||||
.then(exists => {
|
||||
if (exists) {
|
||||
fail('Relation collection should not exist after deleting relation field.');
|
||||
}
|
||||
done();
|
||||
}, error => {
|
||||
fail(error);
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
it('can delete string fields and resave as number field', done => {
|
||||
@@ -538,7 +585,7 @@ describe('Schema', () => {
|
||||
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(schema => schema.deleteField('aString', 'HasAllPOD', config.database))
|
||||
.then(() => new Parse.Query('HasAllPOD').get(obj1.id))
|
||||
.then(obj1Reloaded => {
|
||||
expect(obj1Reloaded.get('aString')).toEqual(undefined);
|
||||
@@ -568,7 +615,7 @@ describe('Schema', () => {
|
||||
expect(obj1.get('aPointer').id).toEqual(obj1.id);
|
||||
})
|
||||
.then(() => config.database.loadSchema())
|
||||
.then(schema => schema.deleteField('aPointer', 'NewClass', config.database.db, 'test_'))
|
||||
.then(schema => schema.deleteField('aPointer', 'NewClass', config.database))
|
||||
.then(() => new Parse.Query('NewClass').get(obj1.id))
|
||||
.then(obj1 => {
|
||||
expect(obj1.get('aPointer')).toEqual(undefined);
|
||||
@@ -609,4 +656,21 @@ describe('Schema', () => {
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
it('ignore default field when merge with system class', done => {
|
||||
expect(Schema.buildMergedSchemaObject({
|
||||
_id: '_User',
|
||||
username: 'string',
|
||||
password: 'string',
|
||||
authData: 'object',
|
||||
email: 'string',
|
||||
emailVerified: 'boolean'
|
||||
},{
|
||||
authData: {type: 'string'},
|
||||
customField: {type: 'string'},
|
||||
})).toEqual({
|
||||
customField: {type: 'string'}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
618
spec/ValidationAndPasswordsReset.spec.js
Normal file
618
spec/ValidationAndPasswordsReset.spec.js
Normal file
@@ -0,0 +1,618 @@
|
||||
"use strict";
|
||||
|
||||
var request = require('request');
|
||||
var Config = require("../src/Config");
|
||||
describe("Custom Pages Configuration", () => {
|
||||
it("should set the custom pages", (done) => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
customPages: {
|
||||
invalidLink: "myInvalidLink",
|
||||
verifyEmailSuccess: "myVerifyEmailSuccess",
|
||||
choosePassword: "myChoosePassword",
|
||||
passwordResetSuccess: "myPasswordResetSuccess"
|
||||
},
|
||||
publicServerURL: "https://my.public.server.com/1"
|
||||
});
|
||||
|
||||
var config = new Config("test");
|
||||
|
||||
expect(config.invalidLinkURL).toEqual("myInvalidLink");
|
||||
expect(config.verifyEmailSuccessURL).toEqual("myVerifyEmailSuccess");
|
||||
expect(config.choosePasswordURL).toEqual("myChoosePassword");
|
||||
expect(config.passwordResetSuccessURL).toEqual("myPasswordResetSuccess");
|
||||
expect(config.verifyEmailURL).toEqual("https://my.public.server.com/1/apps/test/verify_email");
|
||||
expect(config.requestResetPasswordURL).toEqual("https://my.public.server.com/1/apps/test/request_password_reset");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Email Verification", () => {
|
||||
it('sends verification email if email verification is enabled', done => {
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => Promise.resolve()
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
spyOn(emailAdapter, 'sendVerificationEmail');
|
||||
var user = new Parse.User();
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.setEmail('cool_guy@parse.com');
|
||||
user.signUp(null, {
|
||||
success: function(user) {
|
||||
expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled();
|
||||
user.fetch()
|
||||
.then(() => {
|
||||
expect(user.get('emailVerified')).toEqual(false);
|
||||
done();
|
||||
});
|
||||
},
|
||||
error: function(userAgain, error) {
|
||||
fail('Failed to save user');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('does not send verification email when verification is enabled and email is not set', done => {
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => Promise.resolve()
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
spyOn(emailAdapter, 'sendVerificationEmail');
|
||||
var user = new Parse.User();
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.signUp(null, {
|
||||
success: function(user) {
|
||||
expect(emailAdapter.sendVerificationEmail).not.toHaveBeenCalled();
|
||||
user.fetch()
|
||||
.then(() => {
|
||||
expect(user.get('emailVerified')).toEqual(undefined);
|
||||
done();
|
||||
});
|
||||
},
|
||||
error: function(userAgain, error) {
|
||||
fail('Failed to save user');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('does send a validation email when updating the email', done => {
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => Promise.resolve()
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
spyOn(emailAdapter, 'sendVerificationEmail');
|
||||
var user = new Parse.User();
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.signUp(null, {
|
||||
success: function(user) {
|
||||
expect(emailAdapter.sendVerificationEmail).not.toHaveBeenCalled();
|
||||
user.fetch()
|
||||
.then((user) => {
|
||||
user.set("email", "cool_guy@parse.com");
|
||||
return user.save();
|
||||
}).then((user) => {
|
||||
return user.fetch();
|
||||
}).then(() => {
|
||||
expect(user.get('emailVerified')).toEqual(false);
|
||||
// Wait as on update emai, we need to fetch the username
|
||||
setTimeout(function(){
|
||||
expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled();
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
},
|
||||
error: function(userAgain, error) {
|
||||
fail('Failed to save user');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('does send with a simple adapter', done => {
|
||||
var calls = 0;
|
||||
var emailAdapter = {
|
||||
sendMail: function(options){
|
||||
expect(options.to).toBe('cool_guy@parse.com');
|
||||
if (calls == 0) {
|
||||
expect(options.subject).toEqual('Please verify your e-mail for My Cool App');
|
||||
expect(options.text.match(/verify_email/)).not.toBe(null);
|
||||
} else if (calls == 1) {
|
||||
expect(options.subject).toEqual('Password Reset for My Cool App');
|
||||
expect(options.text.match(/request_password_reset/)).not.toBe(null);
|
||||
}
|
||||
calls++;
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'My Cool App',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
var user = new Parse.User();
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.set("email", "cool_guy@parse.com");
|
||||
user.signUp(null, {
|
||||
success: function(user) {
|
||||
expect(calls).toBe(1);
|
||||
user.fetch()
|
||||
.then((user) => {
|
||||
return user.save();
|
||||
}).then((user) => {
|
||||
return Parse.User.requestPasswordReset("cool_guy@parse.com");
|
||||
}).then(() => {
|
||||
expect(calls).toBe(2);
|
||||
done();
|
||||
});
|
||||
},
|
||||
error: function(userAgain, error) {
|
||||
fail('Failed to save user');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('does not send verification email if email verification is disabled', done => {
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => Promise.resolve()
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: false,
|
||||
emailAdapter: emailAdapter,
|
||||
});
|
||||
spyOn(emailAdapter, 'sendVerificationEmail');
|
||||
var user = new Parse.User();
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.signUp(null, {
|
||||
success: function(user) {
|
||||
user.fetch()
|
||||
.then(() => {
|
||||
expect(emailAdapter.sendVerificationEmail.calls.count()).toEqual(0);
|
||||
expect(user.get('emailVerified')).toEqual(undefined);
|
||||
done();
|
||||
});
|
||||
},
|
||||
error: function(userAgain, error) {
|
||||
fail('Failed to save user');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('receives the app name and user in the adapter', done => {
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
expect(options.appName).toEqual('emailing app');
|
||||
expect(options.user.get('email')).toEqual('user@parse.com');
|
||||
done();
|
||||
},
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => {}
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'emailing app',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
var user = new Parse.User();
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.set('email', 'user@parse.com');
|
||||
user.signUp(null, {
|
||||
success: () => {},
|
||||
error: function(userAgain, error) {
|
||||
fail('Failed to save user');
|
||||
done();
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
it('when you click the link in the email it sets emailVerified to true and redirects you', done => {
|
||||
var user = new Parse.User();
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
request.get(options.link, {
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(302);
|
||||
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html?username=zxcv');
|
||||
user.fetch()
|
||||
.then(() => {
|
||||
expect(user.get('emailVerified')).toEqual(true);
|
||||
done();
|
||||
}, (err) => {
|
||||
console.error(err);
|
||||
fail("this should not fail");
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => {}
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'emailing app',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.set('email', 'user@parse.com');
|
||||
user.signUp();
|
||||
});
|
||||
|
||||
it('redirects you to invalid link if you try to verify email incorrecly', done => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'emailing app',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => {}
|
||||
},
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
request.get('http://localhost:8378/1/apps/test/verify_email', {
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(302);
|
||||
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
|
||||
done()
|
||||
});
|
||||
});
|
||||
|
||||
it('redirects you to invalid link if you try to validate a nonexistant users email', done => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'emailing app',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => {}
|
||||
},
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
request.get('http://localhost:8378/1/apps/test/verify_email?token=asdfasdf&username=sadfasga', {
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(302);
|
||||
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not update email verified if you use an invalid token', done => {
|
||||
var user = new Parse.User();
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
request.get('http://localhost:8378/1/apps/test/verify_email?token=invalid&username=zxcv', {
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(302);
|
||||
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
|
||||
user.fetch()
|
||||
.then(() => {
|
||||
expect(user.get('emailVerified')).toEqual(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => {}
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'emailing app',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.set('email', 'user@parse.com');
|
||||
user.signUp(null, {
|
||||
success: () => {},
|
||||
error: function(userAgain, error) {
|
||||
fail('Failed to save user');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Password Reset", () => {
|
||||
|
||||
it('should send a password reset link', done => {
|
||||
var user = new Parse.User();
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: options => {
|
||||
request.get(options.link, {
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
fail("Failed to get the reset link");
|
||||
return;
|
||||
}
|
||||
expect(response.statusCode).toEqual(302);
|
||||
var re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=[a-zA-Z0-9]+\&id=test\&username=zxcv/;
|
||||
expect(response.body.match(re)).not.toBe(null);
|
||||
done();
|
||||
});
|
||||
},
|
||||
sendMail: () => {}
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'emailing app',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.set('email', 'user@parse.com');
|
||||
user.signUp().then(() => {
|
||||
Parse.User.requestPasswordReset('user@parse.com', {
|
||||
error: (err) => {
|
||||
console.error(err);
|
||||
fail("Should not fail");
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('redirects you to invalid link if you try to request password for a nonexistant users email', done => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'emailing app',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
sendMail: () => {}
|
||||
},
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
request.get('http://localhost:8378/1/apps/test/request_password_reset?token=asdfasdf&username=sadfasga', {
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(302);
|
||||
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should programatically reset password', done => {
|
||||
var user = new Parse.User();
|
||||
var emailAdapter = {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: options => {
|
||||
request.get(options.link, {
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
fail("Failed to get the reset link");
|
||||
return;
|
||||
}
|
||||
expect(response.statusCode).toEqual(302);
|
||||
var re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&username=zxcv/;
|
||||
var match = response.body.match(re);
|
||||
if (!match) {
|
||||
fail("should have a token");
|
||||
done();
|
||||
return;
|
||||
}
|
||||
var token = match[1];
|
||||
|
||||
request.post({
|
||||
url: "http://localhost:8378/1/apps/test/request_password_reset" ,
|
||||
body: `new_password=hello&token=${token}&username=zxcv`,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
followRedirect: false,
|
||||
}, (error, response, body) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
fail("Failed to POST request password reset");
|
||||
return;
|
||||
}
|
||||
expect(response.statusCode).toEqual(302);
|
||||
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html');
|
||||
|
||||
Parse.User.logIn("zxcv", "hello").then(function(user){
|
||||
done();
|
||||
}, (err) => {
|
||||
console.error(err);
|
||||
fail("should login with new password");
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
},
|
||||
sendMail: () => {}
|
||||
}
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'emailing app',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: emailAdapter,
|
||||
publicServerURL: "http://localhost:8378/1"
|
||||
});
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
user.set('email', 'user@parse.com');
|
||||
user.signUp().then(() => {
|
||||
Parse.User.requestPasswordReset('user@parse.com', {
|
||||
error: (err) => {
|
||||
console.error(err);
|
||||
fail("Should not fail");
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
@@ -100,3 +100,11 @@ Parse.Cloud.define('requiredParameterCheck', function(req, res) {
|
||||
}, function(params) {
|
||||
return params.name;
|
||||
});
|
||||
|
||||
Parse.Cloud.define('echoKeys', function(req, res){
|
||||
return res.success({
|
||||
applicationId: Parse.applicationId,
|
||||
masterKey: Parse.masterKey,
|
||||
javascriptKey: Parse.javascriptKey
|
||||
})
|
||||
});
|
||||
|
||||
44
spec/features.spec.js
Normal file
44
spec/features.spec.js
Normal file
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
var features = require('../src/features');
|
||||
const request = require("request");
|
||||
|
||||
describe('features', () => {
|
||||
it('set and get features', (done) => {
|
||||
features.setFeature('push', {
|
||||
testOption1: true,
|
||||
testOption2: false
|
||||
});
|
||||
|
||||
var _features = features.getFeatures();
|
||||
|
||||
var expected = {
|
||||
testOption1: true,
|
||||
testOption2: false
|
||||
};
|
||||
|
||||
expect(_features.push).toEqual(expected);
|
||||
done();
|
||||
});
|
||||
|
||||
it('get features that does not exist', (done) => {
|
||||
var _features = features.getFeatures();
|
||||
expect(_features.test).toBeUndefined();
|
||||
done();
|
||||
});
|
||||
|
||||
it('requires the master key to get all schemas', done => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/serverInfo',
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest'
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(403);
|
||||
expect(body.error).toEqual('unauthorized: master key is required');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -52,13 +52,13 @@ delete defaultConfiguration.cloud;
|
||||
|
||||
// Allows testing specific configurations of Parse Server
|
||||
var setServerConfiguration = configuration => {
|
||||
api = new ParseServer(configuration);
|
||||
app = express();
|
||||
app.use('/1', api);
|
||||
cache.clearCache();
|
||||
server.close();
|
||||
cache.clearCache();
|
||||
app = express();
|
||||
api = new ParseServer(configuration);
|
||||
app.use('/1', api);
|
||||
server = app.listen(port);
|
||||
}
|
||||
};
|
||||
|
||||
var restoreServerConfiguration = () => setServerConfiguration(defaultConfiguration);
|
||||
|
||||
@@ -250,3 +250,4 @@ global.arrayContains = arrayContains;
|
||||
global.jequal = jequal;
|
||||
global.range = range;
|
||||
global.setServerConfiguration = setServerConfiguration;
|
||||
global.defaultConfiguration = defaultConfiguration;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
var request = require('request');
|
||||
var parseServerPackage = require('../package.json');
|
||||
var MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
|
||||
|
||||
describe('server', () => {
|
||||
it('requires a master key and app id', done => {
|
||||
@@ -37,4 +39,133 @@ describe('server', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can load email adapter via object', done => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: MockEmailAdapterWithOptions({
|
||||
apiKey: 'k',
|
||||
domain: 'd',
|
||||
}),
|
||||
publicServerURL: 'http://localhost:8378/1'
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
it('can load email adapter via class', done => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: {
|
||||
class: MockEmailAdapterWithOptions,
|
||||
options: {
|
||||
apiKey: 'k',
|
||||
domain: 'd',
|
||||
}
|
||||
},
|
||||
publicServerURL: 'http://localhost:8378/1'
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
it('can load email adapter via module name', done => {
|
||||
setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: {
|
||||
module: './Email/SimpleMailgunAdapter',
|
||||
options: {
|
||||
apiKey: 'k',
|
||||
domain: 'd',
|
||||
}
|
||||
},
|
||||
publicServerURL: 'http://localhost:8378/1'
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
it('can load email adapter via only module name', done => {
|
||||
expect(() => setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: './Email/SimpleMailgunAdapter',
|
||||
publicServerURL: 'http://localhost:8378/1'
|
||||
})).toThrow('SimpleMailgunAdapter requires an API Key and domain.');
|
||||
done();
|
||||
});
|
||||
|
||||
it('throws if you initialize email adapter incorrecly', done => {
|
||||
expect(() => setServerConfiguration({
|
||||
serverURL: 'http://localhost:8378/1',
|
||||
appId: 'test',
|
||||
appName: 'unused',
|
||||
javascriptKey: 'test',
|
||||
dotNetKey: 'windows',
|
||||
clientKey: 'client',
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test',
|
||||
verifyUserEmails: true,
|
||||
emailAdapter: {
|
||||
module: './Email/SimpleMailgunAdapter',
|
||||
options: {
|
||||
domain: 'd',
|
||||
}
|
||||
},
|
||||
publicServerURL: 'http://localhost:8378/1'
|
||||
})).toThrow('SimpleMailgunAdapter requires an API Key and domain.');
|
||||
done();
|
||||
});
|
||||
|
||||
it('can report the server version', done => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/serverInfo',
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-Master-Key': 'test',
|
||||
},
|
||||
json: true,
|
||||
}, (error, response, body) => {
|
||||
expect(body.parseServerVersion).toEqual(parseServerPackage.version);
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var Parse = require('parse/node').Parse;
|
||||
var request = require('request');
|
||||
var dd = require('deep-diff');
|
||||
@@ -96,8 +98,8 @@ describe('schemas', () => {
|
||||
json: true,
|
||||
headers: restKeyHeaders,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(body.error).toEqual('master key not specified');
|
||||
expect(response.statusCode).toEqual(403);
|
||||
expect(body.error).toEqual('unauthorized: master key is required');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -108,8 +110,8 @@ describe('schemas', () => {
|
||||
json: true,
|
||||
headers: restKeyHeaders,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(body.error).toEqual('master key not specified');
|
||||
expect(response.statusCode).toEqual(403);
|
||||
expect(body.error).toEqual('unauthorized: master key is required');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -173,7 +175,7 @@ describe('schemas', () => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body).toEqual({
|
||||
code: 103,
|
||||
error: 'class HASALLPOD does not exist',
|
||||
error: 'Class HASALLPOD does not exist.',
|
||||
});
|
||||
done();
|
||||
});
|
||||
@@ -204,8 +206,8 @@ describe('schemas', () => {
|
||||
className: 'MyClass',
|
||||
},
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(body.error).toEqual('master key not specified');
|
||||
expect(response.statusCode).toEqual(403);
|
||||
expect(body.error).toEqual('unauthorized: master key is required');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -222,7 +224,7 @@ describe('schemas', () => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body).toEqual({
|
||||
code: Parse.Error.INVALID_CLASS_NAME,
|
||||
error: 'class name mismatch between B and A',
|
||||
error: 'Class name mismatch between B and A.',
|
||||
});
|
||||
done();
|
||||
});
|
||||
@@ -238,7 +240,7 @@ describe('schemas', () => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body).toEqual({
|
||||
code: 135,
|
||||
error: 'POST /schemas needs class name',
|
||||
error: 'POST /schemas needs a class name.',
|
||||
});
|
||||
done();
|
||||
})
|
||||
@@ -265,7 +267,7 @@ describe('schemas', () => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body).toEqual({
|
||||
code: Parse.Error.INVALID_CLASS_NAME,
|
||||
error: 'class A already exists',
|
||||
error: 'Class A already exists.'
|
||||
});
|
||||
done();
|
||||
});
|
||||
@@ -351,7 +353,7 @@ describe('schemas', () => {
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||
expect(body.error).toEqual('class name mismatch between WrongClassName and NewClass');
|
||||
expect(body.error).toEqual('Class name mismatch between WrongClassName and NewClass.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -369,7 +371,7 @@ describe('schemas', () => {
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||
expect(body.error).toEqual('class NoClass does not exist');
|
||||
expect(body.error).toEqual('Class NoClass does not exist.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -390,13 +392,13 @@ describe('schemas', () => {
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(255);
|
||||
expect(body.error).toEqual('field aString exists, cannot update');
|
||||
expect(body.error).toEqual('Field aString exists, cannot update.');
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
it('refuses to delete non-existant fields', done => {
|
||||
it('refuses to delete non-existent fields', done => {
|
||||
var obj = hasAllPODobject();
|
||||
obj.save()
|
||||
.then(() => {
|
||||
@@ -406,13 +408,13 @@ describe('schemas', () => {
|
||||
json: true,
|
||||
body: {
|
||||
fields: {
|
||||
nonExistantKey: {__op: "Delete"},
|
||||
nonExistentKey: {__op: "Delete"},
|
||||
}
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(255);
|
||||
expect(body.error).toEqual('field nonExistantKey does not exist, cannot delete');
|
||||
expect(body.error).toEqual('Field nonExistentKey does not exist, cannot delete.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -660,7 +662,8 @@ describe('schemas', () => {
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(255);
|
||||
expect(body.error).toEqual('class HasAllPOD not empty, contains 1 objects, cannot drop schema');
|
||||
expect(body.error).toMatch(/HasAllPOD/);
|
||||
expect(body.error).toMatch(/contains 1/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -710,28 +713,106 @@ describe('schemas', () => {
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.body).toEqual({});
|
||||
config.database.db.collection('test__Join:aRelation:MyOtherClass', { strict: true }, (err, coll) => {
|
||||
//Expect Join table to be gone
|
||||
expect(err).not.toEqual(null);
|
||||
config.database.db.collection('test_MyOtherClass', { strict: true }, (err, coll) => {
|
||||
// Expect data table to be gone
|
||||
expect(err).not.toEqual(null);
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/schemas/MyOtherClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
}, (error, response, body) => {
|
||||
//Expect _SCHEMA entry to be gone.
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||
expect(body.error).toEqual('class MyOtherClass does not exist');
|
||||
done();
|
||||
config.database.collectionExists('_Join:aRelation:MyOtherClass').then(exists => {
|
||||
if (exists) {
|
||||
fail('Relation collection should be deleted.');
|
||||
done();
|
||||
}
|
||||
return config.database.collectionExists('MyOtherClass');
|
||||
}).then(exists => {
|
||||
if (exists) {
|
||||
fail('Class collection should be deleted.');
|
||||
done();
|
||||
}
|
||||
}).then(() => {
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/schemas/MyOtherClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
}, (error, response, body) => {
|
||||
//Expect _SCHEMA entry to be gone.
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||
expect(body.error).toEqual('Class MyOtherClass does not exist.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}).then(() => {
|
||||
}, error => {
|
||||
fail(error);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes schema when actual collection does not exist', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas/NewClassForDelete',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
className: 'NewClassForDelete'
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(error).toEqual(null);
|
||||
expect(response.body.className).toEqual('NewClassForDelete');
|
||||
request.del({
|
||||
url: 'http://localhost:8378/1/schemas/NewClassForDelete',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.body).toEqual({});
|
||||
config.database.loadSchema().then(schema => {
|
||||
schema.hasClass('NewClassForDelete').then(exist => {
|
||||
expect(exist).toEqual(false);
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes schema when actual collection exists', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas/NewClassForDelete',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
className: 'NewClassForDelete'
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(error).toEqual(null);
|
||||
expect(response.body.className).toEqual('NewClassForDelete');
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/classes/NewClassForDelete',
|
||||
headers: restKeyHeaders,
|
||||
json: true
|
||||
}, (error, response, body) => {
|
||||
expect(error).toEqual(null);
|
||||
expect(typeof response.body.objectId).toEqual('string');
|
||||
request.del({
|
||||
url: 'http://localhost:8378/1/classes/NewClassForDelete/' + response.body.objectId,
|
||||
headers: restKeyHeaders,
|
||||
json: true,
|
||||
}, (error, response, body) => {
|
||||
expect(error).toEqual(null);
|
||||
request.del({
|
||||
url: 'http://localhost:8378/1/schemas/NewClassForDelete',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.body).toEqual({});
|
||||
config.database.loadSchema().then(schema => {
|
||||
schema.hasClass('NewClassForDelete').then(exist => {
|
||||
expect(exist).toEqual(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}, error => {
|
||||
fail(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user