v1.0.0 #28
|
@ -1,6 +1,7 @@
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import imageService from "../services/ImageService";
|
import imageService from "../services/ImageService";
|
||||||
import mongoose, { mongo } from "mongoose";
|
import mongoose, { mongo } from "mongoose";
|
||||||
|
import { Image } from "../models/ImageModel";
|
||||||
|
|
||||||
class ImageController {
|
class ImageController {
|
||||||
async getImageById(req: Request, res: Response) {
|
async getImageById(req: Request, res: Response) {
|
||||||
|
@ -20,14 +21,24 @@ class ImageController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllImages(_: Request, res: Response): Promise<void> {
|
async getAllImages(req: Request, res: Response): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const images = await imageService.findAll();
|
if (req.query.status !== undefined && !["consumed", "unavailable", "available"].includes(req.query.status as string)) {
|
||||||
|
throw TypeError("if present, `status` should have the values `consumed`, `unavailable`, or `available`")
|
||||||
|
}
|
||||||
|
const limit = req.query.limit ? Number(req.query.limit) : undefined;
|
||||||
|
const status = req.query.status ? req.query.status as Image["status"] : undefined;
|
||||||
|
|
||||||
|
const images = await imageService.findAll(limit, status);
|
||||||
res.json({ images });
|
res.json({ images });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error instanceof TypeError) {
|
||||||
|
res.status(400).json({ error });
|
||||||
|
} else {
|
||||||
res.status(500).json({ error: "Internal Server Error" });
|
res.status(500).json({ error: "Internal Server Error" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async addImage(req: Request, res: Response): Promise<void> {
|
async addImage(req: Request, res: Response): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
@ -47,7 +58,7 @@ class ImageController {
|
||||||
res.status(400).json({ error: error.message });
|
res.status(400).json({ error: error.message });
|
||||||
} else {
|
} else {
|
||||||
// Return 500 in other case
|
// Return 500 in other case
|
||||||
res.status(500).json({ error: error });
|
res.status(500).json({ error });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import mongoose, { Document } from "mongoose";
|
import mongoose, { Document } from "mongoose";
|
||||||
|
|
||||||
export interface Image extends Document {
|
export interface Image extends Document {
|
||||||
url: String;
|
url: string;
|
||||||
enum: "consumed" | "unavailable" | "available";
|
status: "consumed" | "unavailable" | "available";
|
||||||
tags?: String[];
|
tags?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImageSchema = new mongoose.Schema(
|
const ImageSchema = new mongoose.Schema(
|
||||||
|
|
|
@ -5,10 +5,23 @@ class ImageService {
|
||||||
const image = await imageModel.findOne({ _id: id });
|
const image = await imageModel.findOne({ _id: id });
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
async findAll(): Promise<Image[]> {
|
|
||||||
const allImages = await imageModel.find();
|
async findAll(limit?: number, status?: Image["status"]): Promise<Image[]> {
|
||||||
|
const typeError = TypeError("if present, `limit` must be a non-negative integer");
|
||||||
|
const filter = status !== undefined ? { status } : {};
|
||||||
|
let query = imageModel.find(filter);
|
||||||
|
if (limit !== undefined) {
|
||||||
|
if (Number.isInteger(limit)) {
|
||||||
|
if (limit > 0) query = query.limit(limit);
|
||||||
|
else if (limit < 0) throw typeError;
|
||||||
|
} else {
|
||||||
|
throw typeError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const allImages = await query;
|
||||||
return allImages;
|
return allImages;
|
||||||
}
|
}
|
||||||
|
|
||||||
async add(image: Image): Promise<Image> {
|
async add(image: Image): Promise<Image> {
|
||||||
const newImage = await imageModel.create(image);
|
const newImage = await imageModel.create(image);
|
||||||
return newImage;
|
return newImage;
|
||||||
|
|
|
@ -3,6 +3,8 @@ import request, { Response } from "supertest";
|
||||||
import app, { startApp } from "../src/app";
|
import app, { startApp } from "../src/app";
|
||||||
import imageService from "../src/services/ImageService";
|
import imageService from "../src/services/ImageService";
|
||||||
import populateDatabase from "./populateDatabase";
|
import populateDatabase from "./populateDatabase";
|
||||||
|
import { Image } from "../src/models/ImageModel";
|
||||||
|
import { faker } from "@faker-js/faker";
|
||||||
|
|
||||||
const imageServiceOriginal = imageService;
|
const imageServiceOriginal = imageService;
|
||||||
|
|
||||||
|
@ -73,7 +75,7 @@ describe("GET /images works properly", async () => {
|
||||||
it("should return 500 for an error on the service", async () => {
|
it("should return 500 for an error on the service", async () => {
|
||||||
mock.module("../src/services/ImageService", () => ({
|
mock.module("../src/services/ImageService", () => ({
|
||||||
default: {
|
default: {
|
||||||
add: () => {
|
findAll: () => {
|
||||||
throw new Error("This is an expected testing error");
|
throw new Error("This is an expected testing error");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -83,6 +85,68 @@ describe("GET /images works properly", async () => {
|
||||||
|
|
||||||
expect(res.status).toBe(500);
|
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", () => {
|
describe("POST /images works properly", () => {
|
||||||
|
@ -178,7 +242,7 @@ describe("POST /images works properly", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("/images/:id works properly", () => {
|
describe("GET /images/:id works properly", () => {
|
||||||
it("should return 200 for existing ids", async () => {
|
it("should return 200 for existing ids", async () => {
|
||||||
const list = await request(app).get("/images");
|
const list = await request(app).get("/images");
|
||||||
const id = list.body.images[0]._id;
|
const id = list.body.images[0]._id;
|
||||||
|
|
Loading…
Reference in New Issue