Skip to content

Commit aca50c4

Browse files
committed
add login route tests
1 parent 83b29be commit aca50c4

File tree

7 files changed

+154
-11
lines changed

7 files changed

+154
-11
lines changed

src/auth/authUtils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import { IUser } from '../database/model/User';
66
import { tokenInfo } from '../config';
77

88
export const validateTokenData = async (payload: JwtPayload, userId: Types.ObjectId): Promise<JwtPayload> => {
9-
// if (!payload || !payload.iss || !payload.sub || !payload.aud || !payload.prm
10-
// || payload.iss !== tokenInfo.issuer
11-
// || payload.aud !== tokenInfo.audience
12-
// || payload.sub !== userId.toHexString())
13-
// throw new AuthFailureError('Invalid Access Token');
9+
if (!payload || !payload.iss || !payload.sub || !payload.aud || !payload.prm
10+
|| payload.iss !== tokenInfo.issuer
11+
|| payload.aud !== tokenInfo.audience
12+
|| payload.sub !== userId.toHexString())
13+
throw new AuthFailureError('Invalid Access Token');
1414
return payload;
1515
};
1616

tests/auth/authentication/index.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ describe('authentication validation', () => {
6363
});
6464

6565
it('Should response with 404 if correct x-access-token and x-user-id header are provided', async () => {
66-
const response = await addHeaders(request.get(endpoint))
67-
.set('x-access-token', ACCESS_TOKEN)
68-
.set('x-user-id', USER_ID.toHexString());
66+
const response = await addAuthHeaders(request.get(endpoint));
6967
expect(response.body.message).not.toMatch(/not registered/);
7068
expect(response.body.message).not.toMatch(/token/i);
7169
expect(response.status).toBe(404);

tests/auth/authorization/index.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { addAuthHeaders } from '../authentication/mock';
2+
3+
// import the mock for the current test after all other mock imports
4+
// this will prevent the different implementations by the other mock
25
import { mockRoleRepoyFindByCode, mockUserFindByIdForWriter } from './mock';
36

47
import app from '../../../src/app';

tests/core/jwt/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { readFileSpy, ACCESS_TOKEN } from './mock';
1+
import { readFileSpy } from './mock';
22
import JWT, { JwtPayload, ValidationParams } from '../../../src/core/JWT';
33
import { BadTokenError, TokenExpiredError } from '../../../src/core/ApiError';
44

tests/core/jwt/mock.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
import fs from 'fs';
22

3-
export const ACCESS_TOKEN = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZnRlcmFjYWRlbXkuY29tIiwiYXVkIjoiYWZ0ZXJhY2FkZW15X3VzZXJzIiwic3ViIjoiNWU3Yjk1OTIzMDg1ODcyZDNjMzc4ZjM1IiwiaWF0IjoxNTg1Mzk5MDY0LCJleHAiOjE1ODc5OTEwNjQsInBybSI6Ijk1MTY0ZTBlZjFiMDI1ZDJlNGI3ZGNmMzQwNjI0YTc0MTgwZjdhM2RhYWFiMzZhMDU1OTgwNjQ2ZGUzNmFlMGI2N2ZjODZjNGNkNmYxZmQzOTk0Y2Q0MWE5YmE3NjliMWFhMzQ0OTE4YWNiOTFkYmZhYjQ0ZmU0ZmMxZjc4ZGYyIn0.NYojtQhOx151ObtYiPLRIZkNV36ptMj-ZCihgcs2WZu4EDKsLjn3vuBpyK0BNESO1QBOMBh631vm3mCYTl3bCg';
4-
53
export const readFileSpy = jest.spyOn(fs, 'readFile');

tests/routes/v1/login/index.test.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { addHeaders } from '../../../auth/authentication/mock';
2+
3+
// the mock for this class should be below all other mock imports
4+
import {
5+
mockKeystoreCreate, mockUserFindByEmail, USER_EMAIL, USER_PASSWORD
6+
} from './mock';
7+
8+
import supertest from 'supertest';
9+
import app from '../../../../src/app';
10+
11+
describe('Login Route', () => {
12+
13+
const endpoint = '/v1/login/basic';
14+
const request = supertest(app);
15+
16+
beforeAll(() => {
17+
mockKeystoreCreate.mockClear();
18+
mockUserFindByEmail.mockClear();
19+
});
20+
21+
it('Should throw error when empty body is sent', async () => {
22+
const response = await addHeaders(request.post(endpoint));
23+
expect(response.status).toBe(400);
24+
});
25+
26+
it('Should throw error when email is only sent', async () => {
27+
const response = await addHeaders(request.post(endpoint)
28+
.send({ email: USER_EMAIL })
29+
);
30+
expect(response.status).toBe(400);
31+
expect(response.body.message).toMatch(/password/);
32+
});
33+
34+
it('Should throw error when password is only sent', async () => {
35+
const response = await addHeaders(request.post(endpoint)
36+
.send({ password: USER_PASSWORD })
37+
);
38+
expect(response.status).toBe(400);
39+
expect(response.body.message).toMatch(/email/);
40+
});
41+
42+
it('Should throw error when email is not valid format', async () => {
43+
const response = await addHeaders(request.post(endpoint)
44+
.send({ email: '123' })
45+
);
46+
expect(response.status).toBe(400);
47+
expect(response.body.message).toMatch(/valid email/);
48+
});
49+
50+
it('Should throw error when password is not valid format', async () => {
51+
const response = await addHeaders(request.post(endpoint)
52+
.send({
53+
54+
password: '123'
55+
}));
56+
expect(response.status).toBe(400);
57+
expect(response.body.message).toMatch(/password length/);
58+
expect(response.body.message).toMatch(/6 char/);
59+
});
60+
61+
it('Should throw error when user not registered for email', async () => {
62+
const response = await addHeaders(request.post(endpoint)
63+
.send({
64+
65+
password: '123456',
66+
}));
67+
expect(response.status).toBe(400);
68+
expect(response.body.message).toMatch(/not registered/);
69+
});
70+
71+
it('Should throw error for wrong password', async () => {
72+
const response = await addHeaders(request.post(endpoint)
73+
.send({
74+
email: USER_EMAIL,
75+
password: '123456',
76+
}));
77+
expect(response.status).toBe(401);
78+
expect(response.body.message).toMatch(/authentication failure/i);
79+
});
80+
81+
it('Should send success response for correct credentials', async () => {
82+
const response = await addHeaders(request.post(endpoint)
83+
.send({
84+
email: USER_EMAIL,
85+
password: USER_PASSWORD,
86+
}));
87+
expect(response.status).toBe(200);
88+
expect(response.body.message).toMatch(/Success/i);
89+
expect(response.body.data).toBeDefined();
90+
91+
expect(response.body.data.user).toHaveProperty('_id');
92+
expect(response.body.data.user).toHaveProperty('name');
93+
expect(response.body.data.user).toHaveProperty('roles');
94+
expect(response.body.data.user).toHaveProperty('profilePicUrl');
95+
96+
expect(response.body.data.tokens).toBeDefined();
97+
expect(response.body.data.tokens).toHaveProperty('accessToken');
98+
expect(response.body.data.tokens).toHaveProperty('refreshToken');
99+
});
100+
});

tests/routes/v1/login/mock.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { USER_ID } from '../../../auth/authentication/mock';
2+
import { IKeystore } from '../../../../src/database/model/Keystore';
3+
import { IUser } from '../../../../src/database/model/User';
4+
import { Types } from 'mongoose';
5+
import bcrypt from 'bcrypt';
6+
7+
export const USER_EMAIL = '[email protected]';
8+
export const USER_PASSWORD = 'abc123';
9+
export const USER_PASSWORD_HASH = bcrypt.hashSync(USER_PASSWORD, 10);
10+
11+
export const mockKeystoreCreate =
12+
jest.fn(
13+
async (client: IUser, primaryKey: string, secondaryKey: string): Promise<IKeystore> => {
14+
return <IKeystore>{
15+
_id: new Types.ObjectId(),
16+
client: client,
17+
primaryKey: primaryKey,
18+
secondaryKey: secondaryKey
19+
};
20+
});
21+
22+
export const mockUserFindByEmail =
23+
jest.fn(async (email: string): Promise<IUser> => {
24+
if (email === USER_EMAIL) return <IUser>{
25+
_id: USER_ID,
26+
email: USER_EMAIL,
27+
password: USER_PASSWORD_HASH,
28+
name: 'abc',
29+
profilePicUrl: 'abc',
30+
roles: []
31+
};
32+
return null;
33+
});
34+
35+
jest.mock('../../../../src/database/repository/KeystoreRepo', () => ({
36+
get create() { return mockKeystoreCreate; }
37+
}));
38+
39+
40+
jest.mock('../../../../src/database/repository/UserRepo', () => ({
41+
get findByEmail() { return mockUserFindByEmail; }
42+
}));
43+
44+
jest.unmock('../../../../src/auth/authUtils');

0 commit comments

Comments
 (0)