backend/tests/integration/user.test.js
2020-11-28 19:30:45 +01:00

617 lines
19 KiB
JavaScript

const request = require('supertest');
const faker = require('faker');
const httpStatus = require('http-status');
const app = require('../../src/app');
const setupTestDB = require('../utils/setupTestDB');
const { User } = require('../../src/models');
const { userOne, userTwo, admin, insertUsers } = require('../fixtures/user.fixture');
const { userOneAccessToken, adminAccessToken } = require('../fixtures/token.fixture');
setupTestDB();
describe('User routes', () => {
describe('POST /v1/users', () => {
let newUser;
beforeEach(() => {
newUser = {
name: faker.name.findName(),
email: faker.internet.email().toLowerCase(),
password: 'password1',
role: 'user',
};
});
test('should return 201 and successfully create new user if data is ok', async () => {
await insertUsers([admin]);
const res = await request(app)
.post('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.send(newUser)
.expect(httpStatus.CREATED);
expect(res.body).not.toHaveProperty('password');
expect(res.body).toEqual({ id: expect.anything(), name: newUser.name, email: newUser.email, role: newUser.role });
const dbUser = await User.findById(res.body.id);
expect(dbUser).toBeDefined();
expect(dbUser.password).not.toBe(newUser.password);
expect(dbUser).toMatchObject({ name: newUser.name, email: newUser.email, role: newUser.role });
});
test('should be able to create an admin as well', async () => {
await insertUsers([admin]);
newUser.role = 'admin';
const res = await request(app)
.post('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.send(newUser)
.expect(httpStatus.CREATED);
expect(res.body.role).toBe('admin');
const dbUser = await User.findById(res.body.id);
expect(dbUser.role).toBe('admin');
});
test('should return 401 error is access token is missing', async () => {
await request(app).post('/v1/users').send(newUser).expect(httpStatus.UNAUTHORIZED);
});
test('should return 403 error if logged in user is not admin', async () => {
await insertUsers([userOne]);
await request(app)
.post('/v1/users')
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send(newUser)
.expect(httpStatus.FORBIDDEN);
});
test('should return 400 error if email is invalid', async () => {
await insertUsers([admin]);
newUser.email = 'invalidEmail';
await request(app)
.post('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.send(newUser)
.expect(httpStatus.BAD_REQUEST);
});
test('should return 400 error if email is already used', async () => {
await insertUsers([admin, userOne]);
newUser.email = userOne.email;
await request(app)
.post('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.send(newUser)
.expect(httpStatus.BAD_REQUEST);
});
test('should return 400 error if password length is less than 8 characters', async () => {
await insertUsers([admin]);
newUser.password = 'passwo1';
await request(app)
.post('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.send(newUser)
.expect(httpStatus.BAD_REQUEST);
});
test('should return 400 error if password does not contain both letters and numbers', async () => {
await insertUsers([admin]);
newUser.password = 'password';
await request(app)
.post('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.send(newUser)
.expect(httpStatus.BAD_REQUEST);
newUser.password = '1111111';
await request(app)
.post('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.send(newUser)
.expect(httpStatus.BAD_REQUEST);
});
test('should return 400 error if role is neither user nor admin', async () => {
await insertUsers([admin]);
newUser.role = 'invalid';
await request(app)
.post('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.send(newUser)
.expect(httpStatus.BAD_REQUEST);
});
});
describe('GET /v1/users', () => {
test('should return 200 and apply the default query options', async () => {
await insertUsers([userOne, userTwo, admin]);
const res = await request(app)
.get('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.send()
.expect(httpStatus.OK);
expect(res.body).toEqual({
results: expect.any(Array),
page: 1,
limit: 10,
totalPages: 1,
totalResults: 3,
});
expect(res.body.results).toHaveLength(3);
expect(res.body.results[0]).toEqual({
id: userOne._id.toHexString(),
name: userOne.name,
email: userOne.email,
role: userOne.role,
});
});
test('should return 401 if access token is missing', async () => {
await insertUsers([userOne, userTwo, admin]);
await request(app).get('/v1/users').send().expect(httpStatus.UNAUTHORIZED);
});
test('should return 403 if a non-admin is trying to access all users', async () => {
await insertUsers([userOne, userTwo, admin]);
await request(app)
.get('/v1/users')
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send()
.expect(httpStatus.FORBIDDEN);
});
test('should correctly apply filter on name field', async () => {
await insertUsers([userOne, userTwo, admin]);
const res = await request(app)
.get('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.query({ name: userOne.name })
.send()
.expect(httpStatus.OK);
expect(res.body).toEqual({
results: expect.any(Array),
page: 1,
limit: 10,
totalPages: 1,
totalResults: 1,
});
expect(res.body.results).toHaveLength(1);
expect(res.body.results[0].id).toBe(userOne._id.toHexString());
});
test('should correctly apply filter on role field', async () => {
await insertUsers([userOne, userTwo, admin]);
const res = await request(app)
.get('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.query({ role: 'user' })
.send()
.expect(httpStatus.OK);
expect(res.body).toEqual({
results: expect.any(Array),
page: 1,
limit: 10,
totalPages: 1,
totalResults: 2,
});
expect(res.body.results).toHaveLength(2);
expect(res.body.results[0].id).toBe(userOne._id.toHexString());
expect(res.body.results[1].id).toBe(userTwo._id.toHexString());
});
test('should correctly sort the returned array if descending sort param is specified', async () => {
await insertUsers([userOne, userTwo, admin]);
const res = await request(app)
.get('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.query({ sortBy: 'role:desc' })
.send()
.expect(httpStatus.OK);
expect(res.body).toEqual({
results: expect.any(Array),
page: 1,
limit: 10,
totalPages: 1,
totalResults: 3,
});
expect(res.body.results).toHaveLength(3);
expect(res.body.results[0].id).toBe(userOne._id.toHexString());
expect(res.body.results[1].id).toBe(userTwo._id.toHexString());
expect(res.body.results[2].id).toBe(admin._id.toHexString());
});
test('should correctly sort the returned array if ascending sort param is specified', async () => {
await insertUsers([userOne, userTwo, admin]);
const res = await request(app)
.get('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.query({ sortBy: 'role:asc' })
.send()
.expect(httpStatus.OK);
expect(res.body).toEqual({
results: expect.any(Array),
page: 1,
limit: 10,
totalPages: 1,
totalResults: 3,
});
expect(res.body.results).toHaveLength(3);
expect(res.body.results[0].id).toBe(admin._id.toHexString());
expect(res.body.results[1].id).toBe(userOne._id.toHexString());
expect(res.body.results[2].id).toBe(userTwo._id.toHexString());
});
test('should correctly sort the returned array if multiple sorting criteria are specified', async () => {
await insertUsers([userOne, userTwo, admin]);
const res = await request(app)
.get('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.query({ sortBy: 'role:desc,name:asc' })
.send()
.expect(httpStatus.OK);
expect(res.body).toEqual({
results: expect.any(Array),
page: 1,
limit: 10,
totalPages: 1,
totalResults: 3,
});
expect(res.body.results).toHaveLength(3);
const expectedOrder = [userOne, userTwo, admin].sort((a, b) => {
if (a.role < b.role) {
return 1;
}
if (a.role > b.role) {
return -1;
}
return a.name < b.name ? -1 : 1;
});
expectedOrder.forEach((user, index) => {
expect(res.body.results[index].id).toBe(user._id.toHexString());
});
});
test('should limit returned array if limit param is specified', async () => {
await insertUsers([userOne, userTwo, admin]);
const res = await request(app)
.get('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.query({ limit: 2 })
.send()
.expect(httpStatus.OK);
expect(res.body).toEqual({
results: expect.any(Array),
page: 1,
limit: 2,
totalPages: 2,
totalResults: 3,
});
expect(res.body.results).toHaveLength(2);
expect(res.body.results[0].id).toBe(userOne._id.toHexString());
expect(res.body.results[1].id).toBe(userTwo._id.toHexString());
});
test('should return the correct page if page and limit params are specified', async () => {
await insertUsers([userOne, userTwo, admin]);
const res = await request(app)
.get('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.query({ page: 2, limit: 2 })
.send()
.expect(httpStatus.OK);
expect(res.body).toEqual({
results: expect.any(Array),
page: 2,
limit: 2,
totalPages: 2,
totalResults: 3,
});
expect(res.body.results).toHaveLength(1);
expect(res.body.results[0].id).toBe(admin._id.toHexString());
});
});
describe('GET /v1/users/:userId', () => {
test('should return 200 and the user object if data is ok', async () => {
await insertUsers([userOne]);
const res = await request(app)
.get(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send()
.expect(httpStatus.OK);
expect(res.body).not.toHaveProperty('password');
expect(res.body).toEqual({
id: userOne._id.toHexString(),
email: userOne.email,
name: userOne.name,
role: userOne.role,
});
});
test('should return 401 error if access token is missing', async () => {
await insertUsers([userOne]);
await request(app).get(`/v1/users/${userOne._id}`).send().expect(httpStatus.UNAUTHORIZED);
});
test('should return 403 error if user is trying to get another user', async () => {
await insertUsers([userOne, userTwo]);
await request(app)
.get(`/v1/users/${userTwo._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send()
.expect(httpStatus.FORBIDDEN);
});
test('should return 200 and the user object if admin is trying to get another user', async () => {
await insertUsers([userOne, admin]);
await request(app)
.get(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${adminAccessToken}`)
.send()
.expect(httpStatus.OK);
});
test('should return 400 error if userId is not a valid mongo id', async () => {
await insertUsers([admin]);
await request(app)
.get('/v1/users/invalidId')
.set('Authorization', `Bearer ${adminAccessToken}`)
.send()
.expect(httpStatus.BAD_REQUEST);
});
test('should return 404 error if user is not found', async () => {
await insertUsers([admin]);
await request(app)
.get(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${adminAccessToken}`)
.send()
.expect(httpStatus.NOT_FOUND);
});
});
describe('DELETE /v1/users/:userId', () => {
test('should return 204 if data is ok', async () => {
await insertUsers([userOne]);
await request(app)
.delete(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send()
.expect(httpStatus.NO_CONTENT);
const dbUser = await User.findById(userOne._id);
expect(dbUser).toBeNull();
});
test('should return 401 error if access token is missing', async () => {
await insertUsers([userOne]);
await request(app).delete(`/v1/users/${userOne._id}`).send().expect(httpStatus.UNAUTHORIZED);
});
test('should return 403 error if user is trying to delete another user', async () => {
await insertUsers([userOne, userTwo]);
await request(app)
.delete(`/v1/users/${userTwo._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send()
.expect(httpStatus.FORBIDDEN);
});
test('should return 204 if admin is trying to delete another user', async () => {
await insertUsers([userOne, admin]);
await request(app)
.delete(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${adminAccessToken}`)
.send()
.expect(httpStatus.NO_CONTENT);
});
test('should return 400 error if userId is not a valid mongo id', async () => {
await insertUsers([admin]);
await request(app)
.delete('/v1/users/invalidId')
.set('Authorization', `Bearer ${adminAccessToken}`)
.send()
.expect(httpStatus.BAD_REQUEST);
});
test('should return 404 error if user already is not found', async () => {
await insertUsers([admin]);
await request(app)
.delete(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${adminAccessToken}`)
.send()
.expect(httpStatus.NOT_FOUND);
});
});
describe('PATCH /v1/users/:userId', () => {
test('should return 200 and successfully update user if data is ok', async () => {
await insertUsers([userOne]);
const updateBody = {
name: faker.name.findName(),
email: faker.internet.email().toLowerCase(),
password: 'newPassword1',
};
const res = await request(app)
.patch(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send(updateBody)
.expect(httpStatus.OK);
expect(res.body).not.toHaveProperty('password');
expect(res.body).toEqual({
id: userOne._id.toHexString(),
name: updateBody.name,
email: updateBody.email,
role: 'user',
});
const dbUser = await User.findById(userOne._id);
expect(dbUser).toBeDefined();
expect(dbUser.password).not.toBe(updateBody.password);
expect(dbUser).toMatchObject({ name: updateBody.name, email: updateBody.email, role: 'user' });
});
test('should return 401 error if access token is missing', async () => {
await insertUsers([userOne]);
const updateBody = { name: faker.name.findName() };
await request(app).patch(`/v1/users/${userOne._id}`).send(updateBody).expect(httpStatus.UNAUTHORIZED);
});
test('should return 403 if user is updating another user', async () => {
await insertUsers([userOne, userTwo]);
const updateBody = { name: faker.name.findName() };
await request(app)
.patch(`/v1/users/${userTwo._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send(updateBody)
.expect(httpStatus.FORBIDDEN);
});
test('should return 200 and successfully update user if admin is updating another user', async () => {
await insertUsers([userOne, admin]);
const updateBody = { name: faker.name.findName() };
await request(app)
.patch(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${adminAccessToken}`)
.send(updateBody)
.expect(httpStatus.OK);
});
test('should return 404 if admin is updating another user that is not found', async () => {
await insertUsers([admin]);
const updateBody = { name: faker.name.findName() };
await request(app)
.patch(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${adminAccessToken}`)
.send(updateBody)
.expect(httpStatus.NOT_FOUND);
});
test('should return 400 error if userId is not a valid mongo id', async () => {
await insertUsers([admin]);
const updateBody = { name: faker.name.findName() };
await request(app)
.patch(`/v1/users/invalidId`)
.set('Authorization', `Bearer ${adminAccessToken}`)
.send(updateBody)
.expect(httpStatus.BAD_REQUEST);
});
test('should return 400 if email is invalid', async () => {
await insertUsers([userOne]);
const updateBody = { email: 'invalidEmail' };
await request(app)
.patch(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send(updateBody)
.expect(httpStatus.BAD_REQUEST);
});
test('should return 400 if email is already taken', async () => {
await insertUsers([userOne, userTwo]);
const updateBody = { email: userTwo.email };
await request(app)
.patch(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send(updateBody)
.expect(httpStatus.BAD_REQUEST);
});
test('should not return 400 if email is my email', async () => {
await insertUsers([userOne]);
const updateBody = { email: userOne.email };
await request(app)
.patch(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send(updateBody)
.expect(httpStatus.OK);
});
test('should return 400 if password length is less than 8 characters', async () => {
await insertUsers([userOne]);
const updateBody = { password: 'passwo1' };
await request(app)
.patch(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send(updateBody)
.expect(httpStatus.BAD_REQUEST);
});
test('should return 400 if password does not contain both letters and numbers', async () => {
await insertUsers([userOne]);
const updateBody = { password: 'password' };
await request(app)
.patch(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send(updateBody)
.expect(httpStatus.BAD_REQUEST);
updateBody.password = '11111111';
await request(app)
.patch(`/v1/users/${userOne._id}`)
.set('Authorization', `Bearer ${userOneAccessToken}`)
.send(updateBody)
.expect(httpStatus.BAD_REQUEST);
});
});
});