// 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 MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter'); var request = require('request'); const rp = require('request-promise'); const Parse = require("parse/node"); let Config = require('../src/Config'); const SchemaController = require('../src/Controllers/SchemaController'); var TestUtils = require('../src/TestUtils'); const deepcopy = require('deepcopy'); const userSchema = SchemaController.convertSchemaToAdapterSchema({ className: '_User', fields: Object.assign({}, SchemaController.defaultColumns._Default, SchemaController.defaultColumns._User) }); describe_only_db('mongo')('miscellaneous', () => { it('test rest_create_app', function(done) { var 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'); var obj = new Parse.Object('TestObject'); obj.set('foo', 'bar'); return obj.save(); }).then(() => { let config = new Config(appId); return config.database.adapter.find('TestObject', { fields: {} }, {}, {}); }).then((results) => { expect(results.length).toEqual(1); expect(results[0]['foo']).toEqual('bar'); done(); }).fail(error => { fail(JSON.stringify(error)); done(); }) }); }) describe('miscellaneous', function() { it('create a GameScore object', function(done) { var 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(); }, error => { fail(JSON.stringify(error)); done(); }); }); it('get a TestObject', function(done) { create({ 'bloop' : 'blarg' }, function(obj) { var t2 = new TestObject({ objectId: obj.id }); t2.fetch({ success: function(obj2) { expect(obj2.get('bloop')).toEqual('blarg'); expect(obj2.id).toBeTruthy(); expect(obj2.id).toEqual(obj.id); done(); }, error: error => { fail(JSON.stringify(error)); done(); } }); }); }); it('create a valid parse user', function(done) { createTestUser(function(data) { expect(data.id).not.toBeUndefined(); expect(data.getSessionToken()).not.toBeUndefined(); expect(data.get('password')).toBeUndefined(); done(); }, error => { fail(JSON.stringify(error)); done(); }); }); it('fail to create a duplicate username', done => { let numCreated = 0; let numFailed = 0; let p1 = createTestUser(); p1.then(user => { numCreated++; expect(numCreated).toEqual(1); }) .catch(error => { numFailed++; expect(numFailed).toEqual(1); expect(error.code).toEqual(Parse.Error.USERNAME_TAKEN); }); let p2 = createTestUser(); p2.then(user => { numCreated++; expect(numCreated).toEqual(1); }) .catch(error => { numFailed++; expect(numFailed).toEqual(1); expect(error.code).toEqual(Parse.Error.USERNAME_TAKEN); }); Parse.Promise.when([p1, p2]) .then(() => { fail('one of the users should not have been created'); done(); }) .catch(done); }); it('ensure that email is uniquely indexed', done => { let numFailed = 0; let numCreated = 0; let user1 = new Parse.User(); user1.setPassword('asdf'); user1.setUsername('u1'); user1.setEmail('dupe@dupe.dupe'); let p1 = user1.signUp(); p1.then(user => { numCreated++; expect(numCreated).toEqual(1); }, error => { numFailed++; expect(numFailed).toEqual(1); expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); }); let user2 = new Parse.User(); user2.setPassword('asdf'); user2.setUsername('u2'); user2.setEmail('dupe@dupe.dupe'); let p2 = user2.signUp(); p2.then(user => { numCreated++; expect(numCreated).toEqual(1); }, error => { numFailed++; expect(numFailed).toEqual(1); expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); }); Parse.Promise.when([p1, p2]) .then(() => { fail('one of the users should not have been created'); done(); }) .catch(done); }); it('ensure that if people already have duplicate users, they can still sign up new users', done => { let config = new Config('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); let user = new Parse.User(); user.setPassword('asdf'); user.setUsername('zxcv'); return user.signUp().catch(fail); }) .then(() => { let user = new Parse.User(); user.setPassword('asdf'); user.setUsername('u'); return user.signUp() }) .then(result => { 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 => { let config = new Config('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(() => { let user = new Parse.User(); user.setPassword('asdf'); user.setUsername('qqq'); user.setEmail('unique@unique.unique'); return user.signUp().catch(fail); }) .then(() => { let 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 => { let config = new Config('test'); config.database.adapter.addFieldIfNotExists('_User', 'randomField', { type: 'String' }) .then(() => config.database.adapter.ensureUniqueness('_User', userSchema, ['randomField'])) .then(() => { let user = new Parse.User(); user.setPassword('asdf'); user.setUsername('1'); user.setEmail('1@b.c'); user.set('randomField', 'a'); return user.signUp() }) .then(() => { let 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(function(u) { expect(typeof u.id).toEqual('string'); Parse.User.logIn('test', 'moon-y', { success: function(user) { expect(typeof user.id).toEqual('string'); expect(user.get('password')).toBeUndefined(); expect(user.getSessionToken()).not.toBeUndefined(); Parse.User.logOut().then(done); }, error: error => { fail(JSON.stringify(error)); 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) { var obj = new TestObject(); obj.set('date', new Date()); obj.set('array', [1, 2, 3]); obj.set('object', {one: 1, two: 2}); obj.save().then(() => { var 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) { var baz = new TestObject({ foo: 'baz' }); var qux = new TestObject({ foo: 'qux' }); baz.save().then(() => { return qux.save(); }).then(() => { var 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) { var objects = []; for (var 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) { var alpha = new TestObject({ letter: 'alpha' }); var 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) { var acl = new Parse.ACL({ '*': { read: true, write: false } }); Parse.Cloud.beforeSave('BeforeSaveAddACL', function(req, res) { req.object.setACL(acl); res.success(); }); var obj = new Parse.Object('BeforeSaveAddACL'); obj.set('lol', true); obj.save().then(function() { var 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, res) => { let 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 { res.error(); } triggerTime++; res.success(); }); let 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, res) => { let object = req.object; object.set('foo', 'bar'); triggerTime++; res.success(object); }); let 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, res) => { let object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); expect(object.get('fooAgain')).toEqual('barAgain'); let 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 { res.error(); } triggerTime++; res.success(); }); let 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 => { let className = 'GameScore'; Parse.Cloud.beforeSave(className, (req, res) => { let object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); let child = object.get('child'); expect(child instanceof Parse.Object).toBeTruthy(); child.set('a', 'b'); child.save().then(() => { res.success(); }); }); let obj = new Parse.Object(className); obj.set('foo', 'bar'); let child = new Parse.Object('Child'); child.save().then(() => { obj.set('child', child); return obj.save(); }).then(() => { let query = new Parse.Query(className); query.include('child'); return query.get(obj.id).then(objAgain => { expect(objAgain.get('foo')).toEqual('bar'); let 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, res) => { var obj = req.object; if (obj.get('point')) { return res.success(); } var TestObject1 = Parse.Object.extend('TestObject1'); var newObj = new TestObject1({'key1': 1}); return newObj.save().then((newObj) => { obj.set('point' , newObj); res.success(); }); }); var pointId; var 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) { var triggerTime = 0; // Register a mock beforeSave hook Parse.Cloud.afterSave('GameScore', function(req, res) { var 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 { res.error(); } triggerTime++; res.success(); }); var 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) { var triggerTime = 0; // Register a mock beforeSave hook Parse.Cloud.afterSave('GameScore', function(req, res) { var 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(); var 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 { res.error(); } triggerTime++; res.success(); }); var 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) => { var triggerTime = 0; // Register a mock beforeSave hook Parse.Cloud.afterSave('GameScore', function(req, res) { var object = req.object; var 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 { res.error(); } triggerTime++; res.success(); }); var obj = new Parse.Object('GameScore'); obj.set('foo', 'bar'); obj.set('fooAgain', 'barAgain'); var 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 => { 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); done(); }, error => { jfail(error); done(); }); }); it('beforeSave receives ACL', done => { let triggerTime = 0; // Register a mock beforeSave hook Parse.Cloud.beforeSave('GameScore', function(req, res) { let object = req.object; if (triggerTime == 0) { let acl = object.getACL(); expect(acl.getPublicReadAccess()).toBeTruthy(); expect(acl.getPublicWriteAccess()).toBeTruthy(); } else if (triggerTime == 1) { let acl = object.getACL(); expect(acl.getPublicReadAccess()).toBeFalsy(); expect(acl.getPublicWriteAccess()).toBeTruthy(); } else { res.error(); } triggerTime++; res.success(); }); let obj = new Parse.Object('GameScore'); let 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, res) { let object = req.object; if (triggerTime == 0) { let acl = object.getACL(); expect(acl.getPublicReadAccess()).toBeTruthy(); expect(acl.getPublicWriteAccess()).toBeTruthy(); } else if (triggerTime == 1) { let acl = object.getACL(); expect(acl.getPublicReadAccess()).toBeFalsy(); expect(acl.getPublicWriteAccess()).toBeTruthy(); } else { res.error(); } triggerTime++; res.success(); }); let obj = new Parse.Object('GameScore'); let 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 => { let obj = new Parse.Object('GameScore'); obj.save({a:'hello', c: 1, d: ['1'], e:['1'], f:['1','2']}).then(( ) => { var 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(); }); }).fail((err) => { fail('Should not fail'); done(); }) }) it('test cloud function error handling', (done) => { // Register a function which will fail Parse.Cloud.define('willFail', (req, res) => { res.error('noway'); }); Parse.Cloud.run('willFail').then((s) => { 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', (req, res) => { res.error(999, 'noway'); }); Parse.Cloud.run('willFail').then((s) => { 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', (req, res) => { res.error('noway'); }); Parse.Cloud.run('willFail').then((s) => { 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, 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); 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); done(); }); }); }); it('test cloud function query parameters', (done) => { Parse.Cloud.define('echoParams', (req, res) => { res.success(req.params); }); var 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); var 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', (req, res) => { res.success('noway'); }, (request) => { return request.params.success === 100; }); Parse.Cloud.run('functionWithParameterValidationFailure', {"success":500}).then((s) => { 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, response) => { expect(request.params.nullParam).toEqual(null); response.success('yay'); }); Parse.Cloud.run('func', {nullParam: null}) .then(() => { done() }, e => { fail('cloud code call failed'); done(); }); }); it('can handle date params in cloud functions (#2214)', done => { let date = new Date(); Parse.Cloud.define('dateFunc', (request, response) => { expect(request.params.date.__type).toEqual('Date'); expect(request.params.date.iso).toEqual(date.toISOString()); response.success('yay'); }); Parse.Cloud.run('dateFunc', {date: date}) .then(() => { done() }, e => { fail('cloud code call failed'); done(); }); }); it('fails on invalid client key', done => { var 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); var b = JSON.parse(body); expect(b.error).toEqual('unauthorized'); done(); }); }); it('fails on invalid windows key', done => { var 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); var b = JSON.parse(body); expect(b.error).toEqual('unauthorized'); done(); }); }); it('fails on invalid javascript key', done => { var 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); var b = JSON.parse(body); expect(b.error).toEqual('unauthorized'); done(); }); }); it('fails on invalid rest api key', done => { var 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); var b = JSON.parse(body); expect(b.error).toEqual('unauthorized'); done(); }); }); it('fails on invalid function', done => { Parse.Cloud.run('somethingThatDoesDefinitelyNotExist').then((s) => { 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) => { 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(); }); }); }); it('android login providing empty authData block works', (done) => { let headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': 'test', 'X-Parse-REST-API-Key': 'rest' }; let data = { username: 'pulse1989', password: 'password1234', authData: {} }; let requestOptions = { headers: headers, url: 'http://localhost:8378/1/users', body: JSON.stringify(data) }; request.post(requestOptions, (error, response, body) => { expect(error).toBe(null); requestOptions.url = 'http://localhost:8378/1/login'; request.get(requestOptions, (error, response, body) => { expect(error).toBe(null); let b = JSON.parse(body); expect(typeof b['sessionToken']).toEqual('string'); done(); }); }); }); it('gets relation fields', (done) => { let object = new Parse.Object('AnObject'); let relatedObject = new Parse.Object('RelatedObject'); Parse.Object.saveAll([object, relatedObject]).then(() => { object.relation('related').add(relatedObject); return object.save(); }).then(() => { let headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': 'test', 'X-Parse-REST-API-Key': 'rest' }; let requestOptions = { headers: headers, url: 'http://localhost:8378/1/classes/AnObject', json: true }; request.get(requestOptions, (err, res, body) => { expect(body.results.length).toBe(1); let 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) => { let headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': 'test', 'X-Parse-REST-API-Key': 'rest' }; let requestOptions = { headers: headers, url: 'http://localhost:8378/1/classes/AnObject', json: true }; let object = new Parse.Object('AnObject');; function runIncrement(amount) { let 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) => { let 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); let result = body.results[0]; expect(result.a).toBe('b'); done(); }) }); }); it('doesnt convert interior keys of objects that use special names', done => { let 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_exclude_dbs(['postgres'])('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 => { let file = new Parse.File('myfile.txt', { base64: 'eAo=' }); file.save() .then(f => { let 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) => { let object = new Parse.Object('TestObject'); object.set('foo', 'bar'); let object2 = new Parse.Object('TestObject'); object2.set('alice', 'wonderland'); Parse.Object.saveAll([object, object2]) .then(() => { let query = new Parse.Query(TestObject); return query.count() }).then((count) => { expect(count).toBe(2); let 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, res, body) => { expect(err).toBe(null); let 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) => { let 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(body => { 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) => { let headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': 'test', 'X-Parse-Master-Key': 'test' }; var user, object; createTestUser().then((x) => { user = x; let acl = new Parse.ACL(); acl.setPublicReadAccess(true); acl.setPublicWriteAccess(false); let role = new Parse.Object('_Role'); role.set('name', 'TestRole'); role.setACL(acl); let users = role.relation('users'); users.add(user); return role.save({}, { useMasterKey: true }); }).then((x) => { let query = new Parse.Query('_Role'); return query.find({ useMasterKey: true }); }).then((x) => { expect(x.length).toEqual(1); let relation = x[0].relation('users').query(); return relation.first({ useMasterKey: true }); }).then((x) => { expect(x.id).toEqual(user.id); object = new Parse.Object('TestObject'); let 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((x) => { let 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((x) => { let query = new Parse.Query('TestObject'); return query.get(object.id, { sessionToken: user.getSessionToken() }); }).then((x) => { fail('Should not succeed'); }, (e) => { expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); done(); }); }); it('should not update schema beforeSave #2672', (done) => { Parse.Cloud.beforeSave('MyObject', (request, response) => { if (request.object.get('secret')) { response.error('cannot set secret here'); return; } response.success(); }); let 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) => { let 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) =>  { let 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(() => { let config = new Config('test'); let adapter = config.database.adapter; return adapter._adaptiveCollection("Report") .then(collection => collection.find({})) }).then((results) => { expect(results.length).toBe(1); let 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(); }); }); });