Skip to content

Commit ab9b57d

Browse files
authored
Merge pull request #4105 from dvynshuu/migrate-session-controller-ts
Migrate session.controller to TypeScript
2 parents c5cdecb + 3bd1f18 commit ab9b57d

2 files changed

Lines changed: 236 additions & 11 deletions

File tree

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
import { Request as MockRequest } from 'jest-express/lib/request';
2+
import { Response as MockResponse } from 'jest-express/lib/response';
3+
import { NextFunction as MockNext } from 'jest-express/lib/next';
4+
import { Request, Response } from 'express';
5+
import passport from 'passport';
6+
import {
7+
createSession,
8+
getSession,
9+
destroySession
10+
} from '../session.controller';
11+
import { userResponse } from '../user.controller';
12+
13+
jest.mock('passport', () => ({
14+
authenticate: jest.fn()
15+
}));
16+
17+
jest.mock('../user.controller', () => ({
18+
userResponse: jest.fn((user) => ({ id: user.id, username: user.username }))
19+
}));
20+
21+
describe('session.controller', () => {
22+
let request: MockRequest;
23+
let response: MockResponse;
24+
let next: MockNext;
25+
26+
beforeEach(() => {
27+
request = new MockRequest();
28+
response = new MockResponse();
29+
next = jest.fn();
30+
31+
(request as any).logIn = jest.fn();
32+
(request as any).logout = jest.fn();
33+
(request as any).session = {
34+
destroy: jest.fn()
35+
} as any;
36+
});
37+
38+
afterEach(() => {
39+
request.resetMocked();
40+
response.resetMocked();
41+
jest.clearAllMocks();
42+
});
43+
44+
describe('createSession', () => {
45+
it('calls next with error if passport authentication fails', () => {
46+
const error = new Error('Auth failed');
47+
(passport.authenticate as jest.Mock).mockImplementation(
48+
(strategy, callback) => (mockReq: any, mockRes: any, mockNext: any) =>
49+
callback(error, null)
50+
);
51+
52+
createSession(
53+
(request as unknown) as Request,
54+
(response as unknown) as Response,
55+
next
56+
);
57+
58+
expect(next).toHaveBeenCalledWith(error);
59+
});
60+
61+
it('returns 401 if user is not found', () => {
62+
(passport.authenticate as jest.Mock).mockImplementation(
63+
(strategy, callback) => (mockReq: any, mockRes: any, mockNext: any) =>
64+
callback(null, false)
65+
);
66+
67+
createSession(
68+
(request as unknown) as Request,
69+
(response as unknown) as Response,
70+
next
71+
);
72+
73+
expect(response.status).toHaveBeenCalledWith(401);
74+
expect(response.json).toHaveBeenCalledWith({
75+
message: 'Invalid username or password.'
76+
});
77+
});
78+
79+
it('calls next with error if req.logIn fails', () => {
80+
const user = { id: '1', username: 'test' };
81+
const loginError = new Error('Login failed');
82+
83+
(passport.authenticate as jest.Mock).mockImplementation(
84+
(strategy, callback) => (mockReq: any, mockRes: any, mockNext: any) =>
85+
callback(null, user)
86+
);
87+
88+
((request as any)
89+
.logIn as jest.Mock).mockImplementation(
90+
(mockUser: any, callback: any) => callback(loginError)
91+
);
92+
93+
createSession(
94+
(request as unknown) as Request,
95+
(response as unknown) as Response,
96+
next
97+
);
98+
99+
expect(next).toHaveBeenCalledWith(loginError);
100+
});
101+
102+
it('returns user data on successful login', () => {
103+
const user = { id: '1', username: 'test' };
104+
105+
(passport.authenticate as jest.Mock).mockImplementation(
106+
(strategy, callback) => (mockReq: any, mockRes: any, mockNext: any) =>
107+
callback(null, user)
108+
);
109+
110+
((request as any).logIn as jest.Mock).mockImplementation(
111+
(mockUser: any, callback: any) => {
112+
request.user = user as any;
113+
callback(null);
114+
}
115+
);
116+
117+
createSession(
118+
(request as unknown) as Request,
119+
(response as unknown) as Response,
120+
next
121+
);
122+
123+
expect(response.json).toHaveBeenCalledWith({ id: '1', username: 'test' });
124+
});
125+
});
126+
127+
describe('getSession', () => {
128+
it('returns null user if not authenticated', () => {
129+
getSession(
130+
(request as unknown) as Request,
131+
(response as unknown) as Response,
132+
next
133+
);
134+
135+
expect(response.status).toHaveBeenCalledWith(200);
136+
expect(response.send).toHaveBeenCalledWith({ user: null });
137+
});
138+
139+
it('returns 403 if user is banned', () => {
140+
request.user = { banned: true } as any;
141+
142+
getSession(
143+
(request as unknown) as Request,
144+
(response as unknown) as Response,
145+
next
146+
);
147+
148+
expect(response.status).toHaveBeenCalledWith(403);
149+
expect(response.send).toHaveBeenCalledWith({
150+
message: 'Forbidden: User is banned.'
151+
});
152+
});
153+
154+
it('returns user data if authenticated and not banned', () => {
155+
request.user = { id: '1', username: 'test', banned: false } as any;
156+
157+
getSession(
158+
(request as unknown) as Request,
159+
(response as unknown) as Response,
160+
next
161+
);
162+
163+
expect(response.json).toHaveBeenCalledWith({ id: '1', username: 'test' });
164+
});
165+
});
166+
167+
describe('destroySession', () => {
168+
it('calls next with error if logout fails', () => {
169+
const logoutError = new Error('Logout failed');
170+
((request as any)
171+
.logout as jest.Mock).mockImplementation((callback: any) =>
172+
callback(logoutError)
173+
);
174+
175+
destroySession(
176+
(request as unknown) as Request,
177+
(response as unknown) as Response,
178+
next
179+
);
180+
181+
expect(next).toHaveBeenCalledWith(logoutError);
182+
});
183+
184+
it('calls next with error if session destruction fails', () => {
185+
const destroyError = new Error('Destroy failed');
186+
((request as any)
187+
.logout as jest.Mock).mockImplementation((callback: any) =>
188+
callback(null)
189+
);
190+
((request as any).session
191+
.destroy as jest.Mock).mockImplementation((callback: any) =>
192+
callback(destroyError)
193+
);
194+
195+
destroySession(
196+
(request as unknown) as Request,
197+
(response as unknown) as Response,
198+
next
199+
);
200+
201+
expect(next).toHaveBeenCalledWith(destroyError);
202+
});
203+
204+
it('returns success true if logout and session destruction succeed', () => {
205+
((request as any)
206+
.logout as jest.Mock).mockImplementation((callback: any) =>
207+
callback(null)
208+
);
209+
((request as any).session
210+
.destroy as jest.Mock).mockImplementation((callback: any) =>
211+
callback(null)
212+
);
213+
214+
destroySession(
215+
(request as unknown) as Request,
216+
(response as unknown) as Response,
217+
next
218+
);
219+
220+
expect(response.json).toHaveBeenCalledWith({ success: true });
221+
});
222+
});
223+
});
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
/* global Express */
12
import passport from 'passport';
3+
import { RequestHandler } from 'express';
24

