From 9473eddbd92bf883c7a627c64d9ed17307cef646 Mon Sep 17 00:00:00 2001 From: "Prince NZAMUWE(Fudji Bokande)" <134228300+princenzmw@users.noreply.github.com> Date: Wed, 26 Jun 2024 18:05:56 +0200 Subject: [PATCH] [finishes #187854543] write tests for profileController and permissionController --- package.json | 1 + src/test/permissionController.test.ts | 284 ++++++++++++++++++++++++++ src/test/profileController.test.ts | 230 +++++++++++++++++++++ 3 files changed, 515 insertions(+) create mode 100644 src/test/permissionController.test.ts create mode 100644 src/test/profileController.test.ts diff --git a/package.json b/package.json index f6bbde56..55971f1d 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "nodemon": "^3.1.0", "prettier": "^3.2.5", "sequelize-cli": "^6.6.2", + "sequelize-mock": "^0.10.2", "supertest": "^6.3.4", "swagger-jsdoc": "^6.2.8", "ts-jest": "^29.1.2", diff --git a/src/test/permissionController.test.ts b/src/test/permissionController.test.ts new file mode 100644 index 00000000..1935a821 --- /dev/null +++ b/src/test/permissionController.test.ts @@ -0,0 +1,284 @@ +import { Request, Response, NextFunction } from 'express'; +import * as controller from '../controllers/permissionController'; +import { isAuthenticated, checkUserRoles } from '../middlewares/authMiddlewares'; +import Permission from '../database/models/permission'; + +jest.mock('../database/models/permission', () => ({ + create: jest.fn(), + findAll: jest.fn(), + findByPk: jest.fn(), + destroy: jest.fn(), + findOne: jest.fn(), +})); +jest.mock('../middlewares/authMiddlewares', () => ({ + isAuthenticated: jest.fn(), + checkUserRoles: jest.fn(), +})); +describe('Permission Controller', () => { + let req: Partial; + let res: Partial; + let next: NextFunction; + + beforeEach(() => { + req = {}; + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + next = jest.fn(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('POST /api/permissions', () => { + describe('given a valid request body it should create permission and return 201', () => { + it('should create a permission', async () => { + const mockCreatedPermission = { id: 1, name: 'testPermission' }; + const mockReq = { body: { name: 'testPermission' } }; + const mockRes: Partial = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + const mockNext: NextFunction = jest.fn(); + (isAuthenticated as jest.Mock).mockImplementation((req, res, next) => { + req.user = { Role: { name: 'admin' } }; // Simulating admin user + next(); + }); + (Permission.create as jest.Mock).mockResolvedValue(mockCreatedPermission); + + await controller.createPermission(mockReq as Request, mockRes as unknown as Response); + + expect(mockRes.status).toHaveBeenCalledWith(201); + expect(mockRes.json).toHaveBeenCalledWith({ + ok: true, + data: mockCreatedPermission, + }); + }); + }); + it('should handle missing required fields', async () => { + const mockReq = { body: {} }; + const mockRes: Partial = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + + await controller.createPermission(mockReq as Request, mockRes as unknown as Response); + + expect(mockRes.status).toHaveBeenCalledWith(400); + expect(mockRes.json).toHaveBeenCalledWith({ + ok: false, + errorMessage: 'Missing required fields: name', + }); + }); + it('should handle errors', async () => { + const mockReq = { body: { name: 'testPermission' } }; + const mockError = new Error('Test error'); + (Permission.create as jest.Mock).mockRejectedValue(mockError); + + await controller.createPermission(mockReq as Request, res as Response); + + expect(res.status).toHaveBeenCalledWith(500); + expect(res.json).toBeTruthy(); + }); + }); + + describe('GET /api/permissions', () => { + it('should get all permissions', async () => { + const mockPermissions = [ + { id: 1, name: 'Permission 1' }, + { id: 2, name: 'Permission 2' }, + ]; + const mockReq = {}; + const mockRes: Partial = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + (Permission.findAll as jest.Mock).mockResolvedValue(mockPermissions); + + await controller.getAllPermissions(mockReq as Request, mockRes as unknown as Response); + + expect(mockRes.status).toHaveBeenCalledWith(200); + expect(mockRes.json).toHaveBeenCalledWith({ + ok: true, + data: mockPermissions, + }); + }); + + it('should handle errors', async () => { + const mockReq = {}; + const mockError = new Error('Test error'); + (Permission.findAll as jest.Mock).mockRejectedValue(mockError); + + await controller.getAllPermissions(mockReq as Request, res as unknown as Response); + + expect(res.status).toHaveBeenCalledWith(500); + + expect(res.json).toBeTruthy(); + }); + }); + describe('UPDATE /api/permisions/:id', () => { + it('should update a permission', async () => { + const mockPermissionToUpdate = { + id: 1, + name: 'Permission 1', + save: jest.fn().mockResolvedValue(true), + }; + const mockReq = { + params: { id: '1' }, + body: { name: 'Updated Permission Name' }, + }; + const mockRes: Partial = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + (Permission.findByPk as jest.Mock).mockResolvedValue(mockPermissionToUpdate); + + await controller.updatePermission(mockReq as unknown as Request, mockRes as unknown as Response); + + expect(mockPermissionToUpdate.name).toEqual('Updated Permission Name'); + expect((mockPermissionToUpdate as any).save).toHaveBeenCalled(); + expect(mockRes.status).toHaveBeenCalledWith(200); + expect(mockRes.json).toHaveBeenCalledWith({ + ok: true, + data: mockPermissionToUpdate, + }); + }); + + it('should handle permission not found', async () => { + const mockReq = { + params: { id: '999' }, + body: { name: 'Updated Permission Name' }, + }; + const mockRes: Partial = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + (Permission.findByPk as jest.Mock).mockResolvedValue(null); + + await controller.updatePermission(mockReq as unknown as Request, mockRes as unknown as Response); + + expect(mockRes.status).toHaveBeenCalledWith(404); + expect(mockRes.json).toHaveBeenCalledWith({ + ok: false, + errorMessage: 'Permission not found', + }); + }); + + it('should handle missing name in request body', async () => { + const mockReq = { params: { id: '1' }, body: {} }; + const mockRes: Partial = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + const mockPermissionToUpdate = { id: 1, name: 'Permission 1' }; + (Permission.findByPk as jest.Mock).mockResolvedValue(mockPermissionToUpdate); + + await controller.updatePermission(mockReq as unknown as Request, mockRes as unknown as Response); + + expect(mockRes.status).toHaveBeenCalledWith(400); + }); + + it('should handle errors', async () => { + const mockError = new Error('Test error'); + (Permission.findByPk as jest.Mock).mockRejectedValue(mockError); + + await controller.updatePermission(req as Request, res as unknown as Response); + + expect(res.status).toHaveBeenCalledWith(500); + }); + }); + describe('DELETE /api/permissions/:id', () => { + it('should delete a permission', async () => { + const mockPermissionToDelete = { + id: 1, + name: 'Permission 1', + destroy: jest.fn().mockResolvedValue(true), + }; + const mockReq = { params: { id: '1' } }; + const mockRes: Partial = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + (Permission.findByPk as jest.Mock).mockResolvedValue(mockPermissionToDelete); + + await controller.deletePermission(mockReq as unknown as Request, mockRes as unknown as Response); + + expect((mockPermissionToDelete as any).destroy).toHaveBeenCalled(); + expect(mockRes.status).toHaveBeenCalledWith(200); + expect(mockRes.json).toHaveBeenCalledWith({ + ok: true, + message: 'permission deleted successfully!', + }); + }); + + it('should handle permission not found', async () => { + const mockReq = { params: { id: '999' } }; + const mockRes: Partial = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + (Permission.findByPk as jest.Mock).mockResolvedValue(null); + + await controller.deletePermission(mockReq as unknown as Request, mockRes as unknown as Response); + + expect(mockRes.status).toHaveBeenCalledWith(404); + expect(mockRes.json).toHaveBeenCalledWith({ + ok: false, + errorMessage: 'Permission not found', + }); + }); + + it('should handle errors', async () => { + const mockError = new Error('Test error'); + (Permission.findByPk as jest.Mock).mockRejectedValue(mockError); + + await controller.deletePermission(req as Request, res as unknown as Response); + + expect(res.status).toHaveBeenCalledWith(500); + }); + }); + + describe('GET /api/permissions/:id', () => { + it('should get a single permission', async () => { + const mockPermission = { id: 1, name: 'Permission 1' }; + const mockReq = { params: { id: '1' } }; + const mockRes: Partial = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + (Permission.findByPk as jest.Mock).mockResolvedValue(mockPermission); + + await controller.getSinglePermission(mockReq as unknown as Request, mockRes as unknown as Response); + + expect(mockRes.status).toHaveBeenCalledWith(200); + }); + + it('should handle permission not found', async () => { + const mockReq = { params: { id: '999' } }; + const mockRes: Partial = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + (Permission.findByPk as jest.Mock).mockResolvedValue(null); + + await controller.getSinglePermission(mockReq as unknown as Request, mockRes as unknown as Response); + + expect(mockRes.status).toHaveBeenCalledWith(404); + expect(mockRes.json).toHaveBeenCalledWith({ + ok: false, + errorMessage: 'Permission not found', + }); + }); + + it('should handle errors', async () => { + const mockError = new Error('Test error'); + (Permission.findByPk as jest.Mock).mockRejectedValue(mockError); + + await controller.getSinglePermission(req as Request, res as unknown as Response); + + expect(res.status).toHaveBeenCalledWith(500); + }); + }); +}); diff --git a/src/test/profileController.test.ts b/src/test/profileController.test.ts new file mode 100644 index 00000000..0093404a --- /dev/null +++ b/src/test/profileController.test.ts @@ -0,0 +1,230 @@ +// src/test/profileController.test.ts +import { Request, Response } from 'express'; +import { getUserProfile, updateUser } from '../controllers/profileController'; +import User from '../database/models/user'; +import Role from '../database/models/role'; +import logger from '../logs/config'; +import uploadImage from '../helpers/claudinary'; +import { sendInternalErrorResponse } from '../validations'; +import { Readable } from 'stream'; + +// Mock dependencies +jest.mock('../database/models/user', () => { + const sequelizeMock = jest.requireActual('sequelize-mock'); + const dbMock = new sequelizeMock(); + + // Define UserMock interface + interface UserMock { + findByPk: jest.Mock; + update: jest.Mock; + } + + // Define User variable as UserMock + const User: UserMock = dbMock.define('User', { + id: 'some_uuid_string', + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + password: 'sample_password', + gender: 'male', + phoneNumber: '1234567890', + googleId: 'sample_google_id', + photoUrl: 'http://example.com/photo.jpg', + verified: true, + status: 'active', // UserStatus.ACTIVE + createdAt: new Date(), + updatedAt: new Date(), + RoleId: 'some_role_id', + enable2FA: true, + lastPasswordUpdated: new Date(), + }); + + // Assign jest.fn() to User methods + User.findByPk = jest.fn(); + User.update = jest.fn(); + + return User; +}); + +jest.mock('../database/models/role', () => { + const sequelizeMock = jest.requireActual('sequelize-mock'); + const dbMock = new sequelizeMock(); + + const Role = dbMock.define('Role', { + id: 'some_uuid_string', + name: 'buyer', + displayName: 'Buyer Role Display Name', + createdAt: new Date(), + updatedAt: new Date(), + }); + + return Role; +}); +jest.mock('../logs/config'); +jest.mock('../helpers/claudinary'); +jest.mock('../validations'); + +// Define a type for our mocked User model for TypeScript +type MockedUserModel = typeof User & { + findByPk: jest.Mock; + update: jest.Mock; +}; + +describe('Profile Controller', () => { + let req: Partial; + let res: Partial; + + beforeEach(() => { + req = { + params: {}, + body: {}, + user: { id: 'user-id' }, + file: { + buffer: Buffer.from('image-data'), + fieldname: 'file', + originalname: 'test.png', + encoding: '7bit', + mimetype: 'image/png', + size: 1024, + stream: new Readable(), + destination: '', + filename: '', + path: '', + } as Express.Multer.File, + }; + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + + // Reset and setup mocks for each test + (User.findByPk as jest.Mock).mockClear(); + (uploadImage as jest.Mock).mockClear(); + (User as unknown as MockedUserModel).update.mockClear(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('getUserProfile', () => { + it('should return user profile if user exists', async () => { + const user = { + id: 'user-id', + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + phoneNumber: '1234567890', + photoUrl: 'http://example.com/photo.jpg', + gender: 'male', + Role: { name: 'buyer' }, + }; + (User.findByPk as jest.Mock).mockResolvedValue(user); + + await getUserProfile(req as Request, res as Response); + + expect(User.findByPk).toHaveBeenCalledWith('user-id', { + attributes: ['id', 'firstName', 'lastName', 'email', 'phoneNumber', 'photoUrl', 'gender'], + include: { model: Role, attributes: ['name'] }, + }); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ ok: true, message: user }); + }); + + it('should return 404 if user is not found', async () => { + (User.findByPk as jest.Mock).mockResolvedValue(null); + + await getUserProfile(req as Request, res as Response); + + expect(res.status).toHaveBeenCalledWith(404); + expect(res.json).toHaveBeenCalledWith({ ok: false, message: 'User not found' }); + }); + + it('should handle errors and log them', async () => { + const error = new Error('Database error'); + (User.findByPk as jest.Mock).mockRejectedValue(error); + + await getUserProfile(req as Request, res as Response); + + expect(logger.error).toHaveBeenCalledWith('Error fetching user profile:: ', error); + expect(sendInternalErrorResponse).toHaveBeenCalledWith(res, error); + }); + }); + + describe('updateUser', () => { + it('should update user profile if user exists and image is provided', async () => { + const user = { + id: 'user-id', + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@example.com', + phoneNumber: '1234567890', + photoUrl: 'http://example.com/photo.jpg', + gender: 'male', + update: jest.fn(), + }; + const uploadedImage = 'http://example.com/new-photo.jpg'; + (User.findByPk as jest.Mock).mockResolvedValue(user); + (uploadImage as jest.Mock).mockResolvedValue(uploadedImage); + + // Set the user-id in the params of the request object + req.params = { id: 'user-id' }; + req.body = { firstName: 'Jane', lastName: 'Doe', gender: 'female', phoneNumber: '0987654321' }; + + await updateUser(req as Request, res as Response); + + expect(User.findByPk).toHaveBeenCalledWith('user-id'); + expect(uploadImage).toHaveBeenCalledWith(req.file!.buffer); + expect(user.update).toHaveBeenCalledWith({ + firstName: 'Jane', + lastName: 'Doe', + gender: 'female', + phoneNumber: '0987654321', + photoUrl: uploadedImage, + }); + expect(res.status).toHaveBeenCalledWith(201); + expect(res.json).toHaveBeenCalledWith({ + ok: true, + message: { + id: 'user-id', + firstName: 'Jane', + lastName: 'Doe', + email: 'john.doe@example.com', + phoneNumber: '0987654321', + gender: 'female', + photoUrl: uploadedImage, + }, + }); + }); + + it('should return 404 if user is not found', async () => { + (User.findByPk as jest.Mock).mockResolvedValue(null); + + await updateUser(req as Request, res as Response); + + expect(res.status).toHaveBeenCalledWith(404); + expect(res.json).toHaveBeenCalledWith({ ok: false, error: 'User not found' }); + }); + + it('should return 400 if profile image is not provided', async () => { + req.file = undefined; + const user = { id: 'user-id' }; + (User.findByPk as jest.Mock).mockResolvedValue(user); + + await updateUser(req as Request, res as Response); + + expect(res.status).toHaveBeenCalledWith(400); + expect(res.json).toHaveBeenCalledWith({ ok: false, error: 'Profile Image required.' }); + }); + + it('should handle errors and log them', async () => { + const error = new Error('Database error'); + (User.findByPk as jest.Mock).mockRejectedValue(error); + + await updateUser(req as Request, res as Response); + + expect(logger.error).toHaveBeenCalledWith('Error updating user profile: ', error); + expect(sendInternalErrorResponse).toHaveBeenCalledWith(res, error); + }); + }); +});