1
0
Fork 0
arangodb/tests/js/server/shell/shell-foxx.js

1798 lines
51 KiB
JavaScript

/*jshint globalstrict:false, strict:false, unused:false */
/*global assertEqual, assertTrue, assertFalse, assertUndefined */
////////////////////////////////////////////////////////////////////////////////
/// @brief simple queries
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Lucas Dohmen
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
// Stubbing and Mocking
var stub,
allow,
FunctionStub,
mockConstructor,
joi = require("joi"),
transformRoute = require("@arangodb/foxx/legacy/routing").__test_transformControllerToRoute,
_ = require("lodash");
// Sorry for Yak Shaving. But I can't take it anymore.
// x = stub();
stub = function () {
'use strict';
return function() {};
};
// allow(x)
// .toReceive("functionName")
// .andReturn({ x: 1 })
FunctionStub = function(obj) {
'use strict';
this.obj = obj;
};
Object.assign(FunctionStub.prototype, {
toReceive: function (functionName) {
'use strict';
this.functionName = functionName;
this.buildFunctionStub();
return this;
},
andReturn: function (returnValue) {
'use strict';
this.returnValue = returnValue;
this.buildFunctionStub();
return this;
},
buildFunctionStub: function () {
'use strict';
var returnValue = this.returnValue;
this.obj[this.functionName] = function () {
return returnValue;
};
}
});
allow = function(obj) {
'use strict';
return (new FunctionStub(obj));
};
/* Create a Mock Constructor
*
* Give the arguments you expect to mockConstructor:
* It checks, if it was called with new and the
* correct arguments.
*
* MyProto = mockConstructor(test);
*
* a = new MyProto(test);
*
* MyProto.assertIsSatisfied();
*/
mockConstructor = function () {
'use strict';
var expectedArguments = arguments,
satisfied = false,
MockConstructor = function () {
if (this.constructor === MockConstructor) {
// Was called as a constructor
satisfied = _.isEqual(arguments, expectedArguments);
}
};
MockConstructor.assertIsSatisfied = function () {
assertTrue(satisfied);
};
return MockConstructor;
};
var jsunity = require("jsunity"),
FoxxController = require("@arangodb/foxx/legacy").Controller,
fakeContext,
fakeContextWithRootElement;
fakeContext = {
prefix: "",
foxxes: [],
manifest: {
rootElement: false
},
collectionName() {}
};
fakeContextWithRootElement = {
prefix: "",
foxxes: [],
manifest: {
rootElement: true
},
collectionName() {}
};
function CreateFoxxControllerSpec () {
'use strict';
return {
testCreationWithoutParameters: function () {
var app = new FoxxController(fakeContext),
routingInfo = app.routingInfo;
assertEqual(routingInfo.routes.length, 0);
assertEqual(routingInfo.urlPrefix, "");
},
testCreationWithURLPrefix: function () {
var app = new FoxxController(fakeContext, {urlPrefix: "/wiese"}),
routingInfo = app.routingInfo;
assertEqual(routingInfo.routes.length, 0);
assertEqual(routingInfo.urlPrefix, "/wiese");
},
testAdditionOfBaseMiddlewareInRoutingInfo: function () {
var app = new FoxxController(fakeContext),
routingInfo = app.routingInfo,
hopefully_base = routingInfo.middleware[0];
assertEqual(routingInfo.middleware.length, 1);
assertEqual(hopefully_base.url.match, "/*");
}
};
}
function SetRoutesFoxxControllerSpec () {
'use strict';
var app;
return {
setUp: function () {
app = new FoxxController(fakeContext);
},
testSettingRoutes: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.get('/simple/route', myFunc);
assertEqual(routes.length, 1);
assertEqual(routes[0].url.match, '/simple/route');
assertUndefined(routes[0].url.constraint);
},
testSetMethodToHead: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.head('/simple/route', myFunc);
assertEqual(routes[0].docs.httpMethod, 'HEAD');
assertEqual(routes[0].url.methods, ["head"]);
},
testSetMethodToGet: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.get('/simple/route', myFunc);
assertEqual(routes[0].docs.httpMethod, 'GET');
assertEqual(routes[0].url.methods, ["get"]);
},
testSetMethodToPost: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.post('/simple/route', myFunc);
assertEqual(routes[0].docs.httpMethod, 'POST');
assertEqual(routes[0].url.methods, ["post"]);
},
testSetMethodToPut: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.put('/simple/route', myFunc);
assertEqual(routes[0].docs.httpMethod, 'PUT');
assertEqual(routes[0].url.methods, ["put"]);
},
testSetMethodToPatch: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.patch('/simple/route', myFunc);
assertEqual(routes[0].docs.httpMethod, 'PATCH');
assertEqual(routes[0].url.methods, ["patch"]);
},
testSetMethodToDelete: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app['delete']('/simple/route', myFunc);
assertEqual(routes[0].docs.httpMethod, 'DELETE');
assertEqual(routes[0].url.methods, ["delete"]);
},
testSetMethodToDeleteViaAlias: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.del('/simple/route', myFunc);
assertEqual(routes[0].docs.httpMethod, 'DELETE');
assertEqual(routes[0].url.methods, ["delete"]);
},
testRefuseRoutesWithRoutesThatAreNumbers: function () {
var myFunc = function () {},
routes = app.routingInfo.routes,
error;
try {
app.get(2, myFunc);
} catch(e) {
error = e;
}
assertEqual(error, new Error("URL has to be a String"));
assertEqual(routes.length, 0);
},
testRefuseRoutesWithRoutesThatAreRegularExpressions: function () {
var myFunc = function () {},
routes = app.routingInfo.routes,
error;
try {
app.get(/[a-z]*/, myFunc);
} catch(e) {
error = e;
}
assertEqual(error, new Error("URL has to be a String"));
assertEqual(routes.length, 0);
},
testRefuseRoutesWithRoutesThatAreArrays: function () {
var myFunc = function () {},
routes = app.routingInfo.routes,
error;
try {
app.get(["/a", "/b"], myFunc);
} catch(e) {
error = e;
}
assertEqual(error, new Error("URL has to be a String"));
assertEqual(routes.length, 0);
},
testAddALoginRoute: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.activateAuthentication({
type: "cookie",
cookieLifetime: 360000,
cookieName: "my_cookie",
sessionLifetime: 400
});
app.login('/simple/route', myFunc);
assertEqual(routes[0].docs.httpMethod, 'POST');
assertEqual(routes[0].url.methods, ["post"]);
},
testRefuseLoginWhenAuthIsNotSetUp: function () {
var myFunc = function () {},
error;
try {
app.login('/simple/route', myFunc);
} catch(e) {
error = e;
}
assertEqual(error, new Error("Setup authentication first"));
},
testAddALogoutRoute: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.activateAuthentication({
type: "cookie",
cookieLifetime: 360000,
cookieName: "my_cookie",
sessionLifetime: 400
});
app.logout('/simple/route', myFunc);
assertEqual(routes[0].docs.httpMethod, 'POST');
assertEqual(routes[0].url.methods, ["post"]);
},
testRefuseLogoutWhenAuthIsNotSetUp: function () {
var myFunc = function () {},
error;
try {
app.logout('/simple/route', myFunc);
} catch(e) {
error = e;
}
assertEqual(error, new Error("Setup authentication first"));
},
testAddADestroySessionRoute: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.activateSessions({
sessionStorage: 'sessions',
cookie: {
name: 'sid',
secret: 'secret'
}
});
app.destroySession('/simple/route', myFunc);
assertEqual(routes[0].docs.httpMethod, 'POST');
assertEqual(routes[0].url.methods, ["post"]);
},
testRefuseDestroySessionWhenSessionsAreNotSetUp: function () {
var myFunc = function () {},
error;
try {
app.destroySession('/simple/route', myFunc);
} catch(e) {
error = e;
}
assertEqual(error, new Error("Setup sessions first"));
}
};
}
function ControllerInjectionSpec () {
'use strict';
return {
testInjectFactoryFunction: function () {
var app = new FoxxController(fakeContext),
req = {},
res = {},
timesCalled = 0;
app.addInjector({thing: function() {timesCalled++;}});
app.get('/foxx', function () {});
var callback = transformRoute(app.routingInfo.routes[0].action);
callback(req, res);
callback(req, res);
assertEqual(timesCalled, 1);
},
testInjectNameValue: function () {
var app = new FoxxController(fakeContext),
req = {},
res = {},
timesCalled = 0;
app.addInjector('thing', function() {timesCalled++;});
app.get('/foxx', function () {});
var callback = transformRoute(app.routingInfo.routes[0].action);
callback(req, res);
callback(req, res);
assertEqual(timesCalled, 1);
},
testInjectOverwrite: function () {
var app = new FoxxController(fakeContext),
req = {},
res = {},
wrongFuncCalled = false,
timesCalled = 0;
app.addInjector({thing: function() {wrongFuncCalled = true;}});
app.addInjector({thing: function() {timesCalled++;}});
app.get('/foxx', function () {});
var callback = transformRoute(app.routingInfo.routes[0].action);
callback(req, res);
callback(req, res);
assertFalse(wrongFuncCalled);
assertEqual(timesCalled, 1);
},
testInjectInRoute: function () {
var app = new FoxxController(fakeContext),
req = {},
res = {},
calledA = false,
calledB = false,
calledC = false;
app.addInjector({thing: function() {calledA = true;}});
app.get('/foxx', function () {
app.addInjector({
thing: function() {calledB = true;},
other: function() {calledC = true;}
});
});
var callback = transformRoute(app.routingInfo.routes[0].action);
callback(req, res);
callback(req, res);
callback(req, res);
assertTrue(calledA);
assertFalse(calledB);
assertTrue(calledC);
},
testInjectResultPassedThrough: function () {
var app = new FoxxController(fakeContext),
req = {},
res = {},
called = false;
app.addInjector({thing: function() {return 'value';}});
app.get('/foxx', function (req, res, injected) {
assertEqual(injected.thing, 'value');
called = true;
});
var callback = transformRoute(app.routingInfo.routes[0].action);
callback(req, res);
assertTrue(called);
},
testInjectSimpleValues: function () {
var app = new FoxxController(fakeContext),
req = {},
res = {},
called = false;
var injectors = {
obj: {a: 0, b: 1},
arr: ['one', 'two'],
str: 'hello',
num: 42
};
app.addInjector(injectors);
app.get('/foxx', function (req, res, injected) {
assertEqual(typeof injected.obj, 'object');
assertEqual(typeof injected.arr, 'object');
assertEqual(typeof injected.str, 'string');
assertEqual(typeof injected.num, 'number');
assertTrue(Array.isArray(injected.arr));
assertEqual(injected.obj, injectors.obj);
assertEqual(injected.arr, injectors.arr);
assertEqual(injected.str, injectors.str);
assertEqual(injected.num, injectors.num);
called = true;
});
var callback = transformRoute(app.routingInfo.routes[0].action);
callback(req, res);
assertTrue(called);
}
};
}
function DocumentationAndConstraintsSpec () {
'use strict';
var app, routes, models, Model;
return {
setUp: function () {
app = new FoxxController(fakeContext);
routes = app.routingInfo.routes;
models = app.models;
Model = require('@arangodb/foxx/legacy').Model;
},
testDefinePathParam: function () {
var constraint = joi.number().integer().description("Id of the Foxx"),
context = app.get('/foxx/:id', function () {
//nothing
}).pathParam("id", {
type: constraint
});
assertEqual(routes.length, 1);
assertEqual(routes[0].url.constraint.id, "/[0-9]+/");
assertEqual(routes[0].docs.parameters[0].paramType, "path");
assertEqual(routes[0].docs.parameters[0].name, "id");
assertEqual(routes[0].docs.parameters[0].description, "Id of the Foxx");
assertEqual(routes[0].docs.parameters[0].dataType, "integer");
assertEqual(context.constraints.urlParams, {id: constraint});
},
testDefinePathParamShorthand: function () {
var constraint = joi.number().integer().description("Id of the Foxx"),
context = app.get('/foxx/:id', function () {
//nothing
}).pathParam("id", constraint);
assertEqual(routes.length, 1);
assertEqual(routes[0].url.constraint.id, "/[0-9]+/");
assertEqual(routes[0].docs.parameters[0].paramType, "path");
assertEqual(routes[0].docs.parameters[0].name, "id");
assertEqual(routes[0].docs.parameters[0].description, "Id of the Foxx");
assertEqual(routes[0].docs.parameters[0].dataType, "integer");
assertEqual(context.constraints.urlParams, {id: constraint});
},
testDefinePathCaseParam: function () {
var constraint = joi.number().integer().description("Id of the Foxx"),
context = app.get('/foxx/:idParam', function () {
//nothing
}).pathParam("idParam", {
type: constraint
});
assertEqual(routes.length, 1);
assertEqual(routes[0].url.constraint.idParam, "/[0-9]+/");
assertEqual(routes[0].docs.parameters[0].paramType, "path");
assertEqual(routes[0].docs.parameters[0].name, "idParam");
assertEqual(routes[0].docs.parameters[0].description, "Id of the Foxx");
assertEqual(routes[0].docs.parameters[0].dataType, "integer");
assertEqual(context.constraints.urlParams, {idParam: constraint});
},
testDefineMultiplePathParams: function () {
var foxxConstraint = joi.string().description("Kind of Foxx"),
idConstraint = joi.number().integer().description("Id of the Foxx"),
context = app.get('/:foxx/:id', function () {
//nothing
}).pathParam("foxx", {
type: foxxConstraint
}).pathParam("id", {
type: idConstraint
});
assertEqual(routes.length, 1);
assertEqual(routes[0].url.constraint.foxx, "/[^/]+/");
assertEqual(routes[0].docs.parameters[0].paramType, "path");
assertEqual(routes[0].docs.parameters[0].name, "foxx");
assertEqual(routes[0].docs.parameters[0].description, "Kind of Foxx");
assertEqual(routes[0].docs.parameters[0].dataType, "string");
assertEqual(routes[0].url.constraint.id, "/[0-9]+/");
assertEqual(routes[0].docs.parameters[1].paramType, "path");
assertEqual(routes[0].docs.parameters[1].name, "id");
assertEqual(routes[0].docs.parameters[1].description, "Id of the Foxx");
assertEqual(routes[0].docs.parameters[1].dataType, "integer");
assertEqual(context.constraints.urlParams, {foxx: foxxConstraint, id: idConstraint});
},
testDefineMultiplePathCaseParams: function () {
var foxxConstraint = joi.string().description("Kind of Foxx"),
idConstraint = joi.number().integer().description("Id of the Foxx"),
context = app.get('/:foxxParam/:idParam', function () {
//nothing
}).pathParam("foxxParam", {
type: foxxConstraint
}).pathParam("idParam", {
type: idConstraint
});
assertEqual(routes.length, 1);
assertEqual(routes[0].url.constraint.foxxParam, "/[^/]+/");
assertEqual(routes[0].docs.parameters[0].paramType, "path");
assertEqual(routes[0].docs.parameters[0].name, "foxxParam");
assertEqual(routes[0].docs.parameters[0].description, "Kind of Foxx");
assertEqual(routes[0].docs.parameters[0].dataType, "string");
assertEqual(routes[0].url.constraint.idParam, "/[0-9]+/");
assertEqual(routes[0].docs.parameters[1].paramType, "path");
assertEqual(routes[0].docs.parameters[1].name, "idParam");
assertEqual(routes[0].docs.parameters[1].description, "Id of the Foxx");
assertEqual(routes[0].docs.parameters[1].dataType, "integer");
assertEqual(context.constraints.urlParams, {foxxParam: foxxConstraint, idParam: idConstraint});
},
testDefineQueryParam: function () {
var constraint = joi.number().integer().description("The value of an a"),
context = app.get('/foxx', function () {
//nothing
}).queryParam("a", {
type: constraint
});
assertEqual(routes.length, 1);
assertEqual(routes[0].docs.parameters[0].paramType, "query");
assertEqual(routes[0].docs.parameters[0].name, "a");
assertEqual(routes[0].docs.parameters[0].description, "The value of an a");
assertEqual(routes[0].docs.parameters[0].dataType, "integer");
assertEqual(routes[0].docs.parameters[0].required, false);
assertEqual(routes[0].docs.parameters[0].allowMultiple, false);
assertEqual(context.constraints.queryParams, {a: constraint});
},
testDefineQueryParamWithOverrides: function () {
var constraint = joi.number().integer(),
context = app.get('/foxx', function () {
//nothing
}).queryParam("a", {
type: constraint,
description: "The value of an a",
allowMultiple: true,
required: true
});
assertEqual(routes.length, 1);
assertEqual(routes[0].docs.parameters[0].paramType, "query");
assertEqual(routes[0].docs.parameters[0].name, "a");
assertEqual(routes[0].docs.parameters[0].description, "The value of an a");
assertEqual(routes[0].docs.parameters[0].dataType, "integer");
assertEqual(routes[0].docs.parameters[0].required, true, "param required not equal?");
assertEqual(routes[0].docs.parameters[0].allowMultiple, true, "allows multiple parameters?");
assertEqual(context.constraints.queryParams, {
a: constraint
.description("The value of an a")
.meta({allowMultiple: true})
.required()
});
},
testDefineQueryParamShorthand: function () {
var constraint = joi.number().integer()
.description("The value of an a")
.meta({allowMultiple: true}),
context = app.get('/foxx', function () {
//nothing
}).queryParam("a", constraint);
assertEqual(routes.length, 1);
assertEqual(routes[0].docs.parameters[0].paramType, "query");
assertEqual(routes[0].docs.parameters[0].name, "a");
assertEqual(routes[0].docs.parameters[0].description, "The value of an a");
assertEqual(routes[0].docs.parameters[0].dataType, "integer");
assertEqual(routes[0].docs.parameters[0].required, false);
assertEqual(routes[0].docs.parameters[0].allowMultiple, true);
assertEqual(context.constraints.queryParams, {a: constraint});
},
testAddBodyParam: function () {
var paramName = stub(),
description = stub(),
ModelPrototype = stub(),
jsonSchema = { required: [], properties: {} };
allow(ModelPrototype)
.toReceive("toJSONSchema")
.andReturn(jsonSchema);
app.get('/foxx', function () {
//nothing
}).bodyParam(paramName, {
description: description,
type: ModelPrototype
});
assertEqual(routes.length, 1);
assertEqual(routes[0].docs.parameters[0].name, paramName);
assertEqual(routes[0].docs.parameters[0].paramType, "body");
assertEqual(routes[0].docs.parameters[0].description, description);
var token = routes[0].docs.parameters[0].dataType;
assertEqual(app.models.hasOwnProperty(token), true);
},
testAddBodyParamWithMultipleItems: function () {
var paramName = stub(),
description = stub(),
ModelPrototype = stub(),
jsonSchema = { required: [], properties: {} };
allow(ModelPrototype)
.toReceive("toJSONSchema")
.andReturn(jsonSchema);
app.get('/foxx', function () {
//nothing
}).bodyParam(paramName, {
description: description,
type: [ModelPrototype]
});
assertEqual(routes.length, 1);
assertEqual(routes[0].docs.parameters[0].name, paramName);
assertEqual(routes[0].docs.parameters[0].paramType, "body");
assertEqual(routes[0].docs.parameters[0].description, description);
var token = routes[0].docs.parameters[0].dataType;
assertEqual(app.models.hasOwnProperty(token), true);
},
testDefineBodyParamAddsJSONSchemaToModels: function () {
var paramName = stub(),
description = stub(),
ModelPrototype = stub(),
jsonSchema = { required: [], properties: {} };
allow(ModelPrototype)
.toReceive("toJSONSchema")
.andReturn(jsonSchema);
app.get('/foxx', function () {
//nothing
}).bodyParam(paramName, {
description: description,
type: ModelPrototype
});
var token = routes[0].docs.parameters[0].dataType;
assertEqual(app.models[token], jsonSchema);
},
testSetParamForBodyParam: function () {
var req = { parameters: {} },
res = {},
paramName = stub(),
description = stub(),
requestBody = stub(),
ModelPrototype = stub(),
jsonSchemaId = stub(),
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
ModelPrototype = mockConstructor(requestBody);
ModelPrototype.toJSONSchema = function () { return { }; };
app.get('/foxx', function (providedReq) {
called = (providedReq.parameters[paramName] instanceof ModelPrototype);
}).bodyParam(paramName, {
description: description,
type: ModelPrototype
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
ModelPrototype.assertIsSatisfied();
},
testSetParamForJoiBodyParam: function () {
var req = { parameters: {} },
res = {},
paramName = 'flurb',
description = stub(),
requestBody = {x: 1},
schema = {x: joi.number().integer().required()},
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
app.get('/foxx', function (providedReq) {
called = _.isEqual(providedReq.parameters[paramName], {x: 1});
}).bodyParam(paramName, {
description: description,
type: schema
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
},
testSetParamForJoiBodyParamWithAllowUnknownTrue: function () {
var req = { parameters: {} },
res = {},
paramName = 'flurb',
description = stub(),
requestBody = {x: 1, y: 2},
schema = {x: joi.number().integer().required()},
called = false;
schema = joi.object().keys(schema).options({allowUnknown: true});
allow(req)
.toReceive("body")
.andReturn(requestBody);
app.get('/foxx', function (providedReq) {
called = _.isEqual(providedReq.parameters[paramName], {x: 1, y: 2});
}).bodyParam(paramName, {
description: description,
type: schema
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
},
testSetParamForJoiBodyParamWithAllowUnknownFalse: function () {
var req = { parameters: {} },
res = {},
paramName = 'flurb',
description = stub(),
requestBody = {x: 1, y: 2},
schema = {x: joi.number().integer().required()},
called = false;
schema = joi.object().keys(schema).options({allowUnknown: false});
allow(req)
.toReceive("body")
.andReturn(requestBody);
app.get('/foxx', function (providedReq) {
called = true;
}).bodyParam(paramName, {
description: description,
type: schema
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertFalse(called);
assertEqual(res.responseCode, 422);
},
testSetParamForPureJoiBodyParam: function () {
var req = { parameters: {} },
res = {},
paramName = 'flurb',
description = stub(),
requestBody = {x: 1},
schema = joi.object().keys({x: joi.number().integer().required()}),
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
app.get('/foxx', function (providedReq) {
called = _.isEqual(providedReq.parameters[paramName], {x: 1});
}).bodyParam(paramName, {
description: description,
type: schema
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
},
testSetParamForPureJoiBodyParamWithEmbeddedArgs: function () {
var req = { parameters: {} },
res = {},
paramName = 'flurb',
description = 'o hi mark',
requestBody = {x: 1},
schema = joi.object().keys({x: joi.number().integer().required()}),
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
app.get('/foxx', function (providedReq) {
called = _.isEqual(providedReq.parameters[paramName], {x: 1});
}).bodyParam(paramName, schema.description(description));
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
},
testSetParamForExoticJoiBodyParam: function () {
var req = { parameters: {} },
res = {},
paramName = 'flurb',
description = stub(),
requestBody = [{x: 1}],
schema = joi.array().items({x: joi.number().integer().required()}),
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
app.get('/foxx', function (providedReq) {
called = _.isEqual(providedReq.parameters[paramName], [{x: 1}]);
}).bodyParam(paramName, {
description: description,
type: schema
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
},
testSetParamForInvalidJoiBodyParamWithAllowInvalid: function () {
var req = { parameters: {} },
res = {},
paramName = 'flurb',
description = stub(),
requestBody = 'banana',
schema = joi.array().items({x: joi.number().integer().required()}),
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
app.get('/foxx', function (providedReq) {
called = _.isEqual(providedReq.parameters[paramName], 'banana');
}).bodyParam(paramName, {
description: description,
allowInvalid: true,
type: schema
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
},
testSetParamForInvalidJoiBodyParamWithEmbeddedAllowInvalid: function () {
var req = { parameters: {} },
res = {},
paramName = 'flurb',
description = stub(),
requestBody = 'banana',
schema = joi.array().items({x: joi.number().integer().required()}),
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
app.get('/foxx', function (providedReq) {
called = _.isEqual(providedReq.parameters[paramName], 'banana');
}).bodyParam(paramName, {
description: description,
type: schema.meta({allowInvalid: true})
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
},
testSetParamForInvalidJoiBodyParam: function () {
var req = { parameters: {} },
res = {},
paramName = 'flurb',
description = stub(),
requestBody = 'banana',
schema = joi.array().items({x: joi.number().integer().required()}),
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
app.get('/foxx', function () {
called = true;
}).bodyParam(paramName, {
description: description,
type: schema
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertFalse(called);
assertEqual(res.responseCode, 422);
},
testSetParamForUndocumentedBodyParam: function () {
var reqBody = '{"foo": "bar"}',
req = {
parameters: {},
body: function () { return JSON.parse(this.rawBody()); },
rawBody: function () { return reqBody; }
},
res = {},
receivedParam = false,
receivedRawBody = null;
app.post('/foxx', function (providedReq) {
receivedParam = providedReq.parameters["undocumented body"];
receivedRawBody = providedReq.rawBody();
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(receivedParam instanceof Model);
assertEqual(receivedParam.forDB(), {foo: "bar"});
assertEqual(receivedRawBody, reqBody);
},
testSetParamForNonJsonUndocumentedBodyParam: function () {
var reqBody = '{:foo "bar"}',
req = {
parameters: {},
body: function () { return JSON.parse(this.rawBody()); },
rawBody: function () { return reqBody; }
},
res = {},
receivedParam = false,
receivedRawBody = null;
app.post('/foxx', function (providedReq) {
receivedParam = providedReq.parameters["undocumented body"];
receivedRawBody = providedReq.rawBody();
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(receivedParam instanceof Model);
assertEqual(receivedParam.forDB(), {});
assertEqual(receivedRawBody, reqBody);
},
testSetParamForBodyParamWithMultipleItems: function () {
var req = { parameters: {} },
res = {},
paramName = stub(),
description = stub(),
rawElement = stub(),
requestBody = [rawElement],
ModelPrototype = stub(),
jsonSchemaId = stub(),
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
ModelPrototype = mockConstructor(rawElement);
ModelPrototype.toJSONSchema = function () { return { id: jsonSchemaId }; };
app.get('/foxx', function (providedReq) {
called = (providedReq.parameters[paramName][0] instanceof ModelPrototype);
}).bodyParam(paramName, {
description: description,
type: [ModelPrototype]
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
ModelPrototype.assertIsSatisfied();
},
testDocumentationForErrorResponse: function () {
var CustomErrorClass = function () {};
app.get('/foxx', function () {
//nothing
}).errorResponse(CustomErrorClass, 400, "I don't understand a word you're saying");
assertEqual(routes.length, 1);
assertEqual(routes[0].docs.errorResponses.length, 1);
assertEqual(routes[0].docs.errorResponses[0].code, 400);
assertEqual(routes[0].docs.errorResponses[0].reason, "I don't understand a word you're saying");
},
testCatchesDefinedError: function () {
var CustomErrorClass = function () {},
req = {},
res,
code = 400,
reason = "This error was really... something!",
statusWasCalled = false,
jsonWasCalled = false,
passedRequestAndResponse = false;
res = {
status: function (givenCode) {
statusWasCalled = (givenCode === code);
},
json: function (givenBody) {
jsonWasCalled = (givenBody.error === reason);
}
};
app.get('/foxx', function (providedReq, providedRes) {
if (providedReq === req && providedRes === res) {
passedRequestAndResponse = true;
}
throw new CustomErrorClass();
}).errorResponse(CustomErrorClass, code, reason);
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(statusWasCalled);
assertTrue(jsonWasCalled);
assertTrue(passedRequestAndResponse);
},
testCatchesDefinedErrorWithCustomFunction: function () {
var jsonWasCalled = false,
req = {},
res,
code = 400,
reason = "This error was really... something!",
CustomErrorClass = function () {};
res = {
status: function () {},
json: function (givenBody) {
jsonWasCalled = givenBody.success;
}
};
app.get('/foxx', function (providedReq, providedRes) {
throw new CustomErrorClass();
}).errorResponse(CustomErrorClass, code, reason, function (e) {
if (e instanceof CustomErrorClass) {
return { success: "true" };
}
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(jsonWasCalled);
},
testControllerWideErrorResponse: function () {
var CustomErrorClass = function () {};
app.allRoutes.errorResponse(CustomErrorClass, 400, "I don't understand a word you're saying");
app.get('/foxx', function () {
//nothing
});
assertEqual(routes.length, 1);
assertEqual(routes[0].docs.errorResponses.length, 1);
assertEqual(routes[0].docs.errorResponses[0].code, 400);
assertEqual(routes[0].docs.errorResponses[0].reason, "I don't understand a word you're saying");
}
};
}
function AddMiddlewareFoxxControllerSpec () {
'use strict';
var app;
return {
setUp: function () {
app = new FoxxController(fakeContext);
},
testAddABeforeMiddlewareForAllRoutes: function () {
var myFunc = function (req, res) { var a = (req > res); },
middleware = app.routingInfo.middleware,
callback;
app.before(myFunc);
assertEqual(middleware.length, 2);
assertEqual(middleware[1].url.match, '/*');
callback = String(middleware[1].action.callback);
assertTrue(callback.indexOf((String(myFunc) + "(req, res)") > 0));
assertTrue(callback.indexOf("next()" > 0));
assertTrue(callback.indexOf(String(myFunc)) < callback.indexOf("next()"));
},
testAddABeforeMiddlewareForCertainRoutes: function () {
var myFunc = function (req, res) { var a = (req > res); },
middleware = app.routingInfo.middleware,
callback;
app.before('/fancy/path', myFunc);
assertEqual(middleware.length, 2);
assertEqual(middleware[1].url.match, '/fancy/path');
callback = String(middleware[1].action.callback);
assertTrue(callback.indexOf((String(myFunc) + "(req, res)") > 0));
assertTrue(callback.indexOf("next()" > 0));
assertTrue(callback.indexOf(String(myFunc)) < callback.indexOf("next()"));
},
testAddAnAfterMiddlewareForAllRoutes: function () {
var myFunc = function (req, res) { var a = (req > res); },
middleware = app.routingInfo.middleware,
callback;
app.after(myFunc);
assertEqual(middleware.length, 2);
assertEqual(middleware[1].url.match, '/*');
callback = String(middleware[1].action.callback);
assertTrue(callback.indexOf((String(myFunc) + "(req, res)") > 0));
assertTrue(callback.indexOf("next()" > 0));
assertTrue(callback.indexOf("func(req") > callback.indexOf("next()"));
},
testAddAnAfterMiddlewareForCertainRoutes: function () {
var myFunc = function (req, res) { var a = (req > res); },
middleware = app.routingInfo.middleware,
callback;
app.after('/fancy/path', myFunc);
assertEqual(middleware.length, 2);
assertEqual(middleware[1].url.match, '/fancy/path');
callback = String(middleware[1].action.callback);
assertTrue(callback.indexOf((String(myFunc) + "(req, res)") > 0));
assertTrue(callback.indexOf("next()" > 0));
assertTrue(callback.indexOf("func(req") > callback.indexOf("next()"));
}
};
}
function CommentDrivenDocumentationSpec () {
'use strict';
var app, routingInfo, noop;
return {
setUp: function () {
app = new FoxxController(fakeContext);
routingInfo = app.routingInfo;
noop = function () {};
},
testSettingTheSummary: function () {
app.get('/simple/route', noop)
.summary('Get all the foxes');
assertEqual(routingInfo.routes[0].docs.summary, 'Get all the foxes');
},
testSettingTheNotes: function () {
app.get('/simple/route', noop)
.notes('A function to get all foxes from the database\nin a good way.');
assertEqual(routingInfo.routes[0].docs.notes, "A function to get all foxes from the database\nin a good way.");
}
};
}
function SetupAuthorization () {
'use strict';
var app;
return {
testWorksWithAllParameters: function () {
var err;
app = new FoxxController(fakeContext);
try {
app.activateAuthentication({
type: "cookie",
cookieLifetime: 360000,
cookieName: "mycookie",
sessionLifetime: 600
});
} catch (e) {
err = e;
}
assertUndefined(err && err.stack);
},
testRefusesUnknownAuthTypes: function () {
var err;
app = new FoxxController(fakeContext);
try {
app.activateAuthentication({
type: "brainwave",
cookieLifetime: 360000,
cookieName: "mycookie",
sessionLifetime: 600
});
} catch (e) {
err = e;
}
assertEqual(err.message, "Currently only the following auth types are supported: cookie");
},
testRefusesMissingCookieLifetime: function () {
var err;
app = new FoxxController(fakeContext);
try {
app.activateAuthentication({
type: "cookie",
cookieName: "mycookie",
sessionLifetime: 600
});
} catch (e) {
err = e;
}
assertEqual(err.message, "Please provide the cookieLifetime");
},
testRefusesMissingSessionLifetime: function () {
var err;
app = new FoxxController(fakeContext);
try {
app.activateAuthentication({
type: "cookie",
cookieName: "mycookie",
cookieLifetime: 360000
});
} catch (e) {
err = e;
}
assertEqual(err.message, "Please provide the sessionLifetime");
}
};
}
function SetupSessions () {
'use strict';
var app;
return {
testWorksWithUnsignedCookies: function () {
var err;
app = new FoxxController(fakeContext);
try {
app.activateSessions({
sessionStorage: 'sessions',
cookie: {
name: 'sid'
}
});
} catch (e) {
err = e;
}
assertUndefined(err);
},
testWorksWithUnsignedCookiesShorthand: function () {
var err;
app = new FoxxController(fakeContext);
try {
app.activateSessions({
sessionStorage: 'sessions',
cookie: 'sid'
});
} catch (e) {
err = e;
}
assertUndefined(err);
},
testWorksWithSignedCookies: function () {
var err;
app = new FoxxController(fakeContext);
try {
app.activateSessions({
sessionStorage: 'sessions',
cookie: {
name: 'sid',
secret: 'keyboardcat'
}
});
} catch (e) {
err = e;
}
assertUndefined(err);
},
testWorksWithHeaders: function () {
var err;
app = new FoxxController(fakeContext);
try {
app.activateSessions({
sessionStorage: 'sessions',
header: 'X-Session-Id'
});
} catch (e) {
err = e;
}
assertUndefined(err);
}
};
}
function FoxxControllerWithRootElement () {
'use strict';
var app, routes;
return {
setUp: function () {
app = new FoxxController(fakeContextWithRootElement);
routes = app.routingInfo.routes;
},
testBodyParamWithOneElement: function () {
var req = { parameters: {} },
res = {},
paramName = 'myBodyParam',
description = stub(),
rawElement = stub(),
requestBody = { myBodyParam: rawElement },
ModelPrototype = stub(),
jsonSchemaId = stub(),
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
ModelPrototype = mockConstructor(rawElement);
ModelPrototype.toJSONSchema = function () { return { id: jsonSchemaId }; };
app.get('/foxx', function (providedReq) {
called = (providedReq.parameters[paramName] instanceof ModelPrototype);
}).bodyParam(paramName, {
description: description,
type: ModelPrototype
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
ModelPrototype.assertIsSatisfied();
},
testBodyParamWithMultipleElement: function () {
var req = { parameters: {} },
res = {},
paramName = 'myBodyParam',
description = stub(),
rawElement = stub(),
requestBody = { myBodyParam: [rawElement] },
ModelPrototype = stub(),
jsonSchemaId = stub(),
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
ModelPrototype = mockConstructor(rawElement);
ModelPrototype.toJSONSchema = function () { return { id: jsonSchemaId }; };
app.get('/foxx', function (providedReq) {
called = (providedReq.parameters[paramName][0] instanceof ModelPrototype);
}).bodyParam(paramName, {
description: description,
type: [ModelPrototype]
});
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
ModelPrototype.assertIsSatisfied();
}
};
}
function ExtendFoxxControllerSpec () {
'use strict';
var app, routes;
return {
setUp: function () {
app = new FoxxController(fakeContext);
routes = app.routingInfo.routes;
},
testDocumentationBodyParameterInExtension: function() {
var paramName = stub(),
description = stub(),
ModelPrototype = stub(),
jsonSchema = { id: 'a', required: [], properties: {} };
allow(ModelPrototype)
.toReceive("toJSONSchema")
.andReturn(jsonSchema);
app.extend({
"extension": function() {
this.bodyParam(paramName, {
description: description,
type: ModelPrototype
});
}
});
app.get("/foxx", function () {
//nothing
}).extension();
assertEqual(routes.length, 1);
assertEqual(routes[0].docs.parameters[0].name, paramName);
assertEqual(routes[0].docs.parameters[0].paramType, "body");
assertEqual(routes[0].docs.parameters[0].description, description);
var token = routes[0].docs.parameters[0].dataType;
assertEqual(app.models[token], jsonSchema);
},
testFunctionalityBodyParameterInExtension: function() {
var req = { parameters: {} },
res = {},
paramName = stub(),
description = stub(),
requestBody = stub(),
ModelPrototype = stub(),
jsonSchemaId = stub(),
called = false;
allow(req)
.toReceive("body")
.andReturn(requestBody);
ModelPrototype = mockConstructor(requestBody);
ModelPrototype.toJSONSchema = function () { return { id: jsonSchemaId }; };
app.extend({
"extension": function() {
this.bodyParam(paramName, {
description: description,
type: ModelPrototype
});
}
});
app.get('/foxx', function (providedReq) {
called = (providedReq.parameters[paramName] instanceof ModelPrototype);
}).extension();
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(called);
ModelPrototype.assertIsSatisfied();
},
testPathParameterInExtension: function() {
var constraint = joi.number().integer().description("Id of the Foxx"),
context;
app.extend({
"extension": function() {
this.pathParam("id", {
type: constraint
});
}
});
context = app.get('/foxx/:id', function () {
//nothing
}).extension();
assertEqual(routes.length, 1);
assertEqual(routes[0].url.constraint.id, "/[0-9]+/");
assertEqual(routes[0].docs.parameters[0].paramType, "path");
assertEqual(routes[0].docs.parameters[0].name, "id");
assertEqual(routes[0].docs.parameters[0].description, "Id of the Foxx");
assertEqual(routes[0].docs.parameters[0].dataType, "integer");
assertEqual(context.constraints.urlParams, {id: constraint});
},
testQueryParamterInExtension: function () {
var constraint = joi.number().integer().description("The value of an a"),
context;
app.extend({
"extension": function() {
this.queryParam("a", {
type: constraint
});
}
});
context = app.get('/foxx', function () {
//nothing
}).extension();
assertEqual(routes.length, 1);
assertEqual(routes[0].docs.parameters[0].paramType, "query");
assertEqual(routes[0].docs.parameters[0].name, "a");
assertEqual(routes[0].docs.parameters[0].description, "The value of an a");
assertEqual(routes[0].docs.parameters[0].dataType, "integer");
assertEqual(routes[0].docs.parameters[0].required, false);
assertEqual(routes[0].docs.parameters[0].allowMultiple, false);
assertEqual(context.constraints.queryParams, {a: constraint});
},
testDocumentationErrorResponseInExtension: function () {
var CustomErrorClass = function () {};
app.extend({
"extension": function() {
this.errorResponse(CustomErrorClass, 400, "I don't understand a word you're saying");
}
});
app.get('/foxx', function () {
//nothing
}).extension();
assertEqual(routes.length, 1);
assertEqual(routes[0].docs.errorResponses.length, 1);
assertEqual(routes[0].docs.errorResponses[0].code, 400);
assertEqual(routes[0].docs.errorResponses[0].reason, "I don't understand a word you're saying");
},
testFunctionalityErrorResponseInExtension: function () {
var CustomErrorClass = function () {},
req = {},
res,
code = 400,
reason = "This error was really... something!",
statusWasCalled = false,
jsonWasCalled = false,
passedRequestAndResponse = false;
res = {
status: function (givenCode) {
statusWasCalled = (givenCode === code);
},
json: function (givenBody) {
jsonWasCalled = (givenBody.error === reason);
}
};
app.extend({
"extension": function() {
this.errorResponse(CustomErrorClass, code, reason);
}
});
app.get('/foxx', function (providedReq, providedRes) {
if (providedReq === req && providedRes === res) {
passedRequestAndResponse = true;
}
throw new CustomErrorClass();
}).extension();
var callback = transformRoute(routes[0].action);
callback(req, res);
assertTrue(statusWasCalled);
assertTrue(jsonWasCalled);
assertTrue(passedRequestAndResponse);
},
testExtensionIsChainable: function () {
var constraint = joi.number().integer().description("The value of an a"),
context;
app.extend({
"extension": function() {
this.queryParam("a", {
type: constraint
});
}
});
context = app.get('/foxx', function () {
//nothing
}).extension()
.queryParam("b", {
type: constraint
});
assertEqual(context.constraints.queryParams, {a: constraint, b: constraint});
context = app.get('/foxx2', function () {
//nothing
})
.queryParam("b", {
type: constraint
})
.extension();
assertEqual(context.constraints.queryParams, {a: constraint, b: constraint});
},
testExtensionUsingParams: function () {
var constraint = joi.number().integer().description("The value of an a"),
context;
app.extend({
"extension": function(name, type) {
this.queryParam(name, {
type: type
});
}
});
context = app.get('/foxx', function () {
//nothing
}).extension("a", constraint);
assertEqual(context.constraints.queryParams, {a: constraint});
}
};
}
jsunity.run(CreateFoxxControllerSpec);
jsunity.run(SetRoutesFoxxControllerSpec);
jsunity.run(ControllerInjectionSpec);
//jsunity.run(LegacyDocumentationAndConstraintsSpec);
jsunity.run(DocumentationAndConstraintsSpec);
jsunity.run(AddMiddlewareFoxxControllerSpec);
jsunity.run(CommentDrivenDocumentationSpec);
jsunity.run(SetupAuthorization);
jsunity.run(SetupSessions);
jsunity.run(FoxxControllerWithRootElement);
return jsunity.done();