import { afterEach, beforeAll, describe, expect, it, mock } from "bun:test"; 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"; import { faker } from "@faker-js/faker"; const imageServiceOriginal = imageService; let token: string; beforeAll(async () => { await startApp(); await populateDatabase(); const tok = await request(app) .post("/login") .send({ app: "tester", secret: "test" }); token = tok.body.token; }); afterEach(() => { mock.restore(); mock.module("../src/services/ImageService", () => ({ default: imageServiceOriginal, })); }); describe("/login works as intended", async () => { let correctRespose: Response; beforeAll(async () => { correctRespose = await request(app) .post("/login") .send({ app: "tester", secret: "test" }); }); it("should return 200 for correct login", async () => { expect(correctRespose.status).toBe(200); }); it("should contain a token", () => { expect(correctRespose.body).toHaveProperty("token"); }); it("should return 403 for invalid credentials", async () => { const res = await request(app).post("/login").send({}); expect(res.status).toBe(403); }); }); describe("GET / shows all of the endpoints", async () => { const res = await request(app).get("/"); it("should have the endpoints in the `endpoints` property, as an array", async () => { expect(res.body).toHaveProperty("endpoints"); expect(Array.isArray(res.body.endpoints)).toBeTrue(); }); }); describe("GET /images works properly", async () => { const res = await request(app).get("/images"); it("should be an array", () => { expect(Array.isArray(res.body.images)).toBeTrue(); }); it("should return a 200", async () => { expect(res.statusCode).toBe(200); }); it("should return 500 for an error on the service", async () => { mock.module("../src/services/ImageService", () => ({ default: { findAll: () => { throw new Error("This is an expected testing error"); }, }, })); const res = await request(app).get("/images"); expect(res.status).toBe(500); }); it("should return 400 for an invalid status param value", async () => { const res = await request(app).get("/images?status=foo"); expect(res.statusCode).toBe(400); }); it("should return 200 for a request with valid status param", async () => { const status = faker.helpers.arrayElement([ "consumed", "available", "unavailable", ]); const res = await request(app).get(`/images?status=${status}`); expect(res.statusCode).toBe(200); }); it("should only have the requested status in the images", async () => { const status = faker.helpers.arrayElement([ "consumed", "available", "unavailable", ]); const res = await request(app).get(`/images?status=${status}`); expect( res.body.images .map((image: Image) => image.status) .every((imageStatus: string) => imageStatus === status) ).toBeTrue(); }); it("should return 400 for a floating point value in limit value", async () => { const res = await request(app).get("/images?limit=2.4"); expect(res.statusCode).toBe(400); }); it("should return 400 for a negative value in limit value", async () => { const res = await request(app).get("/images?limit=-1"); expect(res.statusCode).toBe(400); }); it("should return 400 for a NaN limit value", async () => { const res = await request(app).get("/images?limit=foo"); expect(res.statusCode).toBe(400); }); it("should return 200 for a request with valid limit param", async () => { const limit = faker.number.int({ min: 5, max: 50 }); const res = await request(app).get(`/images?limit=${limit}`); expect(res.statusCode).toBe(200); }); it("should return 200 for a request with valid limit param", async () => { const limit = faker.number.int({ min: 5, max: 50 }); const res = await request(app).get(`/images?limit=${limit}`); expect(res.body.images.length).toBeLessThanOrEqual(limit); }); it("should return 200 for a request with both valid params", async () => { const res = await request(app).get("/images?limit=3&status=available"); expect(res.statusCode).toBe(200); }); it("should return the same ids using limit=0 and using no limit parameter", async () => { const resLimit0 = await request(app).get("/images?limit=0"); const resNoLimit = await request(app).get("/images"); const ids1 = resNoLimit.body.images.map((image: Image) => image._id); const ids2 = resLimit0.body.images.map((image: Image) => image._id); expect(ids1.length).toBe(ids2.length); ids1.forEach((id: string) => expect(ids2).toContain(id)); }); }); describe("POST /images works properly", () => { it("should return 401 for unauthenticated requests", async () => { const res = await request(app) .post("/images") .send({ url: "https://test.url.com/0", status: "available", tags: ["2girls", "touhou"], }); expect(res.status).toBe(401); }); it("should return 403 for invalid tokens", async () => { const res = await request(app) .post("/images") .set("authorization", `Bearer token`) .send({ url: "https://test.url.com/0", status: "available", tags: ["2girls", "touhou"], }); expect(res.status).toBe(403); }); it("should return 201 for new image", async () => { const res = await request(app) .post("/images") .set("authorization", `Bearer ${token}`) .send({ url: "https://test.url.com/1", status: "available", tags: ["2girls", "touhou"], }); expect(res.status).toBe(201); }); it("should return 409 for a repeated images", async () => { await request(app) .post("/images") .set("authorization", `Bearer ${token}`) .send({ url: "https://test.url.com/2", status: "available", tags: ["2girls", "touhou"], }); const res = await request(app) .post("/images") .set("authorization", `Bearer ${token}`) .send({ url: "https://test.url.com/2", status: "available", tags: ["2girls", "touhou"], }); expect(res.status).toBe(409); }); it("should return 500 for an error on the service", async () => { mock.module("../src/services/ImageService", () => ({ default: { add: () => { throw new Error("This is an expected testing error"); }, }, })); const res = await request(app) .post("/images") .set("authorization", `Bearer ${token}`) .send({ url: "https://test.url.com/3", status: "available", tags: ["2girls", "touhou"], }); expect(res.status).toBe(500); }); it("should return 400 for malformed requests", async () => { const res = await request(app) .post("/images") .set("authorization", `Bearer ${token}`) .send({ url: "https://test.url.com/4", status: "wrong", tags: ["2girls", "touhou"], }); expect(res.status).toBe(400); }); }); 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 res = await request(app).get(`/images/${id}`); expect(res.status).toBe(200); }); 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).get(`/images/${id}`); expect(res.status).toBe(404); } }); it("should return 400 for malformed ids", async () => { const res = await request(app).get("/images/98439384"); 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); }); 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"); }); });