arkos 1.0.3-alpha → 1.0.4-alpha
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.
- package/README.md +0 -32
- package/package.json +1 -1
- package/dist/cjs/modules/auth/__tests__/auth.controller.test.js +0 -494
- package/dist/cjs/modules/auth/__tests__/auth.controller.test.js.map +0 -1
- package/dist/cjs/modules/auth/__tests__/auth.service.test.js +0 -470
- package/dist/cjs/modules/auth/__tests__/auth.service.test.js.map +0 -1
- package/dist/cjs/modules/auth/utils/helpers/__tests__/auth.helpers.test.js +0 -43
- package/dist/cjs/modules/auth/utils/helpers/__tests__/auth.helpers.test.js.map +0 -1
- package/dist/cjs/modules/base/utils/helpers/__tests__/base.helpers.test.js +0 -754
- package/dist/cjs/modules/base/utils/helpers/__tests__/base.helpers.test.js.map +0 -1
- package/dist/cjs/modules/file-upload/file-upload.controller.js +0 -226
- package/dist/cjs/modules/file-upload/file-upload.controller.js.map +0 -1
- package/dist/cjs/modules/file-upload/file-upload.router.js +0 -50
- package/dist/cjs/modules/file-upload/file-upload.router.js.map +0 -1
- package/dist/cjs/modules/file-upload/file-upload.service.js +0 -353
- package/dist/cjs/modules/file-upload/file-upload.service.js.map +0 -1
- package/dist/cjs/modules/file-uploader/__tests__/file-uploader.service.test.js +0 -402
- package/dist/cjs/modules/file-uploader/__tests__/file-uploader.service.test.js.map +0 -1
- package/dist/cjs/modules/file-uploader/utils/helpers/__tests__/file-uploader.helpers.test.js +0 -164
- package/dist/cjs/modules/file-uploader/utils/helpers/__tests__/file-uploader.helpers.test.js.map +0 -1
- package/dist/es2020/modules/file-upload/file-upload.controller.js +0 -220
- package/dist/es2020/modules/file-upload/file-upload.controller.js.map +0 -1
- package/dist/es2020/modules/file-upload/file-upload.router.js +0 -44
- package/dist/es2020/modules/file-upload/file-upload.router.js.map +0 -1
- package/dist/es2020/modules/file-upload/file-upload.service.js +0 -345
- package/dist/es2020/modules/file-upload/file-upload.service.js.map +0 -1
- package/dist/types/modules/file-upload/file-upload.controller.d.ts +0 -3
- package/dist/types/modules/file-upload/file-upload.router.d.ts +0 -3
- package/dist/types/modules/file-upload/file-upload.service.d.ts +0 -30
package/README.md
CHANGED
|
@@ -20,38 +20,6 @@ Used to simplify the development of a secure and scalable RESTful API with minim
|
|
|
20
20
|
|
|
21
21
|
**BETA VERSION**
|
|
22
22
|
|
|
23
|
-
## Getting Started
|
|
24
|
-
|
|
25
|
-
### 1. Install
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
npm install arkos
|
|
29
|
-
# or
|
|
30
|
-
pnpm install arkos
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### 2. Set up Prisma
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npm install @prisma/client
|
|
37
|
-
npm install prisma --save-dev
|
|
38
|
-
|
|
39
|
-
# Initialize and generate
|
|
40
|
-
npx prisma generate
|
|
41
|
-
npx prisma migrate dev
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### 3. Start your API in one line
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
// src/app.ts
|
|
48
|
-
import arkos from "arkos";
|
|
49
|
-
|
|
50
|
-
arkos.init({ port: 3000 });
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
That's it! Your RESTful API is ready to use.
|
|
54
|
-
|
|
55
23
|
## Documentation
|
|
56
24
|
|
|
57
25
|
For detailed documentation, visit our [official docs](https://arkosjs.com/docs/intro).
|
package/package.json
CHANGED
|
@@ -1,494 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
const auth_controller_1 = require("../auth.controller");
|
|
16
|
-
const auth_service_1 = __importDefault(require("../auth.service"));
|
|
17
|
-
const base_service_1 = require("../../base/base.service");
|
|
18
|
-
const prisma_helpers_1 = require("../../../utils/helpers/prisma.helpers");
|
|
19
|
-
const models_helpers_1 = require("../../../utils/helpers/models.helpers");
|
|
20
|
-
const server_1 = require("../../../server");
|
|
21
|
-
jest.mock("bcrypt", () => ({
|
|
22
|
-
default: {
|
|
23
|
-
compare: jest.fn(),
|
|
24
|
-
hash: jest.fn(),
|
|
25
|
-
},
|
|
26
|
-
}));
|
|
27
|
-
jest.mock("../auth.service", () => ({
|
|
28
|
-
isCorrectPassword: jest.fn(),
|
|
29
|
-
signJwtToken: jest.fn(),
|
|
30
|
-
isPasswordStrong: jest.fn(),
|
|
31
|
-
hashPassword: jest.fn(),
|
|
32
|
-
authenticate: jest.fn(),
|
|
33
|
-
handleAuthenticationControl: jest.fn(),
|
|
34
|
-
handleActionAccessControl: jest.fn(),
|
|
35
|
-
}));
|
|
36
|
-
jest.mock("../../base/base.service", () => ({
|
|
37
|
-
getBaseServices: jest.fn(),
|
|
38
|
-
}));
|
|
39
|
-
jest.mock("../../../utils/helpers/prisma.helpers", () => ({
|
|
40
|
-
getPrismaInstance: jest.fn(),
|
|
41
|
-
}));
|
|
42
|
-
jest.mock("../../../utils/helpers/models.helpers", () => ({
|
|
43
|
-
importPrismaModelModules: jest.fn(),
|
|
44
|
-
getPrismaModelRelations: jest.fn(),
|
|
45
|
-
getModels: jest.fn(() => []),
|
|
46
|
-
getModelUniqueFields: jest.fn(() => []),
|
|
47
|
-
models: [],
|
|
48
|
-
prismaModelRelationFields: {},
|
|
49
|
-
}));
|
|
50
|
-
jest.mock("../../../server", () => ({
|
|
51
|
-
getArkosConfig: jest.fn(),
|
|
52
|
-
close: jest.fn(),
|
|
53
|
-
}));
|
|
54
|
-
describe("Auth Controller Factory", () => {
|
|
55
|
-
let req;
|
|
56
|
-
let res;
|
|
57
|
-
let next;
|
|
58
|
-
let mockPrisma;
|
|
59
|
-
let authController;
|
|
60
|
-
let userService;
|
|
61
|
-
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
62
|
-
jest.resetAllMocks();
|
|
63
|
-
userService = {
|
|
64
|
-
findOne: jest.fn(),
|
|
65
|
-
createOne: jest.fn(),
|
|
66
|
-
};
|
|
67
|
-
mockPrisma = {
|
|
68
|
-
user: {
|
|
69
|
-
findUnique: jest.fn(),
|
|
70
|
-
update: jest.fn(),
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
base_service_1.getBaseServices.mockReturnValue({
|
|
74
|
-
user: userService,
|
|
75
|
-
});
|
|
76
|
-
prisma_helpers_1.getPrismaInstance.mockReturnValue(mockPrisma);
|
|
77
|
-
models_helpers_1.importPrismaModelModules.mockResolvedValue({
|
|
78
|
-
prismaQueryOptions: {
|
|
79
|
-
queryOptions: {},
|
|
80
|
-
findOne: {},
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
server_1.getArkosConfig.mockReturnValue({
|
|
84
|
-
authentication: {
|
|
85
|
-
usernameField: "username",
|
|
86
|
-
login: {
|
|
87
|
-
sendAccessTokenThrough: "both",
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
});
|
|
91
|
-
req = {
|
|
92
|
-
user: {
|
|
93
|
-
id: "user-id-123",
|
|
94
|
-
username: "testuser",
|
|
95
|
-
password: "hashedPassword",
|
|
96
|
-
isVerified: true,
|
|
97
|
-
active: true,
|
|
98
|
-
},
|
|
99
|
-
body: {},
|
|
100
|
-
query: {},
|
|
101
|
-
secure: false,
|
|
102
|
-
headers: {},
|
|
103
|
-
};
|
|
104
|
-
res = {
|
|
105
|
-
status: jest.fn().mockReturnThis(),
|
|
106
|
-
json: jest.fn(),
|
|
107
|
-
cookie: jest.fn(),
|
|
108
|
-
send: jest.fn(),
|
|
109
|
-
};
|
|
110
|
-
next = jest.fn();
|
|
111
|
-
authController = yield (0, auth_controller_1.authControllerFactory)();
|
|
112
|
-
}));
|
|
113
|
-
afterEach(() => {
|
|
114
|
-
jest.clearAllMocks();
|
|
115
|
-
});
|
|
116
|
-
describe("getMe", () => {
|
|
117
|
-
it("should get the current user and return it", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
118
|
-
userService.findOne.mockResolvedValueOnce({
|
|
119
|
-
id: "user-id-123",
|
|
120
|
-
username: "testuser",
|
|
121
|
-
email: "test@example.com",
|
|
122
|
-
});
|
|
123
|
-
yield authController.getMe(req, res, next);
|
|
124
|
-
expect(userService.findOne).toHaveBeenCalledWith({ id: "user-id-123" }, expect.any(String));
|
|
125
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
126
|
-
expect(res.json).toHaveBeenCalledWith({ data: req.user });
|
|
127
|
-
}));
|
|
128
|
-
it("should remove excluded fields from the user object", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
129
|
-
const fullUser = {
|
|
130
|
-
id: "user-id-123",
|
|
131
|
-
username: "testuser",
|
|
132
|
-
email: "test@example.com",
|
|
133
|
-
password: "hashedPassword",
|
|
134
|
-
passwordChangedAt: new Date(),
|
|
135
|
-
active: true,
|
|
136
|
-
};
|
|
137
|
-
req.user = Object.assign({}, fullUser);
|
|
138
|
-
userService.findOne.mockResolvedValueOnce(fullUser);
|
|
139
|
-
yield authController.getMe(req, res, next);
|
|
140
|
-
Object.keys(auth_controller_1.defaultExcludedUserFields).forEach((field) => {
|
|
141
|
-
expect(req.user[field]).toBeUndefined();
|
|
142
|
-
});
|
|
143
|
-
}));
|
|
144
|
-
it("should call next middleware when afterGetMe is provided", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
145
|
-
const controllerWithMiddleware = yield (0, auth_controller_1.authControllerFactory)({
|
|
146
|
-
afterGetMe: true,
|
|
147
|
-
});
|
|
148
|
-
userService.findOne.mockResolvedValueOnce({
|
|
149
|
-
id: "user-id-123",
|
|
150
|
-
username: "testuser",
|
|
151
|
-
email: "test@example.com",
|
|
152
|
-
});
|
|
153
|
-
yield controllerWithMiddleware.getMe(req, res, next);
|
|
154
|
-
expect(req.responseData).toBeDefined();
|
|
155
|
-
expect(req.responseStatus).toBe(200);
|
|
156
|
-
expect(next).toHaveBeenCalled();
|
|
157
|
-
expect(res.status).not.toHaveBeenCalled();
|
|
158
|
-
}));
|
|
159
|
-
});
|
|
160
|
-
describe("logout", () => {
|
|
161
|
-
it("should clear the access token cookie and return 204", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
162
|
-
yield authController.logout(req, res, next);
|
|
163
|
-
expect(res.cookie).toHaveBeenCalledWith("arkos_access_token", "no-token", expect.objectContaining({
|
|
164
|
-
httpOnly: true,
|
|
165
|
-
}));
|
|
166
|
-
expect(res.status).toHaveBeenCalledWith(204);
|
|
167
|
-
expect(res.json).toHaveBeenCalled();
|
|
168
|
-
}));
|
|
169
|
-
it("should call next middleware when afterLogout is provided", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
170
|
-
const controllerWithMiddleware = yield (0, auth_controller_1.authControllerFactory)({
|
|
171
|
-
afterLogout: true,
|
|
172
|
-
});
|
|
173
|
-
yield controllerWithMiddleware.logout(req, res, next);
|
|
174
|
-
expect(req.responseData).toBeNull();
|
|
175
|
-
expect(req.responseStatus).toBe(204);
|
|
176
|
-
expect(next).toHaveBeenCalled();
|
|
177
|
-
expect(res.status).not.toHaveBeenCalled();
|
|
178
|
-
}));
|
|
179
|
-
});
|
|
180
|
-
describe("login", () => {
|
|
181
|
-
it("should return 400 if username or password is missing", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
182
|
-
req.body = { username: "testuser" };
|
|
183
|
-
yield authController.login(req, res, next);
|
|
184
|
-
expect(next).toHaveBeenCalledWith(expect.objectContaining({
|
|
185
|
-
statusCode: 400,
|
|
186
|
-
message: expect.stringContaining("username and password"),
|
|
187
|
-
}));
|
|
188
|
-
}));
|
|
189
|
-
it("should use default username field from config when not specified in query", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
190
|
-
req.body = { username: "testuser", password: "Password123" };
|
|
191
|
-
mockPrisma.user.findUnique.mockResolvedValueOnce({
|
|
192
|
-
id: "user-id-123",
|
|
193
|
-
username: "testuser",
|
|
194
|
-
password: "hashedPassword",
|
|
195
|
-
});
|
|
196
|
-
auth_service_1.default.isCorrectPassword.mockResolvedValueOnce(true);
|
|
197
|
-
auth_service_1.default.signJwtToken.mockReturnValue("jwt-token-123");
|
|
198
|
-
yield authController.login(req, res, next);
|
|
199
|
-
expect(mockPrisma.user.findUnique).toHaveBeenCalledWith({
|
|
200
|
-
where: { username: "testuser" },
|
|
201
|
-
});
|
|
202
|
-
}));
|
|
203
|
-
it("should use username field from query parameter when provided", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
204
|
-
req.query.usernameField = "email";
|
|
205
|
-
req.body = { email: "test@example.com", password: "Password123" };
|
|
206
|
-
mockPrisma.user.findUnique.mockResolvedValueOnce({
|
|
207
|
-
id: "user-id-123",
|
|
208
|
-
email: "test@example.com",
|
|
209
|
-
password: "hashedPassword",
|
|
210
|
-
});
|
|
211
|
-
auth_service_1.default.isCorrectPassword.mockResolvedValueOnce(true);
|
|
212
|
-
auth_service_1.default.signJwtToken.mockReturnValue("jwt-token-123");
|
|
213
|
-
yield authController.login(req, res, next);
|
|
214
|
-
expect(mockPrisma.user.findUnique).toHaveBeenCalledWith({
|
|
215
|
-
where: { email: "test@example.com" },
|
|
216
|
-
});
|
|
217
|
-
}));
|
|
218
|
-
it("should return 401 if user is not found", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
219
|
-
req.body = { username: "nonexistentuser", password: "Password123" };
|
|
220
|
-
mockPrisma.user.findUnique.mockResolvedValueOnce(null);
|
|
221
|
-
yield authController.login(req, res, next);
|
|
222
|
-
expect(next).toHaveBeenCalledWith(expect.objectContaining({
|
|
223
|
-
statusCode: 401,
|
|
224
|
-
message: expect.stringContaining("Incorrect username or password"),
|
|
225
|
-
}));
|
|
226
|
-
}));
|
|
227
|
-
it("should return 401 if password is incorrect", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
228
|
-
req.body = { username: "testuser", password: "WrongPassword123" };
|
|
229
|
-
mockPrisma.user.findUnique.mockResolvedValueOnce({
|
|
230
|
-
id: "user-id-123",
|
|
231
|
-
username: "testuser",
|
|
232
|
-
password: "hashedPassword",
|
|
233
|
-
});
|
|
234
|
-
auth_service_1.default.isCorrectPassword.mockResolvedValueOnce(false);
|
|
235
|
-
yield authController.login(req, res, next);
|
|
236
|
-
expect(next).toHaveBeenCalledWith(expect.objectContaining({
|
|
237
|
-
statusCode: 401,
|
|
238
|
-
message: expect.stringContaining("Incorrect username or password"),
|
|
239
|
-
}));
|
|
240
|
-
}));
|
|
241
|
-
it('should set cookie and return token in response when config is "both"', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
242
|
-
req.body = { username: "testuser", password: "Password123" };
|
|
243
|
-
mockPrisma.user.findUnique.mockResolvedValueOnce({
|
|
244
|
-
id: "user-id-123",
|
|
245
|
-
username: "testuser",
|
|
246
|
-
password: "hashedPassword",
|
|
247
|
-
});
|
|
248
|
-
auth_service_1.default.isCorrectPassword.mockResolvedValueOnce(true);
|
|
249
|
-
auth_service_1.default.signJwtToken.mockReturnValue("jwt-token-123");
|
|
250
|
-
server_1.getArkosConfig.mockReturnValue({
|
|
251
|
-
authentication: {
|
|
252
|
-
usernameField: "username",
|
|
253
|
-
login: {
|
|
254
|
-
sendAccessTokenThrough: "both",
|
|
255
|
-
},
|
|
256
|
-
},
|
|
257
|
-
});
|
|
258
|
-
yield authController.login(req, res, next);
|
|
259
|
-
expect(res.cookie).toHaveBeenCalledWith("arkos_access_token", "jwt-token-123", expect.any(Object));
|
|
260
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
261
|
-
expect(res.json).toHaveBeenCalledWith({
|
|
262
|
-
accessToken: "jwt-token-123",
|
|
263
|
-
});
|
|
264
|
-
}));
|
|
265
|
-
it('should only set cookie when config is "cookie-only"', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
266
|
-
req.body = { username: "testuser", password: "Password123" };
|
|
267
|
-
mockPrisma.user.findUnique.mockResolvedValueOnce({
|
|
268
|
-
id: "user-id-123",
|
|
269
|
-
username: "testuser",
|
|
270
|
-
password: "hashedPassword",
|
|
271
|
-
});
|
|
272
|
-
auth_service_1.default.isCorrectPassword.mockResolvedValueOnce(true);
|
|
273
|
-
auth_service_1.default.signJwtToken.mockReturnValue("jwt-token-123");
|
|
274
|
-
server_1.getArkosConfig.mockReturnValue({
|
|
275
|
-
authentication: {
|
|
276
|
-
usernameField: "username",
|
|
277
|
-
login: {
|
|
278
|
-
sendAccessTokenThrough: "cookie-only",
|
|
279
|
-
},
|
|
280
|
-
},
|
|
281
|
-
});
|
|
282
|
-
yield authController.login(req, res, next);
|
|
283
|
-
expect(res.cookie).toHaveBeenCalledWith("arkos_access_token", "jwt-token-123", expect.any(Object));
|
|
284
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
285
|
-
expect(res.send).toHaveBeenCalled();
|
|
286
|
-
expect(res.json).not.toHaveBeenCalled();
|
|
287
|
-
}));
|
|
288
|
-
it('should only return token in response when config is "response-only"', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
289
|
-
req.body = { username: "testuser", password: "Password123" };
|
|
290
|
-
mockPrisma.user.findUnique.mockResolvedValueOnce({
|
|
291
|
-
id: "user-id-123",
|
|
292
|
-
username: "testuser",
|
|
293
|
-
password: "hashedPassword",
|
|
294
|
-
});
|
|
295
|
-
auth_service_1.default.isCorrectPassword.mockResolvedValueOnce(true);
|
|
296
|
-
auth_service_1.default.signJwtToken.mockReturnValue("jwt-token-123");
|
|
297
|
-
server_1.getArkosConfig.mockReturnValue({
|
|
298
|
-
authentication: {
|
|
299
|
-
usernameField: "username",
|
|
300
|
-
login: {
|
|
301
|
-
sendAccessTokenThrough: "response-only",
|
|
302
|
-
},
|
|
303
|
-
},
|
|
304
|
-
});
|
|
305
|
-
yield authController.login(req, res, next);
|
|
306
|
-
expect(res.cookie).not.toHaveBeenCalled();
|
|
307
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
308
|
-
expect(res.json).toHaveBeenCalledWith({
|
|
309
|
-
accessToken: "jwt-token-123",
|
|
310
|
-
});
|
|
311
|
-
}));
|
|
312
|
-
it("should call next middleware when afterLogin is provided", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
313
|
-
const controllerWithMiddleware = yield (0, auth_controller_1.authControllerFactory)({
|
|
314
|
-
afterLogin: true,
|
|
315
|
-
});
|
|
316
|
-
req.body = { username: "testuser", password: "Password123" };
|
|
317
|
-
mockPrisma.user.findUnique.mockResolvedValueOnce({
|
|
318
|
-
id: "user-id-123",
|
|
319
|
-
username: "testuser",
|
|
320
|
-
password: "hashedPassword",
|
|
321
|
-
});
|
|
322
|
-
auth_service_1.default.isCorrectPassword.mockResolvedValueOnce(true);
|
|
323
|
-
auth_service_1.default.signJwtToken.mockReturnValue("jwt-token-123");
|
|
324
|
-
yield controllerWithMiddleware.login(req, res, next);
|
|
325
|
-
expect(req.responseData).toEqual({ accessToken: "jwt-token-123" });
|
|
326
|
-
expect(req.responseStatus).toBe(200);
|
|
327
|
-
expect(next).toHaveBeenCalled();
|
|
328
|
-
expect(res.status).not.toHaveBeenCalled();
|
|
329
|
-
}));
|
|
330
|
-
});
|
|
331
|
-
describe("signup", () => {
|
|
332
|
-
it("should create a new user and return 201", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
333
|
-
req.body = {
|
|
334
|
-
username: "newuser",
|
|
335
|
-
email: "newuser@example.com",
|
|
336
|
-
password: "Password123",
|
|
337
|
-
};
|
|
338
|
-
const createdUser = {
|
|
339
|
-
id: "new-user-id",
|
|
340
|
-
username: "newuser",
|
|
341
|
-
email: "newuser@example.com",
|
|
342
|
-
password: "hashedPassword",
|
|
343
|
-
active: true,
|
|
344
|
-
};
|
|
345
|
-
userService.createOne.mockResolvedValueOnce(Object.assign({}, createdUser));
|
|
346
|
-
yield authController.signup(req, res, next);
|
|
347
|
-
expect(userService.createOne).toHaveBeenCalledWith(Object.assign({}, req.body), "{}");
|
|
348
|
-
expect(res.status).toHaveBeenCalledWith(201);
|
|
349
|
-
const responseUser = res.json.mock.calls[0][0].data;
|
|
350
|
-
Object.keys(auth_controller_1.defaultExcludedUserFields).forEach((field) => {
|
|
351
|
-
expect(responseUser[field]).toBeUndefined();
|
|
352
|
-
});
|
|
353
|
-
}));
|
|
354
|
-
it("should call next middleware when afterSignup is provided", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
355
|
-
const controllerWithMiddleware = yield (0, auth_controller_1.authControllerFactory)({
|
|
356
|
-
afterSignup: true,
|
|
357
|
-
});
|
|
358
|
-
req.body = {
|
|
359
|
-
username: "newuser",
|
|
360
|
-
email: "newuser@example.com",
|
|
361
|
-
password: "Password123",
|
|
362
|
-
};
|
|
363
|
-
const createdUser = {
|
|
364
|
-
id: "new-user-id",
|
|
365
|
-
username: "newuser",
|
|
366
|
-
email: "newuser@example.com",
|
|
367
|
-
password: "hashedPassword",
|
|
368
|
-
};
|
|
369
|
-
userService.createOne.mockResolvedValueOnce(Object.assign({}, createdUser));
|
|
370
|
-
yield controllerWithMiddleware.signup(req, res, next);
|
|
371
|
-
expect(req.responseData).toEqual({ data: createdUser });
|
|
372
|
-
expect(req.responseStatus).toBe(201);
|
|
373
|
-
expect(next).toHaveBeenCalled();
|
|
374
|
-
expect(res.status).not.toHaveBeenCalled();
|
|
375
|
-
}));
|
|
376
|
-
});
|
|
377
|
-
describe("updatePassword", () => {
|
|
378
|
-
it("should return 400 if currentPassword or newPassword is missing", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
379
|
-
req.body = { currentPassword: "CurrentPassword123" };
|
|
380
|
-
yield authController.updatePassword(req, res, next);
|
|
381
|
-
expect(next).toHaveBeenCalledWith(expect.objectContaining({
|
|
382
|
-
statusCode: 400,
|
|
383
|
-
message: expect.stringContaining("currentPassword and newPassword are required"),
|
|
384
|
-
}));
|
|
385
|
-
}));
|
|
386
|
-
it("should return 404 if user is not found or inactive", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
387
|
-
req.user = null;
|
|
388
|
-
req.body = {
|
|
389
|
-
currentPassword: "CurrentPassword123",
|
|
390
|
-
newPassword: "NewPassword123",
|
|
391
|
-
};
|
|
392
|
-
yield authController.updatePassword(req, res, next);
|
|
393
|
-
expect(next).toHaveBeenCalledWith(expect.objectContaining({
|
|
394
|
-
statusCode: 404,
|
|
395
|
-
message: expect.stringContaining("User not found"),
|
|
396
|
-
}));
|
|
397
|
-
}));
|
|
398
|
-
it("should return 400 if current password is incorrect", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
399
|
-
req.user = {
|
|
400
|
-
id: "user-id-123",
|
|
401
|
-
username: "testuser",
|
|
402
|
-
password: "hashedPassword",
|
|
403
|
-
isVerified: true,
|
|
404
|
-
};
|
|
405
|
-
req.body = {
|
|
406
|
-
currentPassword: "WrongPassword123",
|
|
407
|
-
newPassword: "NewPassword123",
|
|
408
|
-
};
|
|
409
|
-
auth_service_1.default.isCorrectPassword.mockResolvedValueOnce(false);
|
|
410
|
-
yield authController.updatePassword(req, res, next);
|
|
411
|
-
expect(next).toHaveBeenCalledWith(expect.objectContaining({
|
|
412
|
-
statusCode: 400,
|
|
413
|
-
message: expect.stringContaining("Current password is incorrect"),
|
|
414
|
-
}));
|
|
415
|
-
}));
|
|
416
|
-
it("should return 400 if new password is not strong enough", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
417
|
-
req.user = {
|
|
418
|
-
id: "user-id-123",
|
|
419
|
-
username: "testuser",
|
|
420
|
-
password: "hashedPassword",
|
|
421
|
-
isVerified: true,
|
|
422
|
-
};
|
|
423
|
-
req.body = {
|
|
424
|
-
currentPassword: "CurrentPassword123",
|
|
425
|
-
newPassword: "weakpassword",
|
|
426
|
-
};
|
|
427
|
-
auth_service_1.default.isCorrectPassword.mockResolvedValueOnce(true);
|
|
428
|
-
auth_service_1.default.isPasswordStrong.mockReturnValue(false);
|
|
429
|
-
yield authController.updatePassword(req, res, next);
|
|
430
|
-
expect(next).toHaveBeenCalledWith(expect.objectContaining({
|
|
431
|
-
statusCode: 400,
|
|
432
|
-
message: expect.stringContaining("Password must contain"),
|
|
433
|
-
}));
|
|
434
|
-
}));
|
|
435
|
-
it("should update password and return 200 on success", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
436
|
-
req.user = {
|
|
437
|
-
id: "user-id-123",
|
|
438
|
-
username: "testuser",
|
|
439
|
-
password: "hashedPassword",
|
|
440
|
-
isVerified: true,
|
|
441
|
-
};
|
|
442
|
-
req.body = {
|
|
443
|
-
currentPassword: "CurrentPassword123",
|
|
444
|
-
newPassword: "NewPassword123",
|
|
445
|
-
};
|
|
446
|
-
auth_service_1.default.isCorrectPassword.mockResolvedValueOnce(true);
|
|
447
|
-
auth_service_1.default.isPasswordStrong.mockReturnValue(true);
|
|
448
|
-
auth_service_1.default.hashPassword.mockResolvedValueOnce("newHashedPassword");
|
|
449
|
-
yield authController.updatePassword(req, res, next);
|
|
450
|
-
expect(mockPrisma.user.update).toHaveBeenCalledWith({
|
|
451
|
-
where: { id: "user-id-123" },
|
|
452
|
-
data: {
|
|
453
|
-
password: "newHashedPassword",
|
|
454
|
-
passwordChangedAt: expect.any(Date),
|
|
455
|
-
},
|
|
456
|
-
});
|
|
457
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
458
|
-
expect(res.json).toHaveBeenCalledWith({
|
|
459
|
-
status: "success",
|
|
460
|
-
message: "Password updated successfully!",
|
|
461
|
-
});
|
|
462
|
-
}));
|
|
463
|
-
it("should call next middleware when afterUpdatePassword is provided", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
464
|
-
const controllerWithMiddleware = yield (0, auth_controller_1.authControllerFactory)({
|
|
465
|
-
afterUpdatePassword: true,
|
|
466
|
-
});
|
|
467
|
-
req.user = {
|
|
468
|
-
id: "user-id-123",
|
|
469
|
-
username: "testuser",
|
|
470
|
-
password: "hashedPassword",
|
|
471
|
-
isVerified: true,
|
|
472
|
-
};
|
|
473
|
-
req.body = {
|
|
474
|
-
currentPassword: "CurrentPassword123",
|
|
475
|
-
newPassword: "NewPassword123",
|
|
476
|
-
};
|
|
477
|
-
auth_service_1.default.isCorrectPassword.mockResolvedValueOnce(true);
|
|
478
|
-
auth_service_1.default.isPasswordStrong.mockReturnValue(true);
|
|
479
|
-
auth_service_1.default.hashPassword.mockResolvedValueOnce("newHashedPassword123");
|
|
480
|
-
yield controllerWithMiddleware.updatePassword(req, res, next);
|
|
481
|
-
expect(req.responseData).toEqual({
|
|
482
|
-
status: "success",
|
|
483
|
-
message: "Password updated successfully!",
|
|
484
|
-
});
|
|
485
|
-
expect(req.responseStatus).toBe(200);
|
|
486
|
-
expect(req.additionalData).toEqual({
|
|
487
|
-
user: req.user,
|
|
488
|
-
});
|
|
489
|
-
expect(next).toHaveBeenCalled();
|
|
490
|
-
expect(res.status).not.toHaveBeenCalled();
|
|
491
|
-
}));
|
|
492
|
-
});
|
|
493
|
-
});
|
|
494
|
-
//# sourceMappingURL=auth.controller.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth.controller.test.js","sourceRoot":"","sources":["../../../../../src/modules/auth/__tests__/auth.controller.test.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n authControllerFactory,\n defaultExcludedUserFields,\n} from \"../auth.controller\";\nimport authService from \"../auth.service\";\nimport { getBaseServices } from \"../../base/base.service\";\nimport { getPrismaInstance } from \"../../../utils/helpers/prisma.helpers\";\nimport { importPrismaModelModules } from \"../../../utils/helpers/models.helpers\";\nimport { getArkosConfig } from \"../../../server\";\n\njest.mock(\"bcrypt\", () => ({\n default: {\n compare: jest.fn(),\n hash: jest.fn(),\n },\n}));\n\n// Mock dependencies\njest.mock(\"../auth.service\", () => ({\n isCorrectPassword: jest.fn(),\n signJwtToken: jest.fn(),\n isPasswordStrong: jest.fn(),\n hashPassword: jest.fn(),\n authenticate: jest.fn(),\n handleAuthenticationControl: jest.fn(),\n handleActionAccessControl: jest.fn(),\n}));\n\njest.mock(\"../../base/base.service\", () => ({\n getBaseServices: jest.fn(),\n}));\n\njest.mock(\"../../../utils/helpers/prisma.helpers\", () => ({\n getPrismaInstance: jest.fn(),\n}));\n\n// Update your mock for models.helpers.ts\njest.mock(\"../../../utils/helpers/models.helpers\", () => ({\n importPrismaModelModules: jest.fn(),\n getPrismaModelRelations: jest.fn(),\n getModels: jest.fn(() => []),\n getModelUniqueFields: jest.fn(() => []),\n models: [],\n prismaModelRelationFields: {},\n}));\n\njest.mock(\"../../../server\", () => ({\n getArkosConfig: jest.fn(),\n close: jest.fn(),\n}));\n\ndescribe(\"Auth Controller Factory\", () => {\n let req: any;\n let res: any;\n let next: any;\n let mockPrisma: any;\n let authController: any;\n let userService: any;\n\n beforeEach(async () => {\n // Reset mocks\n jest.resetAllMocks();\n\n // Setup mocks\n userService = {\n findOne: jest.fn(),\n createOne: jest.fn(),\n };\n\n mockPrisma = {\n user: {\n findUnique: jest.fn(),\n update: jest.fn(),\n },\n };\n\n // Setup mock implementations\n (getBaseServices as jest.Mock).mockReturnValue({\n user: userService,\n });\n\n (getPrismaInstance as jest.Mock).mockReturnValue(mockPrisma);\n (importPrismaModelModules as jest.Mock).mockResolvedValue({\n prismaQueryOptions: {\n queryOptions: {},\n findOne: {},\n },\n });\n\n (getArkosConfig as jest.Mock).mockReturnValue({\n authentication: {\n usernameField: \"username\",\n login: {\n sendAccessTokenThrough: \"both\",\n },\n },\n });\n\n // Create request, response, and next function mocks\n req = {\n user: {\n id: \"user-id-123\",\n username: \"testuser\",\n password: \"hashedPassword\",\n isVerified: true,\n active: true,\n },\n body: {},\n query: {},\n secure: false,\n headers: {},\n };\n\n res = {\n status: jest.fn().mockReturnThis(),\n json: jest.fn(),\n cookie: jest.fn(),\n send: jest.fn(),\n };\n\n next = jest.fn();\n\n // Create the auth controller\n authController = await authControllerFactory();\n });\n\n afterEach(() => {\n jest.clearAllMocks();\n });\n\n describe(\"getMe\", () => {\n it(\"should get the current user and return it\", async () => {\n // Setup\n userService.findOne.mockResolvedValueOnce({\n id: \"user-id-123\",\n username: \"testuser\",\n email: \"test@example.com\",\n });\n\n // Execute\n await authController.getMe(req, res, next);\n\n // Verify\n expect(userService.findOne).toHaveBeenCalledWith(\n { id: \"user-id-123\" },\n expect.any(String)\n );\n expect(res.status).toHaveBeenCalledWith(200);\n expect(res.json).toHaveBeenCalledWith({ data: req.user });\n });\n\n it(\"should remove excluded fields from the user object\", async () => {\n // Setup\n const fullUser = {\n id: \"user-id-123\",\n username: \"testuser\",\n email: \"test@example.com\",\n password: \"hashedPassword\",\n passwordChangedAt: new Date(),\n active: true,\n };\n\n req.user = { ...fullUser };\n userService.findOne.mockResolvedValueOnce(fullUser);\n\n // Execute\n await authController.getMe(req, res, next);\n\n // Verify\n Object.keys(defaultExcludedUserFields).forEach((field) => {\n expect(req.user[field]).toBeUndefined();\n });\n });\n\n it(\"should call next middleware when afterGetMe is provided\", async () => {\n // Setup\n const controllerWithMiddleware = await authControllerFactory({\n afterGetMe: true,\n });\n\n userService.findOne.mockResolvedValueOnce({\n id: \"user-id-123\",\n username: \"testuser\",\n email: \"test@example.com\",\n });\n\n // Execute\n await controllerWithMiddleware.getMe(req, res, next);\n\n // Verify\n expect(req.responseData).toBeDefined();\n expect(req.responseStatus).toBe(200);\n expect(next).toHaveBeenCalled();\n expect(res.status).not.toHaveBeenCalled();\n });\n });\n\n describe(\"logout\", () => {\n it(\"should clear the access token cookie and return 204\", async () => {\n // Execute\n await authController.logout(req, res, next);\n\n // Verify\n expect(res.cookie).toHaveBeenCalledWith(\n \"arkos_access_token\",\n \"no-token\",\n expect.objectContaining({\n httpOnly: true,\n })\n );\n expect(res.status).toHaveBeenCalledWith(204);\n expect(res.json).toHaveBeenCalled();\n });\n\n it(\"should call next middleware when afterLogout is provided\", async () => {\n // Setup\n const controllerWithMiddleware = await authControllerFactory({\n afterLogout: true,\n });\n\n // Execute\n await controllerWithMiddleware.logout(req, res, next);\n\n // Verify\n expect(req.responseData).toBeNull();\n expect(req.responseStatus).toBe(204);\n expect(next).toHaveBeenCalled();\n expect(res.status).not.toHaveBeenCalled();\n });\n });\n\n describe(\"login\", () => {\n it(\"should return 400 if username or password is missing\", async () => {\n // Setup\n req.body = { username: \"testuser\" }; // Missing password\n\n // Execute\n await authController.login(req, res, next);\n\n // Verify\n expect(next).toHaveBeenCalledWith(\n expect.objectContaining({\n statusCode: 400,\n message: expect.stringContaining(\"username and password\"),\n })\n );\n });\n\n it(\"should use default username field from config when not specified in query\", async () => {\n // Setup\n req.body = { username: \"testuser\", password: \"Password123\" };\n\n mockPrisma.user.findUnique.mockResolvedValueOnce({\n id: \"user-id-123\",\n username: \"testuser\",\n password: \"hashedPassword\",\n });\n (authService.isCorrectPassword as jest.Mock).mockResolvedValueOnce(true);\n (authService.signJwtToken as jest.Mock).mockReturnValue(\"jwt-token-123\");\n\n // Execute\n await authController.login(req, res, next);\n\n // Verify\n expect(mockPrisma.user.findUnique).toHaveBeenCalledWith({\n where: { username: \"testuser\" },\n });\n });\n\n it(\"should use username field from query parameter when provided\", async () => {\n // Setup\n req.query.usernameField = \"email\";\n req.body = { email: \"test@example.com\", password: \"Password123\" };\n\n mockPrisma.user.findUnique.mockResolvedValueOnce({\n id: \"user-id-123\",\n email: \"test@example.com\",\n password: \"hashedPassword\",\n });\n\n (authService.isCorrectPassword as jest.Mock).mockResolvedValueOnce(true);\n (authService.signJwtToken as jest.Mock).mockReturnValue(\"jwt-token-123\");\n\n // Execute\n await authController.login(req, res, next);\n\n // Verify\n expect(mockPrisma.user.findUnique).toHaveBeenCalledWith({\n where: { email: \"test@example.com\" },\n });\n });\n\n it(\"should return 401 if user is not found\", async () => {\n // Setup\n req.body = { username: \"nonexistentuser\", password: \"Password123\" };\n mockPrisma.user.findUnique.mockResolvedValueOnce(null);\n\n // Execute\n await authController.login(req, res, next);\n\n // Verify\n expect(next).toHaveBeenCalledWith(\n expect.objectContaining({\n statusCode: 401,\n message: expect.stringContaining(\"Incorrect username or password\"),\n })\n );\n });\n\n it(\"should return 401 if password is incorrect\", async () => {\n // Setup\n req.body = { username: \"testuser\", password: \"WrongPassword123\" };\n\n mockPrisma.user.findUnique.mockResolvedValueOnce({\n id: \"user-id-123\",\n username: \"testuser\",\n password: \"hashedPassword\",\n });\n\n (authService.isCorrectPassword as jest.Mock).mockResolvedValueOnce(false);\n\n // Execute\n await authController.login(req, res, next);\n\n // Verify\n expect(next).toHaveBeenCalledWith(\n expect.objectContaining({\n statusCode: 401,\n message: expect.stringContaining(\"Incorrect username or password\"),\n })\n );\n });\n\n it('should set cookie and return token in response when config is \"both\"', async () => {\n // Setup\n req.body = { username: \"testuser\", password: \"Password123\" };\n\n mockPrisma.user.findUnique.mockResolvedValueOnce({\n id: \"user-id-123\",\n username: \"testuser\",\n password: \"hashedPassword\",\n });\n\n (authService.isCorrectPassword as jest.Mock).mockResolvedValueOnce(true);\n (authService.signJwtToken as jest.Mock).mockReturnValue(\"jwt-token-123\");\n\n (getArkosConfig as jest.Mock).mockReturnValue({\n authentication: {\n usernameField: \"username\",\n login: {\n sendAccessTokenThrough: \"both\",\n },\n },\n });\n\n // Execute\n await authController.login(req, res, next);\n\n // Verify\n expect(res.cookie).toHaveBeenCalledWith(\n \"arkos_access_token\",\n \"jwt-token-123\",\n expect.any(Object)\n );\n expect(res.status).toHaveBeenCalledWith(200);\n expect(res.json).toHaveBeenCalledWith({\n accessToken: \"jwt-token-123\",\n });\n });\n\n it('should only set cookie when config is \"cookie-only\"', async () => {\n // Setup\n req.body = { username: \"testuser\", password: \"Password123\" };\n\n mockPrisma.user.findUnique.mockResolvedValueOnce({\n id: \"user-id-123\",\n username: \"testuser\",\n password: \"hashedPassword\",\n });\n\n (authService.isCorrectPassword as jest.Mock).mockResolvedValueOnce(true);\n (authService.signJwtToken as jest.Mock).mockReturnValue(\"jwt-token-123\");\n\n (getArkosConfig as jest.Mock).mockReturnValue({\n authentication: {\n usernameField: \"username\",\n login: {\n sendAccessTokenThrough: \"cookie-only\",\n },\n },\n });\n\n // Execute\n await authController.login(req, res, next);\n\n // Verify\n expect(res.cookie).toHaveBeenCalledWith(\n \"arkos_access_token\",\n \"jwt-token-123\",\n expect.any(Object)\n );\n expect(res.status).toHaveBeenCalledWith(200);\n expect(res.send).toHaveBeenCalled();\n expect(res.json).not.toHaveBeenCalled();\n });\n\n it('should only return token in response when config is \"response-only\"', async () => {\n // Setup\n req.body = { username: \"testuser\", password: \"Password123\" };\n\n mockPrisma.user.findUnique.mockResolvedValueOnce({\n id: \"user-id-123\",\n username: \"testuser\",\n password: \"hashedPassword\",\n });\n\n (authService.isCorrectPassword as jest.Mock).mockResolvedValueOnce(true);\n (authService.signJwtToken as jest.Mock).mockReturnValue(\"jwt-token-123\");\n\n (getArkosConfig as jest.Mock).mockReturnValue({\n authentication: {\n usernameField: \"username\",\n login: {\n sendAccessTokenThrough: \"response-only\",\n },\n },\n });\n\n // Execute\n await authController.login(req, res, next);\n\n // Verify\n expect(res.cookie).not.toHaveBeenCalled();\n expect(res.status).toHaveBeenCalledWith(200);\n expect(res.json).toHaveBeenCalledWith({\n accessToken: \"jwt-token-123\",\n });\n });\n\n it(\"should call next middleware when afterLogin is provided\", async () => {\n // Setup\n const controllerWithMiddleware = await authControllerFactory({\n afterLogin: true,\n });\n\n req.body = { username: \"testuser\", password: \"Password123\" };\n\n mockPrisma.user.findUnique.mockResolvedValueOnce({\n id: \"user-id-123\",\n username: \"testuser\",\n password: \"hashedPassword\",\n });\n\n (authService.isCorrectPassword as jest.Mock).mockResolvedValueOnce(true);\n (authService.signJwtToken as jest.Mock).mockReturnValue(\"jwt-token-123\");\n\n // Execute\n await controllerWithMiddleware.login(req, res, next);\n\n // Verify\n expect(req.responseData).toEqual({ accessToken: \"jwt-token-123\" });\n expect(req.responseStatus).toBe(200);\n expect(next).toHaveBeenCalled();\n expect(res.status).not.toHaveBeenCalled();\n });\n });\n\n describe(\"signup\", () => {\n it(\"should create a new user and return 201\", async () => {\n // Setup\n req.body = {\n username: \"newuser\",\n email: \"newuser@example.com\",\n password: \"Password123\",\n };\n\n const createdUser = {\n id: \"new-user-id\",\n username: \"newuser\",\n email: \"newuser@example.com\",\n password: \"hashedPassword\",\n active: true,\n };\n\n userService.createOne.mockResolvedValueOnce({ ...createdUser });\n\n // Execute\n await authController.signup(req, res, next);\n\n // Verify\n expect(userService.createOne).toHaveBeenCalledWith({ ...req.body }, \"{}\");\n expect(res.status).toHaveBeenCalledWith(201);\n\n // Check that excluded fields are removed\n const responseUser = res.json.mock.calls[0][0].data;\n Object.keys(defaultExcludedUserFields).forEach((field) => {\n expect(responseUser[field]).toBeUndefined();\n });\n });\n\n it(\"should call next middleware when afterSignup is provided\", async () => {\n // Setup\n const controllerWithMiddleware = await authControllerFactory({\n afterSignup: true,\n });\n\n req.body = {\n username: \"newuser\",\n email: \"newuser@example.com\",\n password: \"Password123\",\n };\n\n const createdUser = {\n id: \"new-user-id\",\n username: \"newuser\",\n email: \"newuser@example.com\",\n password: \"hashedPassword\",\n };\n\n userService.createOne.mockResolvedValueOnce({ ...createdUser });\n\n // Execute\n await controllerWithMiddleware.signup(req, res, next);\n\n // Verify\n expect(req.responseData).toEqual({ data: createdUser });\n expect(req.responseStatus).toBe(201);\n expect(next).toHaveBeenCalled();\n expect(res.status).not.toHaveBeenCalled();\n });\n });\n\n describe(\"updatePassword\", () => {\n it(\"should return 400 if currentPassword or newPassword is missing\", async () => {\n // Setup - missing newPassword\n req.body = { currentPassword: \"CurrentPassword123\" };\n\n // Execute\n await authController.updatePassword(req, res, next);\n\n // Verify\n expect(next).toHaveBeenCalledWith(\n expect.objectContaining({\n statusCode: 400,\n message: expect.stringContaining(\n \"currentPassword and newPassword are required\"\n ),\n })\n );\n });\n\n it(\"should return 404 if user is not found or inactive\", async () => {\n // Setup\n req.user = null;\n req.body = {\n currentPassword: \"CurrentPassword123\",\n newPassword: \"NewPassword123\",\n };\n\n // Execute\n await authController.updatePassword(req, res, next);\n\n // Verify\n expect(next).toHaveBeenCalledWith(\n expect.objectContaining({\n statusCode: 404,\n message: expect.stringContaining(\"User not found\"),\n })\n );\n });\n\n it(\"should return 400 if current password is incorrect\", async () => {\n // Setup\n req.user = {\n id: \"user-id-123\",\n username: \"testuser\",\n password: \"hashedPassword\",\n isVerified: true,\n };\n\n req.body = {\n currentPassword: \"WrongPassword123\",\n newPassword: \"NewPassword123\",\n };\n\n (authService.isCorrectPassword as jest.Mock).mockResolvedValueOnce(false);\n\n // Execute\n await authController.updatePassword(req, res, next);\n\n // Verify\n expect(next).toHaveBeenCalledWith(\n expect.objectContaining({\n statusCode: 400,\n message: expect.stringContaining(\"Current password is incorrect\"),\n })\n );\n });\n\n it(\"should return 400 if new password is not strong enough\", async () => {\n // Setup\n req.user = {\n id: \"user-id-123\",\n username: \"testuser\",\n password: \"hashedPassword\",\n isVerified: true,\n };\n\n req.body = {\n currentPassword: \"CurrentPassword123\",\n newPassword: \"weakpassword\",\n };\n\n (authService.isCorrectPassword as jest.Mock).mockResolvedValueOnce(true);\n (authService.isPasswordStrong as jest.Mock).mockReturnValue(false);\n\n // Execute\n await authController.updatePassword(req, res, next);\n\n // Verify\n expect(next).toHaveBeenCalledWith(\n expect.objectContaining({\n statusCode: 400,\n message: expect.stringContaining(\"Password must contain\"),\n })\n );\n });\n\n it(\"should update password and return 200 on success\", async () => {\n // Setup\n req.user = {\n id: \"user-id-123\",\n username: \"testuser\",\n password: \"hashedPassword\",\n isVerified: true,\n };\n\n req.body = {\n currentPassword: \"CurrentPassword123\",\n newPassword: \"NewPassword123\",\n };\n\n (authService.isCorrectPassword as jest.Mock).mockResolvedValueOnce(true);\n (authService.isPasswordStrong as jest.Mock).mockReturnValue(true);\n (authService.hashPassword as jest.Mock).mockResolvedValueOnce(\n \"newHashedPassword\"\n );\n\n // Execute\n await authController.updatePassword(req, res, next);\n\n // Verify\n expect(mockPrisma.user.update).toHaveBeenCalledWith({\n where: { id: \"user-id-123\" },\n data: {\n password: \"newHashedPassword\",\n passwordChangedAt: expect.any(Date),\n },\n });\n\n expect(res.status).toHaveBeenCalledWith(200);\n expect(res.json).toHaveBeenCalledWith({\n status: \"success\",\n message: \"Password updated successfully!\",\n });\n });\n\n it(\"should call next middleware when afterUpdatePassword is provided\", async () => {\n // Setup\n const controllerWithMiddleware = await authControllerFactory({\n afterUpdatePassword: true,\n });\n\n req.user = {\n id: \"user-id-123\",\n username: \"testuser\",\n password: \"hashedPassword\",\n isVerified: true,\n };\n\n req.body = {\n currentPassword: \"CurrentPassword123\",\n newPassword: \"NewPassword123\",\n };\n\n (authService.isCorrectPassword as jest.Mock).mockResolvedValueOnce(true);\n (authService.isPasswordStrong as jest.Mock).mockReturnValue(true);\n (authService.hashPassword as jest.Mock).mockResolvedValueOnce(\n \"newHashedPassword123\"\n );\n\n // Execute\n await controllerWithMiddleware.updatePassword(req, res, next);\n\n // Verify\n expect(req.responseData).toEqual({\n status: \"success\",\n message: \"Password updated successfully!\",\n });\n expect(req.responseStatus).toBe(200);\n expect(req.additionalData).toEqual({\n user: req.user,\n });\n expect(next).toHaveBeenCalled();\n expect(res.status).not.toHaveBeenCalled();\n });\n });\n});\n"]}
|