Skip to content

Commit e21f1a3

Browse files
authored
Merge pull request #57 from CS3219-AY2425S1/jamie/cs3-22
Update category field to be a String array for Question Service
2 parents c78a3cb + a2ebdc1 commit e21f1a3

File tree

5 files changed

+162
-21
lines changed

5 files changed

+162
-21
lines changed

backend/question/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"scripts": {
77
"build": "rimraf dist && npx tsc",
88
"prestart": "npm run build",
9-
"start": "cp .env dist/.env && npx tsc -w & nodemon dist/server.js",
9+
"start": "npx tsc -w & nodemon dist/server.js",
1010
"test": "cross-env NODE_ENV=TEST && jest src/tests/app.spec.ts"
1111
},
1212
"author": "",

backend/question/sample.env

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
PORT=
2-
MONGO_URL=
2+
MONGODB_URI=
3+
NODE_ENV=

backend/question/src/models/Question.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const questionSchema: Schema = new Schema(
2727
required: true,
2828
},
2929
category: {
30-
type: String,
30+
type: [String],
3131
required: true,
3232
},
3333
complexity: {

backend/question/src/routes/validators.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,19 @@ import { check, body } from "express-validator";
33
export const createQuestionValidators = [
44
check("title").isString().isLength({ min: 1 }),
55
check("description").isString().isLength({ min: 1 }),
6-
check("category").isString().isLength({ min: 1 }),
76
check("complexity").isString().isLength({ min: 1 }),
7+
//custom validation for category
8+
check("category").custom((category) => {
9+
if (!Array.isArray(category) || category.length === 0) {
10+
throw new Error("Category must be a non-empty array");
11+
}
12+
//check if array contains only non-empty strings and trim whitespace
13+
if (!category.every((element) => typeof element === "string" && element.length > 0 && element.trim().length > 0)) {
14+
throw new Error("Category must contain only non-empty strings");
15+
}
16+
return true;
17+
}
18+
),
819
];
920

1021
export const idValidators = [check("id").isInt({ min: 1 })];
@@ -18,7 +29,21 @@ export const updateQuestionValidators = [
1829
}
1930
for (const field of [title, description, category, complexity]) {
2031
if (field) {
21-
check(field).isString().isLength({ min: 1 });
32+
//if field is cateogry
33+
if (field === category) {
34+
if (!Array.isArray(field) || field.length === 0) {
35+
throw new Error("Category must be a non-empty array");
36+
}
37+
//check if array contains only non-empty strings and trim whitespace
38+
if (!field.every((element) => typeof element === "string" && element.length > 0 && element.trim().length > 0)) {
39+
throw new Error("Category must contain only non-empty strings");
40+
}
41+
} else {
42+
//check that it is string
43+
if (typeof field !== "string") {
44+
throw new Error("Field must be a string");
45+
}
46+
}
2247
}
2348
}
2449
return true;

backend/question/src/tests/app.spec.ts

Lines changed: 131 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe("Test Question API", () => {
2525
const newQuestion = {
2626
title: "Sample Question",
2727
description: "This is a sample question",
28-
category: "General",
28+
category: ["General"],
2929
complexity: "Easy",
3030
};
3131

@@ -37,7 +37,7 @@ describe("Test Question API", () => {
3737
"description",
3838
"This is a sample question"
3939
);
40-
expect(res.body.question).toHaveProperty("category", "General");
40+
expect(res.body.question).toHaveProperty("category", ["General"]);
4141
expect(res.body.question).toHaveProperty("complexity", "Easy");
4242
});
4343

@@ -46,7 +46,7 @@ describe("Test Question API", () => {
4646
const newQuestion = {
4747
title: "",
4848
description: "This is a sample question",
49-
category: "General",
49+
category: ["General"],
5050
complexity: "Easy",
5151
};
5252

@@ -61,7 +61,7 @@ describe("Test Question API", () => {
6161
const newQuestion = {
6262
title: "Sample Question",
6363
description: "",
64-
category: "General",
64+
category: ["General"],
6565
complexity: "Easy",
6666
};
6767

@@ -76,13 +76,28 @@ describe("Test Question API", () => {
7676
const newQuestion = {
7777
title: "Sample Question",
7878
description: "This is a sample question",
79-
category: "",
79+
category: [],
8080
complexity: "Easy",
8181
};
8282

8383
const res = await request.post("/api/create").send(newQuestion);
8484
expect(res.statusCode).toBe(400);
85-
expect(res.body.errors[0].msg).toBe("Invalid value");
85+
expect(res.body.errors[0].msg).toBe("Category must be a non-empty array");
86+
expect(res.body.errors[0].path).toBe("category");
87+
});
88+
89+
// Empty category with whitespace only
90+
test("POST /api/create - empty category with whitespace string", async () => {
91+
const newQuestion = {
92+
title: "Sample Question",
93+
description: "This is a sample question",
94+
category: [" "],
95+
complexity: "Easy",
96+
};
97+
98+
const res = await request.post("/api/create").send(newQuestion);
99+
expect(res.statusCode).toBe(400);
100+
expect(res.body.errors[0].msg).toBe("Category must contain only non-empty strings");
86101
expect(res.body.errors[0].path).toBe("category");
87102
});
88103

@@ -91,7 +106,7 @@ describe("Test Question API", () => {
91106
const newQuestion = {
92107
title: "Sample Question",
93108
description: "This is a sample question",
94-
category: "General",
109+
category: ["General"],
95110
complexity: "",
96111
};
97112

@@ -121,7 +136,7 @@ describe("Test Question API", () => {
121136
const newQuestion = {
122137
title: "Sample Question",
123138
description: ["test"],
124-
category: "General",
139+
category: ["General"],
125140
complexity: "Easy",
126141
};
127142

@@ -136,13 +151,58 @@ describe("Test Question API", () => {
136151
const newQuestion = {
137152
title: "Sample Question",
138153
description: "This is a sample question",
139-
category: ["test"],
154+
category: "test",
140155
complexity: "Easy",
141156
};
142157

143158
const res = await request.post("/api/create").send(newQuestion);
144159
expect(res.statusCode).toBe(400);
145-
expect(res.body.errors[0].msg).toBe("Invalid value");
160+
expect(res.body.errors[0].msg).toBe("Category must be a non-empty array");
161+
expect(res.body.errors[0].path).toBe("category");
162+
});
163+
164+
// Invalid category
165+
test("POST /api/create - invalid category", async () => {
166+
const newQuestion = {
167+
title: "Sample Question",
168+
description: "This is a sample question",
169+
category: [3, "test"],
170+
complexity: "Easy",
171+
};
172+
173+
const res = await request.post("/api/create").send(newQuestion);
174+
expect(res.statusCode).toBe(400);
175+
expect(res.body.errors[0].msg).toBe("Category must contain only non-empty strings");
176+
expect(res.body.errors[0].path).toBe("category");
177+
});
178+
179+
// Invalid category
180+
test("POST /api/create - invalid category", async () => {
181+
const newQuestion = {
182+
title: "Sample Question",
183+
description: "This is a sample question",
184+
category: ["test", ""],
185+
complexity: "Easy",
186+
};
187+
188+
const res = await request.post("/api/create").send(newQuestion);
189+
expect(res.statusCode).toBe(400);
190+
expect(res.body.errors[0].msg).toBe("Category must contain only non-empty strings");
191+
expect(res.body.errors[0].path).toBe("category");
192+
});
193+
194+
// Invalid category
195+
test("POST /api/create - invalid category with whitespace", async () => {
196+
const newQuestion = {
197+
title: "Sample Question",
198+
description: "This is a sample question",
199+
category: [" "],
200+
complexity: "Easy",
201+
};
202+
203+
const res = await request.post("/api/create").send(newQuestion);
204+
expect(res.statusCode).toBe(400);
205+
expect(res.body.errors[0].msg).toBe("Category must contain only non-empty strings");
146206
expect(res.body.errors[0].path).toBe("category");
147207
});
148208

@@ -151,7 +211,7 @@ describe("Test Question API", () => {
151211
const newQuestion = {
152212
title: "Sample Question",
153213
description: "This is a sample question",
154-
category: "General",
214+
category: ["General"],
155215
complexity: ["test"],
156216
};
157217

@@ -176,7 +236,7 @@ describe("Test Get All", () => {
176236
"description",
177237
"This is a sample question"
178238
);
179-
expect(sampleQuestion).toHaveProperty("category", "General");
239+
expect(sampleQuestion).toHaveProperty("category", ["General"]);
180240
expect(sampleQuestion).toHaveProperty("complexity", "Easy");
181241
});
182242
});
@@ -191,7 +251,7 @@ describe("Test Get by Id", () => {
191251
expect(res.body).toHaveProperty("questionid", questionId);
192252
expect(res.body).toHaveProperty("title", "Sample Question");
193253
expect(res.body).toHaveProperty("description", "This is a sample question");
194-
expect(res.body).toHaveProperty("category", "General");
254+
expect(res.body).toHaveProperty("category", ["General"]);
195255
expect(res.body).toHaveProperty("complexity", "Easy");
196256
});
197257

@@ -219,7 +279,7 @@ describe("Test Update", () => {
219279
const updateQuestion = {
220280
title: "Update Title",
221281
description: "Update Description",
222-
category: "Update Category",
282+
category: ["Update Category"],
223283
complexity: "Update Complexity",
224284
};
225285
const questionId = 1090;
@@ -235,6 +295,19 @@ describe("Test Update", () => {
235295
});
236296

237297
// Empty update
298+
test("POST - empty title update", async () => {
299+
const updateQuestion = {
300+
title: 3,
301+
};
302+
const questionId = 1090;
303+
const res = await request
304+
.post(`/api/${questionId}/update`)
305+
.send(updateQuestion);
306+
expect(res.statusCode).toBe(400);
307+
expect(res.body.errors[0].msg).toBe("Field must be a string");
308+
});
309+
310+
// Empty field update
238311
test("POST - empty update", async () => {
239312
const updateQuestion = {
240313
title: "",
@@ -250,12 +323,54 @@ describe("Test Update", () => {
250323
expect(res.body.errors[0].msg).toBe("At least one field must be provided");
251324
});
252325

326+
// Update with invalid category
327+
test("POST - empty category", async () => {
328+
const updateQuestion = {
329+
category: []
330+
};
331+
const questionId = 1090;
332+
const res = await request
333+
.post(`/api/${questionId}/update`)
334+
.send(updateQuestion);
335+
expect(res.statusCode).toBe(400);
336+
expect(res.body.errors[0].msg).toBe("Category must be a non-empty array");
337+
});
338+
339+
// Update with invalid category
340+
test("POST - category containing numbers", async () => {
341+
const updateQuestion = {
342+
category: [3],
343+
complexity: ["test"],
344+
};
345+
const questionId = 1090;
346+
const res = await request
347+
.post(`/api/${questionId}/update`)
348+
.send(updateQuestion);
349+
expect(res.statusCode).toBe(400);
350+
expect(res.body.errors[0].msg).toBe("Category must contain only non-empty strings");
351+
});
352+
353+
// Update with invalid category
354+
test("POST - category containing empty string", async () => {
355+
const updateQuestion = {
356+
description: "This is a sample question",
357+
category: ["test", ""],
358+
complexity: ["test"],
359+
};
360+
const questionId = 1090;
361+
const res = await request
362+
.post(`/api/${questionId}/update`)
363+
.send(updateQuestion);
364+
expect(res.statusCode).toBe(400);
365+
expect(res.body.errors[0].msg).toBe("Category must contain only non-empty strings");
366+
});
367+
253368
// Negative id
254369
test("POST - negative id update", async () => {
255370
const updateQuestion = {
256371
title: "Update Title",
257372
description: "Update Description",
258-
category: "Update Category",
373+
category: ["Update Category"],
259374
complexity: "Update Complexity",
260375
};
261376
const questionId = -1;
@@ -271,7 +386,7 @@ describe("Test Update", () => {
271386
const updateQuestion = {
272387
title: "Update Title",
273388
description: "Update Description",
274-
category: "Update Category",
389+
category: ["Update Category"],
275390
complexity: "Update Complexity",
276391
};
277392
const questionId = 999999;

0 commit comments

Comments
 (0)