551 lines
18 KiB
JavaScript
551 lines
18 KiB
JavaScript
/* 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: 'branstark@gmail.com',
|
|
password: passwordHashed,
|
|
name: 'Bran Stark',
|
|
role: 'admin',
|
|
},
|
|
jonSnow: {
|
|
email: 'jonsnow@gmail.com',
|
|
password: passwordHashed,
|
|
name: 'Jon Snow',
|
|
},
|
|
};
|
|
|
|
user = {
|
|
email: 'sousa.dfs@gmail.com',
|
|
password,
|
|
name: 'krish Sousa',
|
|
};
|
|
|
|
admin = {
|
|
email: 'sousa.dfs@gmail.com',
|
|
password,
|
|
name: 'krish 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');
|
|
});
|
|
});
|
|
});
|
|
});
|