diff --git a/README.md b/README.md index 96516fae..5523313b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,51 @@ -## Sunglasses.io Server +# Sunglasses.io Server -This project has been created by a student at Project Shift, a software engineering fellowship located in Downtown Durham. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks. +This project is an API for an e-commerce store selling sunglasses. -If you have any questions about this project or the program in general, visit projectshift.io or email hello@projectshift.io. +## Routes + +The following routes are supported by this API + +``` +GET /brands +GET /brands/:id/products +GET /products +POST /login +GET /me/cart +DELETE /me/cart/:productId +POST /me/cart/:productId +``` + +Documentation for each route can be found in the swagger.yaml file + +## Getting Started + +### Prerequisites + +- Node.js (version 14 or above) +- npm + +### Installation + +1. Clone the repository: + + ```bash + git clone https://github.com/acl13/sunglasses-io.git + cd sunglasses-io + ``` + +2. Install dependencies: + + ```bash + npm install + ``` + +### Testing + +```bash + npm test +``` + +This project has been created by a student at Parsity, an online software engineering course. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks. + +If you have any questions about this project or the program in general, visit [parsity.io](https://parsity.io/) or email hello@parsity.io. diff --git a/app/server.js b/app/server.js index 5201d84d..cc79feae 100644 --- a/app/server.js +++ b/app/server.js @@ -1,31 +1,179 @@ -const express = require('express'); -const bodyParser = require('body-parser'); -const jwt = require('jsonwebtoken'); -const swaggerUi = require('swagger-ui-express'); -const YAML = require('yamljs'); -const swaggerDocument = YAML.load('./swagger.yaml'); // Replace './swagger.yaml' with the path to your Swagger file +const express = require("express"); +const bodyParser = require("body-parser"); +const jwt = require("jsonwebtoken"); +const swaggerUi = require("swagger-ui-express"); +const YAML = require("yamljs"); +const swaggerDocument = YAML.load("./swagger.yaml"); const app = express(); app.use(bodyParser.json()); // Importing the data from JSON files -const users = require('../initial-data/users.json'); -const brands = require('../initial-data/brands.json'); -const products = require('../initial-data/products.json'); +const users = require("../initial-data/users.json"); +const brands = require("../initial-data/brands.json"); +const products = require("../initial-data/products.json"); // Error handling app.use((err, req, res, next) => { - console.error(err.stack); - res.status(500).send('Something broke!'); + console.error(err.stack); + res.status(500).send("Something broke!"); }); // Swagger -app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); +app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument)); + +app.get("/brands", (request, response) => { + response.send(brands); +}); + +app.get("/products", (request, response) => { + response.send(products); +}); + +app.get("/brands/:id/products", (request, response) => { + // Get all products that match the brand id + // The sample data provided has a categoryId property that aligns with the brand id numbers - I think it would be better named "brandId" but chose not to change the existing data + const filteredProducts = products.filter((product) => { + return product.categoryId == request.params.id; + }); + + if (filteredProducts.length === 0) { + response.status(404); + response.send({ message: "No products found" }); + } else { + response.send(filteredProducts); + } +}); + +app.post("/login", (request, response) => { + const { username, password } = request.body; + + const user = users.find((user) => { + return user.login.username == username && user.login.password == password; + }); + + if (!username || !password) { + response.status(401); + response.send({ message: "Both a username and password are required" }); + } + + if (user) { + const token = jwt.sign({ user }, "secretKey", { expiresIn: "1hr" }); + response.set("authorization", `Bearer ${token}`); + response.status(200); + response.send(user); + } else { + response.status(401); + response.send({ message: "Incorrect username or password" }); + } +}); + +const getAuthenticatedUser = (request) => { + const authHeader = request.headers["authorization"]; + if (!authHeader) { + return; + } + const token = authHeader.split(" ")[1]; + const decoded = jwt.verify(token, "secretKey"); + + let authenticatedUser = users.find( + (user) => user.login.username == decoded.user.login.username + ); + return authenticatedUser; +}; + +app.get("/me/cart", (request, response) => { + const user = getAuthenticatedUser(request); + if (!user) { + response.status(401); + response.send({ message: "Authentication failed" }); + } else { + response.status(200); + response.send(user.cart); + } +}); + +app.post("/me/cart", (request, response) => { + const user = getAuthenticatedUser(request); + if (!user) { + response.status(401); + response.send({ message: "Authentication failed" }); + } + + const product = products.find((product) => product.id == request.body.id); + + if (!product) { + response.status(404); + response.send({ message: "Product not found" }); + } + + user.cart.push(product); + response.status(200); + response.send(user.cart); +}); + +app.delete("/me/cart/:productId", (request, response) => { + const user = getAuthenticatedUser(request); + if (!user) { + response.status(401); + response.send({ message: "Authentication failed" }); + } + + const product = products.find( + (product) => product.id == request.params.productId + ); + + if (!product) { + response.status(404); + response.send({ message: "Product not found" }); + } + + const productIndex = user.cart.indexOf(product); + user.cart.splice(productIndex, 1); + response.status(200); + response.send(user.cart); +}); + +app.post("/me/cart/:productId", (request, response) => { + const user = getAuthenticatedUser(request); + if (!user) { + response.status(401); + response.send({ message: "Authentication failed" }); + } + + const product = products.find( + (product) => product.id == request.params.productId + ); + + if (!product) { + response.status(404); + response.send({ message: "Product not found" }); + } + + // Something like an "amountInCart" property on each product would be much easier to update, but this is what I could get working with the provided data + const currentAmount = user.cart.filter( + (product) => product.id == request.params.productId + ).length; + const desiredAmount = request.body.amount; + + if (desiredAmount > currentAmount) { + for (let i = currentAmount; i < desiredAmount; i++) { + user.cart.push(product); + } + } else if (desiredAmount < currentAmount) { + for (let i = desiredAmount; i < currentAmount; i++) { + const productIndex = user.cart.indexOf(product); + user.cart.splice(productIndex, 1); + } + } + response.status(200); + response.send(user.cart); +}); // Starting the server const PORT = process.env.PORT || 3000; app.listen(PORT, () => { - console.log(`Server running on port ${PORT}`); + console.log(`Server running on port ${PORT}`); }); module.exports = app; diff --git a/initial-data/users.json b/initial-data/users.json index 9a6231e8..ec54581f 100644 --- a/initial-data/users.json +++ b/initial-data/users.json @@ -1,104 +1,130 @@ [ - { - "gender": "female", - "cart":[], - "name": { - "title": "mrs", - "first": "susanna", - "last": "richards" - }, - "location": { - "street": "2343 herbert road", - "city": "duleek", - "state": "donegal", - "postcode": 38567 - }, - "email": "susanna.richards@example.com", - "login": { - "username": "yellowleopard753", - "password": "jonjon", - "salt": "eNuMvema", - "md5": "a8be2a69c8c91684588f4e1a29442dd7", - "sha1": "f9a60bbf8b550c10712e470d713784c3ba78a68e", - "sha256": "4dca9535634c102fbadbe62dc5b37cd608f9f3ced9aacf42a5669e5a312690a0" - }, - "dob": "1954-10-09 10:47:17", - "registered": "2003-08-03 01:12:24", - "phone": "031-941-6700", - "cell": "081-032-7884", - "picture": { - "large": "https://randomuser.me/api/portraits/women/55.jpg", - "medium": "https://randomuser.me/api/portraits/med/women/55.jpg", - "thumbnail": "https://randomuser.me/api/portraits/thumb/women/55.jpg" - }, - "nat": "IE" + { + "gender": "female", + "cart": [], + "name": { + "title": "mrs", + "first": "susanna", + "last": "richards" }, - { - "gender": "male", - "cart":[], - "name": { - "title": "mr", - "first": "salvador", - "last": "jordan" - }, - "location": { - "street": "9849 valley view ln", - "city": "burkburnett", - "state": "delaware", - "postcode": 78623 - }, - "email": "salvador.jordan@example.com", - "login": { - "username": "lazywolf342", - "password": "tucker", - "salt": "oSngghny", - "md5": "30079fb24f447efc355585fcd4d97494", - "sha1": "dbeb2d0155dad0de0ab9bbe21c062e260a61d741", - "sha256": "4f9416fa89bfd251e07da3ca0aed4d077a011d6ef7d6ed75e1d439c96d75d2b2" - }, - "dob": "1955-07-28 22:32:14", - "registered": "2010-01-10 06:52:31", - "phone": "(944)-261-2164", - "cell": "(888)-556-7285", - "picture": { - "large": "https://randomuser.me/api/portraits/men/4.jpg", - "medium": "https://randomuser.me/api/portraits/med/men/4.jpg", - "thumbnail": "https://randomuser.me/api/portraits/thumb/men/4.jpg" - }, - "nat": "US" + "location": { + "street": "2343 herbert road", + "city": "duleek", + "state": "donegal", + "postcode": 38567 }, - { - "gender": "female", - "cart":[], - "name": { - "title": "mrs", - "first": "natalia", - "last": "ramos" - }, - "location": { - "street": "7934 avenida de salamanca", - "city": "madrid", - "state": "aragón", - "postcode": 43314 - }, - "email": "natalia.ramos@example.com", - "login": { - "username": "greenlion235", - "password": "waters", - "salt": "w10ZFgoO", - "md5": "19f6fb510c58be44b2df1816d88b739d", - "sha1": "18e545aee27156ee6be35596631353a14ee03007", - "sha256": "2b23b25939ece8ba943fe9abcb3074105867c267d122081a2bc6322f935ac809" - }, - "dob": "1947-03-05 15:23:07", - "registered": "2004-07-19 02:44:19", - "phone": "903-556-986", - "cell": "696-867-013", - "picture": { - "large": "https://randomuser.me/api/portraits/women/54.jpg", - "medium": "https://randomuser.me/api/portraits/med/women/54.jpg", - "thumbnail": "https://randomuser.me/api/portraits/thumb/women/54.jpg" - }, - "nat": "ES" - } -] \ No newline at end of file + "email": "susanna.richards@example.com", + "login": { + "username": "yellowleopard753", + "password": "jonjon", + "salt": "eNuMvema", + "md5": "a8be2a69c8c91684588f4e1a29442dd7", + "sha1": "f9a60bbf8b550c10712e470d713784c3ba78a68e", + "sha256": "4dca9535634c102fbadbe62dc5b37cd608f9f3ced9aacf42a5669e5a312690a0" + }, + "dob": "1954-10-09 10:47:17", + "registered": "2003-08-03 01:12:24", + "phone": "031-941-6700", + "cell": "081-032-7884", + "picture": { + "large": "https://randomuser.me/api/portraits/women/55.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/55.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/55.jpg" + }, + "nat": "IE" + }, + { + "gender": "male", + "cart": [ + { + "id": "2", + "categoryId": "1", + "name": "Black Sunglasses", + "description": "The best glasses in the world", + "price": 100, + "imageUrls": [ + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg", + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg", + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg" + ] + } + ], + "name": { + "title": "mr", + "first": "salvador", + "last": "jordan" + }, + "location": { + "street": "9849 valley view ln", + "city": "burkburnett", + "state": "delaware", + "postcode": 78623 + }, + "email": "salvador.jordan@example.com", + "login": { + "username": "lazywolf342", + "password": "tucker", + "salt": "oSngghny", + "md5": "30079fb24f447efc355585fcd4d97494", + "sha1": "dbeb2d0155dad0de0ab9bbe21c062e260a61d741", + "sha256": "4f9416fa89bfd251e07da3ca0aed4d077a011d6ef7d6ed75e1d439c96d75d2b2" + }, + "dob": "1955-07-28 22:32:14", + "registered": "2010-01-10 06:52:31", + "phone": "(944)-261-2164", + "cell": "(888)-556-7285", + "picture": { + "large": "https://randomuser.me/api/portraits/men/4.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/4.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/4.jpg" + }, + "nat": "US" + }, + { + "gender": "female", + "cart": [ + { + "id": "7", + "categoryId": "3", + "name": "QDogs Glasses", + "description": "They bark", + "price": 1500, + "imageUrls": [ + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg", + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg", + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg" + ] + } + ], + "name": { + "title": "mrs", + "first": "natalia", + "last": "ramos" + }, + "location": { + "street": "7934 avenida de salamanca", + "city": "madrid", + "state": "aragón", + "postcode": 43314 + }, + "email": "natalia.ramos@example.com", + "login": { + "username": "greenlion235", + "password": "waters", + "salt": "w10ZFgoO", + "md5": "19f6fb510c58be44b2df1816d88b739d", + "sha1": "18e545aee27156ee6be35596631353a14ee03007", + "sha256": "2b23b25939ece8ba943fe9abcb3074105867c267d122081a2bc6322f935ac809" + }, + "dob": "1947-03-05 15:23:07", + "registered": "2004-07-19 02:44:19", + "phone": "903-556-986", + "cell": "696-867-013", + "picture": { + "large": "https://randomuser.me/api/portraits/women/54.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/54.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/54.jpg" + }, + "nat": "ES" + } +] diff --git a/swagger.yaml b/swagger.yaml index 1c2eb22f..6b7b4397 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -1,11 +1,167 @@ -swagger: '2.0' +swagger: "2.0" info: - version: '1.0.0' - title: 'E-Commerce API' - description: 'API for managing brands, products, and user cart' -host: 'localhost:3000' + version: "1.0.0" + title: "E-Commerce API" + description: "API for managing brands, products, and user cart" +host: "localhost:3000" schemes: - - 'http' -basePath: '/api' + - "http" +basePath: "/api" produces: - - 'application/json' + - "application/json" +paths: + /brands: + get: + summary: "Returns a list of brands" + responses: + "200": + description: "A list of brands" + schema: + type: array + items: + $ref: "#/definitions/Brand" + default: + description: "Unexpected error" + schema: + $ref: "#/definitions/Error" + /brands/{id}/products: + get: + summary: "Returns a list of products for a specific brand" + parameters: + - name: id + in: path + required: true + type: integer + responses: + "200": + description: "List of brand products" + schema: + type: array + items: + $ref: "#/definitions/Product" + default: + description: "Unexpected error" + schema: + $ref: "#/definitions/Error" + /products: + get: + summary: "Returns a list of products" + responses: + "200": + description: "List of products" + schema: + type: array + items: + $ref: "#/definitions/Product" + default: + description: "Unexpected error" + schema: + $ref: "#/definitions/Error" + /login: + post: + summary: "Allows user to login" + parameters: + - in: body + name: login + description: "The login information (username and password) for the user" + schema: + $ref: "#/definitions/Login" + responses: + "200": + description: "Successful login" + "401": + description: "Unauthorized" + default: + description: "Unexpected error" + schema: + $ref: "#/definitions/Error" + /me/cart: + get: + summary: "Gets the cart for an authenticated user" + responses: + "200": + description: "User cart" + default: + description: "Unexpected error" + schema: + $ref: "#/definitions/Error" + post: + summary: "Adds item to user cart" + responses: + "200": + description: "Product successfully added to cart" + default: + description: "Unexpected error" + schema: + $ref: "#/definitions/Error" + /me/cart/{productId}: + post: + summary: "Edits the quantity of a particular item in the cart" + parameters: + - name: productId + in: path + required: true + type: integer + responses: + "200": + description: "Product quantity successfully updated" + default: + description: "Unexpected error" + schema: + $ref: "#/definitions/Error" + delete: + summary: "Deletes a particular item from the cart" + parameters: + - name: productId + in: path + required: true + type: integer + responses: + "200": + description: "Product successfully deleted" + default: + description: "Unexpected error" + schema: + $ref: "#/definitions/Error" +definitions: + Brand: + type: object + properties: + id: + type: string + name: + type: string + Product: + type: object + properties: + id: + type: string + categoryId: + type: string + name: + type: string + description: + type: string + price: + type: integer + imageUrls: + type: array + items: + type: string + Login: + type: object + properties: + username: + type: string + password: + type: string + Error: + type: object + properties: + code: + type: integer + format: int32 + message: + type: string + fields: + type: string diff --git a/test/server.test.js b/test/server.test.js index 7ff14c8f..321df3d5 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -1,14 +1,421 @@ -const chai = require('chai'); -const chaiHttp = require('chai-http'); -const server = require('../app/server'); // Adjust the path as needed +const chai = require("chai"); +const chaiHttp = require("chai-http"); +const server = require("../app/server"); // Adjust the path as needed const should = chai.should(); chai.use(chaiHttp); // TODO: Write tests for the server -describe('Brands', () => {}); +describe("Brands", () => { + describe("/GET brands", () => { + it("should GET all the brands", (done) => { + chai + .request(server) + .get("/brands") + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an("array"); + res.body.length.should.be.eql(5); + done(); + }); + }); + }); -describe('Login', () => {}); + describe("/GET brands/:id/products", () => { + it("should GET all the products for a specific brand", (done) => { + chai + .request(server) + .get("/brands/1/products") + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an("array"); + res.body.length.should.be.eql(3); + done(); + }); + }); -describe('Cart', () => {}); + it("should return an error if the brand does not exist", (done) => { + chai + .request(server) + .get("/brands/6/products") + .end((err, res) => { + res.should.have.status(404); + res.body.message.should.be.a("string"); + res.body.message.should.be.eql("No products found"); + done(); + }); + }); + }); +}); + +describe("Products", () => { + describe("/GET products", () => { + it("should GET all the products", (done) => { + chai + .request(server) + .get("/products") + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an("array"); + res.body.length.should.be.eql(11); + done(); + }); + }); + }); +}); + +describe("Login", () => { + describe("/POST login", () => { + it("logs in an authenticated user", (done) => { + let login = { + username: "yellowleopard753", + password: "jonjon", + }; + + chai + .request(server) + .post("/login") + .send(login) + .end((err, res) => { + res.should.have.status(200); + res.header.authorization.should.be.a("string"); + res.body.should.be.an("object"); + res.body.should.have.property("gender"); + res.body.should.have.property("cart"); + res.body.should.have.property("name"); + res.body.should.have.property("location"); + res.body.should.have.property("email"); + res.body.should.have.property("login"); + res.body.should.have.property("dob"); + res.body.should.have.property("registered"); + res.body.should.have.property("phone"); + res.body.should.have.property("cell"); + res.body.should.have.property("picture"); + res.body.should.have.property("nat"); + done(); + }); + }); + + it("throws error for incomplete login", (done) => { + let login = { + username: "Bob", + }; + + chai + .request(server) + .post("/login") + .send(login) + .end((err, res) => { + res.should.have.status(401); + res.body.should.be.an("object"); + res.body.message.should.be.a("string"); + res.body.message.should.be.eql( + "Both a username and password are required" + ); + done(); + }); + }); + + it("throws error for unathorized login", (done) => { + let login = { + username: "Bob", + password: "bobpass", + }; + + chai + .request(server) + .post("/login") + .send(login) + .end((err, res) => { + res.should.have.status(401); + res.body.should.be.an("object"); + res.body.message.should.be.a("string"); + res.body.message.should.be.eql("Incorrect username or password"); + done(); + }); + }); + }); +}); + +describe("Cart", () => { + describe("/GET me/cart", () => { + it("gets the cart for an authenticated user", (done) => { + let login = { + username: "yellowleopard753", + password: "jonjon", + }; + + chai + .request(server) + .post("/login") + .send(login) + .end((err, res) => { + const token = res.headers.authorization; + chai + .request(server) + .get("/me/cart") + .set("authorization", token) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an("array"); + done(); + }); + }); + }); + + it("throws error if user is not authenticated", (done) => { + chai + .request(server) + .get("/me/cart") + .end((err, res) => { + res.should.have.status(401); + res.body.message.should.be.eql("Authentication failed"); + done(); + }); + }); + }); + + describe("/POST me/cart", () => { + it("adds an item to user's cart", (done) => { + const product = { + id: "1", + categoryId: "1", + name: "Superglasses", + description: "The best glasses in the world", + price: 150, + imageUrls: [ + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg", + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg", + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg", + ], + }; + + let login = { + username: "yellowleopard753", + password: "jonjon", + }; + + chai + .request(server) + .post("/login") + .send(login) + .end((err, res) => { + const token = res.headers.authorization; + chai + .request(server) + .post("/me/cart") + .set("authorization", token) + .send(product) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an("array"); + res.body.should.have.length(1); + done(); + }); + }); + }); + + it("throws error if product is not in catalog", (done) => { + const product = { + id: "12", + categoryId: "1", + name: "Fake glasses", + description: "These glasses do not exist", + price: 100, + imageUrls: [ + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg", + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg", + "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg", + ], + }; + + let login = { + username: "yellowleopard753", + password: "jonjon", + }; + + chai + .request(server) + .post("/login") + .send(login) + .end((err, res) => { + const token = res.headers.authorization; + chai + .request(server) + .post("/me/cart") + .set("authorization", token) + .send(product) + .end((err, res) => { + res.should.have.status(404); + res.body.message.should.be.eql("Product not found"); + done(); + }); + }); + }); + + it("throws error if user is not authenticated", (done) => { + chai + .request(server) + .post("/me/cart") + .end((err, res) => { + res.should.have.status(401); + res.body.message.should.be.eql("Authentication failed"); + done(); + }); + }); + }); + + describe("/POST me/cart/:productId", () => { + it("Updates the quantity of a particular item in the user's cart (increase)", (done) => { + let login = { + username: "lazywolf342", + password: "tucker", + }; + + chai + .request(server) + .post("/login") + .send(login) + .end((err, res) => { + const token = res.headers.authorization; + chai + .request(server) + .post("/me/cart/2") + .set("authorization", token) + .send({ amount: 3 }) + .end((err, res) => { + console.log(res.body); + res.should.have.status(200); + res.body.should.be.an("array"); + res.body.should.have.length(3); + done(); + }); + }); + }); + + it("Updates the quantity of a particular item in the user's cart (decrease)", (done) => { + let login = { + username: "lazywolf342", + password: "tucker", + }; + + chai + .request(server) + .post("/login") + .send(login) + .end((err, res) => { + const token = res.headers.authorization; + chai + .request(server) + .post("/me/cart/2") + .set("authorization", token) + .send({ amount: 2 }) + .end((err, res) => { + console.log(res.body); + res.should.have.status(200); + res.body.should.be.an("array"); + res.body.should.have.length(2); + done(); + }); + }); + }); + + it("throws error if product is not in cart", (done) => { + let login = { + username: "yellowleopard753", + password: "jonjon", + }; + + chai + .request(server) + .post("/login") + .send(login) + .end((err, res) => { + const token = res.headers.authorization; + chai + .request(server) + .post("/me/cart/13") + .set("authorization", token) + .end((err, res) => { + res.should.have.status(404); + res.body.message.should.be.eql("Product not found"); + done(); + }); + }); + }); + + it("throws error if user is not authenticated", (done) => { + chai + .request(server) + .post("/me/cart/1") + .end((err, res) => { + res.should.have.status(401); + res.body.message.should.be.eql("Authentication failed"); + done(); + }); + }); + }); + + describe("/DELETE me/cart/:productId", () => { + it("deletes an item from the cart", (done) => { + let login = { + username: "greenlion235", + password: "waters", + }; + + chai + .request(server) + .post("/login") + .send(login) + .end((err, res) => { + const token = res.headers.authorization; + chai + .request(server) + .delete("/me/cart/7") + .set("authorization", token) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an("array"); + res.body.should.have.length(0); + done(); + }); + }); + }); + + it("throws error if product is not in cart", (done) => { + let login = { + username: "yellowleopard753", + password: "jonjon", + }; + + chai + .request(server) + .post("/login") + .send(login) + .end((err, res) => { + const token = res.headers.authorization; + chai + .request(server) + .delete("/me/cart/13") + .set("authorization", token) + .end((err, res) => { + res.should.have.status(404); + res.body.message.should.be.eql("Product not found"); + done(); + }); + }); + }); + + it("throws error if user is not authenticated", (done) => { + chai + .request(server) + .delete("/me/cart/1") + .end((err, res) => { + res.should.have.status(401); + res.body.message.should.be.eql("Authentication failed"); + done(); + }); + }); + }); +});