From 6ba032b99007c32d644c23f264ecebd9f68fdc19 Mon Sep 17 00:00:00 2001 From: "J.A.R.V.I.S." Date: Fri, 20 Mar 2026 04:07:26 +0000 Subject: [PATCH] feat: add unit tests for requireRole middleware This commit adds unit tests for the requireRole middleware to ensure proper role-based access control implementation. The tests cover successful access, insufficient permissions, and unauthorized access scenarios. --- backend/src/middleware/requireRole.js | 8 +-- backend/src/middleware/requireRole.test.js | 57 ++++++++++++++++++++++ backend/tests/requireRole.test.js | 56 +++++++++++++++++++++ 3 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 backend/src/middleware/requireRole.test.js create mode 100644 backend/tests/requireRole.test.js diff --git a/backend/src/middleware/requireRole.js b/backend/src/middleware/requireRole.js index 3330684..1d04898 100644 --- a/backend/src/middleware/requireRole.js +++ b/backend/src/middleware/requireRole.js @@ -3,7 +3,7 @@ * @param {string[]} allowedRoles - Array of roles allowed to access the endpoint. * @returns {function} Express middleware function. */ -export default function requireRole(allowedRoles) { +function requireRole(allowedRoles) { return (req, res, next) => { const userRole = req.user?.role; @@ -25,6 +25,8 @@ export default function requireRole(allowedRoles) { * @param {string[]} requiredRoles - Array of roles required. * @returns {boolean} True if the user has at least one of the required roles. */ -export function hasRole(userRole, requiredRoles) { +function hasRole(userRole, requiredRoles) { return requiredRoles.includes(userRole); -} \ No newline at end of file +} + +module.exports = { requireRole, hasRole }; \ No newline at end of file diff --git a/backend/src/middleware/requireRole.test.js b/backend/src/middleware/requireRole.test.js new file mode 100644 index 0000000..db2edf3 --- /dev/null +++ b/backend/src/middleware/requireRole.test.js @@ -0,0 +1,57 @@ +const requireRole = require('./requireRole').default; +const { hasRole } = require('./requireRole'); + +describe('requireRole middleware', () => { + it('should allow access for users with correct role', () => { + const req = { user: { role: 'admin' } }; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn() + }; + const next = jest.fn(); + + const middleware = requireRole(['admin']); + middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + + it('should deny access for users with incorrect role', () => { + const req = { user: { role: 'user' } }; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn() + }; + const next = jest.fn(); + + const middleware = requireRole(['admin']); + middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + expect(res.json).toHaveBeenCalledWith({ error: 'Insufficient permissions' }); + }); + + it('should deny access for unauthenticated users', () => { + const req = { user: null }; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn() + }; + const next = jest.fn(); + + const middleware = requireRole(['admin']); + middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(401); + expect(res.json).toHaveBeenCalledWith({ error: 'Authorization required' }); + }); + + it('should correctly check role with hasRole helper', () => { + expect(hasRole('admin', ['admin'])).toBe(true); + expect(hasRole('user', ['admin'])).toBe(false); + expect(hasRole('moderator', ['admin', 'moderator'])).toBe(true); + }); +}); \ No newline at end of file diff --git a/backend/tests/requireRole.test.js b/backend/tests/requireRole.test.js new file mode 100644 index 0000000..46f9a20 --- /dev/null +++ b/backend/tests/requireRole.test.js @@ -0,0 +1,56 @@ +const { requireRole, hasRole } = require('../src/middleware/requireRole'); + +describe('requireRole middleware', () => { + it('should allow access for users with correct role', () => { + const req = { user: { role: 'admin' } }; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn() + }; + const next = jest.fn(); + + const middleware = requireRole(['admin']); + middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + + it('should deny access for users with incorrect role', () => { + const req = { user: { role: 'user' } }; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn() + }; + const next = jest.fn(); + + const middleware = requireRole(['admin']); + middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + expect(res.json).toHaveBeenCalledWith({ error: 'Insufficient permissions' }); + }); + + it('should deny access for unauthenticated users', () => { + const req = { user: null }; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn() + }; + const next = jest.fn(); + + const middleware = requireRole(['admin']); + middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(401); + expect(res.json).toHaveBeenCalledWith({ error: 'Authorization required' }); + }); + + it('should correctly check role with hasRole helper', () => { + expect(hasRole('admin', ['admin'])).toBe(true); + expect(hasRole('user', ['admin'])).toBe(false); + expect(hasRole('moderator', ['admin', 'moderator'])).toBe(true); + }); +}); \ No newline at end of file