diff --git a/README.md b/README.md index bcf06360..12ea9e4b 100644 --- a/README.md +++ b/README.md @@ -50,14 +50,16 @@ Our e-commerce web application server, developed by Team Ninjas, facilitates smo - Seller get products Endpoint - User get product Endpoint - User search products Endpoint -- buyer get specific product Endpoint +- buyer get specific product Endpoint - seller get specific product Endpoint - Buyer get carts Endpoint - Buyer get single cart Endpoint +- Buyer clear carts Endpoint +- Buyer clear cart Endpoint - Buyer create-update cart Endpoint -- buyer add product to wish list Endpoint +- buyer add product to wish list Endpoint - buyer delete products from wishlist -- buyer delete product from wishlist +- buyer delete product from wishlist ## TABLE OF API ENDPOINTS SPECIFICATION AND DESCRIPTION @@ -90,15 +92,16 @@ Our e-commerce web application server, developed by Team Ninjas, facilitates smo | 25 | GET | /api/shop/seller-get-products | 200 OK | private | Seller get products | | 26 | GET | /api/shop/user-get-products | 200 OK | public | User get product | | 27 | GET | /api/shop/user-search-products | 200 OK | public | User search products | -| 28 | GET | /api/shop/user-get-product/:id | 200 OK | public | Buyer get specific products | -| 29 | GET | /api/shop/seller-get-product/:id | 200 OK | private | seller get specific products | +| 28 | GET | /api/shop/user-get-product/:id | 200 OK | public | Buyer get specific products | +| 29 | GET | /api/shop/seller-get-product/:id | 200 OK | private | seller get specific products | | 30 | GET | /api/cart/buyer-get-carts | 200 OK | private | Buyer get carts | | 31 | GET | /api/cart/buyer-get-cart/:cartId | 200 OK | private | Buyer get cart details | -| 32 | POST | /api/cart/create-update-cart | 201 CREATED | private | Buyer create-update cart | -| 33 | POST | /api/shop/buyer-add-product-wishList/:id | 200 OK | private | buyer add product to wish list | -| 34 | delete | /api/shop/delete-whishlist-products | 200 OK | private | buyer delete products from wishlist | -| 35 | delete | /api/shop/delete-whishlist-product:id | 200 OK | private | buyer delete product from wishlist | - +| 32 | DELETE | /api/cart/buyer-clear-carts | 200 OK | private | Buyer clear carts | +| 33 | DELETE | /api/cart/buyer-clear-cart/:id | 200 OK | private | Buyer clear cart | +| 34 | POST | /api/cart/create-update-cart | 201 CREATED | private | Buyer create-update cart | +| 35 | POST | /api/shop/buyer-add-product-wishList/:id | 200 OK | private | buyer add product to wish list | +| 36 | delete | /api/shop/delete-whishlist-products | 200 OK | private | buyer delete products from wishlist | +| 37 | delete | /api/shop/delete-whishlist-product:id | 200 OK | private | buyer delete product from wishlist | ## INSTALLATION @@ -175,4 +178,4 @@ Our e-commerce web application server, developed by Team Ninjas, facilitates smo 9. Delete the Migration: ```sh npm run deleteAllTables - ``` \ No newline at end of file + ``` diff --git a/package-lock.json b/package-lock.json index 462ec2cb..e2bb787c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "passport-google-oauth2": "^0.2.0", "pg": "^8.11.5", "pg-hstore": "^2.3.4", + "rewire": "^7.0.0", "sequelize": "^6.37.3", "sequelize-cli": "^6.6.2", "sequelize-typescript": "^2.1.6", @@ -620,7 +621,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -635,7 +635,6 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -644,7 +643,6 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -667,7 +665,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -684,7 +681,6 @@ "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -698,14 +694,12 @@ "node_modules/@eslint/eslintrc/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, "engines": { "node": ">=10" }, @@ -717,7 +711,6 @@ "version": "8.57.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -739,7 +732,6 @@ "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^2.0.2", "debug": "^4.3.1", @@ -753,7 +745,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -769,14 +760,12 @@ "node_modules/@humanwhocodes/config-array/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, "engines": { "node": ">=12.22" }, @@ -788,8 +777,7 @@ "node_modules/@humanwhocodes/object-schema": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "dev": true + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -1053,7 +1041,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1066,7 +1053,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -1075,7 +1061,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1864,8 +1849,7 @@ "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, "node_modules/abbrev": { "version": "1.1.1", @@ -1899,7 +1883,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -2560,7 +2543,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -3331,8 +3313,7 @@ "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "node_modules/default-require-extensions": { "version": "3.0.1", @@ -3902,7 +3883,6 @@ "version": "8.57.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -4082,7 +4062,6 @@ "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -4098,7 +4077,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -4110,7 +4088,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -4119,7 +4096,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -4136,7 +4112,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -4152,7 +4127,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -4164,7 +4138,6 @@ "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -4179,7 +4152,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -4193,14 +4165,12 @@ "node_modules/eslint/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/eslint/node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -4215,7 +4185,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -4230,7 +4199,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -4242,7 +4210,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, "engines": { "node": ">=10" }, @@ -4268,7 +4235,6 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -4297,7 +4263,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -4309,7 +4274,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -4321,7 +4285,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -4495,8 +4458,7 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "node_modules/fast-safe-stringify": { "version": "2.1.1", @@ -4507,7 +4469,6 @@ "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -4516,7 +4477,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -4614,7 +4574,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -4627,8 +4586,7 @@ "node_modules/flatted": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" }, "node_modules/for-each": { "version": "0.3.3", @@ -5087,8 +5045,7 @@ "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" }, "node_modules/har-schema": { "version": "2.0.0", @@ -5341,7 +5298,6 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, "engines": { "node": ">= 4" } @@ -5355,7 +5311,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -5371,7 +5326,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -5639,7 +5593,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -6164,8 +6117,7 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", @@ -6185,8 +6137,7 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, "node_modules/json-stringify-safe": { "version": "5.0.1", @@ -6283,7 +6234,6 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "dependencies": { "json-buffer": "3.0.1" } @@ -6308,7 +6258,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -6588,8 +6537,7 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/lodash.mergewith": { "version": "4.6.2", @@ -7400,8 +7348,7 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, "node_modules/negotiator": { "version": "0.6.3", @@ -8075,7 +8022,6 @@ "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -8150,7 +8096,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -8534,7 +8479,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, "engines": { "node": ">= 0.8.0" } @@ -8622,7 +8566,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -8928,12 +8871,19 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, + "node_modules/rewire": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rewire/-/rewire-7.0.0.tgz", + "integrity": "sha512-DyyNyzwMtGYgu0Zl/ya0PR/oaunM+VuCuBxCuhYJHHaV0V+YvYa3bBGxb5OZ71vndgmp1pYY8F4YOwQo1siRGw==", + "dependencies": { + "eslint": "^8.47.0" + } + }, "node_modules/rfdc": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", @@ -8958,7 +8908,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -10356,8 +10305,7 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, "node_modules/timers-ext": { "version": "0.1.7", @@ -10602,7 +10550,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -11001,7 +10948,6 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, "engines": { "node": ">=0.10.0" } diff --git a/package.json b/package.json index fe7ff18d..da2f2ae6 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "passport-google-oauth2": "^0.2.0", "pg": "^8.11.5", "pg-hstore": "^2.3.4", + "rewire": "^7.0.0", "sequelize": "^6.37.3", "sequelize-cli": "^6.6.2", "sequelize-typescript": "^2.1.6", @@ -125,4 +126,4 @@ "eslint-plugin-import": "^2.29.1", "lint-staged": "^15.2.2" } -} \ No newline at end of file +} diff --git a/src/modules/cart/controller/cartControllers.ts b/src/modules/cart/controller/cartControllers.ts index 8bffa77d..56cbc23a 100644 --- a/src/modules/cart/controller/cartControllers.ts +++ b/src/modules/cart/controller/cartControllers.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable comma-dangle */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Response } from "express"; @@ -7,7 +8,9 @@ import productRepositories from "../../product/repositories/productRepositories" import { ExtendRequest, IExtendedCartProduct } from "../../../types"; import { cartStatusEnum } from "../../../enums"; -const getProductDetails = (cartProducts: IExtendedCartProduct[]): { productsDetails: any[]; cartTotal: number } => { +const getProductDetails = ( + cartProducts: IExtendedCartProduct[] +): { productsDetails: any[]; cartTotal: number } => { let cartTotal = 0; const productsDetails = cartProducts.map((cartProduct) => { @@ -30,8 +33,13 @@ const getProductDetails = (cartProducts: IExtendedCartProduct[]): { productsDeta const buyerGetCart = async (req: ExtendRequest, res: Response) => { try { - const cart = await cartRepositories.getCartByUserIdAndCartId(req.user.id, req.params.cartId); - const cartProducts = await cartRepositories.getCartProductsByCartId(cart.id); + const cart = await cartRepositories.getCartByUserIdAndCartId( + req.user.id, + req.params.cartId + ); + const cartProducts = await cartRepositories.getCartProductsByCartId( + cart.id + ); const { productsDetails, cartTotal } = getProductDetails(cartProducts); return res.status(httpStatus.OK).json({ @@ -56,7 +64,9 @@ const buyerGetCarts = async (req: ExtendRequest, res: Response) => { const allCartsDetails = await Promise.all( carts.map(async (cart) => { - const cartProducts = await cartRepositories.getCartProductsByCartId(cart.id); + const cartProducts = await cartRepositories.getCartProductsByCartId( + cart.id + ); const { productsDetails, cartTotal } = getProductDetails(cartProducts); return { @@ -108,7 +118,9 @@ const updateCartProduct = async (cartProduct, quantity, res) => { totalPrice: cartProduct.products.price * quantity, }); - const cartProducts = await cartRepositories.getCartProductsByCartId(cartProduct.cartId); + const cartProducts = await cartRepositories.getCartProductsByCartId( + cartProduct.cartId + ); const { productsDetails, cartTotal } = getProductDetails(cartProducts); return res.status(httpStatus.OK).json({ @@ -128,9 +140,11 @@ const buyerCreateUpdateCart = async (req: ExtendRequest, res: Response) => { const carts = await cartRepositories.getCartsByUserId(userId); for (const cart of carts) { - const cartProducts = await cartRepositories.getCartProductsByCartId(cart.id); + const cartProducts = await cartRepositories.getCartProductsByCartId( + cart.id + ); for (const cartProduct of cartProducts) { - const product = (cartProduct as IExtendedCartProduct).products + const product = (cartProduct as IExtendedCartProduct).products; if (product.id === productId) { return updateCartProduct(cartProduct, quantity, res); } @@ -140,7 +154,9 @@ const buyerCreateUpdateCart = async (req: ExtendRequest, res: Response) => { if (carts.length > 0) { const productToAdd = await productRepositories.findProductById(productId); for (const cart of carts) { - const cartProducts = await cartRepositories.getCartProductsByCartId(cart.id); + const cartProducts = await cartRepositories.getCartProductsByCartId( + cart.id + ); for (const cartProduct of cartProducts) { const product = (cartProduct as IExtendedCartProduct).products; if (product.shopId === productToAdd.shopId) { @@ -150,7 +166,10 @@ const buyerCreateUpdateCart = async (req: ExtendRequest, res: Response) => { } } - const createdCart = await cartRepositories.addCart({ userId, status: cartStatusEnum.PENDING }); + const createdCart = await cartRepositories.addCart({ + userId, + status: cartStatusEnum.PENDING, + }); const product = await productRepositories.findProductById(productId); await cartRepositories.addCartProduct({ cartId: createdCart.id, @@ -161,7 +180,9 @@ const buyerCreateUpdateCart = async (req: ExtendRequest, res: Response) => { totalPrice: product.price * quantity, }); - const cartProducts = await cartRepositories.getCartProductsByCartId(createdCart.id); + const cartProducts = await cartRepositories.getCartProductsByCartId( + createdCart.id + ); const { productsDetails, cartTotal } = getProductDetails(cartProducts); res.status(httpStatus.CREATED).json({ @@ -169,8 +190,8 @@ const buyerCreateUpdateCart = async (req: ExtendRequest, res: Response) => { data: { cartId: createdCart.id, products: productsDetails, - total: cartTotal - } + total: cartTotal, + }, }); } catch (error) { res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ @@ -180,8 +201,49 @@ const buyerCreateUpdateCart = async (req: ExtendRequest, res: Response) => { } }; -export default { +const buyerClearCart = async (req: ExtendRequest, res: Response) => { + try { + const cart = await cartRepositories.getCartByUserIdAndCartId( + req.user.id, + req.params.id + ); + + await cartRepositories.deleteAllCartProducts(cart.id); + + res + .status(httpStatus.OK) + .json({ message: "All products in cart cleared successfully!" }); + } catch (error) { + return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ + status: httpStatus.INTERNAL_SERVER_ERROR, + message: error.message, + }); + } +}; + +const buyerClearCarts = async (req: ExtendRequest, res: Response) => { + try { + const carts = await cartRepositories.getCartsByUserId(req.user.id); + + for (const cart of carts) { + await cartRepositories.deleteAllCartProducts(cart.id); + } + + res + .status(httpStatus.OK) + .json({ message: "All carts cleared successfully!" }); + } catch (error) { + return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ + status: httpStatus.INTERNAL_SERVER_ERROR, + message: error.message, + }); + } +}; + +export { buyerGetCart, buyerGetCarts, - buyerCreateUpdateCart -}; \ No newline at end of file + buyerClearCart, + buyerClearCarts, + buyerCreateUpdateCart, +}; diff --git a/src/modules/cart/repositories/cartRepositories.ts b/src/modules/cart/repositories/cartRepositories.ts index 7162434f..aad40a02 100644 --- a/src/modules/cart/repositories/cartRepositories.ts +++ b/src/modules/cart/repositories/cartRepositories.ts @@ -1,24 +1,34 @@ -import db from "../../../databases/models" +/* eslint-disable comma-dangle */ +import db from "../../../databases/models"; const getCartsByUserId = async (userId: string) => { - return await db.Carts.findAll({ where: { userId, status: "pending" } }) -} + return await db.Carts.findAll({ where: { userId, status: "pending" } }); +}; const addCart = async (body: Record) => { - return await db.Carts.create(body) -} + return await db.Carts.create(body); +}; const addCartProduct = async (body: Record) => { - return await db.CartProducts.create(body) -} + return await db.CartProducts.create(body); +}; -const updateCartProduct = async (id: string, body: Record) => { - return await db.CartProducts.update(body, { where: { id } }) -} +const updateCartProduct = async ( + id: string, + body: Record +) => { + return await db.CartProducts.update(body, { where: { id } }); +}; -const getCartByUserIdAndCartId = async (userId: string, cartId: string) => { - return await db.Carts.findOne({ where: { id: cartId, userId, status: "pending" } }) -} +const getCartByUserIdAndCartId = async ( + userId: string, + cartId: string, + status: string = "pending" +) => { + return await db.Carts.findOne({ + where: { id: cartId, userId, status }, + }); +}; const getCartProductsByCartId = async (cartId: string) => { return await db.CartProducts.findAll({ @@ -27,21 +37,27 @@ const getCartProductsByCartId = async (cartId: string) => { { model: db.Products, as: "products", - attributes: ["id", "name", "price", "images", "shopId"] - } - ] + attributes: ["id", "name", "price", "images", "shopId"], + }, + ], }); -} +}; const getShopIdByProductId = async (id: string): Promise => { - return (await db.Products.findOne({ where: { id }})).shopId; -} + return (await db.Products.findOne({ where: { id } })).shopId; +}; + +const deleteAllCartProducts = async (cartId: string) => { + await db.CartProducts.destroy({ where: { cartId } }); +}; export default { getCartsByUserId, getCartProductsByCartId, getCartByUserIdAndCartId, - addCart, updateCartProduct, + addCart, + updateCartProduct, getShopIdByProductId, - addCartProduct + addCartProduct, + deleteAllCartProducts, }; diff --git a/src/modules/cart/test/cart.spec.ts b/src/modules/cart/test/cart.spec.ts index 52945c3c..23e68b97 100644 --- a/src/modules/cart/test/cart.spec.ts +++ b/src/modules/cart/test/cart.spec.ts @@ -1,12 +1,21 @@ +/* eslint-disable no-shadow */ +/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable comma-dangle */ import chai, { expect } from "chai"; import chaiHttp from "chai-http"; import sinon from "sinon"; import httpStatus from "http-status"; import cartRepositories from "../repositories/cartRepositories"; -import cartController from "../controller/cartControllers"; +import * as cartController from "../controller/cartControllers"; import db from "../../../databases/models"; -import { isCartExist, isCartIdExist, isProductIdExist } from "../../../middlewares/validation"; +import { + isCartExist, + isCartIdExist, + isProductIdExist, +} from "../../../middlewares/validation"; import productRepositories from "../../product/repositories/productRepositories"; +import { buyerClearCart, buyerClearCarts } from "../controller/cartControllers"; + chai.use(chaiHttp); describe("Buyer Get Cart", () => { let req; @@ -15,20 +24,18 @@ describe("Buyer Get Cart", () => { beforeEach(() => { sandbox = sinon.createSandbox(); req = { - user: { id: "user-id" } + user: { id: "user-id" }, }; res = { status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis() + json: sinon.stub().returnsThis(), }; }); afterEach(() => { sandbox.restore(); }); it("should return cart details when cart exists", async () => { - const mockCarts = [ - { id: "6ee2addd-5270-4855-969b-1f56608b122b" } - ]; + const mockCarts = [{ id: "6ee2addd-5270-4855-969b-1f56608b122b" }]; const mockCartProducts = [ { quantity: 2, @@ -36,8 +43,8 @@ describe("Buyer Get Cart", () => { id: "6ee2addd-5270-4855-969b-1f56608b122c", name: "Product 1", price: 50, - images: ["image1.jpg"] - } + images: ["image1.jpg"], + }, }, { quantity: 1, @@ -45,16 +52,18 @@ describe("Buyer Get Cart", () => { id: "6ee2addd-5270-4855-969b-1f56608b122d", name: "Product 2", price: 100, - images: ["image2.jpg"] - } - } + images: ["image2.jpg"], + }, + }, ]; - + sandbox.stub(cartRepositories, "getCartsByUserId").resolves(mockCarts); - sandbox.stub(cartRepositories, "getCartProductsByCartId").resolves(mockCartProducts); - + sandbox + .stub(cartRepositories, "getCartProductsByCartId") + .resolves(mockCartProducts); + await cartController.buyerGetCarts(req, res); - + expect(res.status).to.have.been.calledWith(httpStatus.OK); expect(res.json).to.have.been.calledWith({ message: "Buyer's all carts", @@ -68,7 +77,7 @@ describe("Buyer Get Cart", () => { price: 50, image: "image1.jpg", quantity: 2, - totalPrice: 100 + totalPrice: 100, }, { id: "6ee2addd-5270-4855-969b-1f56608b122d", @@ -76,23 +85,25 @@ describe("Buyer Get Cart", () => { price: 100, image: "image2.jpg", quantity: 1, - totalPrice: 100 - } + totalPrice: 100, + }, ], - total: 200 - } - ] + total: 200, + }, + ], }); }); - + it("should handle errors properly", async () => { const error = new Error("Something went wrong"); sandbox.stub(cartRepositories, "getCartsByUserId").throws(error); await cartController.buyerGetCarts(req, res); - expect(res.status).to.have.been.calledWith(httpStatus.INTERNAL_SERVER_ERROR); + expect(res.status).to.have.been.calledWith( + httpStatus.INTERNAL_SERVER_ERROR + ); expect(res.json).to.have.been.calledWith({ status: httpStatus.INTERNAL_SERVER_ERROR, - error: error.message + error: error.message, }); }); }); @@ -110,13 +121,24 @@ describe("Cart Repositories", () => { describe("getCartsByUserId", () => { it("should return a cart for a given user ID with pending status", async () => { - const mockCarts = [{ id: "6ee2addd-5270-4855-969b-1f56608b122b", userId: "6ee2addd-5270-4855-969b-1f56608b122e", status: "pending" }]; + const mockCarts = [ + { + id: "6ee2addd-5270-4855-969b-1f56608b122b", + userId: "6ee2addd-5270-4855-969b-1f56608b122e", + status: "pending", + }, + ]; sandbox.stub(db.Carts, "findAll").resolves(mockCarts); - const result = await cartRepositories.getCartsByUserId("6ee2addd-5270-4855-969b-1f56608b122e"); + const result = await cartRepositories.getCartsByUserId( + "6ee2addd-5270-4855-969b-1f56608b122e" + ); expect(db.Carts.findAll).to.have.been.calledOnceWith({ - where: { userId: "6ee2addd-5270-4855-969b-1f56608b122e", status: "pending" } + where: { + userId: "6ee2addd-5270-4855-969b-1f56608b122e", + status: "pending", + }, }); expect(result).to.eql(mockCarts); }); @@ -124,10 +146,15 @@ describe("Cart Repositories", () => { it("should return an empty array if no cart is found", async () => { sandbox.stub(db.Carts, "findAll").resolves([]); - const result = await cartRepositories.getCartsByUserId("6ee2addd-5270-4855-969b-1f56608b122e"); + const result = await cartRepositories.getCartsByUserId( + "6ee2addd-5270-4855-969b-1f56608b122e" + ); expect(db.Carts.findAll).to.have.been.calledOnceWith({ - where: { userId: "6ee2addd-5270-4855-969b-1f56608b122e", status: "pending" } + where: { + userId: "6ee2addd-5270-4855-969b-1f56608b122e", + status: "pending", + }, }); expect(result).to.be.an("array").that.is.empty; }); @@ -135,13 +162,24 @@ describe("Cart Repositories", () => { describe("getCartByUserIdAndCartId", () => { it("should return a cart for a given user ID and cart ID with pending status", async () => { - const mockCart = { id: "6ee2addd-5270-4855-969b-1f56608b122b", userId: "6ee2addd-5270-4855-969b-1f56608b122e", status: "pending" }; + const mockCart = { + id: "6ee2addd-5270-4855-969b-1f56608b122b", + userId: "6ee2addd-5270-4855-969b-1f56608b122e", + status: "pending", + }; sandbox.stub(db.Carts, "findOne").resolves(mockCart); - const result = await cartRepositories.getCartByUserIdAndCartId("6ee2addd-5270-4855-969b-1f56608b122e", "cart-id"); + const result = await cartRepositories.getCartByUserIdAndCartId( + "6ee2addd-5270-4855-969b-1f56608b122e", + "cart-id" + ); expect(db.Carts.findOne).to.have.been.calledOnceWith({ - where: { id: "cart-id", userId: "6ee2addd-5270-4855-969b-1f56608b122e", status: "pending" } + where: { + id: "cart-id", + userId: "6ee2addd-5270-4855-969b-1f56608b122e", + status: "pending", + }, }); expect(result).to.eql(mockCart); }); @@ -149,10 +187,17 @@ describe("Cart Repositories", () => { it("should return null if no cart is found", async () => { sandbox.stub(db.Carts, "findOne").resolves(null); - const result = await cartRepositories.getCartByUserIdAndCartId("6ee2addd-5270-4855-969b-1f56608b122e", "cart-id"); + const result = await cartRepositories.getCartByUserIdAndCartId( + "6ee2addd-5270-4855-969b-1f56608b122e", + "cart-id" + ); expect(db.Carts.findOne).to.have.been.calledOnceWith({ - where: { id: "cart-id", userId: "6ee2addd-5270-4855-969b-1f56608b122e", status: "pending" } + where: { + id: "cart-id", + userId: "6ee2addd-5270-4855-969b-1f56608b122e", + status: "pending", + }, }); expect(result).to.be.null; }); @@ -179,7 +224,10 @@ describe("Cart Repositories", () => { await cartRepositories.updateCartProduct(productId, cartProductData); - expect(db.CartProducts.update).to.have.been.calledOnceWith(cartProductData, { where: { id: productId } }); + expect(db.CartProducts.update).to.have.been.calledOnceWith( + cartProductData, + { where: { id: productId } } + ); }); }); @@ -191,26 +239,33 @@ describe("Cart Repositories", () => { const result = await cartRepositories.getShopIdByProductId(productId); - expect(db.Products.findOne).to.have.been.calledOnceWith({ where: { id: productId } }); + expect(db.Products.findOne).to.have.been.calledOnceWith({ + where: { id: productId }, + }); expect(result).to.equal(mockProduct.shopId); }); }); describe("addCartProduct", () => { it("should add a new cart product", async () => { - const cartProductData = { cartId: "cart-id", productId: "product-id", quantity: 3 }; + const cartProductData = { + cartId: "cart-id", + productId: "product-id", + quantity: 3, + }; const mockCartProduct = { id: "cart-product-id", ...cartProductData }; sandbox.stub(db.CartProducts, "create").resolves(mockCartProduct); const result = await cartRepositories.addCartProduct(cartProductData); - expect(db.CartProducts.create).to.have.been.calledOnceWith(cartProductData); + expect(db.CartProducts.create).to.have.been.calledOnceWith( + cartProductData + ); expect(result).to.eql(mockCartProduct); }); }); }); - describe("Validation Middlewares", () => { let req; let res; @@ -222,11 +277,11 @@ describe("Validation Middlewares", () => { req = { user: { id: "6ee2addd-5270-4855-969b-1f56608b122e" }, body: { productId: "6ee2addd-5270-4855-969b-1f56608b122c" }, - params: { cartId: "6ee2addd-5270-4855-969b-1f56608b1229" } + params: { cartId: "6ee2addd-5270-4855-969b-1f56608b1229" }, }; res = { status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis() + json: sinon.stub().returnsThis(), }; next = sinon.stub(); }); @@ -236,7 +291,9 @@ describe("Validation Middlewares", () => { }); it("should check if cart exists", async () => { - sandbox.stub(cartRepositories, "getCartsByUserId").resolves([{ id: "cart-id" }]); + sandbox + .stub(cartRepositories, "getCartsByUserId") + .resolves([{ id: "cart-id" }]); await isCartExist(req, res, next); @@ -251,12 +308,14 @@ describe("Validation Middlewares", () => { expect(res.status).to.have.been.calledWith(httpStatus.NOT_FOUND); expect(res.json).to.have.been.calledWith({ status: httpStatus.NOT_FOUND, - message: "No cart found. Please create cart first." + message: "No cart found. Please create cart first.", }); }); it("should check if product ID exists", async () => { - sandbox.stub(productRepositories, "findProductById").resolves({ id: "6ee2addd-5270-4855-969b-1f56608b1228" }); + sandbox + .stub(productRepositories, "findProductById") + .resolves({ id: "6ee2addd-5270-4855-969b-1f56608b1228" }); await isProductIdExist(req, res, next); @@ -271,12 +330,14 @@ describe("Validation Middlewares", () => { expect(res.status).to.have.been.calledWith(httpStatus.NOT_FOUND); expect(res.json).to.have.been.calledWith({ status: httpStatus.NOT_FOUND, - message: "No product with that ID." + message: "No product with that ID.", }); }); it("should check if cart ID exists", async () => { - sandbox.stub(cartRepositories, "getCartByUserIdAndCartId").resolves({ id: "6ee2addd-5270-4855-969b-1f56608b1229" }); + sandbox + .stub(cartRepositories, "getCartByUserIdAndCartId") + .resolves({ id: "6ee2addd-5270-4855-969b-1f56608b1229" }); await isCartIdExist(req, res, next); @@ -291,7 +352,7 @@ describe("Validation Middlewares", () => { expect(res.status).to.have.been.calledWith(httpStatus.NOT_FOUND); expect(res.json).to.have.been.calledWith({ status: httpStatus.NOT_FOUND, - message: "Cart not found. Please add items to your cart." + message: "Cart not found. Please add items to your cart.", }); }); }); @@ -306,11 +367,11 @@ describe("Cart Controller - GetCart", () => { req = { user: { id: "6ee2addd-5270-4855-969b-1f56608b122e" }, body: { productId: "6ee2addd-5270-4855-969b-1f56608b1228", quantity: 2 }, - params: { cartId: "6ee2addd-5270-4855-969b-1f56608b1229" } + params: { cartId: "6ee2addd-5270-4855-969b-1f56608b1229" }, }; res = { status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis() + json: sinon.stub().returnsThis(), }; }); @@ -327,8 +388,8 @@ describe("Cart Controller - GetCart", () => { id: "product-id-1", name: "Product 1", price: 50, - images: ["image1.jpg"] - } + images: ["image1.jpg"], + }, }, { quantity: 1, @@ -336,12 +397,16 @@ describe("Cart Controller - GetCart", () => { id: "product-id-2", name: "Product 2", price: 100, - images: ["image2.jpg"] - } - } + images: ["image2.jpg"], + }, + }, ]; - sandbox.stub(cartRepositories, "getCartByUserIdAndCartId").resolves(mockCart); - sandbox.stub(cartRepositories, "getCartProductsByCartId").resolves(mockCartProducts); + sandbox + .stub(cartRepositories, "getCartByUserIdAndCartId") + .resolves(mockCart); + sandbox + .stub(cartRepositories, "getCartProductsByCartId") + .resolves(mockCartProducts); await cartController.buyerGetCart(req, res); @@ -357,7 +422,7 @@ describe("Cart Controller - GetCart", () => { price: 50, image: "image1.jpg", quantity: 2, - totalPrice: 100 + totalPrice: 100, }, { id: "product-id-2", @@ -365,11 +430,11 @@ describe("Cart Controller - GetCart", () => { price: 100, image: "image2.jpg", quantity: 1, - totalPrice: 100 - } + totalPrice: 100, + }, ], - total: 200 - } + total: 200, + }, }); }); @@ -379,10 +444,12 @@ describe("Cart Controller - GetCart", () => { await cartController.buyerGetCart(req, res); - expect(res.status).to.have.been.calledWith(httpStatus.INTERNAL_SERVER_ERROR); + expect(res.status).to.have.been.calledWith( + httpStatus.INTERNAL_SERVER_ERROR + ); expect(res.json).to.have.been.calledWith({ status: httpStatus.INTERNAL_SERVER_ERROR, - error: error.message + error: error.message, }); }); @@ -395,8 +462,8 @@ describe("Cart Controller - GetCart", () => { id: "product-id-1", name: "Product 1", price: 50, - images: ["image1.jpg"] - } + images: ["image1.jpg"], + }, }, { quantity: 1, @@ -404,12 +471,14 @@ describe("Cart Controller - GetCart", () => { id: "product-id-2", name: "Product 2", price: 100, - images: ["image2.jpg"] - } - } + images: ["image2.jpg"], + }, + }, ]; sandbox.stub(cartRepositories, "getCartsByUserId").resolves([mockCart]); - sandbox.stub(cartRepositories, "getCartProductsByCartId").resolves(mockCartProducts); + sandbox + .stub(cartRepositories, "getCartProductsByCartId") + .resolves(mockCartProducts); await cartController.buyerGetCarts(req, res); @@ -426,7 +495,7 @@ describe("Cart Controller - GetCart", () => { price: 50, image: "image1.jpg", quantity: 2, - totalPrice: 100 + totalPrice: 100, }, { id: "product-id-2", @@ -434,12 +503,12 @@ describe("Cart Controller - GetCart", () => { price: 100, image: "image2.jpg", quantity: 1, - totalPrice: 100 - } + totalPrice: 100, + }, ], - total: 200 - } - ] + total: 200, + }, + ], }); }); @@ -449,10 +518,12 @@ describe("Cart Controller - GetCart", () => { await cartController.buyerGetCarts(req, res); - expect(res.status).to.have.been.calledWith(httpStatus.INTERNAL_SERVER_ERROR); + expect(res.status).to.have.been.calledWith( + httpStatus.INTERNAL_SERVER_ERROR + ); expect(res.json).to.have.been.calledWith({ status: httpStatus.INTERNAL_SERVER_ERROR, - error: error.message + error: error.message, }); }); @@ -465,8 +536,8 @@ describe("Cart Controller - GetCart", () => { id: "product-id-1", name: "Product 1", price: 50, - images: ["image1.jpg"] - } + images: ["image1.jpg"], + }, }, { quantity: 1, @@ -474,41 +545,45 @@ describe("Cart Controller - GetCart", () => { id: "product-id-2", name: "Product 2", price: 100, - images: ["image2.jpg"] - } - } + images: ["image2.jpg"], + }, + }, ]; sandbox.stub(cartRepositories, "getCartsByUserId").resolves([mockCart]); - sandbox.stub(cartRepositories, "getCartProductsByCartId").resolves(mockCartProducts); + sandbox + .stub(cartRepositories, "getCartProductsByCartId") + .resolves(mockCartProducts); await cartController.buyerGetCarts(req, res); expect(res.status).to.have.been.calledWith(httpStatus.OK); expect(res.json).to.have.been.calledWith({ message: "Buyer's all carts", - data: [{ - cartId: mockCart.id, - products: [ - { - id: "product-id-1", - name: "Product 1", - price: 50, - image: "image1.jpg", - quantity: 2, - totalPrice: 100 - }, - { - id: "product-id-2", - name: "Product 2", - price: 100, - image: "image2.jpg", - quantity: 1, - totalPrice: 100 - } - ], - total: 200 - }] + data: [ + { + cartId: mockCart.id, + products: [ + { + id: "product-id-1", + name: "Product 1", + price: 50, + image: "image1.jpg", + quantity: 2, + totalPrice: 100, + }, + { + id: "product-id-2", + name: "Product 2", + price: 100, + image: "image2.jpg", + quantity: 1, + totalPrice: 100, + }, + ], + total: 200, + }, + ], }); }); @@ -521,8 +596,8 @@ describe("Cart Controller - GetCart", () => { id: "product-id-1", name: "Product 1", price: 50, - images: ["image1.jpg"] - } + images: ["image1.jpg"], + }, }, { quantity: 1, @@ -530,13 +605,17 @@ describe("Cart Controller - GetCart", () => { id: "product-id-2", name: "Product 2", price: 100, - images: ["image2.jpg"] - } - } + images: ["image2.jpg"], + }, + }, ]; - sandbox.stub(cartRepositories, "getCartByUserIdAndCartId").resolves(mockCart); - sandbox.stub(cartRepositories, "getCartProductsByCartId").resolves(mockCartProducts); + sandbox + .stub(cartRepositories, "getCartByUserIdAndCartId") + .resolves(mockCart); + sandbox + .stub(cartRepositories, "getCartProductsByCartId") + .resolves(mockCartProducts); await cartController.buyerGetCart(req, res); @@ -552,7 +631,7 @@ describe("Cart Controller - GetCart", () => { price: 50, image: "image1.jpg", quantity: 2, - totalPrice: 100 + totalPrice: 100, }, { id: "product-id-2", @@ -560,14 +639,13 @@ describe("Cart Controller - GetCart", () => { price: 100, image: "image2.jpg", quantity: 1, - totalPrice: 100 - } + totalPrice: 100, + }, ], - total: 200 - } + total: 200, + }, }); }); - }); describe("Cart Controller Tests", () => { @@ -580,11 +658,11 @@ describe("Cart Controller Tests", () => { req = { user: { id: "user-id" }, body: { productId: "product-id", quantity: 2 }, - params: { cartId: "cart-id" } + params: { cartId: "cart-id" }, }; res = { status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis() + json: sinon.stub().returnsThis(), }; }); @@ -600,7 +678,7 @@ describe("Cart Controller Tests", () => { name: "Product 1", price: 50, images: ["image1.jpg"], - shopId: "shop-id" + shopId: "shop-id", }; const mockCartProducts = [ { @@ -609,14 +687,18 @@ describe("Cart Controller Tests", () => { id: "product-id", name: "Product 1", price: 50, - images: ["image1.jpg"] - } - } + images: ["image1.jpg"], + }, + }, ]; sandbox.stub(cartRepositories, "getCartsByUserId").resolves([mockCart]); - sandbox.stub(cartRepositories, "getCartProductsByCartId").resolves(mockCartProducts); - sandbox.stub(productRepositories, "findProductById").resolves(mockProduct); + sandbox + .stub(cartRepositories, "getCartProductsByCartId") + .resolves(mockCartProducts); + sandbox + .stub(productRepositories, "findProductById") + .resolves(mockProduct); sandbox.stub(cartRepositories, "addCartProduct").resolves(); sandbox.stub(cartRepositories, "updateCartProduct").resolves(); @@ -634,11 +716,11 @@ describe("Cart Controller Tests", () => { price: 50, image: "image1.jpg", quantity: 2, - totalPrice: 100 - } + totalPrice: 100, + }, ], - total: 100 - } + total: 100, + }, }); }); @@ -649,7 +731,7 @@ describe("Cart Controller Tests", () => { name: "Product 1", price: 50, images: ["image1.jpg"], - shopId: "shop-id" + shopId: "shop-id", }; const mockCartProducts = [ { @@ -659,14 +741,18 @@ describe("Cart Controller Tests", () => { name: "Product 1", price: 50, images: ["image1.jpg"], - shopId: "shop-id" - } - } + shopId: "shop-id", + }, + }, ]; sandbox.stub(cartRepositories, "getCartsByUserId").resolves([mockCart]); - sandbox.stub(cartRepositories, "getCartProductsByCartId").resolves(mockCartProducts); - sandbox.stub(productRepositories, "findProductById").resolves(mockProduct); + sandbox + .stub(cartRepositories, "getCartProductsByCartId") + .resolves(mockCartProducts); + sandbox + .stub(productRepositories, "findProductById") + .resolves(mockProduct); sandbox.stub(cartRepositories, "addCartProduct").resolves(); sandbox.stub(cartRepositories, "updateCartProduct").resolves(); @@ -684,22 +770,26 @@ describe("Cart Controller Tests", () => { price: 50, image: "image1.jpg", quantity: 2, - totalPrice: 100 - } + totalPrice: 100, + }, ], - total: 100 - } + total: 100, + }, }); }); it("should create new cart and add product if no cart exists", async () => { - const mockCreatedCart = { id: "new-cart-id", userId: "user-id", status: "pending" }; + const mockCreatedCart = { + id: "new-cart-id", + userId: "user-id", + status: "pending", + }; const mockProduct = { id: "product-id", name: "Product 1", price: 50, images: ["image1.jpg"], - shopId: "shop-id" + shopId: "shop-id", }; const mockCartProducts = [ { @@ -708,16 +798,20 @@ describe("Cart Controller Tests", () => { id: "product-id", name: "Product 1", price: 50, - images: ["image1.jpg"] - } - } + images: ["image1.jpg"], + }, + }, ]; sandbox.stub(cartRepositories, "getCartsByUserId").resolves([]); sandbox.stub(cartRepositories, "addCart").resolves(mockCreatedCart); - sandbox.stub(productRepositories, "findProductById").resolves(mockProduct); + sandbox + .stub(productRepositories, "findProductById") + .resolves(mockProduct); sandbox.stub(cartRepositories, "addCartProduct").resolves(); - sandbox.stub(cartRepositories, "getCartProductsByCartId").resolves(mockCartProducts); + sandbox + .stub(cartRepositories, "getCartProductsByCartId") + .resolves(mockCartProducts); await cartController.buyerCreateUpdateCart(req, res); @@ -733,11 +827,11 @@ describe("Cart Controller Tests", () => { price: 50, image: "image1.jpg", quantity: 2, - totalPrice: 100 - } + totalPrice: 100, + }, ], - total: 100 - } + total: 100, + }, }); }); @@ -747,11 +841,175 @@ describe("Cart Controller Tests", () => { await cartController.buyerCreateUpdateCart(req, res); - expect(res.status).to.have.been.calledWith(httpStatus.INTERNAL_SERVER_ERROR); + expect(res.status).to.have.been.calledWith( + httpStatus.INTERNAL_SERVER_ERROR + ); expect(res.json).to.have.been.calledWith({ status: httpStatus.INTERNAL_SERVER_ERROR, - error: error.message + error: error.message, }); }); }); -}); \ No newline at end of file +}); + +describe("buyerClearCart", () => { + let req; + let res; + let getCartByUserIdAndCartIdStub; + let deleteAllCartProductsStub; + + beforeEach(() => { + req = { + user: { id: "user-id" }, + params: { id: "cart-id" }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub().returnsThis(), + }; + + getCartByUserIdAndCartIdStub = sinon.stub( + cartRepositories, + "getCartByUserIdAndCartId" + ); + deleteAllCartProductsStub = sinon.stub( + cartRepositories, + "deleteAllCartProducts" + ); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should clear all products in the cart successfully", async () => { + const cart = { id: "cart-id" }; + getCartByUserIdAndCartIdStub.resolves(cart); + deleteAllCartProductsStub.resolves(); + + await buyerClearCart(req, res); + + expect(getCartByUserIdAndCartIdStub).to.have.been.calledWith( + req.user.id, + req.params.id + ); + expect(deleteAllCartProductsStub).to.have.been.calledWith(cart.id); + expect(res.status).to.have.been.calledWith(httpStatus.OK); + expect(res.json).to.have.been.calledWith({ + message: "All products in cart cleared successfully!", + }); + }); + + it("should return an error if getCartByUserIdAndCartId fails", async () => { + const error = new Error("Something went wrong"); + getCartByUserIdAndCartIdStub.rejects(error); + + await buyerClearCart(req, res); + + expect(res.status).to.have.been.calledWith( + httpStatus.INTERNAL_SERVER_ERROR + ); + expect(res.json).to.have.been.calledWith({ + status: httpStatus.INTERNAL_SERVER_ERROR, + message: error.message, + }); + }); + + it("should return an error if deleteAllCartProducts fails", async () => { + const cart = { id: "cart-id" }; + const error = new Error("Something went wrong"); + getCartByUserIdAndCartIdStub.resolves(cart); + deleteAllCartProductsStub.rejects(error); + + await buyerClearCart(req, res); + + expect(res.status).to.have.been.calledWith( + httpStatus.INTERNAL_SERVER_ERROR + ); + expect(res.json).to.have.been.calledWith({ + status: httpStatus.INTERNAL_SERVER_ERROR, + message: error.message, + }); + }); +}); + +describe("buyerClearCarts", () => { + let req; + let res; + let getCartsByUserIdStub; + let deleteAllCartProductsStub; + + beforeEach(() => { + req = { + user: { id: "user-id" }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub().returnsThis(), + }; + + getCartsByUserIdStub = sinon.stub(cartRepositories, "getCartsByUserId"); + deleteAllCartProductsStub = sinon.stub( + cartRepositories, + "deleteAllCartProducts" + ); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should clear all products in all carts successfully", async () => { + const carts = [{ id: "cart-id-1" }, { id: "cart-id-2" }]; + getCartsByUserIdStub.resolves(carts); + deleteAllCartProductsStub.resolves(); + + await buyerClearCarts(req, res); + + expect(getCartsByUserIdStub).to.have.been.calledWith(req.user.id); + expect(deleteAllCartProductsStub).to.have.been.calledTwice; + expect(deleteAllCartProductsStub.firstCall).to.have.been.calledWith( + "cart-id-1" + ); + expect(deleteAllCartProductsStub.secondCall).to.have.been.calledWith( + "cart-id-2" + ); + expect(res.status).to.have.been.calledWith(httpStatus.OK); + expect(res.json).to.have.been.calledWith({ + message: "All carts cleared successfully!", + }); + }); + + it("should return an error if getCartsByUserId fails", async () => { + const error = new Error("Something went wrong"); + getCartsByUserIdStub.rejects(error); + + await buyerClearCarts(req, res); + + expect(res.status).to.have.been.calledWith( + httpStatus.INTERNAL_SERVER_ERROR + ); + expect(res.json).to.have.been.calledWith({ + status: httpStatus.INTERNAL_SERVER_ERROR, + message: error.message, + }); + }); + + it("should return an error if deleteAllCartProducts fails for any cart", async () => { + const carts = [{ id: "cart-id-1" }, { id: "cart-id-2" }]; + const error = new Error("Something went wrong"); + getCartsByUserIdStub.resolves(carts); + deleteAllCartProductsStub.onFirstCall().resolves(); + deleteAllCartProductsStub.onSecondCall().rejects(error); + + await buyerClearCarts(req, res); + + expect(res.status).to.have.been.calledWith( + httpStatus.INTERNAL_SERVER_ERROR + ); + expect(res.json).to.have.been.calledWith({ + status: httpStatus.INTERNAL_SERVER_ERROR, + message: error.message, + }); + }); +}); diff --git a/src/routes/cartRouter.ts b/src/routes/cartRouter.ts index e0ea767c..4799f985 100644 --- a/src/routes/cartRouter.ts +++ b/src/routes/cartRouter.ts @@ -1,8 +1,13 @@ /* eslint-disable comma-dangle */ import { Router } from "express"; import { userAuthorization } from "../middlewares/authorization"; -import { isCartExist, isCartIdExist, isProductIdExist, validation } from "../middlewares/validation"; -import cartControllers from "../modules/cart/controller/cartControllers"; +import { + isCartExist, + isCartIdExist, + isProductIdExist, + validation, +} from "../middlewares/validation"; +import * as cartControllers from "../modules/cart/controller/cartControllers"; import { cartSchema } from "../modules/cart/validation/cartValidations"; const router: Router = Router(); @@ -29,4 +34,18 @@ router.get( cartControllers.buyerGetCart ); -export default router; \ No newline at end of file +router.delete( + "/buyer-clear-cart/:id", + userAuthorization(["buyer"]), + isCartExist, + cartControllers.buyerClearCart +); + +router.delete( + "/buyer-clear-carts", + userAuthorization(["buyer"]), + isCartExist, + cartControllers.buyerClearCarts +); + +export default router; diff --git a/src/routes/productRouter.ts b/src/routes/productRouter.ts index 1dd048f7..5cbca271 100644 --- a/src/routes/productRouter.ts +++ b/src/routes/productRouter.ts @@ -1,1405 +1,125 @@ /* eslint-disable comma-dangle */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { Request, Response, NextFunction } from "express"; -import chai, { expect } from "chai"; -import chaiHttp from "chai-http"; -import app from "../../.."; -import path from "path"; -import fs from "fs"; -import { fileFilter } from "../../../helpers/multer"; +import { Router } from "express"; +import productController from "../modules/product/controller/productController"; +import { userAuthorization } from "../middlewares/authorization"; import { - credential, + validation, isProductExist, isShopExist, transformFilesToBody, + isSellerShopExist, isPaginated, + isSearchFiltered, + isProductExistById, isProductExistToWishlist, - isUserWishlistExistById, isUserWishlistExist, -} from "../../../middlewares/validation"; -import sinon, { SinonStub } from "sinon"; -import productRepositories from "../repositories/productRepositories"; -import httpStatus from "http-status"; -import productController from "../controller/productController"; -import userRepositories from "../../user/repository/userRepositories"; -import userControllers from "../../user/controller/userControllers"; -import authRepositories from "../../auth/repository/authRepositories"; -import { ExtendRequest } from "../../../types"; - -chai.use(chaiHttp); -const router = () => chai.request(app); -const imagePath = path.join( - __dirname, - "../test/69180880-2138-11eb-8b06-03db3ef1abad.jpeg" + isUserWishlistExistById, +} from "../middlewares/validation"; +import { + shopSchema, + productSchema, + statisticsSchema, + statusSchema, +} from "../modules/product/validation/productValidation"; +import upload from "../helpers/multer"; + +const router: Router = Router(); + +router.post( + "/seller-create-product", + userAuthorization(["seller"]), + upload.array("images"), + transformFilesToBody, + validation(productSchema), + isProductExist, + productController.sellerCreateProduct +); +router.post( + "/seller-create-shop", + userAuthorization(["seller"]), + validation(shopSchema), + isShopExist, + productController.sellerCreateShop +); +router.delete( + "/seller-delete-product/:id", + userAuthorization(["seller"]), + isProductExist, + productController.sellerDeleteProduct +); +router.post( + "/seller-statistics", + validation(statisticsSchema), + userAuthorization(["seller"]), + productController.sellerGetStatistics +); +router.put( + "/seller-update-product/:id", + userAuthorization(["seller"]), + upload.array("images"), + transformFilesToBody, + validation(productSchema), + isProductExist, + productController.sellerUpdateProduct ); -const imageBuffer = fs.readFileSync(imagePath); -describe("Product and Shops API Tests", () => { - let token: string; - before((done) => { - router() - .post("/api/auth/login") - .send({ email: "dj@gmail.com", password: "Password@123" }) - .end((err, res) => { - token = res.body.data.token; - done(err); - }); - }); - describe("POST /api/shop/seller-create-shop", () => { - it("should give an error", (done) => { - router() - .get("/api/shop/seller-get-products") - .set("Authorization", `Bearer ${token}`) - .end((err, res) => { - expect(res).to.have.status(404); - done(); - }); - }); - - it("should create a Shop successfully", (done) => { - router() - .post("/api/shop/seller-create-shop") - .set("Authorization", `Bearer ${token}`) - .send({ - name: "New Shops", - description: "A new Shops description", - }) - .end((err, res) => { - expect(res).to.have.status(201); - expect(res.body).to.have.property( - "message", - "Shop created successfully" - ); - expect(res.body.data.shop).to.include({ - name: "New Shops", - description: "A new Shops description", - }); - done(); - }); - }); - - it("should return a validation error when name is missing", (done) => { - router() - .post("/api/shop/seller-create-shop") - .set("Authorization", `Bearer ${token}`) - .send({ description: "A new Shops description" }) - .end((err, res) => { - expect(res).to.have.status(400); - expect(res.body).to.have.property("status", 400); - expect(res.body).to.have.property("message", "Name is required"); - done(); - }); - }); - - it("should Already have a shop", (done) => { - router() - .post("/api/shop/seller-create-shop") - .set("Authorization", `Bearer ${token}`) - .send({ - name: "New Shops", - description: "A new Shops description", - }) - .end((err, res) => { - expect(res).to.have.status(httpStatus.BAD_REQUEST); - expect(res.body).to.have.property("message", "Already have a shop."); - expect(res.body).to.have.property("data"); - done(); - }); - }); - }); - - describe("POST /api/shop/seller-create-product", () => { - let productId: string; - it("should create a product successfully", (done) => { - router() - .post("/api/shop/seller-create-product") - .set("Authorization", `Bearer ${token}`) - .field("name", "New Product") - .field("description", "A new product description") - .field("price", "99.99") - .field("category", "Electronics") - .field("quantity", "10") - .field("bonus", "10%") - .field("discount", "10%") - .field("expiryDate", "2040-4-4") - .attach( - "images", - imageBuffer, - "69180880-2138-11eb-8b06-03db3ef1abad.jpeg" - ) - .attach( - "images", - imageBuffer, - "69180880-2138-11eb-8b06-03db3ef1abad.jpeg" - ) - .attach( - "images", - imageBuffer, - "69180880-2138-11eb-8b06-03db3ef1abad.jpeg" - ) - .attach( - "images", - imageBuffer, - "69180880-2138-11eb-8b06-03db3ef1abad.jpeg" - ) - .end((err, res) => { - expect(res).to.have.status(httpStatus.CREATED); - expect(res.body).to.have.property( - "message", - "Product created successfully" - ); - expect(res.body.data.product).to.include({ - name: "New Product", - description: "A new product description", - }); - productId = res.body.data.product.id; - done(); - }); - }); - - it("should get available products successfully", (done) => { - router() - .get("/api/shop/user-get-products") - .end((err, res) => { - expect(res).to.have.status(httpStatus.OK); - expect(res.body).to.have.property("status", httpStatus.OK); - done(); - }); - }); - - it("should update a product successfully", (done) => { - router() - .put(`/api/shop/seller-update-product/${productId}`) - .set("Authorization", `Bearer ${token}`) - .field("name", "Updated Product") - .field("description", "An updated product description") - .field("price", "88.44") - .field("category", "Electronics") - .field("quantity", "15") - .field("bonus", "15%") - .field("discount", "11%") - .field("expiryDate", "2040-11-12") - .attach( - "images", - imageBuffer, - "69180880-2138-11eb-8b06-03db3ef1abad.jpeg" - ) - .attach( - "images", - imageBuffer, - "69180880-2138-11eb-8b06-03db3ef1abad.jpeg" - ) - .attach( - "images", - imageBuffer, - "69180880-2138-11eb-8b06-03db3ef1abad.jpeg" - ) - .attach( - "images", - imageBuffer, - "69180880-2138-11eb-8b06-03db3ef1abad.jpeg" - ) - .end((err, res) => { - expect(res).to.have.status(httpStatus.OK); - expect(res.body).to.have.property( - "message", - "Product updated successfully" - ); - done(); - }); - }); - - it("should update product status to unavailable", (done) => { - router() - .put(`/api/shop/seller-update-product-status/${productId}`) - .set("Authorization", `Bearer ${token}`) - .send({ status: "unavailable" }) - .end((err, res) => { - expect(res).to.have.status(200); - expect(res.body).to.have.property( - "message", - "Status updated successfully." - ); - done(); - }); - }); - - it("should get all products", (done) => { - router() - .get("/api/shop/seller-get-products") - .set("Authorization", `Bearer ${token}`) - .end((err, res) => { - expect(res).to.have.status(200); - expect(res.body).to.have.property( - "message", - "All products fetched successfully." - ); - done(); - }); - }); - - it("should return a validation error when images are missing", (done) => { - router() - .post("/api/shop/seller-create-product") - .set("Authorization", `Bearer ${token}`) - .field("name", "New Product") - .field("description", "A new product description") - .field("price", "99.99") - .field("category", "Electronics") - .field("quantity", "10") - .field("bonus", "10%") - .field("discount", "10%") - .field("expiryDate", "2040-4-4") - .end((err, res) => { - expect(res).to.have.status(400); - expect(res.body).to.have.property("status", 400); - expect(res.body).to.have.property( - "message", - "Images must have at least 4 items" - ); - done(); - }); - }); - - it("should delete items in collection", (done) => { - router() - .delete(`/api/shop/seller-delete-product/${productId}`) - .set("Authorization", `Bearer ${token}`) - .end((error, response) => { - expect(response.status).to.be.equal(httpStatus.OK); - expect(response.body).to.have.property( - "message", - "Product deleted successfully" - ); - done(error); - }); - }); - }); - describe("Multer Middleware", () => { - it("should return an error if a non-image file is uploaded", (done) => { - const req = {} as any; - const file = { - originalname: "test.txt", - } as Express.Multer.File; - - const cb = (err: Error | null) => { - try { - expect(err).to.be.an("error"); - expect(err!.message).to.equal("Only images are allowed"); - done(); - } catch (error) { - done(error); - } - }; - - fileFilter(req, file, cb); - }); - }); -}); - -describe("transformFilesToBody Middleware", () => { - it("should return 400 if no files are provided", () => { - const req = { - files: null, - } as any; - const res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - } as any; - const next = sinon.spy(); - - transformFilesToBody(req, res, next); - - expect(res.status.calledWith(400)).to.be.true; - expect( - res.json.calledWith({ - status: 400, - message: "Images are required", - }) - ).to.be.true; - }); -}); - -describe("Seller test cases", () => { - let token: string; - before((done) => { - router() - .post("/api/auth/login") - .send({ email: "seller@gmail.com", password: "Password@123" }) - .end((err, res) => { - token = res.body.data.token; - done(err); - }); - }); - - it("should return statistics of Seller in specified timeframe", (done) => { - router() - .post("/api/shop/seller-statistics") - .set("Authorization", `Bearer ${token}`) - .send({ - startDate: "2024-01-01", - endDate: "2024-12-31", - }) - .end((error, response) => { - expect(response.status).to.equal(httpStatus.OK); - expect(response.body).to.be.a("object"); - expect(response.body).to.have.property("data"); - expect(response.body.message).to.be.a("string"); - done(error); - }); - }); - - it("should catch server error during fetching statistics", (done) => { - sinon - .stub(productRepositories, "getOrdersPerTimeframe") - .throws(new Error("Database error")); - router() - .post("/api/shop/seller-statistics") - .set("Authorization", `Bearer ${token}`) - .send({ - startDate: "2024-01-01", - endDate: "2024-12-31", - }) - .end((err, res) => { - expect(res).to.have.status(httpStatus.INTERNAL_SERVER_ERROR); - done(err); - }); - }); -}); - -describe("internal server error", () => { - let token: string; - before((done) => { - router() - .post("/api/auth/login") - .send({ email: "seller3@gmail.com", password: "Password@123" }) - .end((err, res) => { - token = res.body.data.token; - done(err); - }); - }); - - it("should handle errors and return 500 status", (done) => { - sinon - .stub(productRepositories, "createShop") - .throws(new Error("Internal Server Error")); - router() - .post("/api/shop/seller-create-shop") - .set("Authorization", `Bearer ${token}`) - .send({ - name: "International Server Error", - description: "A new Shops description", - }) - .end((err, res) => { - expect(res).to.have.status(httpStatus.INTERNAL_SERVER_ERROR); - expect(res.body).to.have.property("error", "Internal Server Error"); - done(err); - }); - }); -}); - -describe("Product Middleware", () => { - describe("isProductExist", () => { - let req, res, next; - - beforeEach(() => { - req = { - user: { id: 1 }, - body: { name: "Product1" }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - }; - next = sinon.stub(); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should return 404 if no shop is found", async () => { - sinon.stub(productRepositories, "findShopByAttributes").resolves(null); - - await isProductExist(req, res, next); - - expect(res.status).to.have.been.calledWith(httpStatus.NOT_FOUND); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.NOT_FOUND, - message: "Not shop found.", - }); - }); - - it("should return 400 if the product already exists", async () => { - sinon - .stub(productRepositories, "findShopByAttributes") - .resolves({ id: 1 }); - sinon - .stub(productRepositories, "findByModelsAndAttributes") - .resolves(true); - - await isProductExist(req, res, next); - - expect(res.status).to.have.been.calledWith(httpStatus.BAD_REQUEST); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.BAD_REQUEST, - message: "Please update the quantities.", - }); - }); - - it("should call next if product does not exist", async () => { - sinon - .stub(productRepositories, "findShopByAttributes") - .resolves({ id: 1 }); - sinon - .stub(productRepositories, "findByModelsAndAttributes") - .resolves(false); - - await isProductExist(req, res, next); - - expect(req.shop).to.deep.equal({ id: 1 }); - expect(next).to.have.been.called; - }); - - it("should return 500 on error", async () => { - sinon - .stub(productRepositories, "findShopByAttributes") - .throws(new Error("Internal Server Error")); - - await isProductExist(req, res, next); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - message: "Internal Server Error", - }); - }); - }); - - describe("isShopExist", () => { - let req, res, next; - - beforeEach(() => { - req = { user: { id: 1 } }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - }; - next = sinon.stub(); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should call next if no shop is found", async () => { - sinon.stub(productRepositories, "findShopByAttributes").resolves(null); - - await isShopExist(req, res, next); - - expect(next).to.have.been.called; - }); - - it("should return 400 if a shop already exists", async () => { - sinon - .stub(productRepositories, "findShopByAttributes") - .resolves({ id: 1 }); - - await isShopExist(req, res, next); - - expect(res.status).to.have.been.calledWith(httpStatus.BAD_REQUEST); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.BAD_REQUEST, - message: "Already have a shop.", - data: { shop: { id: 1 } }, - }); - }); - - it("should return 500 on error", async () => { - sinon - .stub(productRepositories, "findShopByAttributes") - .throws(new Error("Internal Server Error")); - - await isShopExist(req, res, next); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - message: "Internal Server Error", - }); - }); - }); -}); - -describe("Product Controller", () => { - let token: string; - - before((done) => { - router() - .post("/api/auth/login") - .send({ email: "seller3@gmail.com", password: "Password@123" }) - .end((err, res) => { - token = res.body.data.token; - done(err); - }); - }); - - afterEach(() => { - sinon.restore(); - }); - - let req: any, res: any, next: any; - - beforeEach(() => { - req = {}; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - }; - next = sinon.stub(); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should return 500 if an error occurs in updateProductStatus", async () => { - const error = new Error("Internal server error"); - sinon.stub(productRepositories, "updateProductByAttributes").throws(error); - - req.body = { status: "available" }; - req.params = { id: "123" }; - - await productController.updateProductStatus(req, res); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - error: error.message, - }); - }); - - it("should return 500 if an error occurs in userGetAvailableProducts", async () => { - const error = new Error("Internal server error"); - sinon.stub(productRepositories, "userGetProducts").throws(error); - - await productController.userGetProducts(req, res); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - }); - - describe("sellerCreateProduct", () => { - let req, res; - - beforeEach(() => { - req = { - shop: { id: 1 }, - files: [{ filename: "image1.jpg" }, { filename: "image2.jpg" }], - body: { name: "Product1" }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - }; - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should handle internal server error", async () => { - sinon.stub(req.files, "map").throws(new Error("File upload error")); - - await productController.sellerCreateProduct(req, res); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - error: "File upload error", - }); - }); - }); -}); - -describe("Admin Controller", () => { - describe("adminGetUsers", () => { - let req, res; - - beforeEach(() => { - req = {}; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - }; - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should handle internal server error", async () => { - sinon - .stub(userRepositories, "getAllUsers") - .throws(new Error("Internal Server Error")); - - await userControllers.adminGetUsers(req, res); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - message: "Internal Server Error", - }); - }); - }); - - describe("adminGetUser", () => { - let req, res; - - beforeEach(() => { - req = { params: { id: 1 } }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - }; - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should handle internal server error", async () => { - sinon - .stub(authRepositories, "findUserByAttributes") - .throws(new Error("Internal Server Error")); - - await userControllers.adminGetUser(req, res); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - message: "Internal Server Error", - }); - }); - }); - - describe("getUserDetails", () => { - let req, res; - - beforeEach(() => { - req = { user: { id: 1 } }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - }; - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should handle internal server error", async () => { - sinon - .stub(authRepositories, "findUserByAttributes") - .throws(new Error("Internal Server Error")); - - await userControllers.getUserDetails(req, res); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - message: "Internal Server Error", - }); - }); - }); - - describe("updateUserProfile", () => { - let req, res; - - beforeEach(() => { - req = { - user: { id: 1 }, - file: { - path: "../test/69180880-2138-11eb-8b06-03db3ef1abad.jpeg", - filename: "69180880-2138-11eb-8b06-03db3ef1abad.jpeg", - }, - body: { name: "John Doe" }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - }; - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should handle internal server error", async () => { - sinon - .stub(userRepositories, "updateUserProfile") - .throws(new Error("Internal Server Error")); - - await userControllers.updateUserProfile(req, res); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - }); - it("should handle missing required parameter - file", async () => { - delete req.file; - await userControllers.updateUserProfile(req, res); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - }); - }); -}); - -describe("Change Password Test Cases", () => { - let token: string = null; - before((done) => { - router() - .post("/api/auth/login") - .send({ - email: "admin@gmail.com", - password: "Newpassword#12", - }) - .end((error, response) => { - token = response.body.data.token; - done(error); - }); - }); - it("should change the password when the user changes the password", (done) => { - router() - .put("/api/user/change-password") - .set("authorization", `Bearer ${token}`) - .send({ - oldPassword: "Newpassword#12", - newPassword: "NewPassword!123", - confirmPassword: "NewPassword!123", - }) - .end((err, res) => { - expect(res).to.have.status(httpStatus.OK); - expect(res.body).to.be.an("object"); - expect(res.body).to.have.property( - "message", - "Password updated successfully" - ); - done(err); - }); - }); - it("should return an error if the password is invalid", (done) => { - router() - .put("/api/user/change-password") - .set("authorization", `Bearer ${token}`) - .send({ - oldPassword: "Newpassword#12", - newPassword: "NewPassword!123", - confirmPassword: "NewPassword!123", - }) - .end((err, res) => { - expect(res).to.have.status(httpStatus.BAD_REQUEST); - expect(res.body).to.be.an("object"); - expect(res.body).to.have.property("message", "Invalid password."); - done(err); - }); - }); -}); - -describe("isPaginated middleware", () => { - let req: Partial; - let res: Partial; - let nextCalled: boolean; - - beforeEach(() => { - req = { - query: {}, - pagination: { - limit: undefined, - page: undefined, - offset: undefined, - }, - }; - res = {}; - nextCalled = false; - }); - - const next: NextFunction = () => { - nextCalled = true; - }; - - it("should set limit and page parameters if provided in the request query", () => { - req.query.limit = "10"; - req.query.page = "1"; - - isPaginated(req as Request, res as Response, next); - - expect(req.pagination).to.deep.equal({ - limit: 10, - page: 1, - offset: 0, - }); - expect(nextCalled).to.be.true; - }); - - it("should set limit and page as undefined if not provided in the request query", () => { - isPaginated(req as Request, res as Response, next); - - expect(req.pagination).to.deep.equal({ - limit: undefined, - page: undefined, - offset: undefined, - }); - expect(nextCalled).to.be.true; - }); - - it("should calculate offset if both limit and page are provided in the request query", () => { - req.query.limit = "10"; - req.query.page = "2"; - - isPaginated(req as Request, res as Response, next); - - expect(req.pagination).to.deep.equal({ - limit: 10, - page: 2, - offset: 10, - }); - expect(nextCalled).to.be.true; - }); -}); - -describe("User filter products", () => { - it("Should reject if one of Min and Max Price Provided without other", (done) => { - router() - .get("/api/shop/user-search-products?minprice=1") - .end((error, response) => { - expect(response.status).to.equal(httpStatus.BAD_REQUEST); - expect(response.body).to.be.an("object"); - expect(response.body).to.have.property("message"); - done(error); - }); - }); - it("Should reject if min price is greater than max price", (done) => { - router() - .get("/api/shop/user-search-products?minprice=10&maxprice=1") - .end((error, response) => { - expect(response.status).to.equal(httpStatus.BAD_REQUEST); - expect(response.body).to.be.an("object"); - expect(response.body).to.have.property("message"); - done(error); - }); - }); - it("Should return data if data are provided", (done) => { - router() - .get( - "/api/shop/user-search-products?minprice=10&maxprice=100&category=Cosmetics&name=l" - ) - .end((error, response) => { - expect(response.status).to.equal(httpStatus.OK); - expect(response.body).to.be.an("object"); - expect(response.body).to.have.property("data"); - done(error); - }); - }); -}); - -describe("sellerViewSpecificProduct", () => { - let req: Partial; - let res: Partial; - let findProductStub: sinon.SinonStub; - - beforeEach(() => { - req = { - params: { id: "test-product-id" }, - shop: { id: "test-shop-id" }, - }; - - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - }; - - findProductStub = sinon.stub(productRepositories, "sellerGetProductById"); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should fetch product successfully", async () => { - const productData = { id: "test-product-id", name: "Test Product" }; - findProductStub.resolves(productData); - - await productController.sellerGetProduct( - req as ExtendRequest, - res as Response - ); - expect(res.status).to.have.been.calledWith(httpStatus.OK); - expect(res.json).to.have.been.calledWith({ - message: "Product fetched successfully.", - data: productData, - }); - }); - - it("should handle errors", async () => { - const error = new Error("Something went wrong"); - findProductStub.rejects(error); - - await productController.sellerGetProduct( - req as ExtendRequest, - res as Response - ); - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - error: error.message, - }); - }); -}); - -describe("userGetProduct", () => { - let req; - let res; - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - req = { - params: { id: "product-id" }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - }; - }); - - afterEach(() => { - sandbox.restore(); - }); - - it("should return product details when product is found", async () => { - const mockProduct = { - id: "product-id", - name: "Product Name", - price: 100, - description: "Product Description", - }; - - sandbox.stub(productRepositories, "findProductById").resolves(mockProduct); - - await productController.userGetProduct(req, res); - - expect(res.status).to.have.been.calledWith(httpStatus.OK); - expect(res.json).to.have.been.calledWith({ - message: "Products is fetched successfully.", - product: mockProduct, - }); - }); - - it("should handle errors properly", async () => { - const error = new Error("Something went wrong"); - sandbox.stub(productRepositories, "findProductById").throws(error); - - await productController.userGetProduct(req, res); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - error: error.message, - }); - }); -}); - -describe("buyerAddProductToWishList", () => { - let mockReq: any; - let mockRes: Partial; - let addProductToWishListStub: SinonStub; - - beforeEach(() => { - mockReq = { - params: { id: "validProductId" }, - user: { id: "validUserId" }, - }; - mockRes = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - } as Partial; - - addProductToWishListStub = sinon.stub( - productRepositories, - "addProductToWishList" - ); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should add product to wishlist successfully", async () => { - const mockProduct = { - productId: "validProductId", - userId: "validUserId", - }; - addProductToWishListStub.resolves(mockProduct); - - await productController.buyerAddProductToWishList( - mockReq, - mockRes as Response - ); - - expect(mockRes.status).to.have.been.calledWith(httpStatus.OK); - }); - - it("should handle internal server error", async () => { - const errorMessage = "Database error"; - addProductToWishListStub.rejects(new Error(errorMessage)); - - await productController.buyerAddProductToWishList( - mockReq, - mockRes as Response - ); - - expect(mockRes.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(mockRes.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - error: errorMessage, - }); - }); -}); - -describe("isProductExistToWishlist Middleware", () => { - let req: Partial; - let res: Partial; - let next: NextFunction; - let findProductfromWishListStub: sinon.SinonStub; - - beforeEach(() => { - req = { - params: { id: "productId" }, - user: { id: "userId" } as any, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - } as Partial; - next = sinon.stub() as unknown as NextFunction; - findProductfromWishListStub = sinon.stub( - productRepositories, - "findProductfromWishList" - ); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should return 200 and product data if product exists in wishlist", async () => { - const product = { id: "productId", name: "Product Name" }; - findProductfromWishListStub.resolves(product); - - await isProductExistToWishlist(req as Request, res as Response, next); - - expect(findProductfromWishListStub).to.have.been.calledWith( - "productId", - "userId" - ); - expect(res.status).to.have.been.calledWith(httpStatus.OK); - expect(res.json).to.have.been.calledWith({ - message: "Product is added to wishlist successfully.", - data: { product }, - }); - expect(next).not.to.have.been.called; - }); - - it("should call next if product does not exist in wishlist", async () => { - findProductfromWishListStub.resolves(null); - - await isProductExistToWishlist(req as Request, res as Response, next); - - expect(findProductfromWishListStub).to.have.been.calledWith( - "productId", - "userId" - ); - expect(next).to.have.been.called; - expect(res.status).not.to.have.been.called; - expect(res.json).not.to.have.been.called; - }); - - it("should return 500 if an error occurs", async () => { - const errorMessage = "Internal Server Error"; - findProductfromWishListStub.rejects(new Error(errorMessage)); - - await isProductExistToWishlist(req as Request, res as Response, next); - - expect(findProductfromWishListStub).to.have.been.calledWith( - "productId", - "userId" - ); - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - message: errorMessage, - }); - expect(next).not.to.have.been.called; - }); -}); -describe("Wishlist Middlewares", () => { - let req: Partial; - let res: Partial; - let next: NextFunction; - let findProductFromWishListByUserIdStub: SinonStub; - let findProductfromWishListStub: SinonStub; - - beforeEach(() => { - req = { - user: { id: "userId" }, - params: { id: "productId" }, - } as any; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub().returnsThis(), - } as Partial; - next = sinon.stub() as unknown as NextFunction; - findProductFromWishListByUserIdStub = sinon.stub( - productRepositories, - "findProductFromWishListByUserId" - ); - findProductfromWishListStub = sinon.stub( - productRepositories, - "findProductfromWishList" - ); - }); - - afterEach(() => { - sinon.restore(); - }); - - describe("isUserWishlistExist Middleware", () => { - it("should return 404 if no wishlist is found", async () => { - findProductFromWishListByUserIdStub.resolves(null); - - await isUserWishlistExist(req as Request, res as Response, next); - - expect(res.status).to.have.been.calledWith(httpStatus.NOT_FOUND); - expect(res.json).to.have.been.calledWith({ - message: "No wishlist Found", - }); - expect(next).not.to.have.been.called; - }); - - it("should return 404 if wishlist is an empty array", async () => { - findProductFromWishListByUserIdStub.resolves([]); - - await isUserWishlistExist(req as Request, res as Response, next); - - expect(res.status).to.have.been.calledWith(httpStatus.NOT_FOUND); - expect(res.json).to.have.been.calledWith({ - message: "No wishlist Found", - }); - expect(next).not.to.have.been.called; - }); - - it("should call next if wishlist is found", async () => { - const wishList = [{ id: "item1" }]; - findProductFromWishListByUserIdStub.resolves(wishList); - - await isUserWishlistExist(req as Request, res as Response, next); - - expect(next).to.have.been.called; - expect(res.status).not.to.have.been.called; - expect(res.json).not.to.have.been.called; - }); - - it("should return 500 if an error occurs", async () => { - const errorMessage = "Internal server error"; - findProductFromWishListByUserIdStub.rejects(new Error(errorMessage)); - - await isUserWishlistExist(req as Request, res as Response, next); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - message: errorMessage, - }); - expect(next).not.to.have.been.called; - }); - }); - - describe("isUserWishlistExistById Middleware", () => { - it("should return 404 if product is not found in wishlist", async () => { - findProductfromWishListStub.resolves(null); - - await isUserWishlistExistById(req as Request, res as Response, next); - - expect(res.status).to.have.been.calledWith(httpStatus.NOT_FOUND); - expect(res.json).to.have.been.calledWith({ - message: "Product Not Found From WishList", - }); - expect(next).not.to.have.been.called; - }); - - it("should call next if product is found in wishlist", async () => { - const product = { id: "productId" }; - findProductfromWishListStub.resolves(product); - - await isUserWishlistExistById(req as Request, res as Response, next); - - expect(next).to.have.been.called; - expect(res.status).not.to.have.been.called; - expect(res.json).not.to.have.been.called; - }); - - it("should return 500 if an error occurs", async () => { - const errorMessage = "Internal server error"; - findProductfromWishListStub.rejects(new Error(errorMessage)); - - await isUserWishlistExistById(req as Request, res as Response, next); - - expect(res.status).to.have.been.calledWith( - httpStatus.INTERNAL_SERVER_ERROR - ); - expect(res.json).to.have.been.calledWith({ - status: httpStatus.INTERNAL_SERVER_ERROR, - message: errorMessage, - }); - expect(next).not.to.have.been.called; - }); - }); -}); - -describe("Wishlist Routes", () => { - let deleteAllWishListByUserIdStub: SinonStub; - let deleteProductFromWishListByIdStub: SinonStub; - beforeEach(() => { - deleteAllWishListByUserIdStub = sinon.stub( - productRepositories, - "deleteAllWishListByUserId" - ); - deleteProductFromWishListByIdStub = sinon.stub( - productRepositories, - "deleteProductFromWishListById" - ); - }); +router.put( + "/seller-update-product-status/:id", + userAuthorization(["seller"]), + validation(statusSchema), + productController.updateProductStatus +); +router.get( + "/seller-get-products", + userAuthorization(["seller"]), + isSellerShopExist, + isPaginated, + productController.sellerGetProducts +); - afterEach(() => { - sinon.restore(); - }); +router.get( + "/user-get-products", + isPaginated, + productController.userGetProducts +); - describe("buyerDeleteAllProductFromWishlist", () => { - it("should clear all products from wishlist", async () => { - deleteAllWishListByUserIdStub.resolves(); - const req = { user: { id: "user-id" } }; - const res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - await productController.buyerDeleteAllProductFromWishlist( - req as any, - res as any - ); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - message: "Your wishlist is cleared successfully.", - }) - ).to.be.true; - }); +router.get( + "/user-search-products", + isSearchFiltered, + isPaginated, + productController.userSearchProducts +); - it("should return 500 if an error occurs", async () => { - const errorMessage = "Internal server error"; - deleteAllWishListByUserIdStub.rejects(new Error(errorMessage)); - const req = { user: { id: "user-id" } }; - const res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - await productController.buyerDeleteAllProductFromWishlist( - req as any, - res as any - ); - expect(res.status.calledWith(500)).to.be.true; - expect( - res.json.calledWith({ - message: "Internal server error", - error: errorMessage, - }) - ).to.be.true; - }); - }); +router.get( + "/user-get-product/:id", + isProductExistById, + productController.userGetProduct +); +router.get( + "/seller-get-product/:id", + userAuthorization(["seller"]), + isSellerShopExist, + isProductExistById, + productController.sellerGetProduct +); - describe("buyerDeleteProductFromWishList", () => { - it("should remove a product from wishlist", async () => { - deleteProductFromWishListByIdStub.resolves(); - const req = { - params: { id: "product-id" }, - user: { id: "user-id" }, - }; - const res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - await productController.buyerDeleteProductFromWishList( - req as any, - res as any - ); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - message: "The product removed from wishlist successfully.", - }) - ).to.be.true; - }); +router.post( + "/buyer-add-product-wishList/:id", + userAuthorization(["buyer"]), + isProductExistToWishlist, + productController.buyerAddProductToWishList +); +router.delete( + "/delete-whishlist-products", + userAuthorization(["buyer"]), + isUserWishlistExist, + productController.buyerDeleteAllProductFromWishlist +); +router.delete( + "/delete-whishlist-product/:id", + userAuthorization(["buyer"]), + isUserWishlistExistById, + productController.buyerDeleteProductFromWishList +); - it("should return 500 if an error occurs", async () => { - const errorMessage = "Internal server error"; - deleteProductFromWishListByIdStub.rejects(new Error(errorMessage)); - const req = { - params: { id: "product-id" }, - user: { id: "user-id" }, - }; - const res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - await productController.buyerDeleteProductFromWishList( - req as any, - res as any - ); - expect(res.status.calledWith(500)).to.be.true; - expect( - res.json.calledWith({ - message: "Internal server error", - error: errorMessage, - }) - ).to.be.true; - }); - }); -}); +export default router; diff --git a/swagger.json b/swagger.json index 8f7f001d..347eb95b 100644 --- a/swagger.json +++ b/swagger.json @@ -2651,6 +2651,69 @@ } } }, + "/api/cart/buyer-clear-carts": { + "delete": { + "tags": ["Buyer Routes"], + "summary": "Buyer clear all carts", + "description": "Buyer delete all products in all their carts", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "All carts cleared successfully!" + }, + "404": { + "description": "No cart." + }, + "401": { + "description": "Not logged in" + }, + "500": { + "description": "Internal Server error" + } + } + } + }, + "/api/cart/buyer-clear-cart/{id}": { + "delete": { + "tags": ["Buyer Routes"], + "summary": "Buyer clear cart", + "description": "Buyer delete all products in the cart", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + }, + "description": "Cart Id" + } + ], + "responses": { + "200": { + "description": "All products in cart cleared successfully!" + }, + "404": { + "description": "No cart." + }, + "401": { + "description": "Not logged in" + }, + "500": { + "description": "Internal Server error" + } + } + } + }, "/api/cart/create-update-cart": { "post": { "tags": ["Buyer Routes"],