Skip to content

Commit 1eb0302

Browse files
committed
[finishes #187845784] writing auth controllers tests
1 parent 9da70d9 commit 1eb0302

13 files changed

+846
-49
lines changed

Diff for: src/controllers/authController.ts

+24-25
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const authenticateViaGoogle = (req: Request, res: Response, next: NextFun
3131
})(req, res, next);
3232
};
3333
// calculate password expiration
34-
const calculatePasswordExpirationDate = (user: UserAttributes): Date | null => {
34+
export const calculatePasswordExpirationDate = (user: UserAttributes): Date | null => {
3535
const expirationMinutes = parseInt(process.env.PASSWORD_EXPIRATION_MINUTES as string);
3636
let expirationDate: Date | null = null;
3737

@@ -47,23 +47,19 @@ const calculatePasswordExpirationDate = (user: UserAttributes): Date | null => {
4747

4848
return expirationDate;
4949
};
50-
const redirectToPasswordUpdate = (res: Response): void => {
50+
export const redirectToPasswordUpdate = (res: Response): void => {
5151
res.redirect('/api/user/passwordUpdate');
5252
};
5353
// login function
5454
export const login = async (req: Request, res: Response): Promise<void> => {
5555
try {
5656
const { email, password } = req.body;
5757

58-
const requiredFields = ['email', 'password'];
59-
const missingFields = validateFields(req, requiredFields);
60-
6158
// Field validation
62-
if (missingFields.length > 0) {
63-
logger.error(`Adding User:Required fields are missing:${missingFields.join(', ')}`);
59+
if (!email || !password) {
6460
res.status(400).json({
6561
ok: false,
66-
message: `Required fields are missing: ${missingFields.join(', ')}`,
62+
message: 'Required fields are missing',
6763
});
6864
return;
6965
}
@@ -124,24 +120,27 @@ export const verifyOTP = async (req: Request, res: Response) => {
124120
};
125121
// Function to create OTP Token, Save it Postgres,
126122
export const sendOTP = async (req: Request, res: Response, email: string) => {
127-
const userInfo = await User.findOne({ where: { email } });
128-
if (userInfo) {
129-
const { id, email, firstName } = userInfo.dataValues;
130-
131-
const token = await createOTPToken(id, email, firstName);
132-
133-
const otpSaved = await saveOTPDB(id, token);
134-
135-
if (otpSaved) {
136-
/**
137-
* The token used for comparing the received OTP via email with the
138-
* generated token, which contains the user's ID.
139-
*/
140-
const accessToken = jwt.sign({ id, FAEnabled: true }, process.env.SECRET_KEY as string, {
141-
expiresIn: process.env.JWT_EXPIRATION as string,
142-
});
143-
res.status(200).json({ ok: true, token: accessToken });
123+
try {
124+
const userInfo = await User.findOne({ where: { email } });
125+
126+
if (userInfo) {
127+
const { id, email, firstName } = userInfo.dataValues;
128+
const token = await createOTPToken(id, email, firstName);
129+
const otpSaved = await saveOTPDB(id, token);
130+
131+
if (otpSaved) {
132+
const accessToken = jwt.sign({ id, FAEnabled: true }, process.env.SECRET_KEY as string, {
133+
expiresIn: process.env.JWT_EXPIRATION as string,
134+
});
135+
return res.status(200).json({ ok: true, token: accessToken });
136+
} else {
137+
return res.status(500).json({ ok: false, message: 'Failed to save OTP' });
138+
}
139+
} else {
140+
return res.status(404).json({ ok: false, message: 'User not found' });
144141
}
142+
} catch (error) {
143+
return res.status(500).json({ ok: false, message: 'Internal Server Error' });
145144
}
146145
};
147146

Diff for: src/controllers/testController.ts

-3
This file was deleted.

Diff for: src/test/auth/auth.test.ts

+263
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
import { Request } from 'express';
2+
import bcrypt from 'bcrypt';
3+
import dotenv from 'dotenv';
4+
import User from '../../database/models/user';
5+
import Role from '../../database/models/role';
6+
import * as authController from '../../controllers/authController';
7+
import { sendErrorResponse } from '../../helpers/helper';
8+
import { sendInternalErrorResponse } from '../../validations';
9+
10+
dotenv.config();
11+
12+
const mockResponse = () => {
13+
const res = {} as any;
14+
res.status = jest.fn().mockReturnValue(res);
15+
res.json = jest.fn().mockReturnValue(res);
16+
res.send = jest.fn().mockReturnValue(res);
17+
return res;
18+
};
19+
20+
jest.mock('../../database/models/user', () => ({
21+
__esModule: true,
22+
default: {
23+
findOne: jest.fn(),
24+
findByPk: jest.fn(),
25+
hasMany: jest.fn(),
26+
},
27+
}));
28+
29+
jest.mock('../../database/models/role', () => ({
30+
__esModule: true,
31+
default: {
32+
findOne: jest.fn(),
33+
},
34+
}));
35+
36+
jest.mock('../../database/models/otp', () => ({
37+
__esModule: true,
38+
default: {
39+
belongsTo: jest.fn(),
40+
},
41+
}));
42+
43+
jest.mock('bcrypt', () => ({
44+
compare: jest.fn(() => Promise.resolve(true)),
45+
}));
46+
47+
jest.mock('jsonwebtoken', () => ({
48+
sign: jest.fn(() => 'dgshdgshdgshgdhs-hghgashagsh-jhj'),
49+
}));
50+
51+
jest.mock('../../helpers/helper', () => ({
52+
sendErrorResponse: jest.fn(),
53+
}));
54+
55+
jest.mock('../../validations', () => ({
56+
sendInternalErrorResponse: jest.fn(),
57+
}));
58+
59+
jest.mock('../../controllers/authController', () => ({
60+
...jest.requireActual('../../controllers/authController'),
61+
calculatePasswordExpirationDate: jest.fn(),
62+
redirectToPasswordUpdate: jest.fn(),
63+
}));
64+
65+
describe('AuthController', () => {
66+
afterEach(() => {
67+
jest.clearAllMocks();
68+
});
69+
70+
describe('POST/ User login route', () => {
71+
it('should return 400 if required fields are missing', async () => {
72+
const req = {
73+
body: {
74+
75+
},
76+
} as Request;
77+
const res = mockResponse();
78+
79+
await authController.login(req, res);
80+
81+
expect(res.status).toHaveBeenCalledWith(400);
82+
expect(res.json).toHaveBeenCalledWith({
83+
ok: false,
84+
message: 'Required fields are missing',
85+
});
86+
87+
expect(User.findOne).not.toHaveBeenCalled();
88+
expect(User.findByPk).not.toHaveBeenCalled();
89+
expect(Role.findOne).not.toHaveBeenCalled();
90+
expect(bcrypt.compare).not.toHaveBeenCalled();
91+
});
92+
93+
it('should return 401 if user status is inactive', async () => {
94+
const req = {
95+
body: {
96+
97+
password: 'correctPassword',
98+
},
99+
} as Request;
100+
const res = mockResponse();
101+
102+
const mockUser = {
103+
id: 1,
104+
105+
password: 'hashedPassword',
106+
status: 'inactive',
107+
verified: true,
108+
dataValues: {
109+
RoleId: 1,
110+
enable2FA: false,
111+
112+
},
113+
};
114+
115+
(User.findOne as jest.Mock).mockResolvedValueOnce(mockUser);
116+
(bcrypt.compare as jest.Mock).mockResolvedValueOnce(true);
117+
118+
await authController.login(req, res);
119+
120+
expect(User.findOne).toHaveBeenCalledWith({ where: { email: '[email protected]' } });
121+
expect(sendErrorResponse).toHaveBeenCalledWith(res, 'inactiveUser');
122+
});
123+
124+
it('should return 401 if user is not verified', async () => {
125+
const req = {
126+
body: {
127+
128+
password: 'correctPassword',
129+
},
130+
} as Request;
131+
const res = mockResponse();
132+
133+
const mockUser = {
134+
id: 1,
135+
136+
password: 'hashedPassword',
137+
status: 'active',
138+
verified: false,
139+
dataValues: {
140+
RoleId: 1,
141+
enable2FA: false,
142+
143+
},
144+
};
145+
146+
(User.findOne as jest.Mock).mockResolvedValueOnce(mockUser);
147+
(bcrypt.compare as jest.Mock).mockResolvedValueOnce(true);
148+
149+
await authController.login(req, res);
150+
151+
expect(User.findOne).toHaveBeenCalledWith({ where: { email: '[email protected]' } });
152+
expect(sendErrorResponse).toHaveBeenCalledWith(res, 'unverifiedUser');
153+
});
154+
155+
it('should handle internal server error', async () => {
156+
const req = {
157+
body: {
158+
159+
password: 'correctPassword',
160+
},
161+
} as Request;
162+
const res = mockResponse();
163+
164+
const mockError = new Error('Database connection failed');
165+
(User.findOne as jest.Mock).mockRejectedValueOnce(mockError);
166+
167+
await authController.login(req, res);
168+
169+
expect(User.findOne).toHaveBeenCalledWith({ where: { email: '[email protected]' } });
170+
expect(sendInternalErrorResponse).toHaveBeenCalledWith(res, mockError);
171+
});
172+
173+
it('should return 401 if user is not found', async () => {
174+
const req = {
175+
body: {
176+
177+
password: 'myPassword',
178+
},
179+
} as Request;
180+
const res = mockResponse();
181+
182+
(User.findOne as jest.Mock).mockResolvedValueOnce(null);
183+
184+
await authController.login(req, res);
185+
186+
expect(User.findOne).toHaveBeenCalledWith({ where: { email: '[email protected]' } });
187+
expect(sendErrorResponse).toHaveBeenCalledWith(res, 'invalidCredentials');
188+
});
189+
190+
it('should return 401 if password is invalid', async () => {
191+
const req = {
192+
body: {
193+
194+
password: 'wrongPassword',
195+
},
196+
} as Request;
197+
const res = mockResponse();
198+
199+
const mockUser = {
200+
id: 1,
201+
202+
password: 'hashedPassword',
203+
status: 'active',
204+
verified: true,
205+
dataValues: {
206+
RoleId: 1,
207+
enable2FA: false,
208+
209+
},
210+
};
211+
212+
(User.findOne as jest.Mock).mockResolvedValueOnce(mockUser);
213+
(bcrypt.compare as jest.Mock).mockResolvedValueOnce(false);
214+
215+
await authController.login(req, res);
216+
217+
expect(User.findOne).toHaveBeenCalledWith({ where: { email: '[email protected]' } });
218+
expect(bcrypt.compare).toHaveBeenCalledWith('wrongPassword', 'hashedPassword');
219+
});
220+
221+
it('should return 200 if login is successful', async () => {
222+
const req = {
223+
body: {
224+
225+
password: 'correctPassword',
226+
},
227+
} as Request;
228+
const res = mockResponse();
229+
230+
const mockUser = {
231+
id: 1,
232+
233+
password: 'hashedPassword',
234+
status: 'active',
235+
verified: true,
236+
dataValues: {
237+
RoleId: 1,
238+
enable2FA: false,
239+
240+
},
241+
};
242+
243+
const mockRole = {
244+
dataValues: {
245+
name: 'buyer',
246+
},
247+
};
248+
249+
(User.findOne as jest.Mock).mockResolvedValueOnce(mockUser);
250+
(User.findByPk as jest.Mock).mockResolvedValueOnce(mockUser);
251+
(Role.findOne as jest.Mock).mockResolvedValueOnce(mockRole);
252+
(bcrypt.compare as jest.Mock).mockResolvedValueOnce(true);
253+
254+
await authController.login(req, res);
255+
256+
expect(res.status).toHaveBeenCalledWith(200);
257+
expect(res.json).toHaveBeenCalledWith({
258+
ok: true,
259+
token: 'dgshdgshdgshgdhs-hghgashagsh-jhj',
260+
});
261+
});
262+
});
263+
});

0 commit comments

Comments
 (0)