From 2abc2e32e236dc4d4cf57216198c1dfe9877d22f Mon Sep 17 00:00:00 2001 From: Victor-Palha Date: Mon, 28 Oct 2024 10:04:33 -0300 Subject: [PATCH 1/3] feature: add controller and test to random quote --- backend/controllers/quote.api.test.ts | 18 ++++++++++++++++++ backend/controllers/quote.api.ts | 18 ++++++++++++++++++ backend/package-lock.json | 15 +++++++++++---- backend/package.json | 5 +++-- backend/routes/api.ts | 1 + frontend/src/pages/Documentation.tsx | 5 +++++ 6 files changed, 56 insertions(+), 6 deletions(-) diff --git a/backend/controllers/quote.api.test.ts b/backend/controllers/quote.api.test.ts index 4e2aff8..b62a8c2 100644 --- a/backend/controllers/quote.api.test.ts +++ b/backend/controllers/quote.api.test.ts @@ -13,6 +13,7 @@ const router = Router(); router.route('/quote').get(quoteController.getQuotes); router.route('/quote/:id').get(quoteController.getQuote); +router.route('/quotes/random').get(quoteController.getRandomQuote); app.use(express.json()); app.use('/v2', router); @@ -75,4 +76,21 @@ describe('quote controller', () => { expect(response.body.success).toEqual(false); expect(response.body.message).toEqual('Something went wrong.'); }); + + it("/quotes/random should return a random quote", async () => { + const fakeQuote = { + _id: '5cd96e05de30eff6ebccedfc', + dialog: 'Tomatoes, sausages, nice crispy bacon', + movie: '5cd95395de30eff6ebccde5c', + character: '5cd99d4bde30eff6ebccfc7c', + id: '5cd96e05de30eff6ebccedfc' + }; + + mockingoose(QuoteModel).toReturn(fakeQuote, 'findOne'); + + const response = await request(app).get('/v2/quotes/random'); + + expect(response.statusCode).toEqual(HttpCode.OK); + expect(response.body).toEqual(fakeQuote); + }); }); diff --git a/backend/controllers/quote.api.ts b/backend/controllers/quote.api.ts index 88c0f4f..a635d9b 100644 --- a/backend/controllers/quote.api.ts +++ b/backend/controllers/quote.api.ts @@ -40,5 +40,23 @@ export const quoteController = { } catch (err) { return next(err); } + }, + + getRandomQuote: async (_req: Request, res: Response, next: NextFunction) => { + try { + const count = await QuoteModel.estimatedDocumentCount(); + + if (count === 0) { + return res.status(404).json({ message: "No quotes found" }); + } + + const randomIndex = Math.floor(Math.random() * count); + console.log(randomIndex); + const quote = await QuoteModel.findOne().skip(randomIndex); + + return res.json(quote); + } catch (error) { + return next(error); + } } }; diff --git a/backend/package-lock.json b/backend/package-lock.json index 62574a8..b0a3ba7 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -37,8 +37,9 @@ "@types/express": "^4.17.21", "@types/express-rate-limit": "^6.0.0", "@types/helmet": "^4.0.0", - "@types/jest": "^29.5.11", + "@types/jest": "^29.5.14", "@types/jsonwebtoken": "^9.0.5", + "@types/mocha": "^10.0.9", "@types/mongoose": "^5.11.97", "@types/mongoose-paginate": "^5.0.16", "@types/nanoid": "^3.0.0", @@ -2727,9 +2728,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.11", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", - "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -2782,6 +2783,12 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, + "node_modules/@types/mocha": { + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.9.tgz", + "integrity": "sha512-sicdRoWtYevwxjOHNMPTl3vSfJM6oyW8o1wXeI7uww6b6xHg8eBznQDNSGBCDJmsE8UMxP05JgZRtsKbTqt//Q==", + "dev": true + }, "node_modules/@types/mongodb": { "version": "3.6.20", "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", diff --git a/backend/package.json b/backend/package.json index 75389f0..4ac5a4f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -46,8 +46,9 @@ "@types/express": "^4.17.21", "@types/express-rate-limit": "^6.0.0", "@types/helmet": "^4.0.0", - "@types/jest": "^29.5.11", + "@types/jest": "^29.5.14", "@types/jsonwebtoken": "^9.0.5", + "@types/mocha": "^10.0.9", "@types/mongoose": "^5.11.97", "@types/mongoose-paginate": "^5.0.16", "@types/nanoid": "^3.0.0", @@ -68,4 +69,4 @@ "ts-node": "^10.9.1", "typescript": "^5.3.2" } -} \ No newline at end of file +} diff --git a/backend/routes/api.ts b/backend/routes/api.ts index b4b7cfb..a592c62 100644 --- a/backend/routes/api.ts +++ b/backend/routes/api.ts @@ -28,6 +28,7 @@ router.route('/character/:id/quote').get([passportHelpers.authenticate, characte router.route('/quote').get([passportHelpers.authenticate, quoteController.getQuotes]); router.route('/quote/:id').get([passportHelpers.authenticate, quoteController.getQuote]); +router.route('/quotes/random').get([passportHelpers.authenticate, quoteController.getRandomQuote]); router.route('*').get(async (req, res) => { return res.status(HttpCode.NOT_FOUND).send(notFoundResponse); }); diff --git a/frontend/src/pages/Documentation.tsx b/frontend/src/pages/Documentation.tsx index c0e0200..bd41594 100644 --- a/frontend/src/pages/Documentation.tsx +++ b/frontend/src/pages/Documentation.tsx @@ -230,6 +230,11 @@ const Documentation: React.FC = () => { Request one specific movie quote yes + + /quotes/random/ + Request one random movie quote + yes + /chapter From 6fc6c76bb863eb39b444e0870c056d6bfb7917dd Mon Sep 17 00:00:00 2001 From: Victor-Palha Date: Mon, 28 Oct 2024 18:56:48 -0300 Subject: [PATCH 2/3] fix: update test to validate random quote selection --- backend/controllers/quote.api.test.ts | 62 ++++++++++++++++++++------- backend/controllers/quote.api.ts | 7 +-- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/backend/controllers/quote.api.test.ts b/backend/controllers/quote.api.test.ts index b62a8c2..1344834 100644 --- a/backend/controllers/quote.api.test.ts +++ b/backend/controllers/quote.api.test.ts @@ -78,19 +78,51 @@ describe('quote controller', () => { }); it("/quotes/random should return a random quote", async () => { - const fakeQuote = { - _id: '5cd96e05de30eff6ebccedfc', - dialog: 'Tomatoes, sausages, nice crispy bacon', - movie: '5cd95395de30eff6ebccde5c', - character: '5cd99d4bde30eff6ebccfc7c', - id: '5cd96e05de30eff6ebccedfc' - }; - - mockingoose(QuoteModel).toReturn(fakeQuote, 'findOne'); - - const response = await request(app).get('/v2/quotes/random'); - - expect(response.statusCode).toEqual(HttpCode.OK); - expect(response.body).toEqual(fakeQuote); - }); + const fakeQuotes = [ + { + _id: '5cd96e05de30eff6ebccedfc', + dialog: 'Tomatoes, sausages, nice crispy bacon', + movie: '5cd95395de30eff6ebccde5c', + character: '5cd99d4bde30eff6ebccfc7c', + id: '5cd96e05de30eff6ebccedfc' + }, + { + _id: '5cd96e05de30eff6ebcce99c', + dialog: 'Sam, no!', + movie: '5cd95395de30eff6ebccde5d', + character: '5cd99d4bde30eff6ebccfc15', + id: '5cd96e05de30eff6ebcce99c' + }, + { + _id: "5cd96e05de30eff6ebcce89a", + dialog: "DEATH!", + movie: "5cd95395de30eff6ebccde5d", + character: "5cdbe49b7ed9587226e794a0", + id: "5cd96e05de30eff6ebcce89a" + }, + { + _id: "5cd96e05de30eff6ebcce8cd", + dialog: "You'll see. Oh yes, you will see.", + movie: "5cd95395de30eff6ebccde5d", + character: "5cd99d4bde30eff6ebccfe9e", + id: "5cd96e05de30eff6ebcce8cd" + }, + { + _id: "5cd96e05de30eff6ebcced9d", + dialog: "What business does an Elf, a Man and a Dwarf have in the Riddermark? Speak quickly!", + movie: "5cd95395de30eff6ebccde5b", + character: "5cdbdecb6dc0baeae48cfa5a", + id: "5cd96e05de30eff6ebcced9d" + } + ]; + + mockingoose(QuoteModel).toReturn(fakeQuotes.length, 'estimatedDocumentCount'); + mockingoose(QuoteModel).toReturn(fakeQuotes, 'find'); + + const response = await request(app).get('/v2/quotes/random'); + + expect(response.statusCode).toEqual(HttpCode.OK); + expect(fakeQuotes).toContainEqual(response.body); + }); + }); diff --git a/backend/controllers/quote.api.ts b/backend/controllers/quote.api.ts index a635d9b..62a5e8d 100644 --- a/backend/controllers/quote.api.ts +++ b/backend/controllers/quote.api.ts @@ -51,9 +51,10 @@ export const quoteController = { } const randomIndex = Math.floor(Math.random() * count); - console.log(randomIndex); - const quote = await QuoteModel.findOne().skip(randomIndex); - + + const quotes = await QuoteModel.find(); + const quote = quotes[randomIndex]; + return res.json(quote); } catch (error) { return next(error); From bf9a9d4e4b81ae271df350f7156fdb68d4761cc7 Mon Sep 17 00:00:00 2001 From: Victor-Palha Date: Tue, 29 Oct 2024 08:16:12 -0300 Subject: [PATCH 3/3] fix: replace hardcoded HTTP status with HttpCode enum for consistency --- backend/controllers/quote.api.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/controllers/quote.api.ts b/backend/controllers/quote.api.ts index 62a5e8d..b327fd0 100644 --- a/backend/controllers/quote.api.ts +++ b/backend/controllers/quote.api.ts @@ -2,6 +2,7 @@ import { Request, Response, NextFunction } from 'express'; import { getOptions } from './../helpers/config'; import { QuoteModel } from '../models/quote.model'; +import { HttpCode } from '../helpers/constants'; export const quoteController = { getQuotes: async (req: Request, res: Response, next: NextFunction) => { @@ -47,7 +48,7 @@ export const quoteController = { const count = await QuoteModel.estimatedDocumentCount(); if (count === 0) { - return res.status(404).json({ message: "No quotes found" }); + return res.status(HttpCode.NOT_FOUND).json({ message: "No quotes found" }); } const randomIndex = Math.floor(Math.random() * count);