BREAKING CHANGE: This upgrades the internally used Express framework from version 4 to 5, which may be a breaking change. If Parse Server is set up to be mounted on an Express application, we recommend to also use version 5 of the Express framework to avoid any compatibility issues. Note that even if there are no issues after upgrading, future releases of Parse Server may introduce issues if Parse Server internally relies on Express 5-specific features which are unsupported by the Express version on which it is mounted. See the Express [migration guide](https://expressjs.com/en/guide/migrating-5.html) and [release announcement](https://expressjs.com/2024/10/15/v5-release.html#breaking-changes) for more info.
729 lines
21 KiB
JavaScript
729 lines
21 KiB
JavaScript
'use strict';
|
|
|
|
const request = require('../lib/request');
|
|
const triggers = require('../lib/triggers');
|
|
const HooksController = require('../lib/Controllers/HooksController').default;
|
|
const express = require('express');
|
|
const auth = require('../lib/Auth');
|
|
const Config = require('../lib/Config');
|
|
|
|
const port = 34567;
|
|
const hookServerURL = 'http://localhost:' + port;
|
|
|
|
describe('Hooks', () => {
|
|
let server;
|
|
let app;
|
|
beforeEach(done => {
|
|
if (!app) {
|
|
app = express();
|
|
app.use(express.json({ type: '*/*' }));
|
|
server = app.listen(port, undefined, done);
|
|
} else {
|
|
done();
|
|
}
|
|
});
|
|
|
|
afterAll(done => {
|
|
server.close(done);
|
|
});
|
|
|
|
it('should have no hooks registered', done => {
|
|
Parse.Hooks.getFunctions().then(
|
|
res => {
|
|
expect(res.constructor).toBe(Array.prototype.constructor);
|
|
done();
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('should have no triggers registered', done => {
|
|
Parse.Hooks.getTriggers().then(
|
|
res => {
|
|
expect(res.constructor).toBe(Array.prototype.constructor);
|
|
done();
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)('should CRUD a function registration', done => {
|
|
// Create
|
|
Parse.Hooks.createFunction('My-Test-Function', 'http://someurl')
|
|
.then(response => {
|
|
expect(response.functionName).toBe('My-Test-Function');
|
|
expect(response.url).toBe('http://someurl');
|
|
// Find
|
|
return Parse.Hooks.getFunction('My-Test-Function');
|
|
})
|
|
.then(response => {
|
|
expect(response.objectId).toBeUndefined();
|
|
expect(response.url).toBe('http://someurl');
|
|
return Parse.Hooks.updateFunction('My-Test-Function', 'http://anotherurl');
|
|
})
|
|
.then(res => {
|
|
expect(res.objectId).toBeUndefined();
|
|
expect(res.functionName).toBe('My-Test-Function');
|
|
expect(res.url).toBe('http://anotherurl');
|
|
// delete
|
|
return Parse.Hooks.removeFunction('My-Test-Function');
|
|
})
|
|
.then(() => {
|
|
// Find again! but should be deleted
|
|
return Parse.Hooks.getFunction('My-Test-Function').then(
|
|
res => {
|
|
fail('Failed to delete hook');
|
|
fail(res);
|
|
done();
|
|
return Promise.resolve();
|
|
},
|
|
err => {
|
|
expect(err.code).toBe(143);
|
|
expect(err.message).toBe('no function named: My-Test-Function is defined');
|
|
done();
|
|
return Promise.resolve();
|
|
}
|
|
);
|
|
})
|
|
.catch(error => {
|
|
jfail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_id('7a81069e-2ee9-47fb-8e27-1120eda09e99')(it)('should CRUD a trigger registration', done => {
|
|
// Create
|
|
Parse.Hooks.createTrigger('MyClass', 'beforeDelete', 'http://someurl')
|
|
.then(
|
|
res => {
|
|
expect(res.className).toBe('MyClass');
|
|
expect(res.triggerName).toBe('beforeDelete');
|
|
expect(res.url).toBe('http://someurl');
|
|
// Find
|
|
return Parse.Hooks.getTrigger('MyClass', 'beforeDelete');
|
|
},
|
|
err => {
|
|
fail(err);
|
|
done();
|
|
}
|
|
)
|
|
.then(
|
|
res => {
|
|
expect(res).not.toBe(null);
|
|
expect(res).not.toBe(undefined);
|
|
expect(res.objectId).toBeUndefined();
|
|
expect(res.url).toBe('http://someurl');
|
|
// delete
|
|
return Parse.Hooks.updateTrigger('MyClass', 'beforeDelete', 'http://anotherurl');
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
done();
|
|
}
|
|
)
|
|
.then(
|
|
res => {
|
|
expect(res.className).toBe('MyClass');
|
|
expect(res.url).toBe('http://anotherurl');
|
|
expect(res.objectId).toBeUndefined();
|
|
|
|
return Parse.Hooks.removeTrigger('MyClass', 'beforeDelete');
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
done();
|
|
}
|
|
)
|
|
.then(
|
|
() => {
|
|
// Find again! but should be deleted
|
|
return Parse.Hooks.getTrigger('MyClass', 'beforeDelete');
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
done();
|
|
}
|
|
)
|
|
.then(
|
|
function () {
|
|
fail('should not succeed');
|
|
done();
|
|
},
|
|
err => {
|
|
if (err) {
|
|
expect(err).not.toBe(null);
|
|
expect(err).not.toBe(undefined);
|
|
expect(err.code).toBe(143);
|
|
expect(err.message).toBe('class MyClass does not exist');
|
|
} else {
|
|
fail('should have errored');
|
|
}
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('should fail to register hooks without Master Key', done => {
|
|
request({
|
|
method: 'POST',
|
|
url: Parse.serverURL + '/hooks/functions',
|
|
headers: {
|
|
'X-Parse-Application-Id': Parse.applicationId,
|
|
},
|
|
body: JSON.stringify({
|
|
url: 'http://hello.word',
|
|
functionName: 'SomeFunction',
|
|
}),
|
|
}).then(fail, response => {
|
|
const body = response.data;
|
|
expect(body.error).toBe('unauthorized');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_id('f7ad092f-81dc-4729-afd1-3b02db2f0948')(it)('should fail trying to create two times the same function', done => {
|
|
Parse.Hooks.createFunction('my_new_function', 'http://url.com')
|
|
.then(() => jasmine.timeout())
|
|
.then(
|
|
() => {
|
|
return Parse.Hooks.createFunction('my_new_function', 'http://url.com');
|
|
},
|
|
() => {
|
|
fail('should create a new function');
|
|
}
|
|
)
|
|
.then(
|
|
() => {
|
|
fail('should not be able to create the same function');
|
|
},
|
|
err => {
|
|
expect(err).not.toBe(undefined);
|
|
expect(err).not.toBe(null);
|
|
if (err) {
|
|
expect(err.code).toBe(143);
|
|
expect(err.message).toBe('function name: my_new_function already exists');
|
|
}
|
|
return Parse.Hooks.removeFunction('my_new_function');
|
|
}
|
|
)
|
|
.then(
|
|
() => {
|
|
done();
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it_id('4db8c249-9174-4e8e-b959-55c8ea959a02')(it)('should fail trying to create two times the same trigger', done => {
|
|
Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com')
|
|
.then(
|
|
() => {
|
|
return Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com');
|
|
},
|
|
() => {
|
|
fail('should create a new trigger');
|
|
}
|
|
)
|
|
.then(
|
|
() => {
|
|
fail('should not be able to create the same trigger');
|
|
},
|
|
err => {
|
|
expect(err).not.toBe(undefined);
|
|
expect(err).not.toBe(null);
|
|
if (err) {
|
|
expect(err.code).toBe(143);
|
|
expect(err.message).toBe('class MyClass already has trigger beforeSave');
|
|
}
|
|
return Parse.Hooks.removeTrigger('MyClass', 'beforeSave');
|
|
}
|
|
)
|
|
.then(
|
|
() => {
|
|
done();
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it("should fail trying to update a function that don't exist", done => {
|
|
Parse.Hooks.updateFunction('A_COOL_FUNCTION', 'http://url.com')
|
|
.then(
|
|
() => {
|
|
fail('Should not succeed');
|
|
},
|
|
err => {
|
|
expect(err).not.toBe(undefined);
|
|
expect(err).not.toBe(null);
|
|
if (err) {
|
|
expect(err.code).toBe(143);
|
|
expect(err.message).toBe('no function named: A_COOL_FUNCTION is defined');
|
|
}
|
|
return Parse.Hooks.getFunction('A_COOL_FUNCTION');
|
|
}
|
|
)
|
|
.then(
|
|
() => {
|
|
fail('the function should not exist');
|
|
done();
|
|
},
|
|
err => {
|
|
expect(err).not.toBe(undefined);
|
|
expect(err).not.toBe(null);
|
|
if (err) {
|
|
expect(err.code).toBe(143);
|
|
expect(err.message).toBe('no function named: A_COOL_FUNCTION is defined');
|
|
}
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it("should fail trying to update a trigger that don't exist", done => {
|
|
Parse.Hooks.updateTrigger('AClassName', 'beforeSave', 'http://url.com')
|
|
.then(
|
|
() => {
|
|
fail('Should not succeed');
|
|
},
|
|
err => {
|
|
expect(err).not.toBe(undefined);
|
|
expect(err).not.toBe(null);
|
|
if (err) {
|
|
expect(err.code).toBe(143);
|
|
expect(err.message).toBe('class AClassName does not exist');
|
|
}
|
|
return Parse.Hooks.getTrigger('AClassName', 'beforeSave');
|
|
}
|
|
)
|
|
.then(
|
|
() => {
|
|
fail('the function should not exist');
|
|
done();
|
|
},
|
|
err => {
|
|
expect(err).not.toBe(undefined);
|
|
expect(err).not.toBe(null);
|
|
if (err) {
|
|
expect(err.code).toBe(143);
|
|
expect(err.message).toBe('class AClassName does not exist');
|
|
}
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('should fail trying to create a malformed function', done => {
|
|
Parse.Hooks.createFunction('MyFunction').then(
|
|
res => {
|
|
fail(res);
|
|
},
|
|
err => {
|
|
expect(err).not.toBe(undefined);
|
|
expect(err).not.toBe(null);
|
|
if (err) {
|
|
expect(err.code).toBe(143);
|
|
expect(err.error).toBe('invalid hook declaration');
|
|
}
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('should fail trying to create a malformed function (REST)', done => {
|
|
request({
|
|
method: 'POST',
|
|
url: Parse.serverURL + '/hooks/functions',
|
|
headers: {
|
|
'X-Parse-Application-Id': Parse.applicationId,
|
|
'X-Parse-Master-Key': Parse.masterKey,
|
|
},
|
|
body: JSON.stringify({ functionName: 'SomeFunction' }),
|
|
}).then(fail, response => {
|
|
const body = response.data;
|
|
expect(body.error).toBe('invalid hook declaration');
|
|
expect(body.code).toBe(143);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_id('96d99414-b739-4e36-b3f4-8135e0be83ea')(it)('should create hooks and properly preload them', done => {
|
|
const promises = [];
|
|
for (let i = 0; i < 5; i++) {
|
|
promises.push(
|
|
Parse.Hooks.createTrigger('MyClass' + i, 'beforeSave', 'http://url.com/beforeSave/' + i)
|
|
);
|
|
promises.push(Parse.Hooks.createFunction('AFunction' + i, 'http://url.com/function' + i));
|
|
}
|
|
|
|
Promise.all(promises)
|
|
.then(
|
|
function () {
|
|
for (let i = 0; i < 5; i++) {
|
|
// Delete everything from memory, as the server just started
|
|
triggers.removeTrigger('beforeSave', 'MyClass' + i, Parse.applicationId);
|
|
triggers.removeFunction('AFunction' + i, Parse.applicationId);
|
|
expect(
|
|
triggers.getTrigger('MyClass' + i, 'beforeSave', Parse.applicationId)
|
|
).toBeUndefined();
|
|
expect(triggers.getFunction('AFunction' + i, Parse.applicationId)).toBeUndefined();
|
|
}
|
|
const hooksController = new HooksController(
|
|
Parse.applicationId,
|
|
Config.get('test').database
|
|
);
|
|
return hooksController.load();
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
fail('Should properly create all hooks');
|
|
done();
|
|
}
|
|
)
|
|
.then(
|
|
function () {
|
|
for (let i = 0; i < 5; i++) {
|
|
expect(
|
|
triggers.getTrigger('MyClass' + i, 'beforeSave', Parse.applicationId)
|
|
).not.toBeUndefined();
|
|
expect(triggers.getFunction('AFunction' + i, Parse.applicationId)).not.toBeUndefined();
|
|
}
|
|
done();
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
fail('should properly load all hooks');
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it_id('fe7d41eb-e570-4804-ac1f-8b6c407fdafe')(it)('should run the function on the test server', done => {
|
|
app.post('/SomeFunction', function (req, res) {
|
|
res.json({ success: 'OK!' });
|
|
});
|
|
|
|
Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/SomeFunction')
|
|
.then(
|
|
function () {
|
|
return Parse.Cloud.run('SOME_TEST_FUNCTION');
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
fail('Should not fail creating a function');
|
|
done();
|
|
}
|
|
)
|
|
.then(
|
|
function (res) {
|
|
expect(res).toBe('OK!');
|
|
done();
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
fail('Should not fail calling a function');
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it_id('63985b4c-a212-4a86-aa0e-eb4600bb485b')(it)('should run the function on the test server (error handling)', done => {
|
|
app.post('/SomeFunctionError', function (req, res) {
|
|
res.json({ error: { code: 1337, error: 'hacking that one!' } });
|
|
});
|
|
// The function is deleted as the DB is dropped between calls
|
|
Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/SomeFunctionError')
|
|
.then(
|
|
function () {
|
|
return Parse.Cloud.run('SOME_TEST_FUNCTION');
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
fail('Should not fail creating a function');
|
|
done();
|
|
}
|
|
)
|
|
.then(
|
|
function () {
|
|
fail('Should not succeed calling that function');
|
|
done();
|
|
},
|
|
err => {
|
|
expect(err).not.toBe(undefined);
|
|
expect(err).not.toBe(null);
|
|
if (err) {
|
|
expect(err.code).toBe(Parse.Error.SCRIPT_FAILED);
|
|
expect(err.message.code).toEqual(1337);
|
|
expect(err.message.error).toEqual('hacking that one!');
|
|
}
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it_id('bacc1754-2a3a-4a7a-8d0e-f80af36da1ef')(it)('should provide X-Parse-Webhook-Key when defined', done => {
|
|
app.post('/ExpectingKey', function (req, res) {
|
|
if (req.get('X-Parse-Webhook-Key') === 'hook') {
|
|
res.json({ success: 'correct key provided' });
|
|
} else {
|
|
res.json({ error: 'incorrect key provided' });
|
|
}
|
|
});
|
|
|
|
Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/ExpectingKey')
|
|
.then(
|
|
function () {
|
|
return Parse.Cloud.run('SOME_TEST_FUNCTION');
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
fail('Should not fail creating a function');
|
|
done();
|
|
}
|
|
)
|
|
.then(
|
|
function (res) {
|
|
expect(res).toBe('correct key provided');
|
|
done();
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
fail('Should not fail calling a function');
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it_id('eeb67946-42c6-4581-89af-2abb4927913e')(it)('should not pass X-Parse-Webhook-Key if not provided', done => {
|
|
reconfigureServer({ webhookKey: undefined }).then(() => {
|
|
app.post('/ExpectingKeyAlso', function (req, res) {
|
|
if (req.get('X-Parse-Webhook-Key') === 'hook') {
|
|
res.json({ success: 'correct key provided' });
|
|
} else {
|
|
res.json({ error: 'incorrect key provided' });
|
|
}
|
|
});
|
|
|
|
Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/ExpectingKeyAlso')
|
|
.then(
|
|
function () {
|
|
return Parse.Cloud.run('SOME_TEST_FUNCTION');
|
|
},
|
|
err => {
|
|
jfail(err);
|
|
fail('Should not fail creating a function');
|
|
done();
|
|
}
|
|
)
|
|
.then(
|
|
function () {
|
|
fail('Should not succeed calling that function');
|
|
done();
|
|
},
|
|
err => {
|
|
expect(err).not.toBe(undefined);
|
|
expect(err).not.toBe(null);
|
|
if (err) {
|
|
expect(err.code).toBe(Parse.Error.SCRIPT_FAILED);
|
|
expect(err.message).toEqual('incorrect key provided');
|
|
}
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
it_id('21decb65-4b93-4791-85a3-ab124a9ea3ac')(it)('should run the beforeSave hook on the test server', done => {
|
|
let triggerCount = 0;
|
|
app.post('/BeforeSaveSome', function (req, res) {
|
|
triggerCount++;
|
|
const object = req.body.object;
|
|
object.hello = 'world';
|
|
// Would need parse cloud express to set much more
|
|
// But this should override the key upon return
|
|
res.json({ success: object });
|
|
});
|
|
// The function is deleted as the DB is dropped between calls
|
|
Parse.Hooks.createTrigger('SomeRandomObject', 'beforeSave', hookServerURL + '/BeforeSaveSome')
|
|
.then(function () {
|
|
const obj = new Parse.Object('SomeRandomObject');
|
|
return obj.save();
|
|
})
|
|
.then(function (res) {
|
|
expect(triggerCount).toBe(1);
|
|
return res.fetch();
|
|
})
|
|
.then(function (res) {
|
|
expect(res.get('hello')).toEqual('world');
|
|
done();
|
|
})
|
|
.catch(err => {
|
|
jfail(err);
|
|
fail('Should not fail creating a function');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_id('52e3152b-5514-4418-9e76-1f394368b8fb')(it)('beforeSave hooks should correctly handle responses containing entire object', done => {
|
|
app.post('/BeforeSaveSome2', function (req, res) {
|
|
const object = Parse.Object.fromJSON(req.body.object);
|
|
object.set('hello', 'world');
|
|
res.json({ success: object });
|
|
});
|
|
Parse.Hooks.createTrigger('SomeRandomObject2', 'beforeSave', hookServerURL + '/BeforeSaveSome2')
|
|
.then(function () {
|
|
const obj = new Parse.Object('SomeRandomObject2');
|
|
return obj.save();
|
|
})
|
|
.then(function (res) {
|
|
return res.save();
|
|
})
|
|
.then(function (res) {
|
|
expect(res.get('hello')).toEqual('world');
|
|
done();
|
|
})
|
|
.catch(err => {
|
|
fail(`Should not fail: ${JSON.stringify(err)}`);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_id('d27a7587-abb5-40d5-9805-051ee91de474')(it)('should run the afterSave hook on the test server', done => {
|
|
let triggerCount = 0;
|
|
let newObjectId;
|
|
app.post('/AfterSaveSome', function (req, res) {
|
|
triggerCount++;
|
|
const obj = new Parse.Object('AnotherObject');
|
|
obj.set('foo', 'bar');
|
|
obj.save().then(function (obj) {
|
|
newObjectId = obj.id;
|
|
res.json({ success: {} });
|
|
});
|
|
});
|
|
// The function is deleted as the DB is dropped between calls
|
|
Parse.Hooks.createTrigger('SomeRandomObject', 'afterSave', hookServerURL + '/AfterSaveSome')
|
|
.then(function () {
|
|
const obj = new Parse.Object('SomeRandomObject');
|
|
return obj.save();
|
|
})
|
|
.then(function () {
|
|
return new Promise(resolve => {
|
|
setTimeout(() => {
|
|
expect(triggerCount).toBe(1);
|
|
new Parse.Query('AnotherObject').get(newObjectId).then(r => resolve(r));
|
|
}, 500);
|
|
});
|
|
})
|
|
.then(function (res) {
|
|
expect(res.get('foo')).toEqual('bar');
|
|
done();
|
|
})
|
|
.catch(err => {
|
|
jfail(err);
|
|
fail('Should not fail creating a function');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('triggers', () => {
|
|
it('should produce a proper request object with context in beforeSave', () => {
|
|
const config = Config.get('test');
|
|
const master = auth.master(config);
|
|
const context = {
|
|
originalKey: 'original',
|
|
};
|
|
const req = triggers.getRequestObject(
|
|
triggers.Types.beforeSave,
|
|
master,
|
|
{},
|
|
{},
|
|
config,
|
|
context
|
|
);
|
|
expect(req.context.originalKey).toBe('original');
|
|
req.context = {
|
|
key: 'value',
|
|
};
|
|
expect(context.key).toBe(undefined);
|
|
req.context = {
|
|
key: 'newValue',
|
|
};
|
|
expect(context.key).toBe(undefined);
|
|
});
|
|
|
|
it('should produce a proper request object with context in afterSave', () => {
|
|
const config = Config.get('test');
|
|
const master = auth.master(config);
|
|
const context = {};
|
|
const req = triggers.getRequestObject(
|
|
triggers.Types.afterSave,
|
|
master,
|
|
{},
|
|
{},
|
|
config,
|
|
context
|
|
);
|
|
expect(req.context).not.toBeUndefined();
|
|
});
|
|
|
|
it('should not set context on beforeFind', () => {
|
|
const config = Config.get('test');
|
|
const master = auth.master(config);
|
|
const context = {};
|
|
const req = triggers.getRequestObject(
|
|
triggers.Types.beforeFind,
|
|
master,
|
|
{},
|
|
{},
|
|
config,
|
|
context
|
|
);
|
|
expect(req.context).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('sanitizing names', () => {
|
|
const invalidNames = [
|
|
`test'%3bdeclare%20@q%20varchar(99)%3bset%20@q%3d'%5c%5cxxxxxxxxxxxxxxx.yyyyy'%2b'fy.com%5cxus'%3b%20exec%20master.dbo.xp_dirtree%20@q%3b--%20`,
|
|
`test.function.name`,
|
|
];
|
|
|
|
it('should not crash server and return error on invalid Cloud Function name', async () => {
|
|
for (const invalidName of invalidNames) {
|
|
let error;
|
|
try {
|
|
await Parse.Cloud.run(invalidName);
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
expect(error).toBeDefined();
|
|
expect(error.message).toMatch(/Invalid function/);
|
|
}
|
|
});
|
|
|
|
it('should not crash server and return error on invalid Cloud Job name', async () => {
|
|
for (const invalidName of invalidNames) {
|
|
let error;
|
|
try {
|
|
await Parse.Cloud.startJob(invalidName);
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
expect(error).toBeDefined();
|
|
expect(error.message).toMatch(/Invalid job/);
|
|
}
|
|
});
|
|
});
|