dolphin-server-modules 2.9.5 → 2.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +45 -83
  2. package/TUTORIAL_NEPALI.md +65 -31
  3. package/dist/ai/dolphin-agent/agent.d.ts +25 -0
  4. package/dist/ai/dolphin-agent/agent.js +362 -0
  5. package/dist/ai/dolphin-agent/agent.js.map +1 -0
  6. package/dist/ai/dolphin-agent/config.d.ts +9 -0
  7. package/dist/ai/dolphin-agent/config.js +52 -0
  8. package/dist/ai/dolphin-agent/config.js.map +1 -0
  9. package/dist/ai/dolphin-agent/index.d.ts +5 -0
  10. package/dist/ai/dolphin-agent/index.js +13 -0
  11. package/dist/ai/dolphin-agent/index.js.map +1 -0
  12. package/dist/bin/cli.js +27 -125
  13. package/dist/bin/cli.js.map +1 -1
  14. package/dist/services/ai-service.d.ts +6 -0
  15. package/dist/services/ai-service.js +142 -23
  16. package/dist/services/ai-service.js.map +1 -1
  17. package/package.json +1 -1
  18. package/dist/adapters/mongoose/index.test.d.ts +0 -1
  19. package/dist/adapters/mongoose/index.test.js +0 -145
  20. package/dist/adapters/mongoose/index.test.js.map +0 -1
  21. package/dist/adapters/mongoose/integration.test.d.ts +0 -5
  22. package/dist/adapters/mongoose/integration.test.js +0 -217
  23. package/dist/adapters/mongoose/integration.test.js.map +0 -1
  24. package/dist/auth/auth.test.d.ts +0 -1
  25. package/dist/auth/auth.test.js +0 -286
  26. package/dist/auth/auth.test.js.map +0 -1
  27. package/dist/authController/authController.test.d.ts +0 -1
  28. package/dist/authController/authController.test.js +0 -359
  29. package/dist/authController/authController.test.js.map +0 -1
  30. package/dist/controller/controller.test.d.ts +0 -1
  31. package/dist/controller/controller.test.js +0 -37
  32. package/dist/controller/controller.test.js.map +0 -1
  33. package/dist/curd/crud.test.d.ts +0 -1
  34. package/dist/curd/crud.test.js +0 -104
  35. package/dist/curd/crud.test.js.map +0 -1
  36. package/dist/demo-server.d.ts +0 -1
  37. package/dist/demo-server.js +0 -191
  38. package/dist/demo-server.js.map +0 -1
  39. package/dist/djson/djson.test.d.ts +0 -1
  40. package/dist/djson/djson.test.js +0 -200
  41. package/dist/djson/djson.test.js.map +0 -1
  42. package/dist/dolphin-bench.d.ts +0 -1
  43. package/dist/dolphin-bench.js +0 -63
  44. package/dist/dolphin-bench.js.map +0 -1
  45. package/dist/hard-performance-test.d.ts +0 -1
  46. package/dist/hard-performance-test.js +0 -97
  47. package/dist/hard-performance-test.js.map +0 -1
  48. package/dist/middleware/zod.test.d.ts +0 -1
  49. package/dist/middleware/zod.test.js +0 -74
  50. package/dist/middleware/zod.test.js.map +0 -1
  51. package/dist/performance-test.d.ts +0 -1
  52. package/dist/performance-test.js +0 -92
  53. package/dist/performance-test.js.map +0 -1
  54. package/dist/real-test-mongoose.d.ts +0 -1
  55. package/dist/real-test-mongoose.js +0 -104
  56. package/dist/real-test-mongoose.js.map +0 -1
  57. package/dist/realtime/realtime.test.d.ts +0 -1
  58. package/dist/realtime/realtime.test.js +0 -623
  59. package/dist/realtime/realtime.test.js.map +0 -1
  60. package/dist/router/router.test.d.ts +0 -1
  61. package/dist/router/router.test.js +0 -45
  62. package/dist/router/router.test.js.map +0 -1
  63. package/dist/server/server.test.d.ts +0 -1
  64. package/dist/server/server.test.js +0 -299
  65. package/dist/server/server.test.js.map +0 -1
  66. package/dist/signaling/signaling.test.d.ts +0 -1
  67. package/dist/signaling/signaling.test.js +0 -112
  68. package/dist/signaling/signaling.test.js.map +0 -1
  69. package/dist/swagger/swagger.test.d.ts +0 -1
  70. package/dist/swagger/swagger.test.js +0 -38
  71. package/dist/swagger/swagger.test.js.map +0 -1
  72. package/dist/test-2fa-real.d.ts +0 -1
  73. package/dist/test-2fa-real.js +0 -105
  74. package/dist/test-2fa-real.js.map +0 -1
  75. package/dist/test-dolphin.d.ts +0 -1
  76. package/dist/test-dolphin.js +0 -98
  77. package/dist/test-dolphin.js.map +0 -1
  78. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,286 +0,0 @@
1
- // test/auth/auth.test.ts
2
- import { createAuth } from './auth';
3
- import crypto from 'node:crypto';
4
- class MockAuthDB {
5
- users = [];
6
- tokens = [];
7
- async createUser(data) {
8
- const id = crypto.randomBytes(8).toString('hex');
9
- const u = { id, ...data };
10
- this.users.push(u);
11
- return u;
12
- }
13
- async findUserByEmail(email) {
14
- return this.users.find(u => u.email === email);
15
- }
16
- async findUserById(id) {
17
- return this.users.find(u => u.id === id);
18
- }
19
- async updateUser(id, data) {
20
- const idx = this.users.findIndex(u => u.id === id);
21
- if (idx > -1)
22
- this.users[idx] = { ...this.users[idx], ...data };
23
- return this.users[idx];
24
- }
25
- async saveRefreshToken(data) {
26
- this.tokens.push(data);
27
- }
28
- async findRefreshToken(token) {
29
- return this.tokens.find(t => t.token === token) || null;
30
- }
31
- async deleteRefreshToken(token) {
32
- this.tokens = this.tokens.filter(t => t.token !== token);
33
- }
34
- }
35
- describe('Auth Module', () => {
36
- let db;
37
- let auth;
38
- beforeEach(() => {
39
- db = new MockAuthDB();
40
- auth = createAuth({
41
- secret: 'test-secret',
42
- cookieMaxAge: 1000 * 60 * 60,
43
- issuer: 'TestApp'
44
- });
45
- });
46
- describe('Registration', () => {
47
- it('registers a new user successfully', async () => {
48
- const user = await auth.register(db, {
49
- email: 'test@example.com',
50
- password: 'password123'
51
- });
52
- expect(user.email).toBe('test@example.com');
53
- expect(user.role).toBe('user');
54
- expect(user.id).toBeDefined();
55
- });
56
- it('throws error for missing fields', async () => {
57
- await expect(auth.register(db, { email: '', password: '123' }))
58
- .rejects.toThrow('Missing fields');
59
- });
60
- });
61
- describe('Login', () => {
62
- beforeEach(async () => {
63
- await auth.register(db, {
64
- email: 'test@example.com',
65
- password: 'password123'
66
- });
67
- });
68
- it('logs in successfully with correct credentials', async () => {
69
- const result = await auth.login(db, {
70
- email: 'test@example.com',
71
- password: 'password123'
72
- });
73
- expect(result.accessToken).toBeDefined();
74
- expect(result.user.email).toBe('test@example.com');
75
- expect(result.user.role).toBe('user');
76
- });
77
- it('rejects invalid password', async () => {
78
- await expect(auth.login(db, {
79
- email: 'test@example.com',
80
- password: 'wrong'
81
- })).rejects.toThrow('Invalid credentials');
82
- });
83
- it('rejects non-existent email', async () => {
84
- await expect(auth.login(db, {
85
- email: 'nonexistent@example.com',
86
- password: 'password123'
87
- })).rejects.toThrow('Invalid credentials');
88
- });
89
- });
90
- describe('Middleware', () => {
91
- let token;
92
- let userId;
93
- beforeEach(async () => {
94
- const user = await auth.register(db, {
95
- email: 'middleware@example.com',
96
- password: 'password123'
97
- });
98
- userId = user.id;
99
- const loginResult = await auth.login(db, {
100
- email: 'middleware@example.com',
101
- password: 'password123'
102
- });
103
- token = loginResult.accessToken;
104
- });
105
- it('should allow access with valid token', async () => {
106
- const req = {
107
- headers: { authorization: `Bearer ${token}` },
108
- user: null
109
- };
110
- const res = {
111
- status: jest.fn().mockReturnThis(),
112
- json: jest.fn()
113
- };
114
- let nextCalled = false;
115
- const next = () => { nextCalled = true; };
116
- const middleware = auth.middleware();
117
- await middleware(req, res, next);
118
- expect(nextCalled).toBe(true);
119
- expect(req.user).toBeDefined();
120
- expect(req.user.id).toBe(userId);
121
- });
122
- it('should reject request without token', async () => {
123
- const req = {
124
- headers: {},
125
- user: null
126
- };
127
- const res = {
128
- status: jest.fn().mockReturnThis(),
129
- json: jest.fn()
130
- };
131
- const next = jest.fn();
132
- const middleware = auth.middleware();
133
- await middleware(req, res, next);
134
- expect(next).not.toHaveBeenCalled();
135
- expect(res.status).toHaveBeenCalledWith(401);
136
- expect(res.json).toHaveBeenCalledWith({ message: 'Unauthorized' });
137
- });
138
- it('should reject request with invalid token', async () => {
139
- const req = {
140
- headers: { authorization: 'Bearer invalid-token' },
141
- user: null
142
- };
143
- const res = {
144
- status: jest.fn().mockReturnThis(),
145
- json: jest.fn()
146
- };
147
- const next = jest.fn();
148
- const middleware = auth.middleware();
149
- await middleware(req, res, next);
150
- expect(next).not.toHaveBeenCalled();
151
- expect(res.status).toHaveBeenCalledWith(401);
152
- expect(res.json).toHaveBeenCalledWith({ message: 'Unauthorized' });
153
- });
154
- it('should handle Dolphin style context (no res, no next)', async () => {
155
- const req = {
156
- headers: { authorization: `Bearer ${token}` },
157
- user: null
158
- };
159
- // Dolphin style: no res, next is not a function
160
- const res = undefined;
161
- let nextCalled = false;
162
- const next = () => { nextCalled = true; };
163
- const middleware = auth.middleware();
164
- await middleware(req, res, next);
165
- expect(nextCalled).toBe(true);
166
- expect(req.user).toBeDefined();
167
- });
168
- });
169
- describe('2FA', () => {
170
- let userId;
171
- beforeEach(async () => {
172
- const user = await auth.register(db, {
173
- email: '2fa@example.com',
174
- password: 'password123'
175
- });
176
- userId = user.id;
177
- });
178
- it('enables 2FA and returns QR code', async () => {
179
- const { secret, uri } = await auth.enable2FA(db, userId);
180
- expect(secret).toBeDefined();
181
- expect(secret.length).toBeGreaterThan(10);
182
- expect(uri).toMatch(/otpauth:\/\/totp\/TestApp:2fa(@|%40)example\.com/);
183
- expect(uri).toContain(`secret=${secret}`);
184
- });
185
- it('allows enabling 2FA multiple times (returns existing secret)', async () => {
186
- const first = await auth.enable2FA(db, userId);
187
- const second = await auth.enable2FA(db, userId);
188
- expect(second.secret).toBe(first.secret);
189
- expect(second.uri).toBe(first.uri);
190
- });
191
- it('rejects invalid TOTP verification', async () => {
192
- await auth.enable2FA(db, userId);
193
- await expect(auth.verify2FA(db, userId, '000000'))
194
- .rejects.toThrow('Invalid verification token');
195
- });
196
- });
197
- describe('Refresh Token', () => {
198
- let refreshToken;
199
- beforeEach(async () => {
200
- await auth.register(db, {
201
- email: 'refresh@example.com',
202
- password: 'password123'
203
- });
204
- await auth.login(db, {
205
- email: 'refresh@example.com',
206
- password: 'password123'
207
- });
208
- refreshToken = db.tokens[0].token;
209
- });
210
- it('refreshes token successfully', async () => {
211
- const newSession = await auth.refresh(db, refreshToken);
212
- expect(newSession.accessToken).toBeDefined();
213
- expect(newSession.user.email).toBe('refresh@example.com');
214
- });
215
- it('detects token reuse', async () => {
216
- await auth.refresh(db, refreshToken);
217
- await expect(auth.refresh(db, refreshToken))
218
- .rejects.toThrow('Token reuse detected');
219
- });
220
- it('rejects expired token', async () => {
221
- const shortAuth = createAuth({
222
- secret: 'test-secret',
223
- cookieMaxAge: 1
224
- });
225
- await shortAuth.register(db, {
226
- email: 'expired@example.com',
227
- password: 'password123'
228
- });
229
- await shortAuth.login(db, {
230
- email: 'expired@example.com',
231
- password: 'password123'
232
- });
233
- const user = db.users.find(u => u.email === 'expired@example.com');
234
- const token = db.tokens.find(t => t.userId === user?.id)?.token;
235
- await new Promise(resolve => setTimeout(resolve, 10));
236
- await expect(shortAuth.refresh(db, token))
237
- .rejects.toThrow('Invalid or expired refresh token');
238
- });
239
- });
240
- describe('Logout', () => {
241
- let refreshToken;
242
- beforeEach(async () => {
243
- await auth.register(db, {
244
- email: 'logout@example.com',
245
- password: 'password123'
246
- });
247
- await auth.login(db, {
248
- email: 'logout@example.com',
249
- password: 'password123'
250
- });
251
- refreshToken = db.tokens[0].token;
252
- });
253
- it('logs out successfully', async () => {
254
- const result = await auth.logout(db, refreshToken);
255
- expect(result.success).toBe(true);
256
- const token = await db.findRefreshToken(refreshToken);
257
- expect(token).toBeNull();
258
- });
259
- });
260
- describe('Rate Limiting', () => {
261
- it('limits login attempts', async () => {
262
- await auth.register(db, {
263
- email: 'ratelimit@example.com',
264
- password: 'password123'
265
- });
266
- let rateLimited = false;
267
- for (let i = 0; i < 7; i++) {
268
- try {
269
- await auth.login(db, {
270
- email: 'ratelimit@example.com',
271
- password: 'wrong'
272
- });
273
- }
274
- catch (err) {
275
- if (err.message === 'Rate limit exceeded') {
276
- rateLimited = true;
277
- expect(err.status).toBe(429);
278
- break;
279
- }
280
- }
281
- }
282
- expect(rateLimited).toBe(true);
283
- });
284
- });
285
- });
286
- //# sourceMappingURL=auth.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auth.test.js","sourceRoot":"","sources":["../../src/auth/auth.test.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- export {};
@@ -1,359 +0,0 @@
1
- // test/auth-controller.test.ts
2
- import { describe, it, expect, beforeEach } from '@jest/globals';
3
- // Mock the auth controller module
4
- jest.mock('../authController/authController', () => ({
5
- createDolphinAuthController: jest.fn((db, config) => ({
6
- register: jest.fn().mockImplementation(async (ctx) => {
7
- const body = ctx.body;
8
- if (body.email === 'exists@example.com') {
9
- return { success: false, error: 'Email already exists', status: 400 };
10
- }
11
- return { success: true, data: { id: '1', email: body.email, role: 'user' } };
12
- }),
13
- login: jest.fn().mockImplementation(async (ctx) => {
14
- const body = ctx.body;
15
- if (body.email === 'test@example.com' && body.password === 'password123') {
16
- // ✅ Cookie set गरेको simulation
17
- ctx.res.setHeader('Set-Cookie', 'rt=refresh-token-123; HttpOnly; Max-Age=604800; Path=/; SameSite=Lax');
18
- return { success: true, accessToken: 'token123', user: { id: '1', email: body.email } };
19
- }
20
- return { success: false, error: 'Invalid credentials', status: 401 };
21
- }),
22
- refresh: jest.fn().mockImplementation(async (ctx) => {
23
- // ✅ Dolphin server style: header बाट cookie extract गर्ने
24
- const getCookie = (req, name) => {
25
- const cookieHeader = req.headers?.cookie;
26
- if (!cookieHeader)
27
- return undefined;
28
- const match = cookieHeader.match(new RegExp('(^| )' + name + '=([^;]+)'));
29
- return match ? match[2] : undefined;
30
- };
31
- const refreshToken = getCookie(ctx.req, 'rt');
32
- if (refreshToken === 'refresh-token-123') {
33
- // ✅ New cookie set गर्ने
34
- ctx.res.setHeader('Set-Cookie', 'rt=new-refresh-token-456; HttpOnly; Max-Age=604800; Path=/; SameSite=Lax');
35
- return { success: true, accessToken: 'new-token-456', user: { id: '1' } };
36
- }
37
- return { success: false, error: 'No refresh token provided', status: 401 };
38
- }),
39
- logout: jest.fn().mockImplementation(async (ctx) => {
40
- // ✅ Cookie clear गर्ने
41
- ctx.res.setHeader('Set-Cookie', 'rt=; HttpOnly; Max-Age=0; Path=/; SameSite=Lax');
42
- return { success: true };
43
- }),
44
- me: jest.fn().mockImplementation(async (ctx) => {
45
- if (ctx.req.user) {
46
- const { password, recoveryCodes, twoFactorSecret, ...safe } = ctx.req.user;
47
- return { success: true, data: safe };
48
- }
49
- return { success: false, error: 'Unauthorized', status: 401 };
50
- }),
51
- changePassword: jest.fn().mockImplementation(async (ctx) => {
52
- const { oldPassword, newPassword } = ctx.body;
53
- if (oldPassword === 'old123' && newPassword?.length >= 8) {
54
- return { success: true, message: 'Password changed successfully' };
55
- }
56
- return { success: false, error: 'Current password is incorrect', status: 400 };
57
- }),
58
- forgotPassword: jest.fn().mockImplementation(async (ctx) => {
59
- const { email } = ctx.body;
60
- if (email === 'test@example.com') {
61
- return { success: true, message: 'Reset link sent' };
62
- }
63
- return { success: true, message: 'If email exists, reset link sent' };
64
- }),
65
- resetPassword: jest.fn().mockImplementation(async (ctx) => {
66
- const { token, newPassword } = ctx.body;
67
- if (token === 'valid-token') {
68
- return { success: true, message: 'Password reset successfully' };
69
- }
70
- return { success: false, error: 'Invalid or expired reset token', status: 400 };
71
- }),
72
- enable2FA: jest.fn().mockImplementation(async () => {
73
- return { success: true, secret: 'SECRET123', uri: 'otpauth://...' };
74
- }),
75
- verify2FA: jest.fn().mockImplementation(async (ctx) => {
76
- const { totp } = ctx.body;
77
- if (totp === '123456') {
78
- return { success: true, recoveryCodes: ['code1', 'code2'] };
79
- }
80
- return { success: false, error: 'Invalid TOTP', status: 401 };
81
- }),
82
- disable2FA: jest.fn().mockImplementation(async () => {
83
- return { success: true };
84
- }),
85
- requireAuth: jest.fn(() => (req, res, next) => next()),
86
- require2FA: jest.fn(() => (req, res, next) => next()),
87
- requireAdmin: jest.fn((ctx, next) => next()),
88
- sanitize: jest.fn((user) => {
89
- if (!user)
90
- return null;
91
- const { password, recoveryCodes, twoFactorSecret, pending2FASecret, resetPasswordToken, resetPasswordExpires, ...safe } = user;
92
- return safe;
93
- })
94
- }))
95
- }));
96
- import { createDolphinAuthController } from '../authController/authController';
97
- describe('Auth Controller Factory - Dolphin Server Compatible', () => {
98
- let mockDb;
99
- let mockCtx;
100
- let auth;
101
- beforeEach(() => {
102
- mockDb = {};
103
- auth = createDolphinAuthController(mockDb, {
104
- secret: 'test-secret',
105
- cookieMaxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
106
- secureCookies: false
107
- });
108
- // ✅ Dolphin Server style mock context
109
- mockCtx = {
110
- req: {
111
- headers: {
112
- cookie: '' // ← cookie header (Dolphin style)
113
- },
114
- user: null
115
- },
116
- res: {
117
- setHeader: jest.fn(),
118
- getHeader: jest.fn()
119
- },
120
- body: {},
121
- status: jest.fn().mockReturnThis(),
122
- json: jest.fn().mockReturnThis()
123
- };
124
- });
125
- describe('createDolphinAuthController', () => {
126
- it('creates auth controller with all required methods', () => {
127
- expect(auth).toHaveProperty('register');
128
- expect(auth).toHaveProperty('login');
129
- expect(auth).toHaveProperty('refresh');
130
- expect(auth).toHaveProperty('logout');
131
- expect(auth).toHaveProperty('me');
132
- expect(auth).toHaveProperty('changePassword');
133
- expect(auth).toHaveProperty('forgotPassword');
134
- expect(auth).toHaveProperty('resetPassword');
135
- expect(auth).toHaveProperty('enable2FA');
136
- expect(auth).toHaveProperty('verify2FA');
137
- expect(auth).toHaveProperty('disable2FA');
138
- expect(auth).toHaveProperty('requireAuth');
139
- expect(auth).toHaveProperty('require2FA');
140
- expect(auth).toHaveProperty('requireAdmin');
141
- expect(auth).toHaveProperty('sanitize');
142
- });
143
- });
144
- describe('register', () => {
145
- it('creates new user successfully', async () => {
146
- mockCtx.body = { email: 'new@example.com', password: 'password123' };
147
- const result = await auth.register(mockCtx);
148
- expect(result.success).toBe(true);
149
- expect(result.data.email).toBe('new@example.com');
150
- });
151
- it('returns error when email already exists', async () => {
152
- mockCtx.body = { email: 'exists@example.com', password: '123' };
153
- const result = await auth.register(mockCtx);
154
- expect(result.success).toBe(false);
155
- expect(result.error).toBe('Email already exists');
156
- expect(result.status).toBe(400);
157
- });
158
- });
159
- describe('login - Cookie Test', () => {
160
- it('logs in user successfully and sets refresh token cookie', async () => {
161
- mockCtx.body = { email: 'test@example.com', password: 'password123' };
162
- const result = await auth.login(mockCtx);
163
- expect(result.success).toBe(true);
164
- expect(result.accessToken).toBe('token123');
165
- expect(result.user.email).toBe('test@example.com');
166
- // ✅ Verify cookie was set with correct parameters
167
- expect(mockCtx.res.setHeader).toHaveBeenCalledWith('Set-Cookie', expect.stringContaining('rt=refresh-token-123'));
168
- expect(mockCtx.res.setHeader).toHaveBeenCalledWith('Set-Cookie', expect.stringContaining('HttpOnly'));
169
- expect(mockCtx.res.setHeader).toHaveBeenCalledWith('Set-Cookie', expect.stringContaining('Path=/'));
170
- expect(mockCtx.res.setHeader).toHaveBeenCalledWith('Set-Cookie', expect.stringContaining('SameSite=Lax'));
171
- });
172
- it('returns error for invalid credentials', async () => {
173
- mockCtx.body = { email: 'wrong@example.com', password: 'wrong' };
174
- const result = await auth.login(mockCtx);
175
- expect(result.success).toBe(false);
176
- expect(result.error).toBe('Invalid credentials');
177
- expect(result.status).toBe(401);
178
- });
179
- });
180
- describe('refresh - Cookie Test (Dolphin Style)', () => {
181
- it('refreshes access token with valid refresh token from cookie header', async () => {
182
- // ✅ Set cookie in headers (Dolphin server style)
183
- mockCtx.req.headers.cookie = 'rt=refresh-token-123; other=value';
184
- const result = await auth.refresh(mockCtx);
185
- expect(result.success).toBe(true);
186
- expect(result.accessToken).toBe('new-token-456');
187
- // ✅ Verify new cookie was set
188
- expect(mockCtx.res.setHeader).toHaveBeenCalledWith('Set-Cookie', expect.stringContaining('rt=new-refresh-token-456'));
189
- expect(mockCtx.res.setHeader).toHaveBeenCalledWith('Set-Cookie', expect.stringContaining('Max-Age=604800'));
190
- });
191
- it('handles multiple cookies correctly', async () => {
192
- // ✅ Multiple cookies in header
193
- mockCtx.req.headers.cookie = 'session=abc123; rt=refresh-token-123; theme=dark';
194
- const result = await auth.refresh(mockCtx);
195
- expect(result.success).toBe(true);
196
- expect(result.accessToken).toBe('new-token-456');
197
- });
198
- it('returns error when refresh token cookie is missing', async () => {
199
- mockCtx.req.headers.cookie = '';
200
- const result = await auth.refresh(mockCtx);
201
- expect(result.success).toBe(false);
202
- expect(result.error).toBe('No refresh token provided');
203
- expect(result.status).toBe(401);
204
- });
205
- it('returns error when cookie header is undefined', async () => {
206
- mockCtx.req.headers.cookie = undefined;
207
- const result = await auth.refresh(mockCtx);
208
- expect(result.success).toBe(false);
209
- expect(result.error).toBe('No refresh token provided');
210
- expect(result.status).toBe(401);
211
- });
212
- });
213
- describe('logout - Cookie Test', () => {
214
- it('clears refresh token cookie on logout', async () => {
215
- mockCtx.req.headers.cookie = 'rt=refresh-token-123';
216
- const result = await auth.logout(mockCtx);
217
- expect(result.success).toBe(true);
218
- // ✅ Verify cookie is cleared (Max-Age=0)
219
- expect(mockCtx.res.setHeader).toHaveBeenCalledWith('Set-Cookie', expect.stringContaining('rt='));
220
- expect(mockCtx.res.setHeader).toHaveBeenCalledWith('Set-Cookie', expect.stringContaining('Max-Age=0'));
221
- expect(mockCtx.res.setHeader).toHaveBeenCalledWith('Set-Cookie', expect.stringContaining('Path=/'));
222
- });
223
- });
224
- describe('me', () => {
225
- it('returns current user profile without sensitive data', async () => {
226
- const user = {
227
- id: '1',
228
- email: 'test@example.com',
229
- password: 'hashed',
230
- recoveryCodes: ['code1'],
231
- name: 'Test User'
232
- };
233
- mockCtx.req.user = user;
234
- const result = await auth.me(mockCtx);
235
- expect(result.success).toBe(true);
236
- expect(result.data).not.toHaveProperty('password');
237
- expect(result.data).not.toHaveProperty('recoveryCodes');
238
- expect(result.data.email).toBe('test@example.com');
239
- expect(result.data.name).toBe('Test User');
240
- });
241
- it('returns unauthorized when user not found', async () => {
242
- mockCtx.req.user = null;
243
- const result = await auth.me(mockCtx);
244
- expect(result.success).toBe(false);
245
- expect(result.error).toBe('Unauthorized');
246
- expect(result.status).toBe(401);
247
- });
248
- });
249
- describe('changePassword', () => {
250
- it('changes password successfully', async () => {
251
- mockCtx.req.user = { id: '1' };
252
- mockCtx.body = { oldPassword: 'old123', newPassword: 'new123456' };
253
- const result = await auth.changePassword(mockCtx);
254
- expect(result.success).toBe(true);
255
- expect(result.message).toBe('Password changed successfully');
256
- });
257
- it('fails with incorrect old password', async () => {
258
- mockCtx.req.user = { id: '1' };
259
- mockCtx.body = { oldPassword: 'wrong', newPassword: 'new123456' };
260
- const result = await auth.changePassword(mockCtx);
261
- expect(result.success).toBe(false);
262
- expect(result.error).toBe('Current password is incorrect');
263
- });
264
- });
265
- describe('forgotPassword', () => {
266
- it('sends reset link for existing email', async () => {
267
- mockCtx.body = { email: 'test@example.com' };
268
- const result = await auth.forgotPassword(mockCtx);
269
- expect(result.success).toBe(true);
270
- expect(result.message).toBe('Reset link sent');
271
- });
272
- });
273
- describe('resetPassword', () => {
274
- it('resets password with valid token', async () => {
275
- mockCtx.body = { token: 'valid-token', newPassword: 'new123456' };
276
- const result = await auth.resetPassword(mockCtx);
277
- expect(result.success).toBe(true);
278
- expect(result.message).toBe('Password reset successfully');
279
- });
280
- it('fails with invalid token', async () => {
281
- mockCtx.body = { token: 'invalid-token', newPassword: 'new123456' };
282
- const result = await auth.resetPassword(mockCtx);
283
- expect(result.success).toBe(false);
284
- expect(result.error).toBe('Invalid or expired reset token');
285
- });
286
- });
287
- describe('2FA Methods', () => {
288
- it('enables 2FA for user', async () => {
289
- mockCtx.req.user = { id: '1' };
290
- const result = await auth.enable2FA(mockCtx);
291
- expect(result.success).toBe(true);
292
- expect(result.secret).toBe('SECRET123');
293
- });
294
- it('verifies 2FA token', async () => {
295
- mockCtx.req.user = { id: '1' };
296
- mockCtx.body = { totp: '123456' };
297
- const result = await auth.verify2FA(mockCtx);
298
- expect(result.success).toBe(true);
299
- expect(result.recoveryCodes).toEqual(['code1', 'code2']);
300
- });
301
- it('fails 2FA verification with invalid token', async () => {
302
- mockCtx.req.user = { id: '1' };
303
- mockCtx.body = { totp: '999999' };
304
- const result = await auth.verify2FA(mockCtx);
305
- expect(result.success).toBe(false);
306
- expect(result.error).toBe('Invalid TOTP');
307
- });
308
- });
309
- describe('sanitize', () => {
310
- it('removes all sensitive fields from user object', () => {
311
- const user = {
312
- id: '1',
313
- email: 'test@example.com',
314
- password: 'secret',
315
- recoveryCodes: ['code1'],
316
- twoFactorSecret: 'secret',
317
- pending2FASecret: 'pending',
318
- resetPasswordToken: 'token',
319
- resetPasswordExpires: new Date(),
320
- name: 'Test User',
321
- role: 'user'
322
- };
323
- const sanitized = auth.sanitize(user);
324
- expect(sanitized).toEqual({
325
- id: '1',
326
- email: 'test@example.com',
327
- name: 'Test User',
328
- role: 'user'
329
- });
330
- expect(sanitized).not.toHaveProperty('password');
331
- expect(sanitized).not.toHaveProperty('recoveryCodes');
332
- expect(sanitized).not.toHaveProperty('twoFactorSecret');
333
- expect(sanitized).not.toHaveProperty('resetPasswordToken');
334
- });
335
- it('returns null for null input', () => {
336
- expect(auth.sanitize(null)).toBeNull();
337
- });
338
- });
339
- describe('Cookie Integration Flow', () => {
340
- it('complete auth flow with cookies', async () => {
341
- // 1. Login - set cookie
342
- mockCtx.body = { email: 'test@example.com', password: 'password123' };
343
- const loginResult = await auth.login(mockCtx);
344
- expect(loginResult.success).toBe(true);
345
- // 2. Simulate cookie being set in browser
346
- mockCtx.req.headers.cookie = 'rt=refresh-token-123';
347
- // 3. Refresh - read cookie and set new one
348
- const refreshResult = await auth.refresh(mockCtx);
349
- expect(refreshResult.success).toBe(true);
350
- expect(refreshResult.accessToken).toBe('new-token-456');
351
- // 4. Logout - clear cookie
352
- const logoutResult = await auth.logout(mockCtx);
353
- expect(logoutResult.success).toBe(true);
354
- // 5. Verify cookie clear header was set
355
- expect(mockCtx.res.setHeader).toHaveBeenLastCalledWith('Set-Cookie', expect.stringContaining('Max-Age=0'));
356
- });
357
- });
358
- });
359
- //# sourceMappingURL=authController.test.js.map