35
import { userResponse } from './user.controller';
46

5-
export function createSession(req, res, next) {
6-
passport.authenticate('local', (err, user) => {
7+
export const createSession: RequestHandler = (req, res, next) => {
8+
passport.authenticate('local', (err: Error, user: Express.User) => {
79
if (err) {
810
next(err);
911
return;
@@ -13,17 +15,17 @@ export function createSession(req, res, next) {
1315
return;
1416
}
1517

16-
req.logIn(user, (innerErr) => {
18+
req.logIn(user, (innerErr: Error) => {
1719
if (innerErr) {
1820
next(innerErr);
1921
return;
2022
}
21-
res.json(userResponse(req.user));
23+
res.json(userResponse(req.user as Express.User));
2224
});
2325
})(req, res, next);
24-
}
26+
};
2527

26-
export function getSession(req, res) {
28+
export const getSession: RequestHandler = (req, res) => {
2729
if (!req.user) {
2830
return res.status(200).send({ user: null });
2931
}
@@ -32,20 +34,20 @@ export function getSession(req, res) {
3234
}
3335

3436
return res.json(userResponse(req.user));
35-
}
37+
};
3638

37-
export function destroySession(req, res, next) {
38-
req.logout((err) => {
39+
export const destroySession: RequestHandler = (req, res, next) => {
40+
req.logout((err: Error) => {
3941
if (err) {
4042
next(err);
4143
return;
4244
}
43-
req.session.destroy((error) => {
45+
(req as any).session.destroy((error: Error) => {
4446
if (error) {
4547
next(error);
4648
return;
4749
}
4850
res.json({ success: true });
4951
});
5052
});
51-
}
53+
};

0 commit comments

Comments
 (0)