Created
June 8, 2023 18:02
-
-
Save Deyems/7a87df42957e8b62684acef261992e13 to your computer and use it in GitHub Desktop.
User Controller file - Handles Post, Get Requests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const express = require('express'); | |
const morgan = require('morgan'); | |
const bodyParser = require('body-parser'); | |
const compress = require('compression'); | |
const methodOverride = require('method-override'); | |
const cors = require('cors'); | |
const helmet = require('helmet'); | |
const passport = require('passport'); | |
const routes = require('../api/routes/v1'); | |
const { logs } = require('./vars'); | |
const strategies = require('./passport'); | |
const error = require('../api/middlewares/error'); | |
/** | |
* Express instance | |
* @public | |
*/ | |
const app = express(); | |
// request logging. dev: console | production: file | |
app.use(morgan(logs)); | |
// parse body params and attache them to req.body | |
app.use(bodyParser.json()); | |
app.use(bodyParser.urlencoded({ extended: true })); | |
// gzip compression | |
app.use(compress()); | |
// lets you use HTTP verbs such as PUT or DELETE | |
// in places where the client doesn't support it | |
app.use(methodOverride()); | |
// secure apps by setting various HTTP headers | |
app.use(helmet()); | |
// enable CORS - Cross Origin Resource Sharing | |
app.use(cors()); | |
// enable authentication | |
app.use(passport.initialize()); | |
passport.use('jwt', strategies.jwt); | |
passport.use('facebook', strategies.facebook); | |
passport.use('google', strategies.google); | |
// mount api v1 routes | |
app.use('/v1', routes); | |
// if error is not an instanceOf APIError, convert it. | |
app.use(error.converter); | |
// catch 404 and forward to error handler | |
app.use(error.notFound); | |
// error handler, send stacktrace only during development | |
app.use(error.handler); | |
module.exports = app; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const express = require('express'); | |
const userRoutes = require('./user.route'); | |
const authRoutes = require('./auth.route'); | |
const router = express.Router(); | |
/** | |
* GET v1/status | |
*/ | |
router.get('/status', (req, res) => res.send('OK')); | |
/** | |
* GET v1/docs | |
*/ | |
router.use('/docs', express.static('docs')); | |
router.use('/users', userRoutes); | |
router.use('/auth', authRoutes); | |
module.exports = router; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const mongoose = require('mongoose'); | |
const logger = require('./logger'); | |
const { mongo, env } = require('./vars'); | |
// set mongoose Promise to Bluebird | |
mongoose.Promise = Promise; | |
// Exit application on error | |
mongoose.connection.on('error', (err) => { | |
logger.error(`MongoDB connection error: ${err}`); | |
process.exit(-1); | |
}); | |
// print mongoose logs in dev env | |
if (env === 'development') { | |
mongoose.set('debug', true); | |
} | |
/** | |
* Connect to mongo db | |
* | |
* @returns {object} Mongoose connection | |
* @public | |
*/ | |
exports.connect = () => { | |
mongoose | |
.connect(mongo.uri, { | |
useCreateIndex: true, | |
keepAlive: 1, | |
useNewUrlParser: true, | |
useUnifiedTopology: true, | |
useFindAndModify: false, | |
}) | |
.then(() => console.log('mongoDB connected...')); | |
return mongoose.connection; | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const httpStatus = require('http-status'); | |
const { omit } = require('lodash'); | |
const User = require('../models/user.model'); | |
/** | |
* Load user and append to req. | |
* @public | |
*/ | |
exports.load = async (req, res, next, id) => { | |
try { | |
const user = await User.get(id); | |
req.locals = { user }; | |
return next(); | |
} catch (error) { | |
return next(error); | |
} | |
}; | |
/** | |
* Get user | |
* @public | |
*/ | |
exports.get = (req, res) => res.json(req.locals.user.transform()); | |
/** | |
* Get logged in user info | |
* @public | |
*/ | |
exports.loggedIn = (req, res) => res.json(req.user.transform()); | |
/** | |
* Create new user | |
* @public | |
*/ | |
exports.create = async (req, res, next) => { | |
try { | |
const user = new User(req.body); | |
const savedUser = await user.save(); | |
res.status(httpStatus.CREATED); | |
res.json(savedUser.transform()); | |
} catch (error) { | |
next(User.checkDuplicateEmail(error)); | |
} | |
}; | |
/** | |
* Replace existing user | |
* @public | |
*/ | |
exports.replace = async (req, res, next) => { | |
try { | |
const { user } = req.locals; | |
const newUser = new User(req.body); | |
const ommitRole = user.role !== 'admin' ? 'role' : ''; | |
const newUserObject = omit(newUser.toObject(), '_id', ommitRole); | |
await user.updateOne(newUserObject, { override: true, upsert: true }); | |
const savedUser = await User.findById(user._id); | |
res.json(savedUser.transform()); | |
} catch (error) { | |
next(User.checkDuplicateEmail(error)); | |
} | |
}; | |
/** | |
* Update existing user | |
* @public | |
*/ | |
exports.update = (req, res, next) => { | |
const ommitRole = req.locals.user.role !== 'admin' ? 'role' : ''; | |
const updatedUser = omit(req.body, ommitRole); | |
const user = Object.assign(req.locals.user, updatedUser); | |
user.save() | |
.then((savedUser) => res.json(savedUser.transform())) | |
.catch((e) => next(User.checkDuplicateEmail(e))); | |
}; | |
/** | |
* Get user list | |
* @public | |
*/ | |
exports.list = async (req, res, next) => { | |
try { | |
const users = await User.list(req.query); | |
const transformedUsers = users.map((user) => user.transform()); | |
res.json(transformedUsers); | |
} catch (error) { | |
next(error); | |
} | |
}; | |
/** | |
* Delete user | |
* @public | |
*/ | |
exports.remove = (req, res, next) => { | |
const { user } = req.locals; | |
user.remove() | |
.then(() => res.status(httpStatus.NO_CONTENT).end()) | |
.catch((e) => next(e)); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const express = require('express'); | |
const validate = require('express-validation'); | |
const controller = require('../../controllers/user.controller'); | |
const { authorize, ADMIN, LOGGED_USER } = require('../../middlewares/auth'); | |
const { | |
listUsers, | |
createUser, | |
replaceUser, | |
updateUser, | |
} = require('../../validations/user.validation'); | |
const router = express.Router(); | |
/** | |
* Load user when API with userId route parameter is hit | |
*/ | |
router.param('userId', controller.load); | |
router | |
.route('/') | |
/** | |
* @api {get} v1/users List Users | |
* @apiDescription Get a list of users | |
* @apiVersion 1.0.0 | |
* @apiName ListUsers | |
* @apiGroup User | |
* @apiPermission admin | |
* | |
* @apiHeader {String} Authorization User's access token | |
* | |
* @apiParam {Number{1-}} [page=1] List page | |
* @apiParam {Number{1-100}} [perPage=1] Users per page | |
* @apiParam {String} [name] User's name | |
* @apiParam {String} [email] User's email | |
* @apiParam {String=user,admin} [role] User's role | |
* | |
* @apiSuccess {Object[]} users List of users. | |
* | |
* @apiError (Unauthorized 401) Unauthorized Only authenticated users can access the data | |
* @apiError (Forbidden 403) Forbidden Only admins can access the data | |
*/ | |
.get(authorize(ADMIN), validate(listUsers), controller.list) | |
/** | |
* @api {post} v1/users Create User | |
* @apiDescription Create a new user | |
* @apiVersion 1.0.0 | |
* @apiName CreateUser | |
* @apiGroup User | |
* @apiPermission admin | |
* | |
* @apiHeader {String} Authorization User's access token | |
* | |
* @apiParam {String} email User's email | |
* @apiParam {String{6..128}} password User's password | |
* @apiParam {String{..128}} [name] User's name | |
* @apiParam {String=user,admin} [role] User's role | |
* | |
* @apiSuccess (Created 201) {String} id User's id | |
* @apiSuccess (Created 201) {String} name User's name | |
* @apiSuccess (Created 201) {String} email User's email | |
* @apiSuccess (Created 201) {String} role User's role | |
* @apiSuccess (Created 201) {Date} createdAt Timestamp | |
* | |
* @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values | |
* @apiError (Unauthorized 401) Unauthorized Only authenticated users can create the data | |
* @apiError (Forbidden 403) Forbidden Only admins can create the data | |
*/ | |
.post(authorize(ADMIN), validate(createUser), controller.create); | |
router | |
.route('/profile') | |
/** | |
* @api {get} v1/users/profile User Profile | |
* @apiDescription Get logged in user profile information | |
* @apiVersion 1.0.0 | |
* @apiName UserProfile | |
* @apiGroup User | |
* @apiPermission user | |
* | |
* @apiHeader {String} Authorization User's access token | |
* | |
* @apiSuccess {String} id User's id | |
* @apiSuccess {String} name User's name | |
* @apiSuccess {String} email User's email | |
* @apiSuccess {String} role User's role | |
* @apiSuccess {Date} createdAt Timestamp | |
* | |
* @apiError (Unauthorized 401) Unauthorized Only authenticated Users can access the data | |
*/ | |
.get(authorize(), controller.loggedIn); | |
router | |
.route('/:userId') | |
/** | |
* @api {get} v1/users/:id Get User | |
* @apiDescription Get user information | |
* @apiVersion 1.0.0 | |
* @apiName GetUser | |
* @apiGroup User | |
* @apiPermission user | |
* | |
* @apiHeader {String} Authorization User's access token | |
* | |
* @apiSuccess {String} id User's id | |
* @apiSuccess {String} name User's name | |
* @apiSuccess {String} email User's email | |
* @apiSuccess {String} role User's role | |
* @apiSuccess {Date} createdAt Timestamp | |
* | |
* @apiError (Unauthorized 401) Unauthorized Only authenticated users can access the data | |
* @apiError (Forbidden 403) Forbidden Only user with same id or admins can access the data | |
* @apiError (Not Found 404) NotFound User does not exist | |
*/ | |
.get(authorize(LOGGED_USER), controller.get) | |
/** | |
* @api {put} v1/users/:id Replace User | |
* @apiDescription Replace the whole user document with a new one | |
* @apiVersion 1.0.0 | |
* @apiName ReplaceUser | |
* @apiGroup User | |
* @apiPermission user | |
* | |
* @apiHeader {String} Authorization User's access token | |
* | |
* @apiParam {String} email User's email | |
* @apiParam {String{6..128}} password User's password | |
* @apiParam {String{..128}} [name] User's name | |
* @apiParam {String=user,admin} [role] User's role | |
* (You must be an admin to change the user's role) | |
* | |
* @apiSuccess {String} id User's id | |
* @apiSuccess {String} name User's name | |
* @apiSuccess {String} email User's email | |
* @apiSuccess {String} role User's role | |
* @apiSuccess {Date} createdAt Timestamp | |
* | |
* @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values | |
* @apiError (Unauthorized 401) Unauthorized Only authenticated users can modify the data | |
* @apiError (Forbidden 403) Forbidden Only user with same id or admins can modify the data | |
* @apiError (Not Found 404) NotFound User does not exist | |
*/ | |
.put(authorize(LOGGED_USER), validate(replaceUser), controller.replace) | |
/** | |
* @api {patch} v1/users/:id Update User | |
* @apiDescription Update some fields of a user document | |
* @apiVersion 1.0.0 | |
* @apiName UpdateUser | |
* @apiGroup User | |
* @apiPermission user | |
* | |
* @apiHeader {String} Authorization User's access token | |
* | |
* @apiParam {String} email User's email | |
* @apiParam {String{6..128}} password User's password | |
* @apiParam {String{..128}} [name] User's name | |
* @apiParam {String=user,admin} [role] User's role | |
* (You must be an admin to change the user's role) | |
* | |
* @apiSuccess {String} id User's id | |
* @apiSuccess {String} name User's name | |
* @apiSuccess {String} email User's email | |
* @apiSuccess {String} role User's role | |
* @apiSuccess {Date} createdAt Timestamp | |
* | |
* @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values | |
* @apiError (Unauthorized 401) Unauthorized Only authenticated users can modify the data | |
* @apiError (Forbidden 403) Forbidden Only user with same id or admins can modify the data | |
* @apiError (Not Found 404) NotFound User does not exist | |
*/ | |
.patch(authorize(LOGGED_USER), validate(updateUser), controller.update) | |
/** | |
* @api {patch} v1/users/:id Delete User | |
* @apiDescription Delete a user | |
* @apiVersion 1.0.0 | |
* @apiName DeleteUser | |
* @apiGroup User | |
* @apiPermission user | |
* | |
* @apiHeader {String} Authorization User's access token | |
* | |
* @apiSuccess (No Content 204) Successfully deleted | |
* | |
* @apiError (Unauthorized 401) Unauthorized Only authenticated users can delete the data | |
* @apiError (Forbidden 403) Forbidden Only user with same id or admins can delete the data | |
* @apiError (Not Found 404) NotFound User does not exist | |
*/ | |
.delete(authorize(LOGGED_USER), controller.remove); | |
module.exports = router; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable arrow-body-style */ | |
/* eslint-disable no-unused-expressions */ | |
const request = require('supertest'); | |
const httpStatus = require('http-status'); | |
const { expect } = require('chai'); | |
const sinon = require('sinon'); | |
const bcrypt = require('bcryptjs'); | |
const { some, omitBy, isNil } = require('lodash'); | |
const app = require('../../../index'); | |
const User = require('../../models/user.model'); | |
const JWT_EXPIRATION = require('../../../config/vars').jwtExpirationInterval; | |
/** | |
* root level hooks | |
*/ | |
async function format(user) { | |
const formated = user; | |
// delete password | |
delete formated.password; | |
// get users from database | |
const dbUser = (await User.findOne({ email: user.email })).transform(); | |
// remove null and undefined properties | |
return omitBy(dbUser, isNil); | |
} | |
describe('Users API', async () => { | |
let adminAccessToken; | |
let userAccessToken; | |
let dbUsers; | |
let user; | |
let admin; | |
const password = '123456'; | |
const passwordHashed = await bcrypt.hash(password, 1); | |
beforeEach(async () => { | |
dbUsers = { | |
branStark: { | |
email: '[email protected]', | |
password: passwordHashed, | |
name: 'Bran Stark', | |
role: 'admin', | |
}, | |
jonSnow: { | |
email: '[email protected]', | |
password: passwordHashed, | |
name: 'Jon Snow', | |
}, | |
}; | |
user = { | |
email: '[email protected]', | |
password, | |
name: 'Daniel Sousa', | |
}; | |
admin = { | |
email: '[email protected]', | |
password, | |
name: 'Daniel Sousa', | |
role: 'admin', | |
}; | |
await User.deleteMany({}); | |
await User.insertMany([dbUsers.branStark, dbUsers.jonSnow]); | |
dbUsers.branStark.password = password; | |
dbUsers.jonSnow.password = password; | |
adminAccessToken = (await User.findAndGenerateToken(dbUsers.branStark)).accessToken; | |
userAccessToken = (await User.findAndGenerateToken(dbUsers.jonSnow)).accessToken; | |
}); | |
describe('POST /v1/users', () => { | |
it('should create a new user when request is ok', () => { | |
return request(app) | |
.post('/v1/users') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.send(admin) | |
.expect(httpStatus.CREATED) | |
.then((res) => { | |
delete admin.password; | |
expect(res.body).to.include(admin); | |
}); | |
}); | |
it('should create a new user and set default role to "user"', () => { | |
return request(app) | |
.post('/v1/users') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.send(user) | |
.expect(httpStatus.CREATED) | |
.then((res) => { | |
expect(res.body.role).to.be.equal('user'); | |
}); | |
}); | |
it('should report error when email already exists', () => { | |
user.email = dbUsers.branStark.email; | |
return request(app) | |
.post('/v1/users') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.send(user) | |
.expect(httpStatus.CONFLICT) | |
.then((res) => { | |
const { field } = res.body.errors[0]; | |
const { location } = res.body.errors[0]; | |
const { messages } = res.body.errors[0]; | |
expect(field).to.be.equal('email'); | |
expect(location).to.be.equal('body'); | |
expect(messages).to.include('"email" already exists'); | |
}); | |
}); | |
it('should report error when email is not provided', () => { | |
delete user.email; | |
return request(app) | |
.post('/v1/users') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.send(user) | |
.expect(httpStatus.BAD_REQUEST) | |
.then((res) => { | |
const { field } = res.body.errors[0]; | |
const { location } = res.body.errors[0]; | |
const { messages } = res.body.errors[0]; | |
expect(field).to.be.equal('email'); | |
expect(location).to.be.equal('body'); | |
expect(messages).to.include('"email" is required'); | |
}); | |
}); | |
it('should report error when password length is less than 6', () => { | |
user.password = '12345'; | |
return request(app) | |
.post('/v1/users') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.send(user) | |
.expect(httpStatus.BAD_REQUEST) | |
.then((res) => { | |
const { field } = res.body.errors[0]; | |
const { location } = res.body.errors[0]; | |
const { messages } = res.body.errors[0]; | |
expect(field).to.be.equal('password'); | |
expect(location).to.be.equal('body'); | |
expect(messages).to.include('"password" length must be at least 6 characters long'); | |
}); | |
}); | |
it('should report error when logged user is not an admin', () => { | |
return request(app) | |
.post('/v1/users') | |
.set('Authorization', `Bearer ${userAccessToken}`) | |
.send(user) | |
.expect(httpStatus.FORBIDDEN) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); | |
expect(res.body.message).to.be.equal('Forbidden'); | |
}); | |
}); | |
}); | |
describe('GET /v1/users', () => { | |
it('should get all users', () => { | |
return request(app) | |
.get('/v1/users') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.expect(httpStatus.OK) | |
.then(async (res) => { | |
const bran = await format(dbUsers.branStark); | |
const john = await format(dbUsers.jonSnow); | |
// before comparing it is necessary to convert String to Date | |
res.body[0].createdAt = new Date(res.body[0].createdAt); | |
res.body[1].createdAt = new Date(res.body[1].createdAt); | |
const includesBranStark = some(res.body, bran); | |
const includesjonSnow = some(res.body, john); | |
expect(res.body).to.be.an('array'); | |
expect(res.body).to.have.lengthOf(2); | |
expect(includesBranStark).to.be.true; | |
expect(includesjonSnow).to.be.true; | |
}); | |
}); | |
it('should get all users with pagination', () => { | |
return request(app) | |
.get('/v1/users') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.query({ page: 2, perPage: 1 }) | |
.expect(httpStatus.OK) | |
.then(async (res) => { | |
delete dbUsers.jonSnow.password; | |
expect(res.body).to.be.an('array'); | |
expect(res.body[0]).to.be.an('object'); | |
expect(res.body).to.have.lengthOf(1); | |
expect(res.body[0].name).to.be.equal('Jon Snow'); | |
}); | |
}); | |
it('should filter users', () => { | |
return request(app) | |
.get('/v1/users') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.query({ email: dbUsers.jonSnow.email }) | |
.expect(httpStatus.OK) | |
.then(async (res) => { | |
delete dbUsers.jonSnow.password; | |
const john = await format(dbUsers.jonSnow); | |
// before comparing it is necessary to convert String to Date | |
res.body[0].createdAt = new Date(res.body[0].createdAt); | |
const includesjonSnow = some(res.body, john); | |
expect(res.body).to.be.an('array'); | |
expect(res.body).to.have.lengthOf(1); | |
expect(includesjonSnow).to.be.true; | |
}); | |
}); | |
it('should report error when pagination\'s parameters are not a number', () => { | |
return request(app) | |
.get('/v1/users') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.query({ page: '?', perPage: 'whaat' }) | |
.expect(httpStatus.BAD_REQUEST) | |
.then((res) => { | |
const { field } = res.body.errors[0]; | |
const { location } = res.body.errors[0]; | |
const { messages } = res.body.errors[0]; | |
expect(field).to.be.equal('page'); | |
expect(location).to.be.equal('query'); | |
expect(messages).to.include('"page" must be a number'); | |
return Promise.resolve(res); | |
}) | |
.then((res) => { | |
const { field } = res.body.errors[1]; | |
const { location } = res.body.errors[1]; | |
const { messages } = res.body.errors[1]; | |
expect(field).to.be.equal('perPage'); | |
expect(location).to.be.equal('query'); | |
expect(messages).to.include('"perPage" must be a number'); | |
}); | |
}); | |
it('should report error if logged user is not an admin', () => { | |
return request(app) | |
.get('/v1/users') | |
.set('Authorization', `Bearer ${userAccessToken}`) | |
.expect(httpStatus.FORBIDDEN) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); | |
expect(res.body.message).to.be.equal('Forbidden'); | |
}); | |
}); | |
}); | |
describe('GET /v1/users/:userId', () => { | |
it('should get user', async () => { | |
const id = (await User.findOne({}))._id; | |
delete dbUsers.branStark.password; | |
return request(app) | |
.get(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.expect(httpStatus.OK) | |
.then((res) => { | |
expect(res.body).to.include(dbUsers.branStark); | |
}); | |
}); | |
it('should report error "User does not exist" when user does not exists', () => { | |
return request(app) | |
.get('/v1/users/56c787ccc67fc16ccc1a5e92') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.expect(httpStatus.NOT_FOUND) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(404); | |
expect(res.body.message).to.be.equal('User does not exist'); | |
}); | |
}); | |
it('should report error "User does not exist" when id is not a valid ObjectID', () => { | |
return request(app) | |
.get('/v1/users/palmeiras1914') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.expect(httpStatus.NOT_FOUND) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(404); | |
expect(res.body.message).to.equal('User does not exist'); | |
}); | |
}); | |
it('should report error when logged user is not the same as the requested one', async () => { | |
const id = (await User.findOne({ email: dbUsers.branStark.email }))._id; | |
return request(app) | |
.get(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${userAccessToken}`) | |
.expect(httpStatus.FORBIDDEN) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); | |
expect(res.body.message).to.be.equal('Forbidden'); | |
}); | |
}); | |
}); | |
describe('PUT /v1/users/:userId', () => { | |
it('should replace user', async () => { | |
delete dbUsers.branStark.password; | |
const id = (await User.findOne(dbUsers.branStark))._id; | |
return request(app) | |
.put(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.send(user) | |
.expect(httpStatus.OK) | |
.then((res) => { | |
delete user.password; | |
expect(res.body).to.include(user); | |
expect(res.body.role).to.be.equal('user'); | |
}); | |
}); | |
it('should report error when email is not provided', async () => { | |
const id = (await User.findOne({}))._id; | |
delete user.email; | |
return request(app) | |
.put(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.send(user) | |
.expect(httpStatus.BAD_REQUEST) | |
.then((res) => { | |
const { field } = res.body.errors[0]; | |
const { location } = res.body.errors[0]; | |
const { messages } = res.body.errors[0]; | |
expect(field).to.be.equal('email'); | |
expect(location).to.be.equal('body'); | |
expect(messages).to.include('"email" is required'); | |
}); | |
}); | |
it('should report error user when password length is less than 6', async () => { | |
const id = (await User.findOne({}))._id; | |
user.password = '12345'; | |
return request(app) | |
.put(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.send(user) | |
.expect(httpStatus.BAD_REQUEST) | |
.then((res) => { | |
const { field } = res.body.errors[0]; | |
const { location } = res.body.errors[0]; | |
const { messages } = res.body.errors[0]; | |
expect(field).to.be.equal('password'); | |
expect(location).to.be.equal('body'); | |
expect(messages).to.include('"password" length must be at least 6 characters long'); | |
}); | |
}); | |
it('should report error "User does not exist" when user does not exists', () => { | |
return request(app) | |
.put('/v1/users/palmeiras1914') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.expect(httpStatus.NOT_FOUND) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(404); | |
expect(res.body.message).to.be.equal('User does not exist'); | |
}); | |
}); | |
it('should report error when logged user is not the same as the requested one', async () => { | |
const id = (await User.findOne({ email: dbUsers.branStark.email }))._id; | |
return request(app) | |
.put(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${userAccessToken}`) | |
.expect(httpStatus.FORBIDDEN) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); | |
expect(res.body.message).to.be.equal('Forbidden'); | |
}); | |
}); | |
it('should not replace the role of the user (not admin)', async () => { | |
const id = (await User.findOne({ email: dbUsers.jonSnow.email }))._id; | |
const role = 'admin'; | |
return request(app) | |
.put(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${userAccessToken}`) | |
.send(admin) | |
.expect(httpStatus.OK) | |
.then((res) => { | |
expect(res.body.role).to.not.be.equal(role); | |
}); | |
}); | |
}); | |
describe('PATCH /v1/users/:userId', () => { | |
it('should update user', async () => { | |
delete dbUsers.branStark.password; | |
const id = (await User.findOne(dbUsers.branStark))._id; | |
const { name } = user; | |
return request(app) | |
.patch(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.send({ name }) | |
.expect(httpStatus.OK) | |
.then((res) => { | |
expect(res.body.name).to.be.equal(name); | |
expect(res.body.email).to.be.equal(dbUsers.branStark.email); | |
}); | |
}); | |
it('should not update user when no parameters were given', async () => { | |
delete dbUsers.branStark.password; | |
const id = (await User.findOne(dbUsers.branStark))._id; | |
return request(app) | |
.patch(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.send() | |
.expect(httpStatus.OK) | |
.then((res) => { | |
expect(res.body).to.include(dbUsers.branStark); | |
}); | |
}); | |
it('should report error "User does not exist" when user does not exists', () => { | |
return request(app) | |
.patch('/v1/users/palmeiras1914') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.expect(httpStatus.NOT_FOUND) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(404); | |
expect(res.body.message).to.be.equal('User does not exist'); | |
}); | |
}); | |
it('should report error when logged user is not the same as the requested one', async () => { | |
const id = (await User.findOne({ email: dbUsers.branStark.email }))._id; | |
return request(app) | |
.patch(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${userAccessToken}`) | |
.expect(httpStatus.FORBIDDEN) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); | |
expect(res.body.message).to.be.equal('Forbidden'); | |
}); | |
}); | |
it('should not update the role of the user (not admin)', async () => { | |
const id = (await User.findOne({ email: dbUsers.jonSnow.email }))._id; | |
const role = 'admin'; | |
return request(app) | |
.patch(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${userAccessToken}`) | |
.send({ role }) | |
.expect(httpStatus.OK) | |
.then((res) => { | |
expect(res.body.role).to.not.be.equal(role); | |
}); | |
}); | |
}); | |
describe('DELETE /v1/users', () => { | |
it('should delete user', async () => { | |
const id = (await User.findOne({}))._id; | |
return request(app) | |
.delete(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.expect(httpStatus.NO_CONTENT) | |
.then(() => request(app).get('/v1/users')) | |
.then(async () => { | |
const users = await User.find({}); | |
expect(users).to.have.lengthOf(1); | |
}); | |
}); | |
it('should report error "User does not exist" when user does not exists', () => { | |
return request(app) | |
.delete('/v1/users/palmeiras1914') | |
.set('Authorization', `Bearer ${adminAccessToken}`) | |
.expect(httpStatus.NOT_FOUND) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(404); | |
expect(res.body.message).to.be.equal('User does not exist'); | |
}); | |
}); | |
it('should report error when logged user is not the same as the requested one', async () => { | |
const id = (await User.findOne({ email: dbUsers.branStark.email }))._id; | |
return request(app) | |
.delete(`/v1/users/${id}`) | |
.set('Authorization', `Bearer ${userAccessToken}`) | |
.expect(httpStatus.FORBIDDEN) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); | |
expect(res.body.message).to.be.equal('Forbidden'); | |
}); | |
}); | |
}); | |
describe('GET /v1/users/profile', () => { | |
it('should get the logged user\'s info', () => { | |
delete dbUsers.jonSnow.password; | |
return request(app) | |
.get('/v1/users/profile') | |
.set('Authorization', `Bearer ${userAccessToken}`) | |
.expect(httpStatus.OK) | |
.then((res) => { | |
expect(res.body).to.include(dbUsers.jonSnow); | |
}); | |
}); | |
it('should report error without stacktrace when accessToken is expired', async () => { | |
// fake time | |
const clock = sinon.useFakeTimers(); | |
const expiredAccessToken = (await User.findAndGenerateToken(dbUsers.branStark)).accessToken; | |
// move clock forward by minutes set in config + 1 minute | |
clock.tick((JWT_EXPIRATION * 60000) + 60000); | |
return request(app) | |
.get('/v1/users/profile') | |
.set('Authorization', `Bearer ${expiredAccessToken}`) | |
.expect(httpStatus.UNAUTHORIZED) | |
.then((res) => { | |
expect(res.body.code).to.be.equal(httpStatus.UNAUTHORIZED); | |
expect(res.body.message).to.be.equal('jwt expired'); | |
expect(res.body).to.not.have.a.property('stack'); | |
}); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment