-
Notifications
You must be signed in to change notification settings - Fork 2
Added some tests with Jest #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| import { IEmail } from '../src/interfaces/email'; | ||
|
|
||
| function isValidEmail(email: IEmail): boolean { | ||
| return ( | ||
| typeof email.from === 'string' && | ||
| typeof email.to === 'string' && | ||
| typeof email.subject === 'string' && | ||
| typeof email.text === 'string' && | ||
| (email.cc === undefined || typeof email.cc === 'string') && | ||
| (email.bcc === undefined || typeof email.bcc === 'string') && | ||
| (email.html === undefined || typeof email.html === 'string') | ||
| ); | ||
| } | ||
|
|
||
| describe('IEmail Interface', () => { | ||
| test('should accept a valid email object', () => { | ||
| const validEmail: IEmail = { | ||
| from: 'test@example.com', | ||
| to: 'recipient@example.com', | ||
| subject: 'Test Subject', | ||
| text: 'Test message body', | ||
| }; | ||
|
|
||
| expect(isValidEmail(validEmail)).toBe(true); | ||
| }); | ||
|
|
||
| test('should accept an email with optional fields', () => { | ||
| const validEmailWithOptionalFields: IEmail = { | ||
| from: 'test@example.com', | ||
| to: 'recipient@example.com', | ||
| subject: 'Test Subject', | ||
| text: 'Test message body', | ||
| cc: 'cc@example.com', | ||
| bcc: 'bcc@example.com', | ||
| html: '<p>Test message body</p>', | ||
| }; | ||
|
|
||
| expect(isValidEmail(validEmailWithOptionalFields)).toBe(true); | ||
| }); | ||
|
|
||
| test('should reject an email without required fields', () => { | ||
| const invalidEmail: any = { | ||
| from: 'test@example.com', | ||
| subject: 'Test Subject', | ||
| text: 'Test message body', | ||
| }; | ||
|
|
||
| expect(isValidEmail(invalidEmail)).toBe(false); | ||
| }); | ||
|
|
||
| test('should reject an email with invalid types', () => { | ||
| const invalidEmail: any = { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above |
||
| from: 'test@example.com', | ||
| to: 123, | ||
| subject: 'Test Subject', | ||
| text: 'Test message body', | ||
| }; | ||
|
|
||
| expect(isValidEmail(invalidEmail)).toBe(false); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import { APIRequest, APIResponse } from '../src/interfaces/express'; | ||
| import { Request, Response } from 'express'; | ||
|
|
||
| describe('APIRequest and APIResponse Interfaces', () => { | ||
| it('should correctly type the body of APIRequest', () => { | ||
| interface ExampleBody { | ||
| testName: string; | ||
| age: number; | ||
| } | ||
|
|
||
| const req: APIRequest<ExampleBody> = { | ||
| body: { | ||
| testName: 'John Doe', | ||
| age: 30, | ||
| }, | ||
| } as APIRequest<ExampleBody>; | ||
|
|
||
| expect(req.body.testName).toBe('John Doe'); | ||
| expect(req.body.age).toBe(30); | ||
| }); | ||
|
|
||
| it('should correctly type the body of APIResponse', () => { | ||
| interface ExampleResponseBody { | ||
| success: boolean; | ||
| data: string; | ||
| } | ||
|
|
||
| const res: APIResponse<ExampleResponseBody> = { | ||
| body: { | ||
| success: true, | ||
| data: 'Operation successful', | ||
| }, | ||
| } as APIResponse<ExampleResponseBody>; | ||
|
|
||
| expect(res.body.success).toBe(true); | ||
| expect(res.body.data).toBe('Operation successful'); | ||
| }); | ||
|
|
||
| it('should allow APIRequest and APIResponse without generics', () => { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you explain a bit more about this test case. Why it's necessary to test if |
||
| const req: APIRequest = { | ||
| body: undefined, | ||
| } as APIRequest; | ||
|
|
||
| const res: APIResponse = { | ||
| body: undefined, | ||
| } as APIResponse; | ||
|
|
||
| expect(req.body).toBeUndefined(); | ||
| expect(res.body).toBeUndefined(); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import { Model } from "mongoose"; | ||
| import { IOtpDoc, IOtpModel } from "../src/interfaces/otp"; | ||
|
|
||
| const OtpModel: IOtpModel = { | ||
| create: jest.fn(), | ||
| findOne: jest.fn(), | ||
| } as any; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I love the idea of mocking and testing these two functions—it's really helpful and great! |
||
|
|
||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| }); | ||
|
|
||
| describe('Otp Model Tests', () => { | ||
| it('should create an OTP correctly', async () => { | ||
| const otpData = { | ||
| email: "test@example.com", | ||
| otp: "123456", | ||
| createdAt: new Date(), | ||
| }; | ||
|
|
||
| (OtpModel.create as jest.Mock).mockResolvedValueOnce(otpData); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the function |
||
|
|
||
| const result = await OtpModel.create(otpData); | ||
|
|
||
| expect(OtpModel.create).toHaveBeenCalledWith(otpData); | ||
|
|
||
| expect(result).toEqual(otpData); | ||
| }); | ||
|
|
||
| it('should validate an OTP correctly', async () => { | ||
| const email = "test@example.com"; | ||
| const otp = "123456"; | ||
| const otpData = { | ||
| email, | ||
| otp, | ||
| createdAt: new Date(), | ||
| }; | ||
|
|
||
| (OtpModel.findOne as jest.Mock).mockResolvedValueOnce(otpData); | ||
|
|
||
| const result = await OtpModel.findOne({ email, otp }); | ||
|
|
||
| expect(OtpModel.findOne).toHaveBeenCalledWith({ email, otp }); | ||
| expect(result).toEqual(otpData); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| import validateToken from '../src/middleware/rbac.middleware'; | ||
| import User from '../src/models/user.model'; | ||
| import { Request, Response, NextFunction } from 'express'; | ||
| import { UserRole } from '../src/constants/userRoles'; | ||
|
|
||
| jest.mock('../src/models/user.model'); | ||
|
|
||
| describe('validateToken Middleware', () => { | ||
| let req: Partial<Request>; | ||
| let res: Partial<Response>; | ||
| let next: NextFunction; | ||
|
|
||
| beforeEach(() => { | ||
| req = { | ||
| headers: {}, | ||
| }; | ||
| res = { | ||
| status: jest.fn().mockReturnThis(), | ||
| json: jest.fn(), | ||
| }; | ||
| next = jest.fn(); | ||
| }); | ||
|
|
||
| it('should return 401 if no authorization header is present', async () => { | ||
| req.headers = {}; | ||
|
|
||
| await validateToken(req as Request, res as Response, next); | ||
|
|
||
| expect(res.status).toHaveBeenCalledWith(401); | ||
| expect(res.json).toHaveBeenCalledWith({ message: 'No authorization header found' }); | ||
| }); | ||
|
|
||
| it('should return 401 if authorization header format is incorrect', async () => { | ||
| req.headers = { | ||
| authorization: 'InvalidToken', | ||
| }; | ||
|
|
||
| await validateToken(req as Request, res as Response, next); | ||
|
|
||
| expect(res.status).toHaveBeenCalledWith(401); | ||
| expect(res.json).toHaveBeenCalledWith({ message: 'Invalid authorization header format. Expected format: Bearer <token>' }); | ||
| }); | ||
|
|
||
| it('should return 404 if user is not found', async () => { | ||
| req.headers = { | ||
| authorization: 'Bearer validToken', | ||
| }; | ||
| (User.findOne as jest.Mock).mockResolvedValueOnce(null); | ||
|
|
||
| await validateToken(req as Request, res as Response, next); | ||
|
|
||
| expect(res.status).toHaveBeenCalledWith(404); | ||
| expect(res.json).toHaveBeenCalledWith({ message: 'Cannot find the user' }); | ||
| }); | ||
|
|
||
| it('should return 403 if user role is not admin', async () => { | ||
| req.headers = { | ||
| authorization: 'Bearer validToken', | ||
| }; | ||
| const mockUser = { | ||
| role: UserRole.visitor, | ||
| }; | ||
| (User.findOne as jest.Mock).mockResolvedValueOnce(mockUser); | ||
|
|
||
| await validateToken(req as Request, res as Response, next); | ||
|
|
||
| expect(res.status).toHaveBeenCalledWith(403); | ||
| expect(res.json).toHaveBeenCalledWith({ message: 'Access denied' }); | ||
| }); | ||
|
|
||
| it('should call next if token is valid and user is admin', async () => { | ||
| req.headers = { | ||
| authorization: 'Bearer validToken', | ||
| }; | ||
| const mockUser = { | ||
| role: UserRole.ADMIN, | ||
| }; | ||
| (User.findOne as jest.Mock).mockResolvedValueOnce(mockUser); | ||
|
|
||
| await validateToken(req as Request, res as Response, next); | ||
|
|
||
| expect(next).toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('should return 500 if an error occurs', async () => { | ||
| req.headers = { | ||
| authorization: 'Bearer validToken', | ||
| }; | ||
| (User.findOne as jest.Mock).mockRejectedValueOnce(new Error('Database error')); | ||
|
|
||
| await validateToken(req as Request, res as Response, next); | ||
|
|
||
| expect(res.status).toHaveBeenCalledWith(500); | ||
| expect(res.json).toHaveBeenCalledWith({ message: 'Database error' }); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| const sum = require('../sum'); | ||
|
|
||
| test('adds 1 + 2 to equal 3', () => { | ||
| expect(sum(1, 2)).toBe(3); | ||
| }) | ||
|
|
||
| //first test to be sure that everything works |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| it('should generate a token correctly', async () => { | ||
| const userDoc = { | ||
| generateToken: jest.fn().mockResolvedValueOnce("new_mocked_token"), | ||
| }; | ||
|
|
||
| const token = await userDoc.generateToken(); | ||
|
|
||
| expect(userDoc.generateToken).toHaveBeenCalled(); | ||
| expect(token).toBe("new_mocked_token"); | ||
| }); | ||
|
|
||
| it('should delete the token correctly', async () => { | ||
| const userDoc = { | ||
| deleteToken: jest.fn().mockResolvedValueOnce(undefined), | ||
| }; | ||
|
|
||
| await userDoc.deleteToken(); | ||
|
|
||
| expect(userDoc.deleteToken).toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('should convert user to JSON correctly excluding the password', () => { | ||
| const jsonResponse = { | ||
| name: "John Doe", | ||
| email: "john@example.com", | ||
| role: "user", | ||
| token: "mocked_token", | ||
| createdAt: new Date(), | ||
| }; | ||
|
|
||
| const userDoc = { | ||
| toJSON: jest.fn().mockReturnValueOnce(jsonResponse), | ||
| }; | ||
|
|
||
| const result = userDoc.toJSON(); | ||
|
|
||
| expect(userDoc.toJSON).toHaveBeenCalled(); | ||
| expect(result).toEqual(jsonResponse); | ||
| expect(result).not.toHaveProperty('password'); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { UserRole, TUserRole } from '../src/constants/userRoles'; | ||
|
|
||
| describe('UserRole', () => { | ||
| it('should have correct role values', () => { | ||
| expect(UserRole.ADMIN).toBe('admin'); | ||
| expect(UserRole.visitor).toBe('user'); | ||
| expect(UserRole.GUEST).toBe('guest'); | ||
| }); | ||
|
|
||
| it('should contain only defined roles', () => { | ||
| const roles: string[] = [UserRole.ADMIN, UserRole.visitor, UserRole.GUEST]; | ||
| const uniqueRoles = new Set(roles); | ||
|
|
||
| expect(uniqueRoles.size).toBe(roles.length); | ||
| expect(roles).toEqual(expect.arrayContaining([UserRole.ADMIN, UserRole.visitor, UserRole.GUEST])); | ||
| }); | ||
| }); | ||
|
|
||
| describe('TUserRole', () => { | ||
| it('should only allow valid UserRole values', () => { | ||
| const validRoles: TUserRole[] = [UserRole.ADMIN, UserRole.visitor, UserRole.GUEST]; | ||
| const invalidRoles: string[] = ['unknown', 'admin_user', 'guest_user']; | ||
|
|
||
| validRoles.forEach(role => { | ||
| expect(validRoles).toContain(role); | ||
| }); | ||
|
|
||
| invalidRoles.forEach(role => { | ||
| expect(validRoles).not.toContain(role); | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to specify the

anytype forinvalidEmailbecause TypeScript will infer its type based on the assigned value. This ensures that passinginvalidEmailtoisValidEmailwill trigger a compiler error if the email object is invalid. As the image below