diff --git a/package.json b/package.json index 7b9c1f11..6362a85b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "start": "pm2 start ecosystem.config.json --no-daemon", "dev": "cross-env NODE_ENV=development nodemon src/index.js", "test": "jest -i", - "test:watch": "jest -i --verbose --watchAll", + "test:watch": "jest -i --watchAll", "coverage": "jest -i --coverage", "lint": "eslint .", "lint:fix": "eslint . --fix", diff --git a/tests/integration/auth.test.js b/tests/integration/auth.test.js index 56b1bc2d..9303390a 100644 --- a/tests/integration/auth.test.js +++ b/tests/integration/auth.test.js @@ -1,14 +1,22 @@ const request = require('supertest'); const faker = require('faker'); const httpStatus = require('http-status'); +const httpMocks = require('node-mocks-http'); +const moment = require('moment'); const app = require('../../src/app'); +const config = require('../../src/config/config'); +const auth = require('../../src/middlewares/auth'); +const tokenService = require('../../src/services/token.service'); +const AppError = require('../../src/utils/AppError'); const setupDatabase = require('../utils/setupDatabase'); const { User } = require('../../src/models'); -const { userOne, insertUsers } = require('../fixtures/user.fixture'); +const { roleRights } = require('../../src/config/roles'); +const { userOne, admin, insertUsers } = require('../fixtures/user.fixture'); +const { userOneAccessToken, adminAccessToken } = require('../fixtures/token.fixture'); setupDatabase(); -describe('Auth route', () => { +describe('Auth routes', () => { describe('POST /v1/auth/register', () => { let newUser; beforeEach(() => { @@ -140,3 +148,121 @@ describe('Auth route', () => { }); }); }); + +describe('Auth middleware', () => { + test('should call next with no errors if access token is valid', async () => { + await insertUsers([userOne]); + const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${userOneAccessToken}` } }); + const next = jest.fn(); + + await auth()(req, httpMocks.createResponse(), next); + + expect(next).toHaveBeenCalledWith(); + expect(req.user._id).toEqual(userOne._id); + }); + + test('should call next with unauthorized error if access token is not found in header', async () => { + await insertUsers([userOne]); + const req = httpMocks.createRequest(); + const next = jest.fn(); + + await auth()(req, httpMocks.createResponse(), next); + + expect(next).toHaveBeenCalledWith(expect.any(AppError)); + expect(next).toHaveBeenCalledWith( + expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' }) + ); + }); + + test('should call next with unauthorized error if access token is not a valid jwt token', async () => { + await insertUsers([userOne]); + const req = httpMocks.createRequest({ headers: { Authorization: 'Bearer randomToken' } }); + const next = jest.fn(); + + await auth()(req, httpMocks.createResponse(), next); + + expect(next).toHaveBeenCalledWith(expect.any(AppError)); + expect(next).toHaveBeenCalledWith( + expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' }) + ); + }); + + test('should call next with unauthorized error if access token is generated with an invalid secret', async () => { + await insertUsers([userOne]); + const tokenExpires = moment().add(config.jwt.accessExpirationMinutes, 'minutes'); + const accessToken = tokenService.generateToken(userOne._id, tokenExpires, 'invalidSecret'); + const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${accessToken}` } }); + const next = jest.fn(); + + await auth()(req, httpMocks.createResponse(), next); + + expect(next).toHaveBeenCalledWith(expect.any(AppError)); + expect(next).toHaveBeenCalledWith( + expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' }) + ); + }); + + test('should call next with unauthorized error if access token is expired', async () => { + await insertUsers([userOne]); + const tokenExpires = moment().subtract(1, 'minutes'); + const accessToken = tokenService.generateToken(userOne._id, tokenExpires); + const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${accessToken}` } }); + const next = jest.fn(); + + await auth()(req, httpMocks.createResponse(), next); + + expect(next).toHaveBeenCalledWith(expect.any(AppError)); + expect(next).toHaveBeenCalledWith( + expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' }) + ); + }); + + test('should call next with unauthorized error if user is not found', async () => { + const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${userOneAccessToken}` } }); + const next = jest.fn(); + + await auth()(req, httpMocks.createResponse(), next); + + expect(next).toHaveBeenCalledWith(expect.any(AppError)); + expect(next).toHaveBeenCalledWith( + expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' }) + ); + }); + + test('should call next with forbidden error if user does not have required rights and userId is not in params', async () => { + await insertUsers([userOne]); + const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${userOneAccessToken}` } }); + const next = jest.fn(); + + await auth('anyRight')(req, httpMocks.createResponse(), next); + + expect(next).toHaveBeenCalledWith(expect.any(AppError)); + expect(next).toHaveBeenCalledWith(expect.objectContaining({ statusCode: httpStatus.FORBIDDEN, message: 'Forbidden' })); + }); + + test('should call next with no errors if user does not have required rights but userId is in params', async () => { + await insertUsers([userOne]); + const req = httpMocks.createRequest({ + headers: { Authorization: `Bearer ${userOneAccessToken}` }, + params: { userId: userOne._id.toHexString() }, + }); + const next = jest.fn(); + + await auth('anyRight')(req, httpMocks.createResponse(), next); + + expect(next).toHaveBeenCalledWith(); + }); + + test('should call next with no errors if user has required rights', async () => { + await insertUsers([admin]); + const req = httpMocks.createRequest({ + headers: { Authorization: `Bearer ${adminAccessToken}` }, + params: { userId: userOne._id.toHexString() }, + }); + const next = jest.fn(); + + await auth(...roleRights.get('admin'))(req, httpMocks.createResponse(), next); + + expect(next).toHaveBeenCalledWith(); + }); +}); diff --git a/tests/integration/user.test.js b/tests/integration/user.test.js index cd523a77..c62ccc17 100644 --- a/tests/integration/user.test.js +++ b/tests/integration/user.test.js @@ -9,7 +9,7 @@ const { userOneAccessToken, adminAccessToken } = require('../fixtures/token.fixt setupDatabase(); -describe('User route', () => { +describe('User routes', () => { describe('POST /v1/users', () => { let newUser;