diff --git a/.vscode/launch.json b/.vscode/launch.json index 510a5878..10ed6eb2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -179,6 +179,22 @@ "${workspaceFolder}/tests/auth.test.js" ], "internalConsoleOptions": "openOnSessionStart" + }, + { + "type": "node", + "request": "launch", + "name": "Mocha Tests - Role", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": [ + "-u", + "tdd", + "--timeout", + "999999", + "--colors", + "${workspaceFolder}/tests/setup.spec.js", + "${workspaceFolder}/tests/role.test.js" + ], + "internalConsoleOptions": "openOnSessionStart" } ] } \ No newline at end of file diff --git a/app.js b/app.js index 7167e691..23093ee7 100755 --- a/app.js +++ b/app.js @@ -28,6 +28,7 @@ const teamRouter = require("./routes/api/team"); const sponsorRouter = require("./routes/api/sponsor"); const searchRouter = require("./routes/api/search"); const volunteerRouter = require("./routes/api/volunteer"); +const roleRouter = require("./routes/api/role"); const app = express(); Services.db.connect(app); @@ -83,6 +84,8 @@ volunteerRouter.activate(apiRouter); Services.log.info("Volunteer router activated"); searchRouter.activate(apiRouter); Services.log.info("Search router activated"); +roleRouter.activate(apiRouter); +Services.log.info("Role router activated"); apiRouter.use("/", indexRouter); app.use("/", indexRouter); diff --git a/constants/error.constant.js b/constants/error.constant.js index 92fe72c5..b802ad1e 100644 --- a/constants/error.constant.js +++ b/constants/error.constant.js @@ -16,6 +16,7 @@ const HACKER_STATUS_409_MESSAGE = "Conflict with hacker status"; const TEAM_MEMBER_422_MESSAGE = "Duplicate team member in input"; const VALIDATION_422_MESSAGE = "Validation failed"; const ACCOUNT_DUPLICATE_422_MESSAGE = "Account already exists"; +const ROLE_DUPLICATE_422_MESSAGE = "Role already exists"; const ACCOUNT_TOKEN_401_MESSAGE = "Invalid token for account"; const AUTH_401_MESSAGE = "Invalid Authentication"; @@ -32,6 +33,7 @@ const VOLUNTEER_CREATE_500_MESSAGE = "Error while creating volunteer"; const EMAIL_500_MESSAGE = "Error while generating email"; const GENERIC_500_MESSAGE = "Internal error"; const LOGIN_500_MESSAGE = "Error while logging in"; +const ROLE_CREATE_500_MESSAGE = "Error while creating role"; module.exports = { ACCOUNT_404_MESSAGE: ACCOUNT_404_MESSAGE, @@ -61,4 +63,6 @@ module.exports = { ACCOUNT_DUPLICATE_422_MESSAGE: ACCOUNT_DUPLICATE_422_MESSAGE, LOGIN_500_MESSAGE: LOGIN_500_MESSAGE, HACKER_STATUS_409_MESSAGE: HACKER_STATUS_409_MESSAGE, + ROLE_DUPLICATE_422_MESSAGE: ROLE_DUPLICATE_422_MESSAGE, + ROLE_CREATE_500_MESSAGE: ROLE_CREATE_500_MESSAGE, }; \ No newline at end of file diff --git a/constants/routes.constant.js b/constants/routes.constant.js index a9461282..4c77e2b6 100644 --- a/constants/routes.constant.js +++ b/constants/routes.constant.js @@ -62,7 +62,7 @@ const accountRoutes = { const hackerRoutes = { "getSelf": { requestType: Constants.REQUEST_TYPES.GET, - uri: "/api/hacker/self/" + uri: "/api/hacker/self/", }, "getSelfById": { requestType: Constants.REQUEST_TYPES.GET, @@ -159,6 +159,13 @@ const volunteerRoutes = { }, }; +const roleRoutes = { + "post": { + requestType: Constants.REQUEST_TYPES.POST, + uri: "/api/role/", + } +}; + const searchRoutes = { "get": { requestType: Constants.REQUEST_TYPES.GET, @@ -180,6 +187,7 @@ const allRoutes = { "Sponsor": sponsorRoutes, "Team": teamRoutes, "Volunteer": volunteerRoutes, + "Role": roleRoutes, "Search": searchRoutes, "Staff": staffRoutes, }; @@ -217,6 +225,7 @@ module.exports = { sponsorRoutes: sponsorRoutes, teamRoutes: teamRoutes, volunteerRoutes: volunteerRoutes, + roleRoutes: roleRoutes, searchRoutes: searchRoutes, staffRoutes: staffRoutes, allRoutes: allRoutes, diff --git a/constants/success.constant.js b/constants/success.constant.js index 0e9a335b..c7831939 100644 --- a/constants/success.constant.js +++ b/constants/success.constant.js @@ -23,10 +23,11 @@ const HACKER_UPDATE = "Hacker update successful."; const RESUME_UPLOAD = "Resume upload successful."; const RESUME_DOWNLOAD = "Resume download successful."; +const ROLE_CREATE = "Role creation successful."; + const SEARCH_QUERY = "Query search successful. Returning results."; const SEARCH_NO_RESULTS = "Query search successful. No results found."; - const SPONSOR_GET_BY_ID = "Sponsor found by id."; const SPONSOR_CREATE = "Sponsor creation successful."; @@ -59,6 +60,8 @@ module.exports = { RESUME_UPLOAD: RESUME_UPLOAD, RESUME_DOWNLOAD: RESUME_DOWNLOAD, + ROLE_CREATE: ROLE_CREATE, + SEARCH_QUERY: SEARCH_QUERY, SEARCH_NO_RESULTS: SEARCH_NO_RESULTS, diff --git a/controllers/account.controller.js b/controllers/account.controller.js index 5f6f8310..4500f245 100644 --- a/controllers/account.controller.js +++ b/controllers/account.controller.js @@ -60,14 +60,13 @@ async function getUserById(req, res) { } /** - * @async * @function addUser * @param {{body: {accountDetails: {_id: ObjectId, firstName: string, lastName: string, email: string, password: string, dietaryRestrictions: string, shirtSize: string}}}} req * @param {*} res * @return {JSON} Success or error status * @description Adds a user from information in req.body.accountDetails */ -async function addUser(req, res) { +function addUser(req, res) { const acc = req.body.account; return res.status(200).json({ message: Constants.Success.ACCOUNT_CREATE, @@ -106,7 +105,7 @@ module.exports = { getUserById: Util.asyncMiddleware(getUserById), // assumes all information in req.body - addUser: Util.asyncMiddleware(addUser), + addUser: addUser, updatedAccount: updatedAccount, invitedAccount: invitedAccount }; \ No newline at end of file diff --git a/controllers/role.controller.js b/controllers/role.controller.js new file mode 100644 index 00000000..ac410feb --- /dev/null +++ b/controllers/role.controller.js @@ -0,0 +1,21 @@ +"use strict"; + +const Success = require("../constants/success.constant"); + +/** + * @function createdRole + * @param {{body: {role: Object}}} req + * @param {*} res + * @return {JSON} Success status and role object + * @description Returns the JSON of role object located in req.body.role + */ +function createdRole(req, res) { + return res.status(200).json({ + message: Success.ROLE_CREATE, + data: req.body.role.toJSON(), + }); +} + +module.exports = { + createdRole: createdRole, +}; \ No newline at end of file diff --git a/docs/api/api_data.js b/docs/api/api_data.js index 1055d609..a68b7fad 100644 --- a/docs/api/api_data.js +++ b/docs/api/api_data.js @@ -1734,6 +1734,91 @@ define({ "url": "https://api.mchacks.ca/api/" }] }, + { + "type": "post", + "url": "/api/role/", + "title": "create a new hacker", + "name": "createRole", + "group": "Role", + "version": "1.1.1", + "parameter": { + "fields": { + "body": [{ + "group": "body", + "type": "String", + "optional": false, + "field": "name", + "description": "

Name of the route

" + }, + { + "group": "body", + "type": "Route[]", + "optional": false, + "field": "routes", + "description": "

The routes that this role gives access to

" + } + ] + }, + "examples": [{ + "title": "application: ", + "content": "{\n \"name\": \"routename\",\n \"routes\": [\n {\n uri: \"/api/hacker/\"\n requestType: \"POST\"\n }\n ]\n}", + "type": "Json" + }] + }, + "success": { + "fields": { + "Success 200": [{ + "group": "Success 200", + "type": "string", + "optional": false, + "field": "message", + "description": "

Success message

" + }, + { + "group": "Success 200", + "type": "object", + "optional": false, + "field": "data", + "description": "

Role object

" + } + ] + }, + "examples": [{ + "title": "Success-Response: ", + "content": "{\n \"message\": \"Role creation successful\", \n \"data\": {\n \"name\": \"routename\",\n \"routes\": [\n {\n uri: \"/api/hacker/\"\n requestType: \"POST\"\n }\n ]\n }\n}", + "type": "object" + }] + }, + "error": { + "fields": { + "Error 4xx": [{ + "group": "Error 4xx", + "type": "string", + "optional": false, + "field": "message", + "description": "

Error message

" + }, + { + "group": "Error 4xx", + "type": "object", + "optional": false, + "field": "data", + "description": "

empty

" + } + ] + }, + "examples": [{ + "title": "Error-Response: ", + "content": "{\"message\": \"Error while creating role\", \"data\": {}}", + "type": "object" + }] + }, + "filename": "routes/api/role.js", + "groupTitle": "Role", + "sampleRequest": [{ + "url": "https://api.mchacks.ca/api/api/role/" + }] + }, { "type": "get", "url": "/search/", diff --git a/docs/api/api_data.json b/docs/api/api_data.json index cdb34440..dbf4070b 100644 --- a/docs/api/api_data.json +++ b/docs/api/api_data.json @@ -1733,6 +1733,91 @@ "url": "https://api.mchacks.ca/api/" }] }, + { + "type": "post", + "url": "/api/role/", + "title": "create a new hacker", + "name": "createRole", + "group": "Role", + "version": "1.1.1", + "parameter": { + "fields": { + "body": [{ + "group": "body", + "type": "String", + "optional": false, + "field": "name", + "description": "

Name of the route

" + }, + { + "group": "body", + "type": "Route[]", + "optional": false, + "field": "routes", + "description": "

The routes that this role gives access to

" + } + ] + }, + "examples": [{ + "title": "application: ", + "content": "{\n \"name\": \"routename\",\n \"routes\": [\n {\n uri: \"/api/hacker/\"\n requestType: \"POST\"\n }\n ]\n}", + "type": "Json" + }] + }, + "success": { + "fields": { + "Success 200": [{ + "group": "Success 200", + "type": "string", + "optional": false, + "field": "message", + "description": "

Success message

" + }, + { + "group": "Success 200", + "type": "object", + "optional": false, + "field": "data", + "description": "

Role object

" + } + ] + }, + "examples": [{ + "title": "Success-Response: ", + "content": "{\n \"message\": \"Role creation successful\", \n \"data\": {\n \"name\": \"routename\",\n \"routes\": [\n {\n uri: \"/api/hacker/\"\n requestType: \"POST\"\n }\n ]\n }\n}", + "type": "object" + }] + }, + "error": { + "fields": { + "Error 4xx": [{ + "group": "Error 4xx", + "type": "string", + "optional": false, + "field": "message", + "description": "

Error message

" + }, + { + "group": "Error 4xx", + "type": "object", + "optional": false, + "field": "data", + "description": "

empty

" + } + ] + }, + "examples": [{ + "title": "Error-Response: ", + "content": "{\"message\": \"Error while creating role\", \"data\": {}}", + "type": "object" + }] + }, + "filename": "routes/api/role.js", + "groupTitle": "Role", + "sampleRequest": [{ + "url": "https://api.mchacks.ca/api/api/role/" + }] + }, { "type": "get", "url": "/search/", diff --git a/docs/api/api_project.js b/docs/api/api_project.js index 0cea1ecb..280527e1 100644 --- a/docs/api/api_project.js +++ b/docs/api/api_project.js @@ -9,7 +9,7 @@ define({ "apidoc": "0.3.0", "generator": { "name": "apidoc", - "time": "2018-12-17T04:51:43.722Z", + "time": "2018-12-20T06:25:49.908Z", "url": "http://apidocjs.com", "version": "0.17.6" } diff --git a/docs/api/api_project.json b/docs/api/api_project.json index 1ac5ce5e..7d7d0bdd 100644 --- a/docs/api/api_project.json +++ b/docs/api/api_project.json @@ -9,7 +9,7 @@ "apidoc": "0.3.0", "generator": { "name": "apidoc", - "time": "2018-12-17T04:51:43.722Z", + "time": "2018-12-20T06:25:49.908Z", "url": "http://apidocjs.com", "version": "0.17.6" } diff --git a/middlewares/role.middleware.js b/middlewares/role.middleware.js new file mode 100644 index 00000000..8418e1d0 --- /dev/null +++ b/middlewares/role.middleware.js @@ -0,0 +1,65 @@ +"use strict"; +const mongoose = require("mongoose"); +const Services = { + Role: require("../services/role.service"), +}; +const Constants = { + Error: require("../constants/error.constant"), +}; + +/** + * @function parseRole + * @param {{body: name: String, routes: route[]}}} req + * @param {*} res + * @param {(err?)=>void} next + * @return {void} + * @description + * Moves name and routes from req.body to req.body.roleDetails. + * Adds _id to roleDetails. + */ +function parseRole(req, res, next) { + const roleDetails = { + _id: mongoose.Types.ObjectId(), + name: req.body.name, + routes: req.body.routes, + }; + + delete req.body.name; + delete req.body.routes; + + req.body.roleDetails = roleDetails; + + return next(); +} + +/** + * @function createRole + * @param {{body: {roleDetails: object}}} req + * @param {*} res + * @param {(err?)=>void} next + * @return {void} + * @description + * Creates role document + */ +async function createRole(req, res, next) { + const roleDetails = req.body.roleDetails; + + const role = await Services.Role.createRole(roleDetails); + + if (!!role) { + delete req.body.roleDetails; + req.body.role = role; + return next(); + } else { + return next({ + status: 500, + message: Constants.Error.ROLE_CREATE_500_MESSAGE, + data: {} + }); + } +} + +module.exports = { + parseRole: parseRole, + createRole: createRole, +}; \ No newline at end of file diff --git a/middlewares/validators/role.validator.js b/middlewares/validators/role.validator.js new file mode 100644 index 00000000..622685c8 --- /dev/null +++ b/middlewares/validators/role.validator.js @@ -0,0 +1,13 @@ +"use strict"; +const VALIDATOR = require("./validator.helper"); +const Constants = require("../../constants/general.constant"); + +module.exports = { + newRoleValidator: [ + VALIDATOR.alphaValidator("body", "name", false), + VALIDATOR.routesValidator("body", "routes", false), + ], + + + +}; \ No newline at end of file diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index 704332da..86fd6d84 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -607,6 +607,91 @@ function accountTypeValidator(fieldLocation, fieldname, optional = true) { } } +/** + * Validates that field must be an array of routes + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {string} fieldname name of the field that needs to be validated. + * @param {boolean} optional whether the field is optional or not. + */ +function routesValidator(fieldLocation, fieldname, optional = true) { + const routes = setProperValidationChainBuilder(fieldLocation, fieldname, "Invalid routes"); + + if (optional) { + return routes + .optional({ + checkFalsy: true + }) + .custom(routesArrayValidationHelper).withMessage("The value must be a route"); + } else { + return routes + .exists() + .withMessage("The route must exist.") + .custom(routesArrayValidationHelper).withMessage("The value must be a route"); + } +} + +/** + * Returns true if value an array of routes + * @param {*} routes value to check against + */ +function routesArrayValidationHelper(routes) { + if (!Array.isArray(routes)) { + return false; + } + for (const route of routes) { + if (route.uri === null || typeof route.uri !== "string") { + return false; + } + if (route.requestType === null || !checkEnum(route.requestType, Constants.REQUEST_TYPES)) { + return false; + } + } + return true; +} + +/** + * Validates that field must be a value within the enum passed in through parameter 'enums' + * @param {"query" | "body" | "header" | "param"} fieldLocation The location where the field should be found. + * @param {string} fieldname The name of the field that needs to be validated. + * @param {Object} enums The enum object that the field must be part of. + * @param {boolean} optional Whether the field is optional or not. + */ +function enumValidator(fieldLocation, fieldname, enums, optional = true) { + const enumValue = setProperValidationChainBuilder(fieldLocation, fieldname, "Invalid enums"); + + if (optional) { + return enumValue + .optional({ + checkFalsy: true + }) + .custom((val) => { + return checkEnum(val, enums); + }).withMessage("The value must be part of the enum"); + } else { + return enumValue + .exists() + .withMessage("The value being checked agains the enums must exist.") + .custom((val) => { + return checkEnum(val, enums); + }).withMessage("The value must be part of the enum"); + } +} + +/** + * Checks that 'value' is part of 'enums'. 'enums' should be an enum dict. + * @param {*} value Should be of the same type as the values of the enum + * @param {Object} enums An object that represents an enum. They keys are the keys of the enum, and the values are the enum values. + * @return {boolean} Returns true if the value is part of the enum, false otherwise. + */ +function checkEnum(value, enums) { + for (var enumKey in enums) { + if (value === enums[enumKey]) { + return true; + } + } + return false; +} + /** * * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found @@ -659,5 +744,7 @@ module.exports = { phoneNumberValidator: phoneNumberValidator, dateValidator: dateValidator, hackerCheckInStatusValidator: hackerCheckInStatusValidator, - accountTypeValidator: accountTypeValidator + accountTypeValidator: accountTypeValidator, + enumValidator: enumValidator, + routesValidator: routesValidator, }; \ No newline at end of file diff --git a/models/role.model.js b/models/role.model.js index 6abf3fa9..c9af26b1 100644 --- a/models/role.model.js +++ b/models/role.model.js @@ -21,7 +21,7 @@ const RoleSchema = new mongoose.Schema({ }, requestType: { type: String, - enum: Object.entries(Constants.REQUEST_TYPES) + enum: Object.values(Constants.REQUEST_TYPES) }, }] }); diff --git a/routes/api/role.js b/routes/api/role.js new file mode 100644 index 00000000..eb428f8f --- /dev/null +++ b/routes/api/role.js @@ -0,0 +1,74 @@ +"use strict"; + +const express = require("express"); +const Controllers = { + Role: require("../../controllers/role.controller") +}; +const Middleware = { + Auth: require("../../middlewares/auth.middleware"), + Validator: { + Role: require("../../middlewares/validators/role.validator"), + }, + parseBody: require("../../middlewares/parse-body.middleware"), + Role: require("../../middlewares/role.middleware"), +}; + +module.exports = { + activate: function (apiRouter) { + const roleRouter = express.Router(); + + /** + * @api {post} /api/role/ create a new hacker + * @apiName createRole + * @apiGroup Role + * @apiVersion 1.1.1 + * + * @apiParam (body) {String} name Name of the route + * @apiParam (body) {Route[]} routes The routes that this role gives access to + * @apiParamExample {Json} application: + * { + "name": "routename", + "routes": [ + { + uri: "/api/hacker/" + requestType: "POST" + } + ] + * } + * + * @apiSuccess {string} message Success message + * @apiSuccess {object} data Role object + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Role creation successful", + * "data": { + "name": "routename", + "routes": [ + { + uri: "/api/hacker/" + requestType: "POST" + } + ] + * } + * } + + * @apiError {string} message Error message + * @apiError {object} data empty + * @apiErrorExample {object} Error-Response: + * {"message": "Error while creating role", "data": {}} + */ + roleRouter.route("/").post( + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized(), + + Middleware.Validator.Role.newRoleValidator, + Middleware.parseBody.middleware, + Middleware.Role.parseRole, + + Middleware.Role.createRole, + Controllers.Role.createdRole + ); + + apiRouter.use("/role", roleRouter); + } +}; \ No newline at end of file diff --git a/services/role.service.js b/services/role.service.js index 76bab7f4..79d05969 100644 --- a/services/role.service.js +++ b/services/role.service.js @@ -1,50 +1,65 @@ "use strict"; const Role = require("../models/role.model"); const logger = require("./logger.service"); +const mongoose = require("mongoose"); + +/** + * @function createRole + * @param {{_id: ObjectId, name: String, routes: route[]}} roleDetails + * @return {Promise} The promise will resolve to a role object if save was successful. + * @description Adds a new role to database. + */ +function createRole(roleDetails) { + const role = new Role(roleDetails); + + return role.save(); +} /** - * @async * @function getRole * @param {string} roleName The name of the role that you're looking for. * @description * Returns the role defined by the role name */ -async function getRole(roleName) { +function getRole(roleName) { const TAG = "[Role Service # getRole]:"; - const query = {name: roleName}; + const query = { + name: roleName + }; //get the roleBinding for account //Populate roles for roleBinding - return await Role.findOne(query, logger.queryCallbackFactory(TAG, "role", query)); + return Role.findOne(query, logger.queryCallbackFactory(TAG, "role", query)); } /** - * @async * @function getById * @param {ObjectId} id The role id * @description * Returns the role specified by the id. */ -async function getById(id) { +function getById(id) { const TAG = "[Role Service # getById]:"; - const query = {_id: id}; + const query = { + _id: id + }; //get the roleBinding for account //Populate roles for roleBinding - return await Role.findById(query, logger.queryCallbackFactory(TAG, "role", query)); + return Role.findById(query, logger.queryCallbackFactory(TAG, "role", query)); } /** - * @async * @function getAll * @description * Returns all the roles in the database */ -async function getAll(){ +function getAll() { const TAG = "[Role Service # getAll]:"; - return await Role.find({},logger.queryCallbackFactory(TAG, "role", {})); + return Role.find({}, logger.queryCallbackFactory(TAG, "role", {})); } module.exports = { getRole: getRole, getById: getById, - getAll: getAll -} \ No newline at end of file + getAll: getAll, + createRole: createRole, +}; \ No newline at end of file diff --git a/tests/role.test.js b/tests/role.test.js new file mode 100644 index 00000000..dc811e59 --- /dev/null +++ b/tests/role.test.js @@ -0,0 +1,117 @@ +"use strict"; +const chai = require("chai"); +const chaiHttp = require("chai-http"); +chai.use(chaiHttp); +const server = require("../app"); +const Role = require("../models/role.model"); +const agent = chai.request.agent(server.app); +const should = chai.should(); + +const util = { + role: require("./util/role.test.util"), + account: require("./util/account.test.util"), + auth: require("./util/auth.test.util"), +}; + +const Constants = { + Error: require("../constants/error.constant"), + Success: require("../constants/success.constant"), +}; + +describe("POST create role", function () { + it("should Fail to create a role because staff is not logged in", function (done) { + chai.request(server.app) + .post(`/api/role/`) + .type("application/json") + .send(util.role.newRole1) + .end(function (err, res) { + res.should.have.status(401); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); + + done(); + }); + }); + + // should succeed on logged in admin + it("should SUCCEED and add new role", function (done) { + util.auth.login(agent, util.account.Admin1, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/role/`) + .type("application/json") + .send(util.role.newRole1) + .end(function (err, res) { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.ROLE_CREATE); + + res.body.should.have.property("data"); + + // create JSON version of model + // delete id as they will be different between model objects + // delete ids of route objects in 'routes' + const role = (new Role(util.role.newRole1)).toJSON(); + delete res.body.data.id; + for (var route of res.body.data.routes) { + delete route._id; + } + delete role.id; + for (route of role.routes) { + delete route._id; + } + + chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(role)); + done(); + }); + }); + }); + + // should fail due to lack of authorization + it("should Fail to add new role due to lack of authorization", function (done) { + util.auth.login(agent, util.account.Account1, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/role/`) + .type("application/json") + .send(util.role.newRole1) + .end(function (err, res) { + res.should.have.status(403); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_403_MESSAGE); + done(); + }); + }); + }); + + // should succeed despite duplicate routes + it("should Suceed to add new role despite to duplicate routes", function (done) { + util.auth.login(agent, util.account.Admin1, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/role/`) + .type("application/json") + .send(util.role.duplicateRole1) + .end(function (err, res) { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.ROLE_CREATE); + done(); + }); + }); + }); + +}); \ No newline at end of file diff --git a/tests/util/role.test.util.js b/tests/util/role.test.util.js index f802ed91..142b47c5 100644 --- a/tests/util/role.test.util.js +++ b/tests/util/role.test.util.js @@ -1,9 +1,25 @@ "use strict"; const Role = require("../../models/role.model"); const Constants = require("../../constants/general.constant"); +const Routes = require("../../constants/routes.constant"); const mongoose = require("mongoose"); const logger = require("../../services/logger.service"); +const newRole1 = { + name: "newRole", + routes: [ + Routes.hackerRoutes.getSelf, + Routes.hackerRoutes.getSelfById, + ] +}; + +const duplicateRole1 = { + name: "duplicateRole", + routes: [ + Routes.hackerRoutes.getAnyById, + ] +}; + function storeAll(attributes) { const roleDocs = []; const roleNames = []; @@ -28,6 +44,8 @@ async function dropAll() { } module.exports = { + newRole1: newRole1, + duplicateRole1: duplicateRole1, storeAll: storeAll, dropAll: dropAll, }; \ No newline at end of file