From f74a0f26cc4ec006e9ac4d55ba4ed7e497f9efbd Mon Sep 17 00:00:00 2001 From: Alie Date: Sat, 6 Jan 2024 12:24:30 +0100 Subject: [PATCH 1/3] made the endpoint, works with manual tests --- src/app.ts | 1 + src/controllers/ImageController.ts | 29 +++++++++++++++++++++++++++++ src/services/ImageService.ts | 4 ++++ 3 files changed, 34 insertions(+) diff --git a/src/app.ts b/src/app.ts index 8ae7edd..9c985eb 100644 --- a/src/app.ts +++ b/src/app.ts @@ -15,6 +15,7 @@ app.get("/", (_, res) => { app.get("/images", imageController.getAllImages); app.get("/images/:id", imageController.getImageById); +app.put("/images/:id", imageController.editImage) app.post("/images", authControler.authorize, imageController.addImage); app.post("/login", authControler.login); diff --git a/src/controllers/ImageController.ts b/src/controllers/ImageController.ts index 4b2301e..ac1c207 100644 --- a/src/controllers/ImageController.ts +++ b/src/controllers/ImageController.ts @@ -1,8 +1,37 @@ import { Request, Response } from "express"; import imageService from "../services/ImageService"; import mongoose, { mongo } from "mongoose"; +import { Image } from "../models/ImageModel"; class ImageController { + async editImage(req: Request, res: Response) { + try { + const change: Image = req.body; + const result = await imageService.replaceOne(req.params.id, change); + if (result.matchedCount > 0) { + res.status(204).json(); + } else { + res.status(404).json({ error: "Image not found" }); + } + } catch (error: any) { + if (error instanceof mongoose.Error.CastError) { + res.status(400).json({ error: "Invalid Id" }); + } else if ( + error instanceof mongo.MongoServerError && + error.code === 11000 + ) { + // Should return 409 Conflict for existing urls + res.status(409).json({ + error: `the image with URL ${error.keyValue.url} already exists`, + }); + } else if (error instanceof mongoose.Error.ValidationError) { + // Should return 400 Bad request for invalid requests + res.status(400).json({ error: error.message }); + } else { + res.status(500).json({ error: "Internal Server Error" }); + } + } + } async getImageById(req: Request, res: Response) { try { const image = await imageService.findById(req.params.id); diff --git a/src/services/ImageService.ts b/src/services/ImageService.ts index 8289ed5..c4b6f7a 100644 --- a/src/services/ImageService.ts +++ b/src/services/ImageService.ts @@ -1,6 +1,10 @@ import imageModel, { Image } from "../models/ImageModel"; class ImageService { + async replaceOne(id: string, newimage: Image) { + const image = await imageModel.updateOne({ _id: id }, newimage); + return image; + } async findById(id: string) { const image = await imageModel.findOne({ _id: id }); return image; From ebf2ec17d134707b35891b47dec4926b9cfa556b Mon Sep 17 00:00:00 2001 From: Alie Date: Sat, 6 Jan 2024 12:57:22 +0100 Subject: [PATCH 2/3] added tests and auth to PUT images --- src/app.ts | 3 +- src/controllers/ImageController.ts | 6 +- src/index.ts | 2 +- src/services/ImageService.ts | 4 +- tests/app.test.ts | 89 +++++++++++++++++++++++++++++- 5 files changed, 96 insertions(+), 8 deletions(-) diff --git a/src/app.ts b/src/app.ts index 9c985eb..e76640e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -15,7 +15,7 @@ app.get("/", (_, res) => { app.get("/images", imageController.getAllImages); app.get("/images/:id", imageController.getImageById); -app.put("/images/:id", imageController.editImage) +app.put("/images/:id", authControler.authorize, imageController.editImage); app.post("/images", authControler.authorize, imageController.addImage); app.post("/login", authControler.login); @@ -31,6 +31,7 @@ export const startApp = async () => { user: mongo_user, pass: mongo_pass, }); + mongoose.set("runValidators", true); app.listen(port, () => console.log(`Express server listening on port ${port}`) ); diff --git a/src/controllers/ImageController.ts b/src/controllers/ImageController.ts index ac1c207..47cc0ae 100644 --- a/src/controllers/ImageController.ts +++ b/src/controllers/ImageController.ts @@ -16,6 +16,9 @@ class ImageController { } catch (error: any) { if (error instanceof mongoose.Error.CastError) { res.status(400).json({ error: "Invalid Id" }); + } else if (error instanceof mongoose.Error.ValidationError) { + // Should return 400 Bad request for invalid requests + res.status(400).json({ error: error.message }); } else if ( error instanceof mongo.MongoServerError && error.code === 11000 @@ -24,9 +27,6 @@ class ImageController { res.status(409).json({ error: `the image with URL ${error.keyValue.url} already exists`, }); - } else if (error instanceof mongoose.Error.ValidationError) { - // Should return 400 Bad request for invalid requests - res.status(400).json({ error: error.message }); } else { res.status(500).json({ error: "Internal Server Error" }); } diff --git a/src/index.ts b/src/index.ts index 0d016b7..b431ebd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,7 @@ await startApp(); // This try carch is to prevent hot reload from making the process die due to coliding entries try { // Not insert test data into production - if (process.env.NODE_ENV != "production"){ + if (process.env.NODE_ENV != "production") { await populateDatabase(); } } catch {} diff --git a/src/services/ImageService.ts b/src/services/ImageService.ts index c4b6f7a..257ef77 100644 --- a/src/services/ImageService.ts +++ b/src/services/ImageService.ts @@ -2,8 +2,8 @@ import imageModel, { Image } from "../models/ImageModel"; class ImageService { async replaceOne(id: string, newimage: Image) { - const image = await imageModel.updateOne({ _id: id }, newimage); - return image; + const result = await imageModel.updateOne({ _id: id }, newimage); + return result; } async findById(id: string) { const image = await imageModel.findOne({ _id: id }); diff --git a/tests/app.test.ts b/tests/app.test.ts index 91987dd..befa85a 100644 --- a/tests/app.test.ts +++ b/tests/app.test.ts @@ -3,6 +3,7 @@ import request, { Response } from "supertest"; import app, { startApp } from "../src/app"; import imageService from "../src/services/ImageService"; import populateDatabase from "./populateDatabase"; +import { Image } from "../src/models/ImageModel"; const imageServiceOriginal = imageService; @@ -178,7 +179,7 @@ describe("POST /images works properly", () => { }); }); -describe("/images/:id works properly", () => { +describe("GET /images/ works properly", () => { it("should return 200 for existing ids", async () => { const list = await request(app).get("/images"); const id = list.body.images[0]._id; @@ -200,3 +201,89 @@ describe("/images/:id works properly", () => { expect(res.status).toBe(400); }); }); + +describe("PUT /images/ works properly", () => { + let id: string; + let list: Image[]; + beforeAll(async () => { + const res = await request(app).get("/images"); + list = res.body.images; + id = list[0]._id; + }); + + it("should return 204 for valid modifications", async () => { + const res = await request(app) + .put(`/images/${id}`) + .set("authorization", `Bearer ${token}`) + .send({ status: "available" }); + expect(res.status).toBe(204); + }); + + it("should return 404 for non-existing ids", async () => { + const id = "000000000000000000000000"; // this was the least posible to exist ID + if (!(id in list)) { + const res = await request(app) + .put(`/images/${id}`) + .set("authorization", `Bearer ${token}`) + .send({ status: "available" }); + expect(res.status).toBe(404); + } + }); + + it("should return 400 for malformed ids", async () => { + const res = await request(app) + .put("/images/98439384") + .set("authorization", `Bearer ${token}`) + .send({ status: "available" }); + expect(res.status).toBe(400); + expect(res.body).toEqual({ error: "Invalid Id" }); + }); + + it("should return 400 for malformed requests", async () => { + const res = await request(app) + .put(`/images/${id}`) + .set("authorization", `Bearer ${token}`) + .send({ status: "wrong" }); + expect(res.status).toBe(400); + }); + + it("should return 409 if we try to modify the url into an existing one", async () => { + const res = await request(app) + .put(`/images/${id}`) + .set("authorization", `Bearer ${token}`) + .send({ url: "https://test.url.com/2" }); + expect(res.status).toBe(409); + }); + + it("should return 500 for an error on the service", async () => { + mock.module("../src/services/ImageService", () => ({ + default: { + replaceOne: () => { + throw new Error("This is an expected testing error"); + }, + }, + })); + + const res = await request(app) + .put(`/images/${id}`) + .set("authorization", `Bearer ${token}`) + .send({ status: "available" }); + + expect(res.status).toBe(500); + }); + + it("should return 401 for unauthenticated requests", async () => { + const res = await request(app) + .put(`/images/${id}`) + .send({ status: "available" }); + expect(res.status).toBe(401); + }); + + it("should return 403 for invalid tokens", async () => { + const res = await request(app) + .put(`/images/${id}`) + .set("authorization", `Bearer token`) + .send({ status: "available" }); + expect(res.status).toBe(403); + }); +}); From fb2d9479133a86ab65893e2a12f9f8e35be7dd56 Mon Sep 17 00:00:00 2001 From: Alie Date: Sat, 6 Jan 2024 13:10:45 +0100 Subject: [PATCH 3/3] added a functional test and optimized a test --- tests/app.test.ts | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/tests/app.test.ts b/tests/app.test.ts index befa85a..d62ee1c 100644 --- a/tests/app.test.ts +++ b/tests/app.test.ts @@ -166,7 +166,6 @@ describe("POST /images works properly", () => { }); it("should return 400 for malformed requests", async () => { - mock.restore(); const res = await request(app) .post("/images") .set("authorization", `Bearer ${token}`) @@ -180,17 +179,22 @@ describe("POST /images works properly", () => { }); describe("GET /images/ works properly", () => { + let id: string; + let list: Image[]; + beforeAll(async () => { + const res = await request(app).get("/images"); + list = res.body.images; + id = list[0]._id; + }); + it("should return 200 for existing ids", async () => { - const list = await request(app).get("/images"); - const id = list.body.images[0]._id; const res = await request(app).get(`/images/${id}`); expect(res.status).toBe(200); }); it("should return 404 for non-existing ids", async () => { - const list = await request(app).get("/images"); const id = "000000000000000000000000"; // this was the least posible to exist ID - if (!(id in list.body.images)) { + if (!(id in list)) { const res = await request(app).get(`/images/${id}`); expect(res.status).toBe(404); } @@ -286,4 +290,23 @@ describe("PUT /images/ works properly", () => { .send({ status: "available" }); expect(res.status).toBe(403); }); + + it("should have its changes be reflected onto the DB", async () => { + const image = await request(app) + .post("/images") + .set("authorization", `Bearer ${token}`) + .send({ + url: "https://test.url.com/5", + status: "available", + tags: ["2girls", "touhou"], + }); + const id = image.body.image._id; + await request(app) + .put(`/images/${id}`) + .set("authorization", `Bearer ${token}`) + .send({ status: "consumed" }); + + const res = await request(app).get(`/images/${id}`); + expect(res.body.image).toHaveProperty("status", "consumed"); + }) });