* reload the right data More passing postgres tests Handle schema updates, and $in for non array columns remove authdata from user and implement ensureUniqueness Make some tests work, detect existing classes Throw proper error for unique index violation fix findOneAndUpdate Support more types support more type Support boolean, fix _rperm/_wperm, add TODO Support string types and also simplify tests Move operator flattening into Parse Server and out of mongo adapters Move authdata transform for create into Parse Server Move authdata transforms completely in to Parse Server Fix test setup inline addSchema Inject default schema to response from DB adapter * Mark tests that don't work in Postgres * Exclude one more test * Exclude some more failing tests * Exclude more tests
462 lines
16 KiB
JavaScript
462 lines
16 KiB
JavaScript
"use strict";
|
|
/* global describe, it, expect, fail, Parse */
|
|
var request = require('request');
|
|
var triggers = require('../src/triggers');
|
|
var HooksController = require('../src/Controllers/HooksController').default;
|
|
var express = require("express");
|
|
var bodyParser = require('body-parser');
|
|
// Inject the hooks API
|
|
Parse.Hooks = require("../src/cloud-code/Parse.Hooks");
|
|
|
|
var port = 12345;
|
|
var hookServerURL = "http://localhost:"+port;
|
|
let AppCache = require('../src/cache').AppCache;
|
|
|
|
var app = express();
|
|
app.use(bodyParser.json({ 'type': '*/*' }))
|
|
app.listen(12345);
|
|
|
|
describe('Hooks', () => {
|
|
it_exclude_dbs(['postgres'])("should have no hooks registered", (done) => {
|
|
Parse.Hooks.getFunctions().then((res) => {
|
|
expect(res.constructor).toBe(Array.prototype.constructor);
|
|
done();
|
|
}, (err) => {
|
|
fail(err);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("should have no triggers registered", (done) => {
|
|
Parse.Hooks.getTriggers().then( (res) => {
|
|
expect(res.constructor).toBe(Array.prototype.constructor);
|
|
done();
|
|
}, (err) => {
|
|
fail(err);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("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.deleteFunction("My-Test-Function")
|
|
})
|
|
.then((res) => {
|
|
// 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.error).toBe("no function named: My-Test-Function is defined")
|
|
done();
|
|
return Promise.resolve();
|
|
})
|
|
})
|
|
.catch(error => {
|
|
fail(error);
|
|
done();
|
|
})
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("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) => {
|
|
fail(err);
|
|
done();
|
|
}).then((res) => {
|
|
expect(res.className).toBe("MyClass");
|
|
expect(res.url).toBe("http://anotherurl")
|
|
expect(res.objectId).toBeUndefined();
|
|
|
|
return Parse.Hooks.deleteTrigger("MyClass","beforeDelete");
|
|
}, (err) => {
|
|
fail(err);
|
|
done();
|
|
}).then((res) => {
|
|
// Find again! but should be deleted
|
|
return Parse.Hooks.getTrigger("MyClass","beforeDelete");
|
|
}, (err) => {
|
|
fail(err);
|
|
done();
|
|
}).then(function(){
|
|
fail("should not succeed");
|
|
done();
|
|
}, (err) => {
|
|
expect(err).not.toBe(null);
|
|
expect(err).not.toBe(undefined);
|
|
expect(err.code).toBe(143);
|
|
expect(err.error).toBe("class MyClass does not exist")
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should fail to register hooks without Master Key", (done) => {
|
|
request.post(Parse.serverURL+"/hooks/functions", {
|
|
headers: {
|
|
"X-Parse-Application-Id": Parse.applicationId,
|
|
"X-Parse-REST-API-Key": Parse.restKey,
|
|
},
|
|
body: JSON.stringify({ url: "http://hello.word", functionName: "SomeFunction"})
|
|
}, (err, res, body) => {
|
|
body = JSON.parse(body);
|
|
expect(body.error).toBe("unauthorized");
|
|
done();
|
|
})
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("should fail trying to create two times the same function", (done) => {
|
|
Parse.Hooks.createFunction("my_new_function", "http://url.com").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);
|
|
expect(err.code).toBe(143);
|
|
expect(err.error).toBe('function name: my_new_function already exits')
|
|
return Parse.Hooks.deleteFunction("my_new_function");
|
|
}).then(() => {
|
|
done();
|
|
}, (err) => {
|
|
fail(err);
|
|
done();
|
|
})
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("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.code).toBe(143);
|
|
expect(err.error).toBe('class MyClass already has trigger beforeSave')
|
|
return Parse.Hooks.deleteTrigger("MyClass", "beforeSave");
|
|
}).then(() => {
|
|
done();
|
|
}, (err) => {
|
|
fail(err);
|
|
done();
|
|
})
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("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.code).toBe(143);
|
|
expect(err.error).toBe('no function named: A_COOL_FUNCTION is defined');
|
|
return Parse.Hooks.getFunction("A_COOL_FUNCTION")
|
|
}).then( (res) => {
|
|
fail("the function should not exist");
|
|
done();
|
|
}, (err) => {
|
|
expect(err.code).toBe(143);
|
|
expect(err.error).toBe('no function named: A_COOL_FUNCTION is defined');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("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.code).toBe(143);
|
|
expect(err.error).toBe('class AClassName does not exist');
|
|
return Parse.Hooks.getTrigger("AClassName","beforeSave")
|
|
}).then( (res) => {
|
|
fail("the function should not exist");
|
|
done();
|
|
}, (err) => {
|
|
expect(err.code).toBe(143);
|
|
expect(err.error).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.code).toBe(143);
|
|
expect(err.error).toBe("invalid hook declaration");
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should fail trying to create a malformed function (REST)", (done) => {
|
|
request.post(Parse.serverURL+"/hooks/functions", {
|
|
headers: {
|
|
"X-Parse-Application-Id": Parse.applicationId,
|
|
"X-Parse-Master-Key": Parse.masterKey,
|
|
},
|
|
body: JSON.stringify({ functionName: "SomeFunction"})
|
|
}, (err, res, body) => {
|
|
body = JSON.parse(body);
|
|
expect(body.error).toBe("invalid hook declaration");
|
|
expect(body.code).toBe(143);
|
|
done();
|
|
})
|
|
});
|
|
|
|
|
|
it_exclude_dbs(['postgres'])("should create hooks and properly preload them", (done) => {
|
|
|
|
var promises = [];
|
|
for (var 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));
|
|
}
|
|
|
|
Parse.Promise.when(promises).then(function(results){
|
|
for (var 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, AppCache.get('test').databaseController);
|
|
return hooksController.load()
|
|
}, (err) => {
|
|
console.error(err);
|
|
fail('Should properly create all hooks');
|
|
done();
|
|
}).then(function() {
|
|
for (var 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) => {
|
|
console.error(err);
|
|
fail('should properly load all hooks');
|
|
done();
|
|
})
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("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) => {
|
|
console.error(err);
|
|
fail("Should not fail creating a function");
|
|
done();
|
|
}).then(function(res){
|
|
expect(res).toBe("OK!");
|
|
done();
|
|
}, (err) => {
|
|
console.error(err);
|
|
fail("Should not fail calling a function");
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("should run the function on the test server", (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) => {
|
|
console.error(err);
|
|
fail("Should not fail creating a function");
|
|
done();
|
|
}).then(function(res){
|
|
fail("Should not succeed calling that function");
|
|
done();
|
|
}, (err) => {
|
|
expect(err.code).toBe(141);
|
|
expect(err.message.code).toEqual(1337)
|
|
expect(err.message.error).toEqual("hacking that one!");
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("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) => {
|
|
console.error(err);
|
|
fail("Should not fail creating a function");
|
|
done();
|
|
}).then(function(res){
|
|
expect(res).toBe("correct key provided");
|
|
done();
|
|
}, (err) => {
|
|
console.error(err);
|
|
fail("Should not fail calling a function");
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("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) => {
|
|
console.error(err);
|
|
fail("Should not fail creating a function");
|
|
done();
|
|
}).then(function(res){
|
|
fail("Should not succeed calling that function");
|
|
done();
|
|
}, (err) => {
|
|
expect(err.code).toBe(141);
|
|
expect(err.message).toEqual("incorrect key provided");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
it_exclude_dbs(['postgres'])("should run the beforeSave hook on the test server", (done) => {
|
|
var triggerCount = 0;
|
|
app.post("/BeforeSaveSome", function(req, res) {
|
|
triggerCount++;
|
|
var 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 delete 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();
|
|
}).fail((err) => {
|
|
console.error(err);
|
|
fail("Should not fail creating a function");
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("beforeSave hooks should correctly handle responses containing entire object", (done) => {
|
|
app.post("/BeforeSaveSome2", function(req, res) {
|
|
var 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();
|
|
}).fail((err) => {
|
|
fail(`Should not fail: ${JSON.stringify(err)}`);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it_exclude_dbs(['postgres'])("should run the afterSave hook on the test server", (done) => {
|
|
var triggerCount = 0;
|
|
var newObjectId;
|
|
app.post("/AfterSaveSome", function(req, res) {
|
|
triggerCount++;
|
|
var obj = new Parse.Object("AnotherObject");
|
|
obj.set("foo", "bar");
|
|
obj.save().then(function(obj){
|
|
newObjectId = obj.id;
|
|
res.json({success: {}});
|
|
})
|
|
});
|
|
// The function is delete 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(res){
|
|
var promise = new Parse.Promise();
|
|
// Wait a bit here as it's an after save
|
|
setTimeout(function(){
|
|
expect(triggerCount).toBe(1);
|
|
var q = new Parse.Query("AnotherObject");
|
|
q.get(newObjectId).then(function(r){
|
|
promise.resolve(r);
|
|
});
|
|
}, 300)
|
|
return promise;
|
|
}).then(function(res){
|
|
expect(res.get("foo")).toEqual("bar");
|
|
done();
|
|
}).fail((err) => {
|
|
console.error(err);
|
|
fail("Should not fail creating a function");
|
|
done();
|
|
});
|
|
});
|
|
});
|