removes job status object, moves messasge method on req object Adds 3.0.0 migration guide Fixes nits about 3.0.0 documentation Adds update guide to README
1570 lines
48 KiB
JavaScript
1570 lines
48 KiB
JavaScript
// A bunch of different tests are in here - it isn't very thematic.
|
|
// It would probably be better to refactor them into different files.
|
|
'use strict';
|
|
|
|
const request = require('request');
|
|
const rp = require('request-promise');
|
|
const Parse = require("parse/node");
|
|
const Config = require('../lib/Config');
|
|
const SchemaController = require('../lib/Controllers/SchemaController');
|
|
const TestUtils = require('../lib/TestUtils');
|
|
|
|
const userSchema = SchemaController.convertSchemaToAdapterSchema({ className: '_User', fields: Object.assign({}, SchemaController.defaultColumns._Default, SchemaController.defaultColumns._User) });
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-REST-API-Key': 'rest',
|
|
'X-Parse-Installation-Id': 'yolo'
|
|
}
|
|
|
|
describe_only_db('mongo')('miscellaneous', () => {
|
|
it('test rest_create_app', function(done) {
|
|
let appId;
|
|
Parse._request('POST', 'rest_create_app').then((res) => {
|
|
expect(typeof res.application_id).toEqual('string');
|
|
expect(res.master_key).toEqual('master');
|
|
appId = res.application_id;
|
|
Parse.initialize(appId, 'unused');
|
|
const obj = new Parse.Object('TestObject');
|
|
obj.set('foo', 'bar');
|
|
return obj.save();
|
|
}).then(() => {
|
|
const config = Config.get(appId);
|
|
return config.database.adapter.find('TestObject', { fields: {} }, {}, {});
|
|
}).then((results) => {
|
|
expect(results.length).toEqual(1);
|
|
expect(results[0]['foo']).toEqual('bar');
|
|
done();
|
|
}).catch(error => {
|
|
fail(JSON.stringify(error));
|
|
done();
|
|
})
|
|
});
|
|
})
|
|
|
|
describe('miscellaneous', function() {
|
|
it('create a GameScore object', function(done) {
|
|
const obj = new Parse.Object('GameScore');
|
|
obj.set('score', 1337);
|
|
obj.save().then(function(obj) {
|
|
expect(typeof obj.id).toBe('string');
|
|
expect(typeof obj.createdAt.toGMTString()).toBe('string');
|
|
done();
|
|
}, done.fail);
|
|
});
|
|
|
|
it('get a TestObject', function(done) {
|
|
create({ 'bloop' : 'blarg' }, async function(obj) {
|
|
const t2 = new TestObject({ objectId: obj.id });
|
|
const obj2 = await t2.fetch();
|
|
expect(obj2.get('bloop')).toEqual('blarg');
|
|
expect(obj2.id).toBeTruthy();
|
|
expect(obj2.id).toEqual(obj.id);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('create a valid parse user', function(done) {
|
|
createTestUser().then(function(data) {
|
|
expect(data.id).not.toBeUndefined();
|
|
expect(data.getSessionToken()).not.toBeUndefined();
|
|
expect(data.get('password')).toBeUndefined();
|
|
done();
|
|
}, done.fail);
|
|
});
|
|
|
|
it('fail to create a duplicate username', async () => {
|
|
let numFailed = 0;
|
|
let numCreated = 0;
|
|
const p1 = rp.post(Parse.serverURL + '/users', {
|
|
json: {
|
|
password: 'asdf',
|
|
username: 'u1',
|
|
email: 'dupe@dupe.dupe'
|
|
},
|
|
headers
|
|
}).then(() => {
|
|
numCreated++;
|
|
expect(numCreated).toEqual(1);
|
|
}, ({ error }) => {
|
|
numFailed++;
|
|
expect(error.code).toEqual(Parse.Error.USERNAME_TAKEN);
|
|
});
|
|
|
|
const p2 = rp.post(Parse.serverURL + '/users', {
|
|
json: {
|
|
password: 'otherpassword',
|
|
username: 'u1',
|
|
email: 'email@other.email'
|
|
},
|
|
headers
|
|
}).then(() => {
|
|
numCreated++;
|
|
}, ({ error }) => {
|
|
numFailed++;
|
|
expect(error.code).toEqual(Parse.Error.USERNAME_TAKEN);
|
|
});
|
|
|
|
await Promise.all([p1, p2])
|
|
expect(numFailed).toEqual(1);
|
|
expect(numCreated).toBe(1);
|
|
});
|
|
|
|
it('ensure that email is uniquely indexed', async () => {
|
|
let numFailed = 0;
|
|
let numCreated = 0;
|
|
const p1 = rp.post(Parse.serverURL + '/users', {
|
|
json: {
|
|
password: 'asdf',
|
|
username: 'u1',
|
|
email: 'dupe@dupe.dupe'
|
|
},
|
|
headers
|
|
}).then(() => {
|
|
numCreated++;
|
|
expect(numCreated).toEqual(1);
|
|
}, ({ error }) => {
|
|
numFailed++;
|
|
expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN);
|
|
});
|
|
|
|
const p2 = rp.post(Parse.serverURL + '/users', {
|
|
json: {
|
|
password: 'asdf',
|
|
username: 'u2',
|
|
email: 'dupe@dupe.dupe'
|
|
},
|
|
headers
|
|
}).then(() => {
|
|
numCreated++;
|
|
expect(numCreated).toEqual(1);
|
|
}, ({ error }) => {
|
|
numFailed++;
|
|
expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN);
|
|
});
|
|
|
|
await Promise.all([p1, p2])
|
|
expect(numFailed).toEqual(1);
|
|
expect(numCreated).toBe(1);
|
|
});
|
|
|
|
it('ensure that if people already have duplicate users, they can still sign up new users', async done => {
|
|
try {
|
|
await Parse.User.logOut();
|
|
} catch(e) { /* ignore */ }
|
|
const config = Config.get('test');
|
|
// Remove existing data to clear out unique index
|
|
TestUtils.destroyAllDataPermanently()
|
|
.then(() => config.database.adapter.createClass('_User', userSchema))
|
|
.then(() => config.database.adapter.createObject('_User', userSchema, { objectId: 'x', username: 'u' }).catch(fail))
|
|
.then(() => config.database.adapter.createObject('_User', userSchema, { objectId: 'y', username: 'u' }).catch(fail))
|
|
// Create a new server to try to recreate the unique indexes
|
|
.then(reconfigureServer)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE);
|
|
const user = new Parse.User();
|
|
user.setPassword('asdf');
|
|
user.setUsername('zxcv');
|
|
return user.signUp().catch(fail);
|
|
})
|
|
.then(() => {
|
|
const user = new Parse.User();
|
|
user.setPassword('asdf');
|
|
user.setUsername('u');
|
|
return user.signUp()
|
|
})
|
|
.then(() => {
|
|
fail('should not have been able to sign up');
|
|
done();
|
|
})
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.USERNAME_TAKEN);
|
|
done();
|
|
})
|
|
});
|
|
|
|
it('ensure that if people already have duplicate emails, they can still sign up new users', done => {
|
|
const config = Config.get('test');
|
|
// Remove existing data to clear out unique index
|
|
TestUtils.destroyAllDataPermanently()
|
|
.then(() => config.database.adapter.createClass('_User', userSchema))
|
|
.then(() => config.database.adapter.createObject('_User', userSchema, { objectId: 'x', email: 'a@b.c' }))
|
|
.then(() => config.database.adapter.createObject('_User', userSchema, { objectId: 'y', email: 'a@b.c' }))
|
|
.then(reconfigureServer)
|
|
.catch(() => {
|
|
const user = new Parse.User();
|
|
user.setPassword('asdf');
|
|
user.setUsername('qqq');
|
|
user.setEmail('unique@unique.unique');
|
|
return user.signUp().catch(fail);
|
|
})
|
|
.then(() => {
|
|
const user = new Parse.User();
|
|
user.setPassword('asdf');
|
|
user.setUsername('www');
|
|
user.setEmail('a@b.c');
|
|
return user.signUp()
|
|
})
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('ensure that if you try to sign up a user with a unique username and email, but duplicates in some other field that has a uniqueness constraint, you get a regular duplicate value error', done => {
|
|
const config = Config.get('test');
|
|
config.database.adapter.addFieldIfNotExists('_User', 'randomField', { type: 'String' })
|
|
.then(() => config.database.adapter.ensureUniqueness('_User', userSchema, ['randomField']))
|
|
.then(() => {
|
|
const user = new Parse.User();
|
|
user.setPassword('asdf');
|
|
user.setUsername('1');
|
|
user.setEmail('1@b.c');
|
|
user.set('randomField', 'a');
|
|
return user.signUp()
|
|
})
|
|
.then(() => {
|
|
const user = new Parse.User();
|
|
user.setPassword('asdf');
|
|
user.setUsername('2');
|
|
user.setEmail('2@b.c');
|
|
user.set('randomField', 'a');
|
|
return user.signUp()
|
|
})
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('succeed in logging in', function(done) {
|
|
createTestUser().then(async function(u) {
|
|
expect(typeof u.id).toEqual('string');
|
|
|
|
const user = await Parse.User.logIn('test', 'moon-y');
|
|
expect(typeof user.id).toEqual('string');
|
|
expect(user.get('password')).toBeUndefined();
|
|
expect(user.getSessionToken()).not.toBeUndefined();
|
|
await Parse.User.logOut();
|
|
done();
|
|
}, fail);
|
|
});
|
|
|
|
it('increment with a user object', function(done) {
|
|
createTestUser().then((user) => {
|
|
user.increment('foo');
|
|
return user.save();
|
|
}).then(() => {
|
|
return Parse.User.logIn('test', 'moon-y');
|
|
}).then((user) => {
|
|
expect(user.get('foo')).toEqual(1);
|
|
user.increment('foo');
|
|
return user.save();
|
|
}).then(() => Parse.User.logOut())
|
|
.then(() => Parse.User.logIn('test', 'moon-y'))
|
|
.then((user) => {
|
|
expect(user.get('foo')).toEqual(2);
|
|
Parse.User.logOut()
|
|
.then(done);
|
|
}, (error) => {
|
|
fail(JSON.stringify(error));
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('save various data types', function(done) {
|
|
const obj = new TestObject();
|
|
obj.set('date', new Date());
|
|
obj.set('array', [1, 2, 3]);
|
|
obj.set('object', {one: 1, two: 2});
|
|
obj.save().then(() => {
|
|
const obj2 = new TestObject({objectId: obj.id});
|
|
return obj2.fetch();
|
|
}).then((obj2) => {
|
|
expect(obj2.get('date') instanceof Date).toBe(true);
|
|
expect(obj2.get('array') instanceof Array).toBe(true);
|
|
expect(obj2.get('object') instanceof Array).toBe(false);
|
|
expect(obj2.get('object') instanceof Object).toBe(true);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('query with limit', function(done) {
|
|
const baz = new TestObject({ foo: 'baz' });
|
|
const qux = new TestObject({ foo: 'qux' });
|
|
baz.save().then(() => {
|
|
return qux.save();
|
|
}).then(() => {
|
|
const query = new Parse.Query(TestObject);
|
|
query.limit(1);
|
|
return query.find();
|
|
}).then((results) => {
|
|
expect(results.length).toEqual(1);
|
|
done();
|
|
}, (error) => {
|
|
fail(JSON.stringify(error));
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('query without limit get default 100 records', function(done) {
|
|
const objects = [];
|
|
for (let i = 0; i < 150; i++) {
|
|
objects.push(new TestObject({name: 'name' + i}));
|
|
}
|
|
Parse.Object.saveAll(objects).then(() => {
|
|
return new Parse.Query(TestObject).find();
|
|
}).then((results) => {
|
|
expect(results.length).toEqual(100);
|
|
done();
|
|
}, error => {
|
|
fail(JSON.stringify(error));
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('basic saveAll', function(done) {
|
|
const alpha = new TestObject({ letter: 'alpha' });
|
|
const beta = new TestObject({ letter: 'beta' });
|
|
Parse.Object.saveAll([alpha, beta]).then(() => {
|
|
expect(alpha.id).toBeTruthy();
|
|
expect(beta.id).toBeTruthy();
|
|
return new Parse.Query(TestObject).find();
|
|
}).then((results) => {
|
|
expect(results.length).toEqual(2);
|
|
done();
|
|
}, (error) => {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('test beforeSave set object acl success', function(done) {
|
|
const acl = new Parse.ACL({
|
|
'*': { read: true, write: false }
|
|
});
|
|
Parse.Cloud.beforeSave('BeforeSaveAddACL', function(req) {
|
|
req.object.setACL(acl);
|
|
});
|
|
|
|
const obj = new Parse.Object('BeforeSaveAddACL');
|
|
obj.set('lol', true);
|
|
obj.save().then(function() {
|
|
const query = new Parse.Query('BeforeSaveAddACL');
|
|
query.get(obj.id).then(function(objAgain) {
|
|
expect(objAgain.get('lol')).toBeTruthy();
|
|
expect(objAgain.getACL().equals(acl));
|
|
done();
|
|
}, function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
}, error => {
|
|
fail(JSON.stringify(error));
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('object is set on create and update', done => {
|
|
let triggerTime = 0;
|
|
// Register a mock beforeSave hook
|
|
Parse.Cloud.beforeSave('GameScore', (req) => {
|
|
const object = req.object;
|
|
expect(object instanceof Parse.Object).toBeTruthy();
|
|
expect(object.get('fooAgain')).toEqual('barAgain');
|
|
if (triggerTime == 0) {
|
|
// Create
|
|
expect(object.get('foo')).toEqual('bar');
|
|
// No objectId/createdAt/updatedAt
|
|
expect(object.id).toBeUndefined();
|
|
expect(object.createdAt).toBeUndefined();
|
|
expect(object.updatedAt).toBeUndefined();
|
|
} else if (triggerTime == 1) {
|
|
// Update
|
|
expect(object.get('foo')).toEqual('baz');
|
|
expect(object.id).not.toBeUndefined();
|
|
expect(object.createdAt).not.toBeUndefined();
|
|
expect(object.updatedAt).not.toBeUndefined();
|
|
} else {
|
|
throw new Error();
|
|
}
|
|
triggerTime++;
|
|
});
|
|
|
|
const obj = new Parse.Object('GameScore');
|
|
obj.set('foo', 'bar');
|
|
obj.set('fooAgain', 'barAgain');
|
|
obj.save().then(() => {
|
|
// We only update foo
|
|
obj.set('foo', 'baz');
|
|
return obj.save();
|
|
}).then(() => {
|
|
// Make sure the checking has been triggered
|
|
expect(triggerTime).toBe(2);
|
|
done();
|
|
}, error => {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
it('works when object is passed to success', done => {
|
|
let triggerTime = 0;
|
|
// Register a mock beforeSave hook
|
|
Parse.Cloud.beforeSave('GameScore', (req) => {
|
|
const object = req.object;
|
|
object.set('foo', 'bar');
|
|
triggerTime++;
|
|
return object;
|
|
});
|
|
|
|
const obj = new Parse.Object('GameScore');
|
|
obj.set('foo', 'baz');
|
|
obj.save().then(() => {
|
|
expect(triggerTime).toBe(1);
|
|
expect(obj.get('foo')).toEqual('bar');
|
|
done();
|
|
}, error => {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('original object is set on update', done => {
|
|
let triggerTime = 0;
|
|
// Register a mock beforeSave hook
|
|
Parse.Cloud.beforeSave('GameScore', (req) => {
|
|
const object = req.object;
|
|
expect(object instanceof Parse.Object).toBeTruthy();
|
|
expect(object.get('fooAgain')).toEqual('barAgain');
|
|
const originalObject = req.original;
|
|
if (triggerTime == 0) {
|
|
// No id/createdAt/updatedAt
|
|
expect(object.id).toBeUndefined();
|
|
expect(object.createdAt).toBeUndefined();
|
|
expect(object.updatedAt).toBeUndefined();
|
|
// Create
|
|
expect(object.get('foo')).toEqual('bar');
|
|
// Check the originalObject is undefined
|
|
expect(originalObject).toBeUndefined();
|
|
} else if (triggerTime == 1) {
|
|
// Update
|
|
expect(object.id).not.toBeUndefined();
|
|
expect(object.createdAt).not.toBeUndefined();
|
|
expect(object.updatedAt).not.toBeUndefined();
|
|
expect(object.get('foo')).toEqual('baz');
|
|
// Check the originalObject
|
|
expect(originalObject instanceof Parse.Object).toBeTruthy();
|
|
expect(originalObject.get('fooAgain')).toEqual('barAgain');
|
|
expect(originalObject.id).not.toBeUndefined();
|
|
expect(originalObject.createdAt).not.toBeUndefined();
|
|
expect(originalObject.updatedAt).not.toBeUndefined();
|
|
expect(originalObject.get('foo')).toEqual('bar');
|
|
} else {
|
|
throw new Error();
|
|
}
|
|
triggerTime++;
|
|
});
|
|
|
|
const obj = new Parse.Object('GameScore');
|
|
obj.set('foo', 'bar');
|
|
obj.set('fooAgain', 'barAgain');
|
|
obj.save().then(() => {
|
|
// We only update foo
|
|
obj.set('foo', 'baz');
|
|
return obj.save();
|
|
}).then(() => {
|
|
// Make sure the checking has been triggered
|
|
expect(triggerTime).toBe(2);
|
|
done();
|
|
}, error => {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('pointer mutation properly saves object', done => {
|
|
const className = 'GameScore';
|
|
|
|
Parse.Cloud.beforeSave(className, (req) => {
|
|
const object = req.object;
|
|
expect(object instanceof Parse.Object).toBeTruthy();
|
|
|
|
const child = object.get('child');
|
|
expect(child instanceof Parse.Object).toBeTruthy();
|
|
child.set('a', 'b');
|
|
return child.save();
|
|
});
|
|
|
|
const obj = new Parse.Object(className);
|
|
obj.set('foo', 'bar');
|
|
|
|
const child = new Parse.Object('Child');
|
|
child.save().then(() => {
|
|
obj.set('child', child);
|
|
return obj.save();
|
|
}).then(() => {
|
|
const query = new Parse.Query(className);
|
|
query.include('child');
|
|
return query.get(obj.id).then(objAgain => {
|
|
expect(objAgain.get('foo')).toEqual('bar');
|
|
|
|
const childAgain = objAgain.get('child');
|
|
expect(childAgain instanceof Parse.Object).toBeTruthy();
|
|
expect(childAgain.get('a')).toEqual('b');
|
|
|
|
return Promise.resolve();
|
|
});
|
|
}).then(() => {
|
|
done();
|
|
}, error => {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('pointer reassign is working properly (#1288)', (done) => {
|
|
Parse.Cloud.beforeSave('GameScore', (req) => {
|
|
|
|
const obj = req.object;
|
|
if (obj.get('point')) {
|
|
return;
|
|
}
|
|
const TestObject1 = Parse.Object.extend('TestObject1');
|
|
const newObj = new TestObject1({'key1': 1});
|
|
|
|
return newObj.save().then((newObj) => {
|
|
obj.set('point' , newObj);
|
|
});
|
|
});
|
|
let pointId;
|
|
const obj = new Parse.Object('GameScore');
|
|
obj.set('foo', 'bar');
|
|
obj.save().then(() => {
|
|
expect(obj.get('point')).not.toBeUndefined();
|
|
pointId = obj.get('point').id;
|
|
expect(pointId).not.toBeUndefined();
|
|
obj.set('foo', 'baz');
|
|
return obj.save();
|
|
}).then((obj) => {
|
|
expect(obj.get('point').id).toEqual(pointId);
|
|
done();
|
|
})
|
|
});
|
|
|
|
it('test afterSave get full object on create and update', function(done) {
|
|
let triggerTime = 0;
|
|
// Register a mock beforeSave hook
|
|
Parse.Cloud.afterSave('GameScore', function(req) {
|
|
const object = req.object;
|
|
expect(object instanceof Parse.Object).toBeTruthy();
|
|
expect(object.id).not.toBeUndefined();
|
|
expect(object.createdAt).not.toBeUndefined();
|
|
expect(object.updatedAt).not.toBeUndefined();
|
|
expect(object.get('fooAgain')).toEqual('barAgain');
|
|
if (triggerTime == 0) {
|
|
// Create
|
|
expect(object.get('foo')).toEqual('bar');
|
|
} else if (triggerTime == 1) {
|
|
// Update
|
|
expect(object.get('foo')).toEqual('baz');
|
|
} else {
|
|
throw new Error();
|
|
}
|
|
triggerTime++;
|
|
});
|
|
|
|
const obj = new Parse.Object('GameScore');
|
|
obj.set('foo', 'bar');
|
|
obj.set('fooAgain', 'barAgain');
|
|
obj.save().then(function() {
|
|
// We only update foo
|
|
obj.set('foo', 'baz');
|
|
return obj.save();
|
|
}).then(function() {
|
|
// Make sure the checking has been triggered
|
|
expect(triggerTime).toBe(2);
|
|
done();
|
|
}, function(error) {
|
|
fail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('test afterSave get original object on update', function(done) {
|
|
let triggerTime = 0;
|
|
// Register a mock beforeSave hook
|
|
|
|
Parse.Cloud.afterSave('GameScore', function(req) {
|
|
const object = req.object;
|
|
expect(object instanceof Parse.Object).toBeTruthy();
|
|
expect(object.get('fooAgain')).toEqual('barAgain');
|
|
expect(object.id).not.toBeUndefined();
|
|
expect(object.createdAt).not.toBeUndefined();
|
|
expect(object.updatedAt).not.toBeUndefined();
|
|
const originalObject = req.original;
|
|
if (triggerTime == 0) {
|
|
// Create
|
|
expect(object.get('foo')).toEqual('bar');
|
|
// Check the originalObject is undefined
|
|
expect(originalObject).toBeUndefined();
|
|
} else if (triggerTime == 1) {
|
|
// Update
|
|
expect(object.get('foo')).toEqual('baz');
|
|
// Check the originalObject
|
|
expect(originalObject instanceof Parse.Object).toBeTruthy();
|
|
expect(originalObject.get('fooAgain')).toEqual('barAgain');
|
|
expect(originalObject.id).not.toBeUndefined();
|
|
expect(originalObject.createdAt).not.toBeUndefined();
|
|
expect(originalObject.updatedAt).not.toBeUndefined();
|
|
expect(originalObject.get('foo')).toEqual('bar');
|
|
} else {
|
|
throw new Error();
|
|
}
|
|
triggerTime++;
|
|
});
|
|
|
|
const obj = new Parse.Object('GameScore');
|
|
obj.set('foo', 'bar');
|
|
obj.set('fooAgain', 'barAgain');
|
|
obj.save().then(function() {
|
|
// We only update foo
|
|
obj.set('foo', 'baz');
|
|
return obj.save();
|
|
}).then(function() {
|
|
// Make sure the checking has been triggered
|
|
expect(triggerTime).toBe(2);
|
|
done();
|
|
}, function(error) {
|
|
jfail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('test afterSave get full original object even req auth can not query it', (done) => {
|
|
let triggerTime = 0;
|
|
// Register a mock beforeSave hook
|
|
Parse.Cloud.afterSave('GameScore', function(req) {
|
|
const object = req.object;
|
|
const originalObject = req.original;
|
|
if (triggerTime == 0) {
|
|
// Create
|
|
} else if (triggerTime == 1) {
|
|
// Update
|
|
expect(object.get('foo')).toEqual('baz');
|
|
// Make sure we get the full originalObject
|
|
expect(originalObject instanceof Parse.Object).toBeTruthy();
|
|
expect(originalObject.get('fooAgain')).toEqual('barAgain');
|
|
expect(originalObject.id).not.toBeUndefined();
|
|
expect(originalObject.createdAt).not.toBeUndefined();
|
|
expect(originalObject.updatedAt).not.toBeUndefined();
|
|
expect(originalObject.get('foo')).toEqual('bar');
|
|
} else {
|
|
throw new Error();
|
|
}
|
|
triggerTime++;
|
|
});
|
|
|
|
const obj = new Parse.Object('GameScore');
|
|
obj.set('foo', 'bar');
|
|
obj.set('fooAgain', 'barAgain');
|
|
const acl = new Parse.ACL();
|
|
// Make sure our update request can not query the object
|
|
acl.setPublicReadAccess(false);
|
|
acl.setPublicWriteAccess(true);
|
|
obj.setACL(acl);
|
|
obj.save().then(function() {
|
|
// We only update foo
|
|
obj.set('foo', 'baz');
|
|
return obj.save();
|
|
}).then(function() {
|
|
// Make sure the checking has been triggered
|
|
expect(triggerTime).toBe(2);
|
|
done();
|
|
}, function(error) {
|
|
jfail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('afterSave flattens custom operations', done => {
|
|
let triggerTime = 0;
|
|
// Register a mock beforeSave hook
|
|
Parse.Cloud.afterSave('GameScore', function(req) {
|
|
const object = req.object;
|
|
expect(object instanceof Parse.Object).toBeTruthy();
|
|
const 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 {
|
|
throw new Error();
|
|
}
|
|
triggerTime++;
|
|
});
|
|
|
|
const 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);
|
|
done();
|
|
}, error => {
|
|
jfail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('beforeSave receives ACL', done => {
|
|
let triggerTime = 0;
|
|
// Register a mock beforeSave hook
|
|
Parse.Cloud.beforeSave('GameScore', function(req) {
|
|
const object = req.object;
|
|
if (triggerTime == 0) {
|
|
const acl = object.getACL();
|
|
expect(acl.getPublicReadAccess()).toBeTruthy();
|
|
expect(acl.getPublicWriteAccess()).toBeTruthy();
|
|
} else if (triggerTime == 1) {
|
|
const acl = object.getACL();
|
|
expect(acl.getPublicReadAccess()).toBeFalsy();
|
|
expect(acl.getPublicWriteAccess()).toBeTruthy();
|
|
} else {
|
|
throw new Error();
|
|
}
|
|
triggerTime++;
|
|
});
|
|
|
|
const obj = new Parse.Object('GameScore');
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(true);
|
|
acl.setPublicWriteAccess(true);
|
|
obj.setACL(acl);
|
|
obj.save().then(() => {
|
|
acl.setPublicReadAccess(false);
|
|
obj.setACL(acl);
|
|
return obj.save();
|
|
}).then(() => {
|
|
// Make sure the checking has been triggered
|
|
expect(triggerTime).toBe(2);
|
|
done();
|
|
}, error => {
|
|
jfail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('afterSave receives ACL', done => {
|
|
let triggerTime = 0;
|
|
// Register a mock beforeSave hook
|
|
Parse.Cloud.afterSave('GameScore', function(req) {
|
|
const object = req.object;
|
|
if (triggerTime == 0) {
|
|
const acl = object.getACL();
|
|
expect(acl.getPublicReadAccess()).toBeTruthy();
|
|
expect(acl.getPublicWriteAccess()).toBeTruthy();
|
|
} else if (triggerTime == 1) {
|
|
const acl = object.getACL();
|
|
expect(acl.getPublicReadAccess()).toBeFalsy();
|
|
expect(acl.getPublicWriteAccess()).toBeTruthy();
|
|
} else {
|
|
throw new Error();
|
|
}
|
|
triggerTime++;
|
|
});
|
|
|
|
const obj = new Parse.Object('GameScore');
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(true);
|
|
acl.setPublicWriteAccess(true);
|
|
obj.setACL(acl);
|
|
obj.save().then(() => {
|
|
acl.setPublicReadAccess(false);
|
|
obj.setACL(acl);
|
|
return obj.save();
|
|
}).then(() => {
|
|
// Make sure the checking has been triggered
|
|
expect(triggerTime).toBe(2);
|
|
done();
|
|
}, error => {
|
|
jfail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should return the updated fields on PUT', done => {
|
|
const obj = new Parse.Object('GameScore');
|
|
obj.save({a:'hello', c: 1, d: ['1'], e:['1'], f:['1','2']}).then(() => {
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-REST-API-Key': 'rest',
|
|
'X-Parse-Installation-Id': 'yolo'
|
|
};
|
|
request.put({
|
|
headers: headers,
|
|
url: 'http://localhost:8378/1/classes/GameScore/' + obj.id,
|
|
body: JSON.stringify({
|
|
a: 'b',
|
|
c: {"__op":"Increment","amount":2},
|
|
d: {"__op":"Add", objects: ['2']},
|
|
e: {"__op":"AddUnique", objects: ['1', '2']},
|
|
f: {"__op":"Remove", objects: ['2']},
|
|
selfThing: {"__type":"Pointer","className":"GameScore","objectId":obj.id},
|
|
})
|
|
}, (error, response, body) => {
|
|
try {
|
|
body = JSON.parse(body);
|
|
expect(body.a).toBeUndefined();
|
|
expect(body.c).toEqual(3); // 2+1
|
|
expect(body.d.length).toBe(2);
|
|
expect(body.d.indexOf('1') > -1).toBe(true);
|
|
expect(body.d.indexOf('2') > -1).toBe(true);
|
|
expect(body.e.length).toBe(2);
|
|
expect(body.e.indexOf('1') > -1).toBe(true);
|
|
expect(body.e.indexOf('2') > -1).toBe(true);
|
|
expect(body.f.length).toBe(1);
|
|
expect(body.f.indexOf('1') > -1).toBe(true);
|
|
// return nothing on other self
|
|
expect(body.selfThing).toBeUndefined();
|
|
// updatedAt is always set
|
|
expect(body.updatedAt).not.toBeUndefined();
|
|
}catch(e) {
|
|
jfail(e);
|
|
}
|
|
done();
|
|
});
|
|
}).catch(() => {
|
|
fail('Should not fail');
|
|
done();
|
|
})
|
|
})
|
|
|
|
it('test cloud function error handling', (done) => {
|
|
// Register a function which will fail
|
|
Parse.Cloud.define('willFail', () => {
|
|
throw new Error('noway');
|
|
});
|
|
Parse.Cloud.run('willFail').then(() => {
|
|
fail('Should not have succeeded.');
|
|
done();
|
|
}, (e) => {
|
|
expect(e.code).toEqual(141);
|
|
expect(e.message).toEqual('noway');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('test cloud function error handling with custom error code', (done) => {
|
|
// Register a function which will fail
|
|
Parse.Cloud.define('willFail', () => {
|
|
throw new Parse.Error(999, 'noway');
|
|
});
|
|
Parse.Cloud.run('willFail').then(() => {
|
|
fail('Should not have succeeded.');
|
|
done();
|
|
}, (e) => {
|
|
expect(e.code).toEqual(999);
|
|
expect(e.message).toEqual('noway');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('test cloud function error handling with standard error code', (done) => {
|
|
// Register a function which will fail
|
|
Parse.Cloud.define('willFail', () => {
|
|
throw new Error('noway');
|
|
});
|
|
Parse.Cloud.run('willFail').then(() => {
|
|
fail('Should not have succeeded.');
|
|
done();
|
|
}, (e) => {
|
|
expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED);
|
|
expect(e.message).toEqual('noway');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('test beforeSave/afterSave get installationId', function(done) {
|
|
let triggerTime = 0;
|
|
Parse.Cloud.beforeSave('GameScore', function(req) {
|
|
triggerTime++;
|
|
expect(triggerTime).toEqual(1);
|
|
expect(req.installationId).toEqual('yolo');
|
|
});
|
|
Parse.Cloud.afterSave('GameScore', function(req) {
|
|
triggerTime++;
|
|
expect(triggerTime).toEqual(2);
|
|
expect(req.installationId).toEqual('yolo');
|
|
});
|
|
|
|
const 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) => {
|
|
expect(error).toBe(null);
|
|
expect(triggerTime).toEqual(2);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('test beforeDelete/afterDelete get installationId', function(done) {
|
|
let triggerTime = 0;
|
|
Parse.Cloud.beforeDelete('GameScore', function(req) {
|
|
triggerTime++;
|
|
expect(triggerTime).toEqual(1);
|
|
expect(req.installationId).toEqual('yolo');
|
|
});
|
|
Parse.Cloud.afterDelete('GameScore', function(req) {
|
|
triggerTime++;
|
|
expect(triggerTime).toEqual(2);
|
|
expect(req.installationId).toEqual('yolo');
|
|
});
|
|
|
|
const 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) => {
|
|
expect(error).toBe(null);
|
|
expect(triggerTime).toEqual(2);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('test beforeDelete with locked down ACL', async () => {
|
|
let called = false;
|
|
Parse.Cloud.beforeDelete('GameScore', () => {
|
|
called = true;
|
|
});
|
|
const object = new Parse.Object('GameScore');
|
|
object.setACL(new Parse.ACL());
|
|
await object.save();
|
|
const objects = await new Parse.Query('GameScore').find();
|
|
expect(objects.length).toBe(0);
|
|
try {
|
|
await object.destroy();
|
|
} catch(e) {
|
|
expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND);
|
|
}
|
|
expect(called).toBe(false);
|
|
});
|
|
|
|
it('test cloud function query parameters', (done) => {
|
|
Parse.Cloud.define('echoParams', (req) => {
|
|
return req.params;
|
|
});
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-Javascript-Key': 'test'
|
|
};
|
|
request.post({
|
|
headers: headers,
|
|
url: 'http://localhost:8378/1/functions/echoParams', //?option=1&other=2
|
|
qs: {
|
|
option: 1,
|
|
other: 2
|
|
},
|
|
body: '{"foo":"bar", "other": 1}'
|
|
}, (error, response, body) => {
|
|
expect(error).toBe(null);
|
|
const res = JSON.parse(body).result;
|
|
expect(res.option).toEqual('1');
|
|
// Make sure query string params override body params
|
|
expect(res.other).toEqual('2');
|
|
expect(res.foo).toEqual("bar");
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('test cloud function parameter validation', (done) => {
|
|
// Register a function with validation
|
|
Parse.Cloud.define('functionWithParameterValidationFailure', () => {
|
|
return 'noway';
|
|
}, (request) => {
|
|
return request.params.success === 100;
|
|
});
|
|
|
|
Parse.Cloud.run('functionWithParameterValidationFailure', {"success":500}).then(() => {
|
|
fail('Validation should not have succeeded');
|
|
done();
|
|
}, (e) => {
|
|
expect(e.code).toEqual(142);
|
|
expect(e.message).toEqual('Validation failed.');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can handle null params in cloud functions (regression test for #1742)', done => {
|
|
Parse.Cloud.define('func', (request) => {
|
|
expect(request.params.nullParam).toEqual(null);
|
|
return 'yay';
|
|
});
|
|
|
|
Parse.Cloud.run('func', {nullParam: null})
|
|
.then(() => {
|
|
done()
|
|
}, () => {
|
|
fail('cloud code call failed');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can handle date params in cloud functions (#2214)', done => {
|
|
const date = new Date();
|
|
Parse.Cloud.define('dateFunc', (request) => {
|
|
expect(request.params.date.__type).toEqual('Date');
|
|
expect(request.params.date.iso).toEqual(date.toISOString());
|
|
return 'yay';
|
|
});
|
|
|
|
Parse.Cloud.run('dateFunc', {date: date})
|
|
.then(() => {
|
|
done()
|
|
}, () => {
|
|
fail('cloud code call failed');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('fails on invalid client key', done => {
|
|
const headers = {
|
|
'Content-Type': 'application/octet-stream',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-Client-Key': 'notclient'
|
|
};
|
|
request.get({
|
|
headers: headers,
|
|
url: 'http://localhost:8378/1/classes/TestObject'
|
|
}, (error, response, body) => {
|
|
expect(error).toBe(null);
|
|
const b = JSON.parse(body);
|
|
expect(b.error).toEqual('unauthorized');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('fails on invalid windows key', done => {
|
|
const headers = {
|
|
'Content-Type': 'application/octet-stream',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-Windows-Key': 'notwindows'
|
|
};
|
|
request.get({
|
|
headers: headers,
|
|
url: 'http://localhost:8378/1/classes/TestObject'
|
|
}, (error, response, body) => {
|
|
expect(error).toBe(null);
|
|
const b = JSON.parse(body);
|
|
expect(b.error).toEqual('unauthorized');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('fails on invalid javascript key', done => {
|
|
const headers = {
|
|
'Content-Type': 'application/octet-stream',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-Javascript-Key': 'notjavascript'
|
|
};
|
|
request.get({
|
|
headers: headers,
|
|
url: 'http://localhost:8378/1/classes/TestObject'
|
|
}, (error, response, body) => {
|
|
expect(error).toBe(null);
|
|
const b = JSON.parse(body);
|
|
expect(b.error).toEqual('unauthorized');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('fails on invalid rest api key', done => {
|
|
const headers = {
|
|
'Content-Type': 'application/octet-stream',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-REST-API-Key': 'notrest'
|
|
};
|
|
request.get({
|
|
headers: headers,
|
|
url: 'http://localhost:8378/1/classes/TestObject'
|
|
}, (error, response, body) => {
|
|
expect(error).toBe(null);
|
|
const b = JSON.parse(body);
|
|
expect(b.error).toEqual('unauthorized');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('fails on invalid function', done => {
|
|
Parse.Cloud.run('somethingThatDoesDefinitelyNotExist').then(() => {
|
|
fail('This should have never suceeded');
|
|
done();
|
|
}, (e) => {
|
|
expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED);
|
|
expect(e.message).toEqual('Invalid function: "somethingThatDoesDefinitelyNotExist"');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('dedupes an installation properly and returns updatedAt', (done) => {
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-REST-API-Key': 'rest'
|
|
};
|
|
const data = {
|
|
'installationId': 'lkjsahdfkjhsdfkjhsdfkjhsdf',
|
|
'deviceType': 'embedded'
|
|
};
|
|
const requestOptions = {
|
|
headers: headers,
|
|
url: 'http://localhost:8378/1/installations',
|
|
body: JSON.stringify(data)
|
|
};
|
|
request.post(requestOptions, (error, response, body) => {
|
|
expect(error).toBe(null);
|
|
const b = JSON.parse(body);
|
|
expect(typeof b.objectId).toEqual('string');
|
|
request.post(requestOptions, (error, response, body) => {
|
|
expect(error).toBe(null);
|
|
const b = JSON.parse(body);
|
|
expect(typeof b.updatedAt).toEqual('string');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('android login providing empty authData block works', (done) => {
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-REST-API-Key': 'rest'
|
|
};
|
|
const data = {
|
|
username: 'pulse1989',
|
|
password: 'password1234',
|
|
authData: {}
|
|
};
|
|
const requestOptions = {
|
|
headers: headers,
|
|
url: 'http://localhost:8378/1/users',
|
|
body: JSON.stringify(data)
|
|
};
|
|
request.post(requestOptions, (error) => {
|
|
expect(error).toBe(null);
|
|
requestOptions.url = 'http://localhost:8378/1/login';
|
|
request.get(requestOptions, (error, response, body) => {
|
|
expect(error).toBe(null);
|
|
const b = JSON.parse(body);
|
|
expect(typeof b['sessionToken']).toEqual('string');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('gets relation fields', (done) => {
|
|
const object = new Parse.Object('AnObject');
|
|
const relatedObject = new Parse.Object('RelatedObject');
|
|
Parse.Object.saveAll([object, relatedObject]).then(() => {
|
|
object.relation('related').add(relatedObject);
|
|
return object.save();
|
|
}).then(() => {
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-REST-API-Key': 'rest'
|
|
};
|
|
const requestOptions = {
|
|
headers: headers,
|
|
url: 'http://localhost:8378/1/classes/AnObject',
|
|
json: true
|
|
};
|
|
request.get(requestOptions, (err, res, body) => {
|
|
expect(body.results.length).toBe(1);
|
|
const result = body.results[0];
|
|
expect(result.related).toEqual({
|
|
__type: "Relation",
|
|
className: 'RelatedObject'
|
|
})
|
|
done();
|
|
});
|
|
}).catch((err) => {
|
|
jfail(err);
|
|
done();
|
|
})
|
|
});
|
|
|
|
it('properly returns incremented values (#1554)', (done) => {
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-REST-API-Key': 'rest'
|
|
};
|
|
const requestOptions = {
|
|
headers: headers,
|
|
url: 'http://localhost:8378/1/classes/AnObject',
|
|
json: true
|
|
};
|
|
const object = new Parse.Object('AnObject');
|
|
|
|
function runIncrement(amount) {
|
|
const options = Object.assign({}, requestOptions, {
|
|
body: {
|
|
"key": {
|
|
__op: 'Increment',
|
|
amount: amount
|
|
}
|
|
},
|
|
url: 'http://localhost:8378/1/classes/AnObject/' + object.id
|
|
})
|
|
return new Promise((resolve, reject) => {
|
|
request.put(options, (err, res, body) => {
|
|
if (err) {
|
|
reject(err);
|
|
} else {
|
|
resolve(body);
|
|
}
|
|
});
|
|
})
|
|
}
|
|
|
|
object.save().then(() => {
|
|
return runIncrement(1);
|
|
}).then((res) => {
|
|
expect(res.key).toBe(1);
|
|
return runIncrement(-1);
|
|
}).then((res) => {
|
|
expect(res.key).toBe(0);
|
|
done();
|
|
})
|
|
})
|
|
|
|
it('ignores _RevocableSession "header" send by JS SDK', (done) => {
|
|
const object = new Parse.Object('AnObject');
|
|
object.set('a', 'b');
|
|
object.save().then(() => {
|
|
request.post({
|
|
headers: {'Content-Type': 'application/json'},
|
|
url: 'http://localhost:8378/1/classes/AnObject',
|
|
body: {
|
|
_method: 'GET',
|
|
_ApplicationId: 'test',
|
|
_JavaScriptKey: 'test',
|
|
_ClientVersion: 'js1.8.3',
|
|
_InstallationId: 'iid',
|
|
_RevocableSession: "1",
|
|
},
|
|
json: true
|
|
}, (err, res, body) => {
|
|
expect(body.error).toBeUndefined();
|
|
expect(body.results).not.toBeUndefined();
|
|
expect(body.results.length).toBe(1);
|
|
const result = body.results[0];
|
|
expect(result.a).toBe('b');
|
|
done();
|
|
})
|
|
});
|
|
});
|
|
|
|
it('doesnt convert interior keys of objects that use special names', done => {
|
|
const obj = new Parse.Object('Obj');
|
|
obj.set('val', { createdAt: 'a', updatedAt: 1 });
|
|
obj.save()
|
|
.then(obj => new Parse.Query('Obj').get(obj.id))
|
|
.then(obj => {
|
|
expect(obj.get('val').createdAt).toEqual('a');
|
|
expect(obj.get('val').updatedAt).toEqual(1);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('bans interior keys containing . or $', done => {
|
|
new Parse.Object('Obj').save({innerObj: {'key with a $': 'fails'}})
|
|
.then(() => {
|
|
fail('should not succeed')
|
|
}, error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY);
|
|
return new Parse.Object('Obj').save({innerObj: {'key with a .': 'fails'}});
|
|
})
|
|
.then(() => {
|
|
fail('should not succeed')
|
|
}, error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY);
|
|
return new Parse.Object('Obj').save({innerObj: {innerInnerObj: {'key with $': 'fails'}}});
|
|
})
|
|
.then(() => {
|
|
fail('should not succeed')
|
|
}, error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY);
|
|
return new Parse.Object('Obj').save({innerObj: {innerInnerObj: {'key with .': 'fails'}}});
|
|
})
|
|
.then(() => {
|
|
fail('should not succeed')
|
|
done();
|
|
}, error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('does not change inner object keys named _auth_data_something', done => {
|
|
new Parse.Object('O').save({ innerObj: {_auth_data_facebook: 7}})
|
|
.then(object => new Parse.Query('O').get(object.id))
|
|
.then(object => {
|
|
expect(object.get('innerObj')).toEqual({_auth_data_facebook: 7});
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('does not change inner object key names _p_somethign', done => {
|
|
new Parse.Object('O').save({ innerObj: {_p_data: 7}})
|
|
.then(object => new Parse.Query('O').get(object.id))
|
|
.then(object => {
|
|
expect(object.get('innerObj')).toEqual({_p_data: 7});
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('does not change inner object key names _rperm, _wperm', done => {
|
|
new Parse.Object('O').save({ innerObj: {_rperm: 7, _wperm: 8}})
|
|
.then(object => new Parse.Query('O').get(object.id))
|
|
.then(object => {
|
|
expect(object.get('innerObj')).toEqual({_rperm: 7, _wperm: 8});
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('does not change inner objects if the key has the same name as a geopoint field on the class, and the value is an array of length 2, or if the key has the same name as a file field on the class, and the value is a string', done => {
|
|
const file = new Parse.File('myfile.txt', { base64: 'eAo=' });
|
|
file.save()
|
|
.then(f => {
|
|
const obj = new Parse.Object('O');
|
|
obj.set('fileField', f);
|
|
obj.set('geoField', new Parse.GeoPoint(0, 0));
|
|
obj.set('innerObj', {
|
|
fileField: "data",
|
|
geoField: [1,2],
|
|
});
|
|
return obj.save();
|
|
})
|
|
.then(object => object.fetch())
|
|
.then(object => {
|
|
expect(object.get('innerObj')).toEqual({
|
|
fileField: "data",
|
|
geoField: [1,2],
|
|
});
|
|
done();
|
|
}).catch((e) => {
|
|
jfail(e);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('purge all objects in class', (done) => {
|
|
const object = new Parse.Object('TestObject');
|
|
object.set('foo', 'bar');
|
|
const object2 = new Parse.Object('TestObject');
|
|
object2.set('alice', 'wonderland');
|
|
Parse.Object.saveAll([object, object2])
|
|
.then(() => {
|
|
const query = new Parse.Query(TestObject);
|
|
return query.count()
|
|
}).then((count) => {
|
|
expect(count).toBe(2);
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-Master-Key': 'test'
|
|
};
|
|
request.del({
|
|
headers: headers,
|
|
url: 'http://localhost:8378/1/purge/TestObject',
|
|
json: true
|
|
}, (err) => {
|
|
expect(err).toBe(null);
|
|
const query = new Parse.Query(TestObject);
|
|
return query.count().then((count) => {
|
|
expect(count).toBe(0);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('fail on purge all objects in class without master key', (done) => {
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-REST-API-Key': 'rest'
|
|
};
|
|
rp({
|
|
method: 'DELETE',
|
|
headers: headers,
|
|
uri: 'http://localhost:8378/1/purge/TestObject',
|
|
json: true
|
|
}).then(() => {
|
|
fail('Should not succeed');
|
|
}).catch(err => {
|
|
expect(err.error.error).toEqual('unauthorized: master key is required');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('purge all objects in _Role also purge cache', (done) => {
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-Master-Key': 'test'
|
|
};
|
|
let user, object;
|
|
createTestUser().then((x) => {
|
|
user = x;
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(true);
|
|
acl.setPublicWriteAccess(false);
|
|
const role = new Parse.Object('_Role');
|
|
role.set('name', 'TestRole');
|
|
role.setACL(acl);
|
|
const users = role.relation('users');
|
|
users.add(user);
|
|
return role.save({}, { useMasterKey: true });
|
|
}).then(() => {
|
|
const query = new Parse.Query('_Role');
|
|
return query.find({ useMasterKey: true });
|
|
}).then((x) => {
|
|
expect(x.length).toEqual(1);
|
|
const relation = x[0].relation('users').query();
|
|
return relation.first({ useMasterKey: true });
|
|
}).then((x) => {
|
|
expect(x.id).toEqual(user.id);
|
|
object = new Parse.Object('TestObject');
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(false);
|
|
acl.setPublicWriteAccess(false);
|
|
acl.setRoleReadAccess('TestRole', true);
|
|
acl.setRoleWriteAccess('TestRole', true);
|
|
object.setACL(acl);
|
|
return object.save();
|
|
}).then(() => {
|
|
const query = new Parse.Query('TestObject');
|
|
return query.find({ sessionToken: user.getSessionToken() });
|
|
}).then((x) => {
|
|
expect(x.length).toEqual(1);
|
|
return rp({
|
|
method: 'DELETE',
|
|
headers: headers,
|
|
uri: 'http://localhost:8378/1/purge/_Role',
|
|
json: true
|
|
});
|
|
}).then(() => {
|
|
const query = new Parse.Query('TestObject');
|
|
return query.get(object.id, { sessionToken: user.getSessionToken() });
|
|
}).then(() => {
|
|
fail('Should not succeed');
|
|
}, (e) => {
|
|
expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('purge empty class', (done) => {
|
|
const testSchema = new Parse.Schema('UnknownClass');
|
|
testSchema.purge().then(done).catch(done.fail);
|
|
});
|
|
|
|
it('should not update schema beforeSave #2672', (done) => {
|
|
Parse.Cloud.beforeSave('MyObject', (request) => {
|
|
if (request.object.get('secret')) {
|
|
throw 'cannot set secret here';
|
|
}
|
|
});
|
|
|
|
const object = new Parse.Object('MyObject');
|
|
object.set('key', 'value');
|
|
object.save().then(() => {
|
|
return object.save({'secret': 'should not update schema'});
|
|
}).then(() => {
|
|
fail();
|
|
done();
|
|
}, () => {
|
|
return rp({
|
|
method: 'GET',
|
|
headers: {
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-Master-Key': 'test'
|
|
},
|
|
uri: 'http://localhost:8378/1/schemas/MyObject',
|
|
json: true
|
|
});
|
|
}).then((res) => {
|
|
const fields = res.fields;
|
|
expect(fields.secret).toBeUndefined();
|
|
done();
|
|
}, (err) => {
|
|
jfail(err);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe_only_db('mongo')('legacy _acl', () => {
|
|
it('should have _acl when locking down (regression for #2465)', (done) => {
|
|
const headers = {
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-REST-API-Key': 'rest'
|
|
}
|
|
rp({
|
|
method: 'POST',
|
|
headers: headers,
|
|
uri: 'http://localhost:8378/1/classes/Report',
|
|
body: {
|
|
ACL: {},
|
|
name: 'My Report'
|
|
},
|
|
json: true
|
|
}).then(() => {
|
|
const config = Config.get('test');
|
|
const adapter = config.database.adapter;
|
|
return adapter._adaptiveCollection("Report")
|
|
.then(collection => collection.find({}))
|
|
}).then((results) => {
|
|
expect(results.length).toBe(1);
|
|
const result = results[0];
|
|
expect(result.name).toEqual('My Report');
|
|
expect(result._wperm).toEqual([]);
|
|
expect(result._rperm).toEqual([]);
|
|
expect(result._acl).toEqual({});
|
|
done();
|
|
}).catch((err) => {
|
|
fail(JSON.stringify(err));
|
|
done();
|
|
});
|
|
});
|
|
});
|