From d8ed9abebf2c182e99aa309828ade4dc703cebfd Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 22 Sep 2025 12:53:44 -0700 Subject: [PATCH] add gitignore --- .gitignore | 3 +- server/.gitignore | 3 +- server/package-lock.json | 530 ++++- server/package.json | 7 +- server/src/config/routes.js | 22 +- .../src/routes/{ => v1}/announcementsRoute.js | 0 server/src/routes/{ => v1}/authRoute.js | 6 +- server/src/routes/{ => v1}/checkRoute.js | 7 +- server/src/routes/{ => v1}/diagnosticRoute.js | 4 +- server/src/routes/{ => v1}/inviteRoute.js | 4 +- server/src/routes/{ => v1}/logRoutes.js | 2 +- .../routes/{ => v1}/maintenanceWindowRoute.js | 2 +- server/src/routes/{ => v1}/monitorRoute.js | 4 +- .../src/routes/{ => v1}/notificationRoute.js | 0 server/src/routes/{ => v1}/queueRoute.js | 2 +- server/src/routes/{ => v1}/settingsRoute.js | 2 +- server/src/routes/{ => v1}/statusPageRoute.js | 2 +- .../tests/controllers/authController.test.js | 962 --------- .../tests/controllers/checkController.test.js | 372 ---- .../tests/controllers/controllerUtils.test.js | 157 -- .../controllers/inviteController.test.js | 202 -- .../maintenanceWindowController.test.js | 422 ---- .../controllers/monitorController.test.js | 1116 ---------- .../tests/controllers/queueController.test.js | 176 -- .../controllers/settingsController.test.js | 109 - .../controllers/statusPageController.test.js | 127 -- server/tests/db/checkModule.test.js | 589 ------ server/tests/db/hardwareCheckModule.test.js | 136 -- server/tests/db/inviteModule.test.js | 103 - .../tests/db/maintenanceWindowModule.test.js | 255 --- server/tests/db/monitorModule.test.js | 1865 ----------------- server/tests/db/notificationModule.test.js | 76 - server/tests/db/pageSpeedCheckModule.test.js | 63 - server/tests/db/recoveryModule.test.js | 168 -- server/tests/db/settingsModule.test.js | 56 - server/tests/db/statusPageModule.test.js | 70 - server/tests/db/userModule.test.js | 294 --- server/tests/services/emailService.test.js | 168 -- server/tests/services/jobQueue.test.js | 813 ------- server/tests/services/networkService.test.js | 439 ---- .../services/notificationService.test.js | 285 --- server/tests/services/settingsService.test.js | 140 -- server/tests/services/statusService.test.js | 255 --- server/tests/utils/dataUtils.test.js | 61 - server/tests/utils/imageProcessing.test.js | 53 - server/tests/utils/logger.test.js | 139 -- server/tests/utils/messages.test.js | 23 - server/tests/utils/utils.test.js | 50 - server/tsconfig.json | 26 + 49 files changed, 590 insertions(+), 9780 deletions(-) rename server/src/routes/{ => v1}/announcementsRoute.js (100%) rename server/src/routes/{ => v1}/authRoute.js (85%) rename server/src/routes/{ => v1}/checkRoute.js (76%) rename server/src/routes/{ => v1}/diagnosticRoute.js (77%) rename server/src/routes/{ => v1}/inviteRoute.js (82%) rename server/src/routes/{ => v1}/logRoutes.js (85%) rename server/src/routes/{ => v1}/maintenanceWindowRoute.js (91%) rename server/src/routes/{ => v1}/monitorRoute.js (94%) rename server/src/routes/{ => v1}/notificationRoute.js (100%) rename server/src/routes/{ => v1}/queueRoute.js (93%) rename server/src/routes/{ => v1}/settingsRoute.js (90%) rename server/src/routes/{ => v1}/statusPageRoute.js (93%) delete mode 100755 server/tests/controllers/authController.test.js delete mode 100755 server/tests/controllers/checkController.test.js delete mode 100755 server/tests/controllers/controllerUtils.test.js delete mode 100755 server/tests/controllers/inviteController.test.js delete mode 100755 server/tests/controllers/maintenanceWindowController.test.js delete mode 100755 server/tests/controllers/monitorController.test.js delete mode 100755 server/tests/controllers/queueController.test.js delete mode 100755 server/tests/controllers/settingsController.test.js delete mode 100755 server/tests/controllers/statusPageController.test.js delete mode 100755 server/tests/db/checkModule.test.js delete mode 100755 server/tests/db/hardwareCheckModule.test.js delete mode 100755 server/tests/db/inviteModule.test.js delete mode 100755 server/tests/db/maintenanceWindowModule.test.js delete mode 100755 server/tests/db/monitorModule.test.js delete mode 100755 server/tests/db/notificationModule.test.js delete mode 100755 server/tests/db/pageSpeedCheckModule.test.js delete mode 100755 server/tests/db/recoveryModule.test.js delete mode 100755 server/tests/db/settingsModule.test.js delete mode 100755 server/tests/db/statusPageModule.test.js delete mode 100755 server/tests/db/userModule.test.js delete mode 100755 server/tests/services/emailService.test.js delete mode 100755 server/tests/services/jobQueue.test.js delete mode 100755 server/tests/services/networkService.test.js delete mode 100755 server/tests/services/notificationService.test.js delete mode 100755 server/tests/services/settingsService.test.js delete mode 100755 server/tests/services/statusService.test.js delete mode 100755 server/tests/utils/dataUtils.test.js delete mode 100755 server/tests/utils/imageProcessing.test.js delete mode 100755 server/tests/utils/logger.test.js delete mode 100755 server/tests/utils/messages.test.js delete mode 100755 server/tests/utils/utils.test.js create mode 100644 server/tsconfig.json diff --git a/.gitignore b/.gitignore index 600d2d33b..172702b40 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.vscode \ No newline at end of file +.vscode +.VSCodeCounter \ No newline at end of file diff --git a/server/.gitignore b/server/.gitignore index 4bc621f83..9ae4e58ed 100755 --- a/server/.gitignore +++ b/server/.gitignore @@ -8,4 +8,5 @@ coverage .clinic node_modules .vscode/* -public \ No newline at end of file +public +dist \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json index 3e1130dc4..e2affbca0 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -54,7 +54,8 @@ "mocha": "11.1.0", "nodemon": "3.1.9", "prettier": "^3.3.3", - "sinon": "19.0.2" + "sinon": "19.0.2", + "tsx": "4.20.5" } }, "node_modules/@asamuzakjp/css-color": { @@ -258,6 +259,448 @@ "tslib": "^2.4.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.8.0.tgz", @@ -3163,6 +3606,48 @@ "node": ">= 0.4" } }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -4079,6 +4564,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -7578,6 +8076,16 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/responselike": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", @@ -8551,6 +9059,26 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tsx": { + "version": "4.20.5", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz", + "integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", diff --git a/server/package.json b/server/package.json index d879776a8..c57c3a4ce 100755 --- a/server/package.json +++ b/server/package.json @@ -6,7 +6,9 @@ "type": "module", "scripts": { "test": "c8 mocha", - "dev": "nodemon src/index.js", + "dev": "nodemon --exec tsx src/index.js", + "start": "nodemon ./dist/index.js", + "build": "tsc", "lint": "eslint .", "lint-fix": "eslint --fix .", "format": "prettier --write .", @@ -61,6 +63,7 @@ "mocha": "11.1.0", "nodemon": "3.1.9", "prettier": "^3.3.3", - "sinon": "19.0.2" + "sinon": "19.0.2", + "tsx": "4.20.5" } } diff --git a/server/src/config/routes.js b/server/src/config/routes.js index 7056393f9..2ec93a2dc 100644 --- a/server/src/config/routes.js +++ b/server/src/config/routes.js @@ -1,17 +1,17 @@ import { verifyJWT } from "../middleware/verifyJWT.js"; import { authApiLimiter } from "../middleware/rateLimiter.js"; -import AuthRoutes from "../routes/authRoute.js"; -import InviteRoutes from "../routes/inviteRoute.js"; -import MonitorRoutes from "../routes/monitorRoute.js"; -import CheckRoutes from "../routes/checkRoute.js"; -import SettingsRoutes from "../routes/settingsRoute.js"; -import MaintenanceWindowRoutes from "../routes/maintenanceWindowRoute.js"; -import StatusPageRoutes from "../routes/statusPageRoute.js"; -import QueueRoutes from "../routes/queueRoute.js"; -import LogRoutes from "../routes/logRoutes.js"; -import DiagnosticRoutes from "../routes/diagnosticRoute.js"; -import NotificationRoutes from "../routes/notificationRoute.js"; +import AuthRoutes from "../routes/v1/authRoute.js"; +import InviteRoutes from "../routes/v1//inviteRoute.js"; +import MonitorRoutes from "../routes/v1/monitorRoute.js"; +import CheckRoutes from "../routes/v1/checkRoute.js"; +import SettingsRoutes from "../routes/v1/settingsRoute.js"; +import MaintenanceWindowRoutes from "../routes/v1/maintenanceWindowRoute.js"; +import StatusPageRoutes from "../routes/v1/statusPageRoute.js"; +import QueueRoutes from "../routes/v1/queueRoute.js"; +import LogRoutes from "../routes/v1/logRoutes.js"; +import DiagnosticRoutes from "../routes/v1//diagnosticRoute.js"; +import NotificationRoutes from "../routes/v1/notificationRoute.js"; export const setupRoutes = (app, controllers) => { const authRoutes = new AuthRoutes(controllers.authController); diff --git a/server/src/routes/announcementsRoute.js b/server/src/routes/v1/announcementsRoute.js similarity index 100% rename from server/src/routes/announcementsRoute.js rename to server/src/routes/v1/announcementsRoute.js diff --git a/server/src/routes/authRoute.js b/server/src/routes/v1/authRoute.js similarity index 85% rename from server/src/routes/authRoute.js rename to server/src/routes/v1/authRoute.js index e790c60b0..f69f94f43 100755 --- a/server/src/routes/authRoute.js +++ b/server/src/routes/v1/authRoute.js @@ -1,9 +1,7 @@ import { Router } from "express"; -import { verifyJWT } from "../middleware/verifyJWT.js"; -import { verifyOwnership } from "../middleware/verifyOwnership.js"; -import { isAllowed } from "../middleware/isAllowed.js"; +import { verifyJWT } from "../../middleware/verifyJWT.js"; +import { isAllowed } from "../../middleware/isAllowed.js"; import multer from "multer"; -import User from "../db/models/User.js"; const upload = multer(); diff --git a/server/src/routes/checkRoute.js b/server/src/routes/v1/checkRoute.js similarity index 76% rename from server/src/routes/checkRoute.js rename to server/src/routes/v1/checkRoute.js index 8bdb2d4fb..673fe51e0 100755 --- a/server/src/routes/checkRoute.js +++ b/server/src/routes/v1/checkRoute.js @@ -1,9 +1,6 @@ import { Router } from "express"; -import { verifyOwnership } from "../middleware/verifyOwnership.js"; -import { verifyTeamAccess } from "../middleware/verifyTeamAccess.js"; -import { isAllowed } from "../middleware/isAllowed.js"; -import Monitor from "../db/models/Monitor.js"; -import Check from "../db/models/Check.js"; + +import { isAllowed } from "../../middleware/isAllowed.js"; class CheckRoutes { constructor(checkController) { diff --git a/server/src/routes/diagnosticRoute.js b/server/src/routes/v1/diagnosticRoute.js similarity index 77% rename from server/src/routes/diagnosticRoute.js rename to server/src/routes/v1/diagnosticRoute.js index a15f5a038..1b7cc06be 100755 --- a/server/src/routes/diagnosticRoute.js +++ b/server/src/routes/v1/diagnosticRoute.js @@ -1,6 +1,6 @@ import { Router } from "express"; -import { verifyJWT } from "../middleware/verifyJWT.js"; -import { isAllowed } from "../middleware/isAllowed.js"; +import { verifyJWT } from "../../middleware/verifyJWT.js"; +import { isAllowed } from "../../middleware/isAllowed.js"; class DiagnosticRoutes { constructor(diagnosticController) { diff --git a/server/src/routes/inviteRoute.js b/server/src/routes/v1/inviteRoute.js similarity index 82% rename from server/src/routes/inviteRoute.js rename to server/src/routes/v1/inviteRoute.js index d495c5476..74fcfeb91 100755 --- a/server/src/routes/inviteRoute.js +++ b/server/src/routes/v1/inviteRoute.js @@ -1,6 +1,6 @@ import { Router } from "express"; -import { verifyJWT } from "../middleware/verifyJWT.js"; -import { isAllowed } from "../middleware/isAllowed.js"; +import { verifyJWT } from "../../middleware/verifyJWT.js"; +import { isAllowed } from "../../middleware/isAllowed.js"; class InviteRoutes { constructor(inviteController) { diff --git a/server/src/routes/logRoutes.js b/server/src/routes/v1/logRoutes.js similarity index 85% rename from server/src/routes/logRoutes.js rename to server/src/routes/v1/logRoutes.js index 867826dde..feabe669a 100755 --- a/server/src/routes/logRoutes.js +++ b/server/src/routes/v1/logRoutes.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import { isAllowed } from "../middleware/isAllowed.js"; +import { isAllowed } from "../../middleware/isAllowed.js"; class LogRoutes { constructor(logController) { this.router = Router(); diff --git a/server/src/routes/maintenanceWindowRoute.js b/server/src/routes/v1/maintenanceWindowRoute.js similarity index 91% rename from server/src/routes/maintenanceWindowRoute.js rename to server/src/routes/v1/maintenanceWindowRoute.js index 774434ed6..622e98f88 100755 --- a/server/src/routes/maintenanceWindowRoute.js +++ b/server/src/routes/v1/maintenanceWindowRoute.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import MaintenanceWindow from "../db/models/MaintenanceWindow.js"; +import MaintenanceWindow from "../../db/models/MaintenanceWindow.js"; class MaintenanceWindowRoutes { constructor(maintenanceWindowController) { this.router = Router(); diff --git a/server/src/routes/monitorRoute.js b/server/src/routes/v1/monitorRoute.js similarity index 94% rename from server/src/routes/monitorRoute.js rename to server/src/routes/v1/monitorRoute.js index 043d361d1..f8f219b83 100755 --- a/server/src/routes/monitorRoute.js +++ b/server/src/routes/v1/monitorRoute.js @@ -1,7 +1,7 @@ import { Router } from "express"; -import { isAllowed } from "../middleware/isAllowed.js"; +import { isAllowed } from "../../middleware/isAllowed.js"; import multer from "multer"; -import { fetchMonitorCertificate } from "../controllers/controllerUtils.js"; +import { fetchMonitorCertificate } from "../../controllers/controllerUtils.js"; const upload = multer({ storage: multer.memoryStorage(), // Store file in memory as Buffer }); diff --git a/server/src/routes/notificationRoute.js b/server/src/routes/v1/notificationRoute.js similarity index 100% rename from server/src/routes/notificationRoute.js rename to server/src/routes/v1/notificationRoute.js diff --git a/server/src/routes/queueRoute.js b/server/src/routes/v1/queueRoute.js similarity index 93% rename from server/src/routes/queueRoute.js rename to server/src/routes/v1/queueRoute.js index 55e829b91..59e1cb744 100755 --- a/server/src/routes/queueRoute.js +++ b/server/src/routes/v1/queueRoute.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import { isAllowed } from "../middleware/isAllowed.js"; +import { isAllowed } from "../../middleware/isAllowed.js"; class QueueRoutes { constructor(queueController) { this.router = Router(); diff --git a/server/src/routes/settingsRoute.js b/server/src/routes/v1/settingsRoute.js similarity index 90% rename from server/src/routes/settingsRoute.js rename to server/src/routes/v1/settingsRoute.js index 88eb67e65..76556e923 100755 --- a/server/src/routes/settingsRoute.js +++ b/server/src/routes/v1/settingsRoute.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import { isAllowed } from "../middleware/isAllowed.js"; +import { isAllowed } from "../../middleware/isAllowed.js"; class SettingsRoutes { constructor(settingsController) { diff --git a/server/src/routes/statusPageRoute.js b/server/src/routes/v1/statusPageRoute.js similarity index 93% rename from server/src/routes/statusPageRoute.js rename to server/src/routes/v1/statusPageRoute.js index 53eb21fba..8300066a8 100755 --- a/server/src/routes/statusPageRoute.js +++ b/server/src/routes/v1/statusPageRoute.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import { verifyJWT } from "../middleware/verifyJWT.js"; +import { verifyJWT } from "../../middleware/verifyJWT.js"; import multer from "multer"; const upload = multer(); diff --git a/server/tests/controllers/authController.test.js b/server/tests/controllers/authController.test.js deleted file mode 100755 index 341c8eda6..000000000 --- a/server/tests/controllers/authController.test.js +++ /dev/null @@ -1,962 +0,0 @@ -import { - issueToken, - registerUser, - loginUser, - editUser, - checkSuperadminExists, - requestRecovery, - validateRecovery, - resetPassword, - deleteUser, - getAllUsers, -} from "../../controllers/authController.js"; -import jwt from "jsonwebtoken"; -import { errorMessages, successMessages } from "../../utils/messages.js"; -import sinon from "sinon"; -import { tokenType } from "../../utils/utils.js"; -import logger from "../../utils/logger.js"; - -describe("Auth Controller - issueToken", function () { - let stub; - - afterEach(function () { - sinon.restore(); // Restore stubs after each test - }); - - it("should reject with an error if jwt.sign fails", function () { - const error = new Error("jwt.sign error"); - stub = sinon.stub(jwt, "sign").throws(error); - const payload = { id: "123" }; - const appSettings = { jwtSecret: "my_secret" }; - expect(() => issueToken(payload, tokenType.ACCESS_TOKEN, appSettings)).to.throw(error); - }); - - it("should return a token if jwt.sign is successful and appSettings.jwtTTL is not defined", function () { - const payload = { id: "123" }; - const appSettings = { jwtSecret: "my_secret" }; - const expectedToken = "mockToken"; - - stub = sinon.stub(jwt, "sign").returns(expectedToken); - const token = issueToken(payload, tokenType.ACCESS_TOKEN, appSettings); - expect(token).to.equal(expectedToken); - }); - - it("should return a token if jwt.sign is successful and appSettings.jwtTTL is defined", function () { - const payload = { id: "123" }; - const appSettings = { jwtSecret: "my_secret", jwtTTL: "1s" }; - const expectedToken = "mockToken"; - - stub = sinon.stub(jwt, "sign").returns(expectedToken); - const token = issueToken(payload, tokenType.ACCESS_TOKEN, appSettings); - expect(token).to.equal(expectedToken); - }); - - it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is not defined", function () { - const payload = {}; - const appSettings = { refreshTokenSecret: "my_refresh_secret" }; - const expectedToken = "mockRefreshToken"; - - stub = sinon.stub(jwt, "sign").returns(expectedToken); - const token = issueToken(payload, tokenType.REFRESH_TOKEN, appSettings); - expect(token).to.equal(expectedToken); - }); - - it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is defined", function () { - const payload = {}; - const appSettings = { - refreshTokenSecret: "my_refresh_secret", - refreshTokenTTL: "7d", - }; - const expectedToken = "mockRefreshToken"; - - stub = sinon.stub(jwt, "sign").returns(expectedToken); - const token = issueToken(payload, tokenType.REFRESH_TOKEN, appSettings); - expect(token).to.equal(expectedToken); - }); -}); - -describe("Auth Controller - registerUser", function () { - let req, res, next; - - beforeEach(function () { - req = { - body: { - firstName: "firstname", - lastName: "lastname", - email: "test@test.com", - password: "Uptime1!", - role: ["admin"], - teamId: "123", - inviteToken: "invite", - }, - db: { - checkSuperadmin: sinon.stub(), - getInviteTokenAndDelete: sinon.stub(), - updateAppSettings: sinon.stub(), - insertUser: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub().resolves({ - jwtSecret: "my_secret", - refreshTokenSecret: "my_refresh_secret", - }), - }, - emailService: { - buildAndSendEmail: sinon.stub(), - }, - file: {}, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - sinon.stub(logger, "error"); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if body validation fails", async function () { - req.body = {}; - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if checkSuperadmin fails", async function () { - req.db.checkSuperadmin.throws(new Error("checkSuperadmin error")); - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error"); - }); - - it("should reject with an error if getInviteTokenAndDelete fails", async function () { - req.db.checkSuperadmin.returns(true); - req.db.getInviteTokenAndDelete.throws(new Error("getInviteTokenAndDelete error")); - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getInviteTokenAndDelete error"); - }); - - it("should reject with an error if updateAppSettings fails", async function () { - req.db.checkSuperadmin.returns(false); - req.db.updateAppSettings.throws(new Error("updateAppSettings error")); - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("updateAppSettings error"); - }); - - it("should reject with an error if insertUser fails", async function () { - req.db.checkSuperadmin.resolves(false); - req.db.updateAppSettings.resolves(); - req.db.insertUser.rejects(new Error("insertUser error")); - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("insertUser error"); - }); - - it("should reject with an error if settingsService.getSettings fails", async function () { - req.db.checkSuperadmin.resolves(false); - req.db.updateAppSettings.resolves(); - req.db.insertUser.resolves({ _id: "123" }); - req.settingsService.getSettings.rejects(new Error("settingsService.getSettings error")); - await registerUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("settingsService.getSettings error"); - }); - - it("should log an error if emailService.buildAndSendEmail fails", async function () { - req.db.checkSuperadmin.resolves(false); - req.db.updateAppSettings.resolves(); - req.db.insertUser.returns({ _id: "123" }); - req.settingsService.getSettings.returns({ - jwtSecret: "my_secret", - refreshTokenSecret: "my_secret", - }); - req.emailService.buildAndSendEmail.rejects(new Error("emailService error")); - await registerUser(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal("emailService error"); - }); - - it("should return a success message and data if all operations are successful", async function () { - const user = { _id: "123" }; - req.db.checkSuperadmin.resolves(false); - req.db.updateAppSettings.resolves(); - req.db.insertUser.returns(user); - req.settingsService.getSettings.returns({ - jwtSecret: "my_secret", - refreshTokenSecret: "my_secret", - }); - req.emailService.buildAndSendEmail.resolves("message-id"); - await registerUser(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_CREATE_USER, - data: { user, token: sinon.match.string, refreshToken: sinon.match.string }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); - - it("should return a success message and data if all operations are successful and superAdmin true", async function () { - const user = { _id: "123" }; - req.db.checkSuperadmin.resolves(true); - req.db.updateAppSettings.resolves(); - req.db.insertUser.returns(user); - req.settingsService.getSettings.returns({ - jwtSecret: "my_secret", - refreshTokenSecret: "my_secret", - }); - req.emailService.buildAndSendEmail.resolves("message-id"); - await registerUser(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_CREATE_USER, - data: { user, token: sinon.match.string, refreshToken: sinon.match.string }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); -}); - -describe("Auth Controller - loginUser", function () { - let req, res, next, user; - - beforeEach(function () { - req = { - body: { email: "test@example.com", password: "Password123!" }, - db: { - getUserByEmail: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub().resolves({ - jwtSecret: "my_secret", - refreshTokenSecret: "my_refresh_token", - }), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - user = { - _doc: { - email: "test@example.com", - }, - comparePassword: sinon.stub(), - }; - }); - - it("should reject with an error if validation fails", async function () { - req.body = {}; - await loginUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if getUserByEmail fails", async function () { - req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); - await loginUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); - }); - - it("should login user successfully", async function () { - req.db.getUserByEmail.resolves(user); - user.comparePassword.resolves(true); - await loginUser(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_LOGIN_USER, - data: { - user: { - email: "test@example.com", - avatarImage: undefined, - }, - token: sinon.match.string, - refreshToken: sinon.match.string, - }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); - - it("should reject a user with an incorrect password", async function () { - req.body = { - email: "test@test.com", - password: "Password123!", - }; - req.db.getUserByEmail.resolves(user); - user.comparePassword.resolves(false); - await loginUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal(errorMessages.AUTH_INCORRECT_PASSWORD); - }); -}); - -describe("Auth Controller - refreshAuthToken", function () { - let req, res, next, issueTokenStub; - - beforeEach(function () { - req = { - headers: { - "x-refresh-token": "valid_refresh_token", - authorization: "Bearer old_auth_token", - }, - settingsService: { - getSettings: sinon.stub().resolves({ - jwtSecret: "my_secret", - refreshTokenSecret: "my_refresh_secret", - }), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - sinon.stub(jwt, "verify"); - - issueTokenStub = sinon.stub().returns("new_auth_token"); - sinon.replace({ issueToken }, "issueToken", issueTokenStub); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject if no refresh token is provided", async function () { - delete req.headers["x-refresh-token"]; - await refreshAuthToken(req, res, next); - - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal(errorMessages.NO_REFRESH_TOKEN); - expect(next.firstCall.args[0].status).to.equal(401); - }); - - it("should reject if the refresh token is invalid", async function () { - jwt.verify.yields(new Error("invalid token")); - await refreshAuthToken(req, res, next); - - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal(errorMessages.INVALID_REFRESH_TOKEN); - expect(next.firstCall.args[0].status).to.equal(401); - }); - - it("should reject if the refresh token is expired", async function () { - const error = new Error("Token expired"); - error.name = "TokenExpiredError"; - jwt.verify.yields(error); - await refreshAuthToken(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal(errorMessages.EXPIRED_REFRESH_TOKEN); - expect(next.firstCall.args[0].status).to.equal(401); - }); - - it("should reject if settingsService.getSettings fails", async function () { - req.settingsService.getSettings.rejects(new Error("settingsService.getSettings error")); - await refreshAuthToken(req, res, next); - - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("settingsService.getSettings error"); - }); - - it("should generate a new auth token if the refresh token is valid", async function () { - const decodedPayload = { expiresIn: "60" }; - jwt.verify.callsFake(() => { - return decodedPayload; - }); - await refreshAuthToken(req, res, next); - - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_TOKEN_REFRESHED, - data: { - user: decodedPayload, - token: sinon.match.string, - refreshToken: "valid_refresh_token", - }, - }) - ).to.be.true; - }); -}); - -describe("Auth Controller - editUser", function () { - let req, res, next, stub, user; - - beforeEach(function () { - req = { - params: { userId: "123" }, - body: { password: "Password1!", newPassword: "Password2!" }, - headers: { authorization: "Bearer token" }, - user: { _id: "123" }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "my_secret" }), - }, - db: { - getUserByEmail: sinon.stub(), - updateUser: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - stub = sinon.stub(jwt, "verify").returns({ email: "test@example.com" }); - }); - - afterEach(function () { - sinon.restore(); - stub.restore(); - }); - - it("should reject with an error if param validation fails", async function () { - req.params = {}; - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if body validation fails", async function () { - req.body = { invalid: 1 }; - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if param.userId !== req.user._id", async function () { - req.params = { userId: "456" }; - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(401); - }); - - it("should reject with an error if !req.body.password and getUserByEmail fails", async function () { - req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); - }); - - it("should reject with an error if user.comparePassword fails", async function () { - req.db.getUserByEmail.returns({ - comparePassword: sinon.stub().rejects(new Error("Bad Password Match")), - }); - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Bad Password Match"); - }); - - it("should reject with an error if user.comparePassword returns false", async function () { - req.db.getUserByEmail.returns({ - comparePassword: sinon.stub().returns(false), - }); - await editUser(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(401); - expect(next.firstCall.args[0].message).to.equal(errorMessages.AUTH_INCORRECT_PASSWORD); - }); - - it("should edit a user if it receives a proper request", async function () { - const user = { - comparePassword: sinon.stub().resolves(true), - }; - req.db.getUserByEmail.resolves(user); - req.db.updateUser.resolves({ email: "test@example.com" }); - - await editUser(req, res, next); - - expect(req.db.getUserByEmail.calledOnce).to.be.true; - expect(req.db.updateUser.calledOnce).to.be.true; - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_UPDATE_USER, - data: { email: "test@example.com" }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); - - it("should edit a user if it receives a proper request and both password fields are undefined", async function () { - req.body.password = undefined; - req.body.newPassword = undefined; - req.db.getUserByEmail.resolves(user); - req.db.updateUser.resolves({ email: "test@example.com" }); - - await editUser(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_UPDATE_USER, - data: { email: "test@example.com" }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); - - it("should reject an edit request if password format is incorrect", async function () { - req.body = { password: "bad_password", newPassword: "bad_password" }; - const user = { - comparePassword: sinon.stub().resolves(true), - }; - req.db.getUserByEmail.resolves(user); - - await editUser(req, res, next); - - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); -}); - -describe("Auth Controller - checkSuperadminExists", function () { - let req, res, next; - - beforeEach(function () { - req = { - db: { - checkSuperadmin: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - it("should reject with an error if checkSuperadmin fails", async function () { - req.db.checkSuperadmin.rejects(new Error("checkSuperadmin error")); - await checkSuperadminExists(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error"); - }); - - it("should return true if a superadmin exists", async function () { - req.db.checkSuperadmin.resolves(true); - await checkSuperadminExists(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_SUPERADMIN_EXISTS, - data: true, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); - - it("should return false if a superadmin does not exist", async function () { - req.db.checkSuperadmin.resolves(false); - await checkSuperadminExists(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.AUTH_SUPERADMIN_EXISTS, - data: false, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); -}); - -describe("Auth Controller - requestRecovery", function () { - let req, res, next; - - beforeEach(function () { - req = { - body: { email: "test@test.com" }, - db: { - getUserByEmail: sinon.stub(), - requestRecoveryToken: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub().returns({ clientHost: "http://localhost" }), - }, - emailService: { - buildAndSendEmail: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - it("should reject with an error if validation fails", async function () { - req.body = {}; - await requestRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if getUserByEmail fails", async function () { - req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); - await requestRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); - }); - - it("should throw an error if the user is not found", async function () { - req.db.getUserByEmail.resolves(null); - await requestRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - // expect(next.firstCall.args[0].message).to.equal( - // errorMessages.FRIENDLY_ERROR - // ); - }); - - it("should throw an error if the email is not provided", async function () { - req.body = {}; - await requestRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should return a success message if the email is provided", async function () { - const user = { firstName: "John" }; - const recoveryToken = { token: "recovery-token" }; - const msgId = "message-id"; - req.db.getUserByEmail.resolves(user); - req.db.requestRecoveryToken.resolves(recoveryToken); - req.emailService.buildAndSendEmail.resolves(msgId); - await requestRecovery(req, res, next); - expect(req.db.getUserByEmail.calledOnceWith("test@test.com")).to.be.true; - expect(req.db.requestRecoveryToken.calledOnceWith(req, res)).to.be.true; - expect( - req.emailService.buildAndSendEmail.calledOnceWith( - "passwordResetTemplate", - { - name: "John", - email: "test@test.com", - url: "http://localhost/set-new-password/recovery-token", - }, - "test@test.com", - "Checkmate Password Reset" - ) - ).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN, - data: msgId, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); -}); - -describe("Auth Controller - validateRecovery", function () { - let req, res, next; - - beforeEach(function () { - req = { - body: { recoveryToken: "recovery-token" }, - db: { - validateRecoveryToken: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - it("should reject with an error if validation fails", async function () { - req.body = {}; - await validateRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if validateRecoveryToken fails", async function () { - req.db.validateRecoveryToken.rejects(new Error("validateRecoveryToken error")); - await validateRecovery(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("validateRecoveryToken error"); - }); - - it("should return a success message if the token is valid", async function () { - req.db.validateRecoveryToken.resolves(); - await validateRecovery(req, res, next); - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); -}); - -describe("Auth Controller - resetPassword", function () { - let req, res, next, newPasswordValidation, handleValidationError, handleError; - - beforeEach(function () { - req = { - body: { - recoveryToken: "recovery-token", - password: "Password1!", - }, - db: { - resetPassword: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - newPasswordValidation = { - validateAsync: sinon.stub(), - }; - handleValidationError = sinon.stub(); - handleError = sinon.stub(); - }); - - it("should reject with an error if validation fails", async function () { - req.body = { password: "bad_password" }; - await resetPassword(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if resetPassword fails", async function () { - const error = new Error("resetPassword error"); - newPasswordValidation.validateAsync.resolves(); - req.db.resetPassword.rejects(error); - await resetPassword(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("resetPassword error"); - }); - - it("should reset password successfully", async function () { - const user = { _doc: {} }; - const appSettings = { jwtSecret: "my_secret" }; - const token = "token"; - - newPasswordValidation.validateAsync.resolves(); - req.db.resetPassword.resolves(user); - req.settingsService.getSettings.resolves(appSettings); - - await resetPassword(req, res, next); - - expect(req.db.resetPassword.calledOnceWith(req, res)).to.be.true; - expect(req.settingsService.getSettings.calledOnce).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.AUTH_RESET_PASSWORD, - data: { user: sinon.match.object, token: sinon.match.string }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); -}); - -describe("Auth Controller - deleteUser", function () { - let req, res, next, handleError; - - beforeEach(function () { - req = { - headers: { - authorization: "Bearer token", - }, - db: { - getUserByEmail: sinon.stub(), - getMonitorsByTeamId: sinon.stub(), - deleteJob: sinon.stub(), - deleteChecks: sinon.stub(), - deletePageSpeedChecksByMonitorId: sinon.stub(), - deleteNotificationsByMonitorId: sinon.stub(), - deleteTeam: sinon.stub(), - deleteAllOtherUsers: sinon.stub(), - deleteMonitorsByUserId: sinon.stub(), - deleteUser: sinon.stub(), - }, - jobQueue: { - deleteJob: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - - sinon.stub(jwt, "decode"); - - handleError = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should throw an error if user is not found", async function () { - jwt.decode.returns({ email: "test@example.com" }); - req.db.getUserByEmail.throws(new Error(errorMessages.DB_USER_NOT_FOUND)); - - await deleteUser(req, res, next); - - expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true; - expect(next.calledOnce).to.be.true; - expect(next.firstCall.args[0].message).to.equal(errorMessages.DB_USER_NOT_FOUND); - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); - - it("should delete user and associated data if user is superadmin", async function () { - const user = { - _id: "user_id", - email: "test@example.com", - role: ["superadmin"], - teamId: "team_id", - }; - const monitors = [{ _id: "monitor_id" }]; - - jwt.decode.returns({ email: "test@example.com" }); - req.db.getUserByEmail.resolves(user); - req.db.getMonitorsByTeamId.resolves({ monitors }); - - await deleteUser(req, res, next); - - expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true; - expect( - req.db.getMonitorsByTeamId.calledOnceWith({ - params: { teamId: "team_id" }, - }) - ).to.be.true; - expect(req.jobQueue.deleteJob.calledOnceWith(monitors[0])).to.be.true; - expect(req.db.deleteChecks.calledOnceWith("monitor_id")).to.be.true; - expect(req.db.deletePageSpeedChecksByMonitorId.calledOnceWith("monitor_id")).to.be.true; - expect(req.db.deleteNotificationsByMonitorId.calledOnceWith("monitor_id")).to.be.true; - expect(req.db.deleteTeam.calledOnceWith("team_id")).to.be.true; - expect(req.db.deleteAllOtherUsers.calledOnce).to.be.true; - expect(req.db.deleteMonitorsByUserId.calledOnceWith("user_id")).to.be.true; - expect(req.db.deleteUser.calledOnceWith("user_id")).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.AUTH_DELETE_USER, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); - - it("should delete user if user is not superadmin", async function () { - const user = { - _id: "user_id", - email: "test@example.com", - role: ["user"], - teamId: "team_id", - }; - - jwt.decode.returns({ email: "test@example.com" }); - req.db.getUserByEmail.resolves(user); - - await deleteUser(req, res, next); - - expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true; - expect( - req.db.getMonitorsByTeamId.calledOnceWith({ - params: { teamId: "team_id" }, - }) - ).to.be.true; - expect(req.db.deleteUser.calledOnceWith("user_id")).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.AUTH_DELETE_USER, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); - - it("should handle errors", async function () { - const error = new Error("Something went wrong"); - const SERVICE_NAME = "AuthController"; - jwt.decode.returns({ email: "test@example.com" }); - req.db.getUserByEmail.rejects(error); - await deleteUser(req, res, next); - expect(next.calledOnce).to.be.true; - expect(next.firstCall.args[0].message).to.equal("Something went wrong"); - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); -}); - -describe("Auth Controller - getAllUsers", function () { - let req, res, next; - - beforeEach(function () { - req = { - db: { - getAllUsers: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); // Restore the original methods after each test - }); - - it("should return 200 and all users", async function () { - const allUsers = [{ id: 1, name: "John Doe" }]; - req.db.getAllUsers.resolves(allUsers); - - await getAllUsers(req, res, next); - - expect(req.db.getAllUsers.calledOnce).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: "Got all users", - data: allUsers, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); - - it("should call next with error when an exception occurs", async function () { - const error = new Error("Something went wrong"); - req.db.getAllUsers.rejects(error); - await getAllUsers(req, res, next); - expect(req.db.getAllUsers.calledOnce).to.be.true; - expect(next.calledOnce).to.be.true; - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); -}); diff --git a/server/tests/controllers/checkController.test.js b/server/tests/controllers/checkController.test.js deleted file mode 100755 index c5fb57d50..000000000 --- a/server/tests/controllers/checkController.test.js +++ /dev/null @@ -1,372 +0,0 @@ -import { createCheck, getChecks, getTeamChecks, deleteChecks, deleteChecksByTeamId, updateChecksTTL } from "../../controllers/checkController.js"; -import jwt from "jsonwebtoken"; -import { errorMessages, successMessages } from "../../utils/messages.js"; -import sinon from "sinon"; -describe("Check Controller - createCheck", function () { - let req, res, next, handleError; - - beforeEach(function () { - req = { - params: {}, - body: {}, - db: { - createCheck: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - handleError = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); // Restore the original methods after each test - }); - - it("should reject with a validation if params are invalid", async function () { - await createCheck(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with a validation error if body is invalid", async function () { - req.params = { - monitorId: "monitorId", - }; - await createCheck(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should call next with error if data retrieval fails", async function () { - req.params = { - monitorId: "monitorId", - }; - req.body = { - monitorId: "monitorId", - status: true, - responseTime: 100, - statusCode: 200, - message: "message", - }; - req.db.createCheck.rejects(new Error("error")); - await createCheck(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - }); - - it("should return a success message if check is created", async function () { - req.params = { - monitorId: "monitorId", - }; - req.db.createCheck.resolves({ id: "123" }); - req.body = { - monitorId: "monitorId", - status: true, - responseTime: 100, - statusCode: 200, - message: "message", - }; - await createCheck(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.CHECK_CREATE, - data: { id: "123" }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); -}); - -describe("Check Controller - getChecks", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: {}, - query: {}, - db: { - getChecks: sinon.stub(), - getChecksCount: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with a validation error if params are invalid", async function () { - await getChecks(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should return a success message if checks are found", async function () { - req.params = { - monitorId: "monitorId", - }; - req.db.getChecks.resolves([{ id: "123" }]); - req.db.getChecksCount.resolves(1); - await getChecks(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: successMessages.CHECK_GET, - data: { checksCount: 1, checks: [{ id: "123" }] }, - }) - ).to.be.true; - expect(next.notCalled).to.be.true; - }); - - it("should call next with error if data retrieval fails", async function () { - req.params = { - monitorId: "monitorId", - }; - req.db.getChecks.rejects(new Error("error")); - await getChecks(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - }); -}); - -describe("Check Controller - getTeamChecks", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: {}, - query: {}, - db: { - getTeamChecks: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with a validation error if params are invalid", async function () { - await getTeamChecks(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should return 200 and check data on successful validation and data retrieval", async function () { - req.params = { teamId: "1" }; - const checkData = [{ id: 1, name: "Check 1" }]; - req.db.getTeamChecks.resolves(checkData); - - await getTeamChecks(req, res, next); - expect(req.db.getTeamChecks.calledOnceWith(req)).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.CHECK_GET, - data: checkData, - }) - ).to.be.true; - }); - - it("should call next with error if data retrieval fails", async function () { - req.params = { teamId: "1" }; - req.db.getTeamChecks.rejects(new Error("Retrieval Error")); - await getTeamChecks(req, res, next); - expect(req.db.getTeamChecks.calledOnceWith(req)).to.be.true; - expect(next.firstCall.args[0]).to.be.an("error"); - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); -}); - -describe("Check Controller - deleteChecks", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: {}, - db: { - deleteChecks: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if param validation fails", async function () { - await deleteChecks(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should call next with error if data retrieval fails", async function () { - req.params = { monitorId: "1" }; - req.db.deleteChecks.rejects(new Error("Deletion Error")); - await deleteChecks(req, res, next); - expect(req.db.deleteChecks.calledOnceWith(req.params.monitorId)).to.be.true; - expect(next.firstCall.args[0]).to.be.an("error"); - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); - - it("should delete checks successfully", async function () { - req.params = { monitorId: "123" }; - req.db.deleteChecks.resolves(1); - await deleteChecks(req, res, next); - expect(req.db.deleteChecks.calledOnceWith(req.params.monitorId)).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.CHECK_DELETE, - data: { deletedCount: 1 }, - }) - ).to.be.true; - }); -}); - -describe("Check Controller - deleteChecksByTeamId", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: {}, - db: { - deleteChecksByTeamId: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if param validation fails", async function () { - await deleteChecksByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should call next with error if data retrieval fails", async function () { - req.params = { teamId: "1" }; - req.db.deleteChecksByTeamId.rejects(new Error("Deletion Error")); - await deleteChecksByTeamId(req, res, next); - expect(req.db.deleteChecksByTeamId.calledOnceWith(req.params.teamId)).to.be.true; - expect(next.firstCall.args[0]).to.be.an("error"); - expect(res.status.notCalled).to.be.true; - expect(res.json.notCalled).to.be.true; - }); - - it("should delete checks successfully", async function () { - req.params = { teamId: "123" }; - req.db.deleteChecksByTeamId.resolves(1); - await deleteChecksByTeamId(req, res, next); - expect(req.db.deleteChecksByTeamId.calledOnceWith(req.params.teamId)).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.CHECK_DELETE, - data: { deletedCount: 1 }, - }) - ).to.be.true; - }); -}); - -describe("Check Controller - updateCheckTTL", function () { - let stub, req, res, next; - - beforeEach(function () { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - - req = { - body: {}, - headers: { authorization: "Bearer token" }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "my_secret" }), - }, - db: { - updateChecksTTL: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - stub.restore(); - }); - - it("should reject if body validation fails", async function () { - await updateChecksTTL(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should throw a JwtError if verification fails", async function () { - stub.restore(); - req.body = { - ttl: 1, - }; - await updateChecksTTL(req, res, next); - expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); - }); - - it("should call next with error if data retrieval fails", async function () { - req.body = { - ttl: 1, - }; - req.db.updateChecksTTL.rejects(new Error("Update Error")); - await updateChecksTTL(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - }); - - it("should update TTL successfully", async function () { - req.body = { - ttl: 1, - }; - req.db.updateChecksTTL.resolves(); - await updateChecksTTL(req, res, next); - expect(req.db.updateChecksTTL.calledOnceWith("123", 1 * 86400)).to.be.true; - expect(res.status.calledOnceWith(200)).to.be.true; - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.CHECK_UPDATE_TTL, - }) - ).to.be.true; - }); -}); diff --git a/server/tests/controllers/controllerUtils.test.js b/server/tests/controllers/controllerUtils.test.js deleted file mode 100755 index a55318857..000000000 --- a/server/tests/controllers/controllerUtils.test.js +++ /dev/null @@ -1,157 +0,0 @@ -import sinon from "sinon"; - -import { handleValidationError, handleError, fetchMonitorCertificate } from "../../controllers/controllerUtils.js"; -import { expect } from "chai"; -import sslChecker from "ssl-checker"; -import { afterEach } from "node:test"; -import exp from "constants"; - -describe("controllerUtils - handleValidationError", function () { - it("should set status to 422", function () { - const error = {}; - const serviceName = "TestService"; - const result = handleValidationError(error, serviceName); - expect(result.status).to.equal(422); - }); - - it("should set service to the provided serviceName", function () { - const error = {}; - const serviceName = "TestService"; - const result = handleValidationError(error, serviceName); - expect(result.service).to.equal(serviceName); - }); - - it("should set message to error.details[0].message if present", function () { - const error = { - details: [{ message: "Detail message" }], - }; - const serviceName = "TestService"; - const result = handleValidationError(error, serviceName); - expect(result.message).to.equal("Detail message"); - }); - - it("should set message to error.message if error.details is not present", function () { - const error = { - message: "Error message", - }; - const serviceName = "TestService"; - const result = handleValidationError(error, serviceName); - expect(result.message).to.equal("Error message"); - }); - - it('should set message to "Validation Error" if neither error.details nor error.message is present', function () { - const error = {}; - const serviceName = "TestService"; - const result = handleValidationError(error, serviceName); - expect(result.message).to.equal("Validation Error"); - }); -}); - -describe("controllerUtils - handleError", function () { - it("should set stats to the provided status if error.code is undefined", function () { - const error = {}; - const serviceName = "TestService"; - const method = "testMethod"; - const status = 400; - const result = handleError(error, serviceName, method, status); - expect(result.status).to.equal(status); - }); - - it("should not overwrite error.code if it is already defined", function () { - const error = { status: 404 }; - const serviceName = "TestService"; - const method = "testMethod"; - const status = 400; - const result = handleError(error, serviceName, method, status); - expect(result.status).to.equal(404); - }); - - it("should set service to the provided serviceName if error.service is undefined", function () { - const error = {}; - const serviceName = "TestService"; - const method = "testMethod"; - const result = handleError(error, serviceName, method); - expect(result.service).to.equal(serviceName); - }); - - it("should not overwrite error.service if it is already defined", function () { - const error = { service: "ExistingService" }; - const serviceName = "TestService"; - const method = "testMethod"; - const result = handleError(error, serviceName, method); - expect(result.service).to.equal("ExistingService"); - }); - - it("should set method to the provided method if error.method is undefined", function () { - const error = {}; - const serviceName = "TestService"; - const method = "testMethod"; - const result = handleError(error, serviceName, method); - expect(result.method).to.equal(method); - }); - - it("should not overwrite error.method if it is already defined", function () { - const error = { method: "existingMethod" }; - const serviceName = "TestService"; - const method = "testMethod"; - const result = handleError(error, serviceName, method); - expect(result.method).to.equal("existingMethod"); - }); - - it("should set code to 500 if error.code is undefined and no code is provided", function () { - const error = {}; - const serviceName = "TestService"; - const method = "testMethod"; - const result = handleError(error, serviceName, method); - expect(result.status).to.equal(500); - }); -}); - -describe("controllerUtils - fetchMonitorCertificate", function () { - let sslChecker, monitor; - - beforeEach(function () { - monitor = { - url: "https://www.google.com", - }; - sslChecker = sinon.stub(); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should reject with an error if a URL does not parse", async function () { - monitor.url = "invalidurl"; - try { - await fetchMonitorCertificate(sslChecker, monitor); - } catch (error) { - expect(error).to.be.an("error"); - expect(error.message).to.equal("Invalid URL"); - } - }); - - it("should reject with an error if sslChecker throws an error", async function () { - sslChecker.rejects(new Error("Test error")); - try { - await fetchMonitorCertificate(sslChecker, monitor); - } catch (error) { - expect(error).to.be.an("error"); - expect(error.message).to.equal("Test error"); - } - }); - - it("should return a certificate if sslChecker resolves", async function () { - sslChecker.resolves({ validTo: "2022-01-01" }); - const result = await fetchMonitorCertificate(sslChecker, monitor); - expect(result).to.deep.equal({ validTo: "2022-01-01" }); - }); - - it("should throw an error if a ssl-checker returns null", async function () { - sslChecker.returns(null); - await fetchMonitorCertificate(sslChecker, monitor).catch((error) => { - expect(error).to.be.an("error"); - expect(error.message).to.equal("Certificate not found"); - }); - }); -}); diff --git a/server/tests/controllers/inviteController.test.js b/server/tests/controllers/inviteController.test.js deleted file mode 100755 index 635bdf653..000000000 --- a/server/tests/controllers/inviteController.test.js +++ /dev/null @@ -1,202 +0,0 @@ -import { issueInvitation, inviteVerifyController } from "../../controllers/inviteController.js"; -import jwt from "jsonwebtoken"; -import sinon from "sinon"; -import joi from "joi"; -describe("inviteController - issueInvitation", function () { - let req, res, next, stub; - - beforeEach(function () { - req = { - headers: { authorization: "Bearer token" }, - body: { - email: "test@test.com", - role: ["admin"], - teamId: "123", - }, - db: { requestInviteToken: sinon.stub() }, - settingsService: { getSettings: sinon.stub() }, - emailService: { buildAndSendEmail: sinon.stub() }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if role validation fails", async function () { - stub = sinon.stub(jwt, "decode").callsFake(() => { - return { role: ["bad_role"], firstname: "first_name", teamId: "1" }; - }); - await issueInvitation(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0]).to.be.instanceOf(joi.ValidationError); - expect(next.firstCall.args[0].status).to.equal(422); - stub.restore(); - }); - - it("should reject with an error if body validation fails", async function () { - stub = sinon.stub(jwt, "decode").callsFake(() => { - return { role: ["admin"], firstname: "first_name", teamId: "1" }; - }); - req.body = {}; - await issueInvitation(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - stub.restore(); - }); - - it("should reject with an error if DB operations fail", async function () { - stub = sinon.stub(jwt, "decode").callsFake(() => { - return { role: ["admin"], firstname: "first_name", teamId: "1" }; - }); - req.db.requestInviteToken.throws(new Error("DB error")); - await issueInvitation(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - stub.restore(); - }); - - it("should send an invite successfully", async function () { - const token = "token"; - const decodedToken = { - role: "admin", - firstname: "John", - teamId: "team123", - }; - const inviteToken = { token: "inviteToken" }; - const clientHost = "http://localhost"; - - stub = sinon.stub(jwt, "decode").callsFake(() => { - return decodedToken; - }); - req.db.requestInviteToken.resolves(inviteToken); - req.settingsService.getSettings.returns({ clientHost }); - req.emailService.buildAndSendEmail.resolves(); - await issueInvitation(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: "Invite sent", - data: inviteToken, - }) - ).to.be.true; - stub.restore(); - }); - - it("should send an email successfully", async function () { - const token = "token"; - const decodedToken = { - role: "admin", - firstname: "John", - teamId: "team123", - }; - const inviteToken = { token: "inviteToken" }; - const clientHost = "http://localhost"; - - stub = sinon.stub(jwt, "decode").callsFake(() => { - return decodedToken; - }); - req.db.requestInviteToken.resolves(inviteToken); - req.settingsService.getSettings.returns({ clientHost }); - req.emailService.buildAndSendEmail.resolves(); - - await issueInvitation(req, res, next); - expect(req.emailService.buildAndSendEmail.calledOnce).to.be.true; - expect( - req.emailService.buildAndSendEmail.calledWith( - "employeeActivationTemplate", - { - name: "John", - link: "http://localhost/register/inviteToken", - }, - "test@test.com", - "Welcome to Uptime Monitor" - ) - ).to.be.true; - stub.restore(); - }); - - it("should continue executing if sending an email fails", async function () { - const token = "token"; - req.emailService.buildAndSendEmail.rejects(new Error("Email error")); - const decodedToken = { - role: "admin", - firstname: "John", - teamId: "team123", - }; - const inviteToken = { token: "inviteToken" }; - const clientHost = "http://localhost"; - - stub = sinon.stub(jwt, "decode").callsFake(() => { - return decodedToken; - }); - req.db.requestInviteToken.resolves(inviteToken); - req.settingsService.getSettings.returns({ clientHost }); - await issueInvitation(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - msg: "Invite sent", - data: inviteToken, - }) - ).to.be.true; - stub.restore(); - }); -}); - -describe("inviteController - inviteVerifyController", function () { - let req, res, next; - - beforeEach(function () { - req = { - body: { token: "token" }, - db: { - getInviteToken: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if body validation fails", async function () { - req.body = {}; - await inviteVerifyController(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if DB operations fail", async function () { - req.db.getInviteToken.throws(new Error("DB error")); - await inviteVerifyController(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return 200 and invite data when validation and invite retrieval are successful", async function () { - req.db.getInviteToken.resolves({ invite: "data" }); - await inviteVerifyController(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - status: "success", - msg: "Invite verified", - data: { invite: "data" }, - }) - ).to.be.true; - expect(next.called).to.be.false; - }); -}); diff --git a/server/tests/controllers/maintenanceWindowController.test.js b/server/tests/controllers/maintenanceWindowController.test.js deleted file mode 100755 index 0ea07b389..000000000 --- a/server/tests/controllers/maintenanceWindowController.test.js +++ /dev/null @@ -1,422 +0,0 @@ -import { - createMaintenanceWindows, - getMaintenanceWindowById, - getMaintenanceWindowsByTeamId, - getMaintenanceWindowsByMonitorId, - deleteMaintenanceWindow, - editMaintenanceWindow, -} from "../../controllers/maintenanceWindowController.js"; - -import jwt from "jsonwebtoken"; -import { successMessages } from "../../utils/messages.js"; -import sinon from "sinon"; - -describe("maintenanceWindowController - createMaintenanceWindows", function () { - let req, res, next, stub; - - beforeEach(function () { - req = { - body: { - monitors: ["66ff52e7c5911c61698ac724"], - name: "window", - active: true, - start: "2024-10-11T05:27:13.747Z", - end: "2024-10-11T05:27:14.747Z", - repeat: "123", - }, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - createMaintenanceWindow: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if body validation fails", async function () { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - req.body = {}; - await createMaintenanceWindows(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - stub.restore(); - }); - - it("should reject with an error if jwt.verify fails", async function () { - stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError()); - await createMaintenanceWindows(req, res, next); - expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); - stub.restore(); - }); - - it("should reject with an error DB operations fail", async function () { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - req.db.createMaintenanceWindow.throws(new Error("DB error")); - await createMaintenanceWindows(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - stub.restore(); - }); - - it("should return success message if all operations are successful", async function () { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - await createMaintenanceWindows(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(201); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_CREATE, - }) - ).to.be.true; - stub.restore(); - }); - - it("should return success message if all operations are successful with active set to undefined", async function () { - req.body.active = undefined; - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - await createMaintenanceWindows(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(201); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_CREATE, - }) - ).to.be.true; - stub.restore(); - }); -}); - -describe("maintenanceWindowController - getMaintenanceWindowById", function () { - let req, res, next; - - beforeEach(function () { - req = { - body: {}, - params: { - id: "123", - }, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - getMaintenanceWindowById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - it("should reject if param validation fails", async function () { - req.params = {}; - await getMaintenanceWindowById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject if DB operations fail", async function () { - req.db.getMaintenanceWindowById.throws(new Error("DB error")); - await getMaintenanceWindowById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return success message with data if all operations are successful", async function () { - req.db.getMaintenanceWindowById.returns({ id: "123" }); - await getMaintenanceWindowById(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID, - data: { id: "123" }, - }) - ).to.be.true; - }); -}); - -describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function () { - let req, res, next, stub; - - beforeEach(function () { - req = { - body: {}, - params: {}, - query: {}, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - getMaintenanceWindowsByTeamId: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - it("should reject if query validation fails", async function () { - req.query = { - invalid: 1, - }; - await getMaintenanceWindowsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject if jwt.verify fails", async function () { - stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError()); - await getMaintenanceWindowsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); - stub.restore(); - }); - - it("should reject with an error if DB operations fail", async function () { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - req.db.getMaintenanceWindowsByTeamId.throws(new Error("DB error")); - await getMaintenanceWindowsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - stub.restore(); - }); - - it("should return success message with data if all operations are successful", async function () { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - req.db.getMaintenanceWindowsByTeamId.returns([{ id: "123" }]); - await getMaintenanceWindowsByTeamId(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM, - data: [{ id: jwt.verify().teamId }], - }) - ).to.be.true; - stub.restore(); - }); -}); - -describe("maintenanceWindowController - getMaintenanceWindowsByMonitorId", function () { - let req, res, next; - - beforeEach(function () { - req = { - body: {}, - params: { - monitorId: "123", - }, - query: {}, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - getMaintenanceWindowsByMonitorId: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject if param validation fails", async function () { - req.params = {}; - await getMaintenanceWindowsByMonitorId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if DB operations fail", async function () { - req.db.getMaintenanceWindowsByMonitorId.throws(new Error("DB error")); - await getMaintenanceWindowsByMonitorId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return success message with data if all operations are successful", async function () { - const data = [{ monitorId: "123" }]; - req.db.getMaintenanceWindowsByMonitorId.returns(data); - await getMaintenanceWindowsByMonitorId(req, res, next); - expect(req.db.getMaintenanceWindowsByMonitorId.calledOnceWith(req.params.monitorId)); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_MONITOR, - data: data, - }) - ).to.be.true; - }); -}); - -describe("maintenanceWindowController - deleteMaintenanceWindow", function () { - let req, res, next; - - beforeEach(function () { - req = { - body: {}, - params: { - id: "123", - }, - query: {}, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - deleteMaintenanceWindowById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject if param validation fails", async function () { - req.params = {}; - await deleteMaintenanceWindow(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if DB operations fail", async function () { - req.db.deleteMaintenanceWindowById.throws(new Error("DB error")); - await deleteMaintenanceWindow(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return success message if all operations are successful", async function () { - await deleteMaintenanceWindow(req, res, next); - expect(req.db.deleteMaintenanceWindowById.calledOnceWith(req.params.id)); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_DELETE, - }) - ).to.be.true; - }); -}); - -describe("maintenanceWindowController - editMaintenanceWindow", function () { - let req, res, next; - - beforeEach(function () { - req = { - body: { - active: true, - name: "test", - }, - params: { - id: "123", - }, - query: {}, - headers: { - authorization: "Bearer token", - }, - settingsService: { - getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), - }, - db: { - editMaintenanceWindowById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject if param validation fails", async function () { - req.params = {}; - await editMaintenanceWindow(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject if body validation fails", async function () { - req.body = { invalid: 1 }; - await editMaintenanceWindow(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if DB operations fail", async function () { - req.db.editMaintenanceWindowById.throws(new Error("DB error")); - await editMaintenanceWindow(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return success message with data if all operations are successful", async function () { - const data = { id: "123" }; - req.db.editMaintenanceWindowById.returns(data); - - await editMaintenanceWindow(req, res, next); - expect(req.db.editMaintenanceWindowById.calledOnceWith(req.params.id, req.body)); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MAINTENANCE_WINDOW_EDIT, - data: data, - }) - ).to.be.true; - }); -}); diff --git a/server/tests/controllers/monitorController.test.js b/server/tests/controllers/monitorController.test.js deleted file mode 100755 index 963862579..000000000 --- a/server/tests/controllers/monitorController.test.js +++ /dev/null @@ -1,1116 +0,0 @@ -import { - getAllMonitors, - getAllMonitorsWithUptimeStats, - getMonitorStatsById, - getMonitorCertificate, - getMonitorById, - getMonitorsAndSummaryByTeamId, - getMonitorsByTeamId, - createMonitor, - checkEndpointResolution, - deleteMonitor, - deleteAllMonitors, - editMonitor, - pauseMonitor, - addDemoMonitors, -} from "../../controllers/monitorController.js"; -import jwt from "jsonwebtoken"; -import sinon from "sinon"; -import { successMessages } from "../../utils/messages.js"; -import logger from "../../utils/logger.js"; -import axios from "axios"; -const SERVICE_NAME = "monitorController"; - -describe("Monitor Controller - getAllMonitors", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: {}, - query: {}, - body: {}, - db: { - getAllMonitors: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if DB operations fail", async function () { - req.db.getAllMonitors.throws(new Error("DB error")); - await getAllMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return success message and data if all operations succeed", async function () { - const data = [{ monitor: "data" }]; - req.db.getAllMonitors.returns(data); - await getAllMonitors(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_GET_ALL, - data: data, - }) - ).to.be.true; - }); -}); -describe("Monitor Controller - getAllMonitorsWithUptimeStats", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: {}, - query: {}, - body: {}, - db: { - getAllMonitorsWithUptimeStats: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if DB operations fail", async function () { - req.db.getAllMonitorsWithUptimeStats.throws(new Error("DB error")); - await getAllMonitorsWithUptimeStats(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return success message and data if all operations succeed", async function () { - const data = [{ monitor: "data" }]; - req.db.getAllMonitorsWithUptimeStats.returns(data); - await getAllMonitorsWithUptimeStats(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_GET_ALL, - data: data, - }) - ).to.be.true; - }); -}); - -describe("Monitor Controller - getMonitorStatsById", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorStatsById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if param validation fails", async function () { - req.params = {}; - await getMonitorStatsById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if query validation fails", async function () { - req.query = { invalid: 1 }; - await getMonitorStatsById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if DB operations fail", async function () { - req.db.getMonitorStatsById.throws(new Error("DB error")); - await getMonitorStatsById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return success message and data if all operations succeed", async function () { - const data = [{ monitorStats: "data" }]; - req.db.getMonitorStatsById.returns(data); - await getMonitorStatsById(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_STATS_BY_ID, - data: data, - }) - ).to.be.true; - }); -}); - -describe("Monitor Controller - getMonitorCertificate", function () { - let req, res, next, fetchMonitorCertificate; - - beforeEach(function () { - req = { - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - fetchMonitorCertificate = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if param validation fails", async function () { - req.params = {}; - await getMonitorCertificate(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if getMonitorById operation fails", async function () { - req.db.getMonitorById.throws(new Error("DB error")); - await getMonitorCertificate(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return success message and data if all operations succeed with a valid cert", async function () { - req.db.getMonitorById.returns({ url: "https://www.google.com" }); - const data = { certificate: "cert", validTo: "2024/08/08" }; - fetchMonitorCertificate.returns(data); - await getMonitorCertificate(req, res, next, fetchMonitorCertificate); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_CERTIFICATE, - data: { certificateDate: new Date(data.validTo) }, - }) - ).to.be.true; - }); - - it("should return an error if fetchMonitorCertificate fails", async function () { - req.db.getMonitorById.returns({ url: "https://www.google.com" }); - fetchMonitorCertificate.throws(new Error("Certificate error")); - await getMonitorCertificate(req, res, next, fetchMonitorCertificate); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Certificate error"); - }); -}); - -describe("Monitor Controller - getMonitorById", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorById: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if param validation fails", async function () { - req.params = {}; - await getMonitorById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if query param validation fails", async function () { - req.query = { invalid: 1 }; - await getMonitorById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if DB operations fail", async function () { - req.db.getMonitorById.throws(new Error("DB error")); - await getMonitorById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return 404 if a monitor is not found", async function () { - const error = new Error("Monitor not found"); - error.status = 404; - req.db.getMonitorById.throws(error); - await getMonitorById(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(404); - }); - - it("should return success message and data if all operations succeed", async function () { - const data = { monitor: "data" }; - req.db.getMonitorById.returns(data); - await getMonitorById(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_GET_BY_ID, - data: data, - }) - ).to.be.true; - }); -}); - -describe("Monitor Controller - getMonitorsAndSummaryByTeamId", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: { - teamId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorsAndSummaryByTeamId: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if param validation fails", async function () { - req.params = {}; - await getMonitorsAndSummaryByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if query validation fails", async function () { - req.query = { invalid: 1 }; - await getMonitorsAndSummaryByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if DB operations fail", async function () { - req.db.getMonitorsAndSummaryByTeamId.throws(new Error("DB error")); - await getMonitorsAndSummaryByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return success message and data if all operations succeed", async function () { - const data = { monitors: "data", summary: "data" }; - req.db.getMonitorsAndSummaryByTeamId.returns(data); - await getMonitorsAndSummaryByTeamId(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId), - data: data, - }) - ).to.be.true; - }); -}); - -describe("Monitor Controller - getMonitorsByTeamId", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: { - teamId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorsByTeamId: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if param validation fails", async function () { - req.params = {}; - await getMonitorsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if query validation fails", async function () { - req.query = { invalid: 1 }; - await getMonitorsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if DB operations fail", async function () { - req.db.getMonitorsByTeamId.throws(new Error("DB error")); - await getMonitorsByTeamId(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should return success message and data if all operations succeed", async function () { - const data = { monitors: "data" }; - req.db.getMonitorsByTeamId.returns(data); - await getMonitorsByTeamId(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId), - data: data, - }) - ).to.be.true; - }); -}); - -describe("Monitor Controller - createMonitor", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: {}, - query: {}, - body: { - userId: "123", - teamId: "123", - name: "test_monitor", - description: "test_monitor_desc", - type: "http", - url: "https://example.com", - notifications: [{ email: "example@example.com" }], - }, - db: { - createMonitor: sinon.stub(), - createNotification: sinon.stub(), - }, - jobQueue: { - addJob: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if body validation fails", async function () { - req.body = {}; - await createMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if DB createMonitor operation fail", async function () { - req.db.createMonitor.throws(new Error("DB error")); - await createMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should reject with an error if DB createNotification operation fail", async function () { - req.db.createNotification.throws(new Error("DB error")); - req.db.createMonitor.returns({ _id: "123" }); - await createMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should reject with an error if monitor.save operation fail", async function () { - req.db.createMonitor.returns({ - _id: "123", - save: sinon.stub().throws(new Error("Monitor save error")), - }); - await createMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Monitor save error"); - }); - - it("should throw an error if addJob operation fails", async function () { - req.db.createMonitor.returns({ _id: "123", save: sinon.stub() }); - req.jobQueue.addJob.throws(new Error("Job error")); - await createMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Job error"); - }); - - it("should return success message and data if all operations succeed", async function () { - const monitor = { _id: "123", save: sinon.stub() }; - req.db.createMonitor.returns(monitor); - await createMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(201); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_CREATE, - data: monitor, - }) - ).to.be.true; - }); -}); - -describe("Monitor Controller - checkEndpointResolution", function () { - let req, res, next, axiosGetStub; - - beforeEach(function () { - req = { query: { monitorURL: "https://example.com" } }; - res = { status: sinon.stub().returnsThis(), json: sinon.stub() }; - next = sinon.stub(); - axiosGetStub = sinon.stub(axios, "get"); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should resolve the URL successfully", async function () { - axiosGetStub.resolves({ status: 200, statusText: "OK" }); - await checkEndpointResolution(req, res, next); - expect(res.status.calledWith(200)).to.be.true; - expect( - res.json.calledWith({ - success: true, - code: 200, - statusText: "OK", - msg: "URL resolved successfully", - }) - ).to.be.true; - expect(next.called).to.be.false; - }); - - it("should return an error if endpoint resolution fails", async function () { - const axiosError = new Error("resolution failed"); - axiosError.code = "ENOTFOUND"; - axiosGetStub.rejects(axiosError); - await checkEndpointResolution(req, res, next); - expect(next.calledOnce).to.be.true; - const errorPassedToNext = next.getCall(0).args[0]; - expect(errorPassedToNext).to.be.an.instanceOf(Error); - expect(errorPassedToNext.message).to.include("resolution failed"); - expect(errorPassedToNext.code).to.equal("ENOTFOUND"); - expect(errorPassedToNext.status).to.equal(500); - }); - - it("should reject with an error if query validation fails", async function () { - req.query.monitorURL = "invalid-url"; - await checkEndpointResolution(req, res, next); - expect(next.calledOnce).to.be.true; - const error = next.getCall(0).args[0]; - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - expect(error.message).to.equal('"monitorURL" must be a valid uri'); - }); -}); - -describe("Monitor Controller - deleteMonitor", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - deleteMonitor: sinon.stub(), - deleteChecks: sinon.stub(), - deletePageSpeedChecksByMonitorId: sinon.stub(), - deleteNotificationsByMonitorId: sinon.stub(), - }, - jobQueue: { - deleteJob: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - sinon.stub(logger, "error"); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if param validation fails", async function () { - req.params = {}; - await deleteMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if DB deleteMonitor operation fail", async function () { - req.db.deleteMonitor.throws(new Error("DB error")); - await deleteMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should log an error if deleteJob throws an error", async function () { - const error = new Error("Job error"); - const monitor = { name: "test_monitor", _id: "123" }; - req.db.deleteMonitor.returns(monitor); - req.jobQueue.deleteJob.rejects(error); - await deleteMonitor(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal(`Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`); - }); - - it("should log an error if deleteChecks throws an error", async function () { - const error = new Error("Checks error"); - const monitor = { name: "test_monitor", _id: "123" }; - req.db.deleteMonitor.returns(monitor); - req.db.deleteChecks.rejects(error); - await deleteMonitor(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal(`Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`); - }); - - it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function () { - const error = new Error("PageSpeed error"); - const monitor = { name: "test_monitor", _id: "123" }; - req.db.deleteMonitor.returns(monitor); - req.db.deletePageSpeedChecksByMonitorId.rejects(error); - await deleteMonitor(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal(`Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`); - }); - - it("should log an error if deleteNotificationsByMonitorId throws an error", async function () { - const error = new Error("Notifications error"); - const monitor = { name: "test_monitor", _id: "123" }; - req.db.deleteMonitor.returns(monitor); - req.db.deleteNotificationsByMonitorId.rejects(error); - await deleteMonitor(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal(`Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`); - }); - - it("should return success message if all operations succeed", async function () { - const monitor = { name: "test_monitor", _id: "123" }; - req.db.deleteMonitor.returns(monitor); - await deleteMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_DELETE, - }) - ).to.be.true; - }); -}); - -describe("Monitor Controller - deleteAllMonitors", function () { - let req, res, next, stub; - - beforeEach(function () { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { teamId: "123" }; - }); - req = { - headers: { - authorization: "Bearer token", - }, - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - deleteAllMonitors: sinon.stub(), - deleteChecks: sinon.stub(), - deletePageSpeedChecksByMonitorId: sinon.stub(), - deleteNotificationsByMonitorId: sinon.stub(), - }, - jobQueue: { - deleteJob: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - sinon.stub(logger, "error"); - }); - - afterEach(function () { - sinon.restore(); - stub.restore(); - }); - - it("should reject with an error if getTokenFromHeaders throws an error", async function () { - req.headers = {}; - await deleteAllMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("No auth headers"); - expect(next.firstCall.args[0].status).to.equal(500); - }); - - it("should reject with an error if token validation fails", async function () { - stub.restore(); - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - await deleteAllMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); - }); - - it("should reject with an error if DB deleteAllMonitors operation fail", async function () { - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.deleteAllMonitors.throws(new Error("DB error")); - await deleteAllMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should log an error if deleteChecks throws an error", async function () { - const monitors = [{ name: "test_monitor", _id: "123" }]; - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); - const error = new Error("Check error"); - req.db.deleteChecks.rejects(error); - await deleteAllMonitors(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal( - `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}` - ); - }); - - it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function () { - const monitors = [{ name: "test_monitor", _id: "123" }]; - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); - const error = new Error("Pagespeed Check error"); - req.db.deletePageSpeedChecksByMonitorId.rejects(error); - await deleteAllMonitors(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal( - `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}` - ); - }); - - it("should log an error if deleteNotificationsByMonitorId throws an error", async function () { - const monitors = [{ name: "test_monitor", _id: "123" }]; - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); - const error = new Error("Notifications Check error"); - req.db.deleteNotificationsByMonitorId.rejects(error); - await deleteAllMonitors(req, res, next); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal( - `Error deleting associated records for monitor ${monitors[0]._id} with name ${monitors[0].name}` - ); - }); - - it("should return success message if all operations succeed", async function () { - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.deleteAllMonitors.returns({ - monitors: [{ name: "test_monitor", _id: "123" }], - deletedCount: 1, - }); - await deleteAllMonitors(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: "Deleted 1 monitors", - }) - ).to.be.true; - }); -}); - -describe("Monitor Controller - editMonitor", function () { - let req, res, next; - - beforeEach(function () { - req = { - headers: {}, - params: { - monitorId: "123", - }, - query: {}, - body: { - notifications: [{ email: "example@example.com" }], - }, - db: { - getMonitorById: sinon.stub(), - editMonitor: sinon.stub(), - deleteNotificationsByMonitorId: sinon.stub(), - createNotification: sinon.stub(), - }, - jobQueue: { - deleteJob: sinon.stub(), - addJob: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if param validation fails", async function () { - req.params = {}; - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if body validation fails", async function () { - req.body = { invalid: 1 }; - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if getMonitorById operation fails", async function () { - req.db.getMonitorById.throws(new Error("DB error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should reject with an error if editMonitor operation fails", async function () { - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.throws(new Error("DB error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should reject with an error if deleteNotificationsByMonitorId operation fails", async function () { - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.returns({ _id: "123" }); - req.db.deleteNotificationsByMonitorId.throws(new Error("DB error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should reject with an error if createNotification operation fails", async function () { - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.returns({ _id: "123" }); - req.db.createNotification.throws(new Error("DB error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should reject with an error if deleteJob operation fails", async function () { - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.returns({ _id: "123" }); - req.jobQueue.deleteJob.throws(new Error("Job error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Job error"); - }); - - it("should reject with an error if addJob operation fails", async function () { - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.returns({ _id: "123" }); - req.jobQueue.addJob.throws(new Error("Add Job error")); - await editMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Add Job error"); - }); - - it("should return success message with data if all operations succeed", async function () { - const monitor = { _id: "123" }; - req.db.getMonitorById.returns({ teamId: "123" }); - req.db.editMonitor.returns(monitor); - await editMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_EDIT, - data: monitor, - }) - ).to.be.true; - }); -}); - -describe("Monitor Controller - pauseMonitor", function () { - let req, res, next; - - beforeEach(function () { - req = { - headers: {}, - params: { - monitorId: "123", - }, - query: {}, - body: {}, - db: { - getMonitorById: sinon.stub(), - }, - jobQueue: { - deleteJob: sinon.stub(), - addJob: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should reject with an error if param validation fails", async function () { - req.params = {}; - await pauseMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if getMonitorById operation fails", async function () { - req.db.getMonitorById.throws(new Error("DB error")); - await pauseMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should reject with an error if deleteJob operation fails", async function () { - const monitor = { _id: req.params.monitorId, isActive: true }; - req.db.getMonitorById.returns(monitor); - req.jobQueue.deleteJob.throws(new Error("Delete Job error")); - await pauseMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Delete Job error"); - }); - - it("should reject with an error if addJob operation fails", async function () { - const monitor = { _id: req.params.monitorId, isActive: false }; - req.db.getMonitorById.returns(monitor); - req.jobQueue.addJob.throws(new Error("Add Job error")); - await pauseMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Add Job error"); - }); - - it("should reject with an error if monitor.save operation fails", async function () { - const monitor = { - _id: req.params.monitorId, - active: false, - save: sinon.stub().throws(new Error("Save error")), - }; - req.db.getMonitorById.returns(monitor); - await pauseMonitor(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Save error"); - }); - - it("should return success pause message with data if all operations succeed with inactive monitor", async function () { - const monitor = { - _id: req.params.monitorId, - isActive: false, - save: sinon.stub().resolves(), - }; - req.db.getMonitorById.returns(monitor); - await pauseMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_PAUSE, - data: monitor, - }) - ).to.be.true; - }); - - it("should return success resume message with data if all operations succeed with active monitor", async function () { - const monitor = { - _id: req.params.monitorId, - isActive: true, - save: sinon.stub().resolves(), - }; - req.db.getMonitorById.returns(monitor); - await pauseMonitor(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_RESUME, - data: monitor, - }) - ).to.be.true; - }); -}); - -describe("Monitor Controller - addDemoMonitors", function () { - let req, res, next, stub; - - beforeEach(function () { - stub = sinon.stub(jwt, "verify").callsFake(() => { - return { _id: "123", teamId: "123" }; - }); - req = { - headers: { - authorization: "Bearer token", - }, - params: {}, - query: {}, - body: {}, - db: { - addDemoMonitors: sinon.stub(), - }, - settingsService: { - getSettings: sinon.stub(), - }, - jobQueue: { - addJob: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - stub.restore(); - }); - - it("should reject with an error if getTokenFromHeaders fails", async function () { - req.headers = {}; - await addDemoMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("No auth headers"); - expect(next.firstCall.args[0].status).to.equal(500); - }); - - it("should reject with an error if getting settings fails", async function () { - req.settingsService.getSettings.throws(new Error("Settings error")); - await addDemoMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Settings error"); - }); - - it("should reject with an error if JWT validation fails", async function () { - stub.restore(); - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - await addDemoMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); - }); - - it("should reject with an error if addDemoMonitors operation fails", async function () { - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.addDemoMonitors.throws(new Error("DB error")); - await addDemoMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("DB error"); - }); - - it("should reject with an error if addJob operation fails", async function () { - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.addDemoMonitors.returns([{ _id: "123" }]); - req.jobQueue.addJob.throws(new Error("Add Job error")); - await addDemoMonitors(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("Add Job error"); - }); - - it("should return success message with data if all operations succeed", async function () { - const monitors = [{ _id: "123" }]; - req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.addDemoMonitors.returns(monitors); - await addDemoMonitors(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect( - res.json.calledOnceWith({ - success: true, - msg: successMessages.MONITOR_DEMO_ADDED, - data: monitors.length, - }) - ).to.be.true; - }); -}); diff --git a/server/tests/controllers/queueController.test.js b/server/tests/controllers/queueController.test.js deleted file mode 100755 index 6201bdb5a..000000000 --- a/server/tests/controllers/queueController.test.js +++ /dev/null @@ -1,176 +0,0 @@ -import { afterEach } from "node:test"; -import { getMetrics, getJobs, addJob, obliterateQueue } from "../../controllers/queueController.js"; -import { successMessages } from "../../utils/messages.js"; -import sinon from "sinon"; - -describe("Queue Controller - getMetrics", function () { - let req, res, next; - - beforeEach(function () { - req = { - headers: {}, - params: {}, - body: {}, - db: {}, - jobQueue: { - getMetrics: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should throw an error if getMetrics throws an error", async function () { - req.jobQueue.getMetrics.throws(new Error("getMetrics error")); - await getMetrics(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getMetrics error"); - }); - - it("should return a success message and data if getMetrics is successful", async function () { - const data = { data: "metrics" }; - req.jobQueue.getMetrics.returns(data); - await getMetrics(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.QUEUE_GET_METRICS, - data, - }); - }); -}); - -describe("Queue Controller - getJobs", function () { - let req, res, next; - - beforeEach(function () { - req = { - headers: {}, - params: {}, - body: {}, - db: {}, - jobQueue: { - getJobStats: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should reject with an error if getJobs throws an error", async function () { - req.jobQueue.getJobStats.throws(new Error("getJobs error")); - await getJobs(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getJobs error"); - }); - - it("should return a success message and data if getJobs is successful", async function () { - const data = { data: "jobs" }; - req.jobQueue.getJobStats.returns(data); - await getJobs(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.QUEUE_GET_METRICS, - data, - }); - }); -}); - -describe("Queue Controller - addJob", function () { - let req, res, next; - - beforeEach(function () { - req = { - headers: {}, - params: {}, - body: {}, - db: {}, - jobQueue: { - addJob: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should reject with an error if addJob throws an error", async function () { - req.jobQueue.addJob.throws(new Error("addJob error")); - await addJob(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("addJob error"); - }); - - it("should return a success message if addJob is successful", async function () { - req.jobQueue.addJob.resolves(); - await addJob(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.QUEUE_ADD_JOB, - }); - }); -}); - -describe("Queue Controller - obliterateQueue", function () { - let req, res, next; - - beforeEach(function () { - req = { - headers: {}, - params: {}, - body: {}, - db: {}, - jobQueue: { - obliterate: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should reject with an error if obliterateQueue throws an error", async function () { - req.jobQueue.obliterate.throws(new Error("obliterateQueue error")); - await obliterateQueue(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("obliterateQueue error"); - }); - - it("should return a success message if obliterateQueue is successful", async function () { - req.jobQueue.obliterate.resolves(); - await obliterateQueue(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.QUEUE_OBLITERATE, - }); - }); -}); diff --git a/server/tests/controllers/settingsController.test.js b/server/tests/controllers/settingsController.test.js deleted file mode 100755 index 721c41c02..000000000 --- a/server/tests/controllers/settingsController.test.js +++ /dev/null @@ -1,109 +0,0 @@ -import { afterEach } from "node:test"; -import { getAppSettings, updateAppSettings } from "../../controllers/settingsController.js"; - -import { successMessages } from "../../utils/messages.js"; -import sinon from "sinon"; - -describe("Settings Controller - getAppSettings", function () { - let req, res, next; - - beforeEach(function () { - req = { - headers: {}, - params: {}, - body: {}, - db: {}, - settingsService: { - getSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should throw an error if getSettings throws an error", async function () { - req.settingsService.getSettings.throws(new Error("getSettings error")); - await getAppSettings(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("getSettings error"); - }); - - it("should return a success message and data if getSettings is successful", async function () { - const data = { data: "settings" }; - req.settingsService.getSettings.returns(data); - await getAppSettings(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.GET_APP_SETTINGS, - data, - }); - }); -}); - -describe("Settings Controller - updateAppSettings", function () { - let req, res, next; - - beforeEach(function () { - req = { - headers: {}, - params: {}, - body: {}, - db: { - updateAppSettings: sinon.stub(), - }, - settingsService: { - reloadSettings: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should reject with an error if body validation fails", async function () { - req.body = { invalid: 1 }; - await updateAppSettings(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].status).to.equal(422); - }); - - it("should reject with an error if updateAppSettings throws an error", async function () { - req.db.updateAppSettings.throws(new Error("updateAppSettings error")); - await updateAppSettings(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("updateAppSettings error"); - }); - - it("should reject with an error if reloadSettings throws an error", async function () { - req.settingsService.reloadSettings.throws(new Error("reloadSettings error")); - await updateAppSettings(req, res, next); - expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal("reloadSettings error"); - }); - - it("should return a success message and data if updateAppSettings is successful", async function () { - const data = { data: "settings" }; - req.settingsService.reloadSettings.returns(data); - await updateAppSettings(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0]).to.deep.equal({ - success: true, - msg: successMessages.UPDATE_APP_SETTINGS, - data, - }); - }); -}); diff --git a/server/tests/controllers/statusPageController.test.js b/server/tests/controllers/statusPageController.test.js deleted file mode 100755 index cada662e0..000000000 --- a/server/tests/controllers/statusPageController.test.js +++ /dev/null @@ -1,127 +0,0 @@ -import sinon from "sinon"; -import { createStatusPage, getStatusPageByUrl } from "../../controllers/statusPageController.js"; - -describe("statusPageController", function () { - let req, res, next; - - beforeEach(function () { - req = { - params: {}, - body: {}, - db: { - createStatusPage: sinon.stub(), - getStatusPageByUrl: sinon.stub(), - }, - }; - res = { - status: sinon.stub().returnsThis(), - json: sinon.stub(), - }; - next = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("createStatusPage", function () { - beforeEach(function () { - req.body = { - companyName: "Test Company", - url: "123456", - timezone: "America/Toronto", - color: "#000000", - theme: "light", - monitors: ["67309ca673788e808884c8ac", "67309ca673788e808884c8ac"], - }; - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should handle a validation error", async function () { - req.body = { - // Invalid data that will trigger validation error - companyName: "", - url: "", - timezone: "", - color: "invalid", - theme: "invalid", - monitors: ["invalid-id"], - }; - try { - await createStatusPage(req, res, next); - } catch (error) { - expect(error).to.be.an.instanceOf(Error); - expect(error.message).to.equal("Validation error"); - } - }); - - it("should handle a db error", async function () { - const err = new Error("DB error"); - req.db.createStatusPage.throws(err); - - try { - await createStatusPage(req, res, next); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - - it("should insert a properly formatted status page", async function () { - const result = await createStatusPage(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0].success).to.be.true; - }); - }); - - describe("getStatusPageByUrl", function () { - beforeEach(function () { - req.params = { - url: "123456", - }; - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should handle a validation error", async function () { - req.params = { - url: "", - }; - - try { - await getStatusPageByUrl(req, res, next); - } catch (error) { - expect(error).to.be.an.instanceOf(Error); - expect(error.message).to.equal("Validation error"); - } - }); - - it("should handle a DB error", async function () { - const err = new Error("DB error"); - req.db.getStatusPageByUrl.throws(err); - - try { - await getStatusPageByUrl(req, res, next); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - - it("should return a status page", async function () { - const statusPage = { - _id: "123456", - companyName: "Test Company", - url: "123456", - }; - req.db.getStatusPageByUrl.resolves(statusPage); - const result = await getStatusPageByUrl(req, res, next); - expect(res.status.firstCall.args[0]).to.equal(200); - expect(res.json.firstCall.args[0].success).to.be.true; - expect(res.json.firstCall.args[0].data).to.deep.equal(statusPage); - }); - }); -}); diff --git a/server/tests/db/checkModule.test.js b/server/tests/db/checkModule.test.js deleted file mode 100755 index 6eef12445..000000000 --- a/server/tests/db/checkModule.test.js +++ /dev/null @@ -1,589 +0,0 @@ -import sinon from "sinon"; -import { - createCheck, - getChecksCount, - getChecks, - getTeamChecks, - deleteChecks, - deleteChecksByTeamId, - updateChecksTTL, -} from "../../db/mongo/modules/checkModule.js"; -import Check from "../../db/models/Check.js"; -import Monitor from "../../db/models/Monitor.js"; -import User from "../../db/models/User.js"; -import logger from "../../utils/logger.js"; - -describe("checkModule", function () { - describe("createCheck", function () { - let checkCountDocumentsStub, checkSaveStub, monitorFindByIdStub, monitorSaveStub; - const mockMonitor = { - _id: "123", - uptimePercentage: 0.5, - status: true, - save: () => this, - }; - const mockCheck = { active: true }; - - beforeEach(function () { - checkSaveStub = sinon.stub(Check.prototype, "save"); - checkCountDocumentsStub = sinon.stub(Check, "countDocuments"); - monitorFindByIdStub = sinon.stub(Monitor, "findById"); - monitorSaveStub = sinon.stub(Monitor.prototype, "save"); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should return undefined early if no monitor is found", async function () { - monitorFindByIdStub.returns(null); - const check = await createCheck({ monitorId: "123" }); - expect(check).to.be.undefined; - }); - - it("should return a check", async function () { - monitorFindByIdStub.returns(mockMonitor); - checkSaveStub.returns(mockCheck); - monitorSaveStub.returns(mockMonitor); - const check = await createCheck({ monitorId: "123", status: true }); - expect(check).to.deep.equal(mockCheck); - }); - - it("should return a check if status is down", async function () { - mockMonitor.status = false; - monitorFindByIdStub.returns(mockMonitor); - checkSaveStub.returns(mockCheck); - monitorSaveStub.returns(mockMonitor); - const check = await createCheck({ monitorId: "123", status: false }); - expect(check).to.deep.equal(mockCheck); - }); - - it("should return a check if uptimePercentage is undefined", async function () { - mockMonitor.uptimePercentage = undefined; - monitorFindByIdStub.returns(mockMonitor); - checkSaveStub.returns(mockCheck); - monitorSaveStub.returns(mockMonitor); - const check = await createCheck({ monitorId: "123", status: true }); - expect(check).to.deep.equal(mockCheck); - }); - - it("should return a check if uptimePercentage is undefined and status is down", async function () { - mockMonitor.uptimePercentage = undefined; - monitorFindByIdStub.returns(mockMonitor); - checkSaveStub.returns(mockCheck); - monitorSaveStub.returns(mockMonitor); - const check = await createCheck({ monitorId: "123", status: false }); - expect(check).to.deep.equal(mockCheck); - }); - - it("should monitor save error", async function () { - const err = new Error("Save Error"); - monitorSaveStub.throws(err); - try { - await createCheck({ monitorId: "123" }); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - - it("should handle errors", async function () { - const err = new Error("DB Error"); - checkCountDocumentsStub.throws(err); - try { - await createCheck({ monitorId: "123" }); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("getChecksCount", function () { - let checkCountDocumentStub; - - beforeEach(function () { - checkCountDocumentStub = sinon.stub(Check, "countDocuments"); - }); - - afterEach(function () { - checkCountDocumentStub.restore(); - }); - - it("should return count with basic monitorId query", async function () { - const req = { - params: { monitorId: "test123" }, - query: {}, - }; - checkCountDocumentStub.resolves(5); - - const result = await getChecksCount(req); - - expect(result).to.equal(5); - expect(checkCountDocumentStub.calledOnce).to.be.true; - expect(checkCountDocumentStub.firstCall.args[0]).to.deep.equal({ - monitorId: "test123", - }); - }); - - it("should include dateRange in query when provided", async function () { - const req = { - params: { monitorId: "test123" }, - query: { dateRange: "day" }, - }; - checkCountDocumentStub.resolves(3); - - const result = await getChecksCount(req); - - expect(result).to.equal(3); - expect(checkCountDocumentStub.firstCall.args[0]).to.have.property("createdAt"); - expect(checkCountDocumentStub.firstCall.args[0].createdAt).to.have.property("$gte"); - }); - - it('should handle "all" filter correctly', async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "all" }, - }; - checkCountDocumentStub.resolves(2); - - const result = await getChecksCount(req); - - expect(result).to.equal(2); - expect(checkCountDocumentStub.firstCall.args[0]).to.deep.equal({ - monitorId: "test123", - status: false, - }); - }); - - it('should handle "down" filter correctly', async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "down" }, - }; - checkCountDocumentStub.resolves(2); - - const result = await getChecksCount(req); - - expect(result).to.equal(2); - expect(checkCountDocumentStub.firstCall.args[0]).to.deep.equal({ - monitorId: "test123", - status: false, - }); - }); - - it('should handle "resolve" filter correctly', async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "resolve" }, - }; - checkCountDocumentStub.resolves(1); - - const result = await getChecksCount(req); - - expect(result).to.equal(1); - expect(checkCountDocumentStub.firstCall.args[0]).to.deep.equal({ - monitorId: "test123", - status: false, - statusCode: 5000, - }); - }); - - it("should handle unknown filter correctly", async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "unknown" }, - }; - checkCountDocumentStub.resolves(1); - - const result = await getChecksCount(req); - - expect(result).to.equal(1); - expect(checkCountDocumentStub.firstCall.args[0]).to.deep.equal({ - monitorId: "test123", - status: false, - }); - }); - - it("should combine dateRange and filter in query", async function () { - const req = { - params: { monitorId: "test123" }, - query: { - dateRange: "week", - filter: "down", - }, - }; - checkCountDocumentStub.resolves(4); - - const result = await getChecksCount(req); - - expect(result).to.equal(4); - expect(checkCountDocumentStub.firstCall.args[0]).to.have.all.keys("monitorId", "createdAt", "status"); - }); - }); - - describe("getChecks", function () { - let checkFindStub, monitorFindStub; - - beforeEach(function () { - checkFindStub = sinon.stub(Check, "find").returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().returns([{ id: 1 }, { id: 2 }]), - }), - }), - }); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should return checks with basic monitorId query", async function () { - const req = { - params: { monitorId: "test123" }, - query: {}, - }; - - const result = await getChecks(req); - - expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]); - }); - - it("should return checks with limit query", async function () { - const req = { - params: { monitorId: "test123" }, - query: { limit: 10 }, - }; - - const result = await getChecks(req); - - expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]); - }); - - it("should handle pagination correctly", async function () { - const req = { - params: { monitorId: "test123" }, - query: { - page: 2, - rowsPerPage: 10, - }, - }; - - const result = await getChecks(req); - - expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]); - }); - - it("should handle dateRange filter", async function () { - const req = { - params: { monitorId: "test123" }, - query: { dateRange: "week" }, - }; - const result = await getChecks(req); - - expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]); - }); - - it('should handle "all" filter', async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "all" }, - }; - - await getChecks(req); - const result = await getChecks(req); - expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]); - }); - - it('should handle "down" filter', async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "down" }, - }; - - await getChecks(req); - const result = await getChecks(req); - expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]); - }); - - it('should handle "resolve" filter', async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "resolve" }, - }; - - await getChecks(req); - const result = await getChecks(req); - expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]); - }); - - it('should handle "unknown" filter', async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "unknown" }, - }; - - await getChecks(req); - const result = await getChecks(req); - expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]); - }); - - it("should handle ascending sort order", async function () { - const req = { - params: { monitorId: "test123" }, - query: { sortOrder: "asc" }, - }; - - await getChecks(req); - const result = await getChecks(req); - expect(result).to.deep.equal([{ id: 1 }, { id: 2 }]); - }); - - it("should handle error case", async function () { - const req = { - params: { monitorId: "test123" }, - query: {}, - }; - - checkFindStub.throws(new Error("Database error")); - - try { - await getChecks(req); - } catch (error) { - expect(error.message).to.equal("Database error"); - expect(error.service).to.equal("checkModule"); - expect(error.method).to.equal("getChecks"); - } - }); - }); - - describe("getTeamChecks", function () { - let checkFindStub, checkCountDocumentsStub, monitorFindStub; - const mockMonitors = [{ _id: "123" }]; - - beforeEach(function () { - monitorFindStub = sinon.stub(Monitor, "find").returns({ - select: sinon.stub().returns(mockMonitors), - }); - checkCountDocumentsStub = sinon.stub(Check, "countDocuments").returns(2); - checkFindStub = sinon.stub(Check, "find").returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().returns({ - select: sinon.stub().returns([{ id: 1 }, { id: 2 }]), - }), - }), - }), - }); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should return checks with basic monitorId query", async function () { - const req = { - params: { teamId: "test123" }, - query: {}, - }; - - const result = await getTeamChecks(req); - expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] }); - }); - - it("should handle pagination correctly", async function () { - const req = { - params: { monitorId: "test123" }, - query: { limit: 1, page: 2, rowsPerPage: 10 }, - }; - - const result = await getTeamChecks(req); - expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] }); - }); - - it("should handle dateRange filter", async function () { - const req = { - params: { monitorId: "test123" }, - query: { dateRange: "week" }, - }; - const result = await getTeamChecks(req); - expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] }); - }); - - it('should handle "all" filter', async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "all" }, - }; - - await getChecks(req); - const result = await getTeamChecks(req); - expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] }); - }); - - it('should handle "down" filter', async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "down" }, - }; - - await getChecks(req); - const result = await getTeamChecks(req); - expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] }); - }); - - it('should handle "resolve" filter', async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "resolve" }, - }; - - await getChecks(req); - const result = await getTeamChecks(req); - expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] }); - }); - - it('should handle "unknown" filter', async function () { - const req = { - params: { monitorId: "test123" }, - query: { filter: "unknown" }, - }; - - await getChecks(req); - const result = await getTeamChecks(req); - expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] }); - }); - - it("should handle ascending sort order", async function () { - const req = { - params: { monitorId: "test123" }, - query: { sortOrder: "asc" }, - }; - - await getChecks(req); - const result = await getTeamChecks(req); - expect(result).to.deep.equal({ checksCount: 2, checks: [{ id: 1 }, { id: 2 }] }); - }); - - it("should handle error case", async function () { - const req = { - params: { monitorId: "test123" }, - query: {}, - }; - - checkFindStub.throws(new Error("Database error")); - - try { - await getTeamChecks(req); - } catch (error) { - expect(error.message).to.equal("Database error"); - expect(error.service).to.equal("checkModule"); - expect(error.method).to.equal("getTeamChecks"); - } - }); - }); - - describe("deleteChecks", function () { - let checkDeleteManyStub; - - beforeEach(function () { - checkDeleteManyStub = sinon.stub(Check, "deleteMany").resolves({ deletedCount: 1 }); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should return a value if a check is deleted", async function () { - const result = await deleteChecks("123"); - expect(result).to.equal(1); - }); - - it("should handle an error", async function () { - checkDeleteManyStub.throws(new Error("Database error")); - try { - await deleteChecks("123"); - } catch (error) { - expect(error.message).to.equal("Database error"); - expect(error.method).to.equal("deleteChecks"); - } - }); - }); - - describe("deleteChecksByTeamId", function () { - let mockMonitors = [{ _id: 123, save: () => this }]; - let monitorFindStub, monitorSaveStub, checkDeleteManyStub; - - beforeEach(function () { - monitorSaveStub = sinon.stub(Monitor.prototype, "save"); - monitorFindStub = sinon.stub(Monitor, "find").returns(mockMonitors); - checkDeleteManyStub = sinon.stub(Check, "deleteMany").resolves({ deletedCount: 1 }); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should return a deleted count", async function () { - const result = await deleteChecksByTeamId("123"); - expect(result).to.equal(1); - }); - - it("should handle errors", async function () { - const err = new Error("DB Error"); - monitorFindStub.throws(err); - try { - const result = await deleteChecksByTeamId("123"); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("updateChecksTTL", function () { - let userUpdateManyStub; - let loggerStub; - - beforeEach(function () { - loggerStub = sinon.stub(logger, "error"); - userUpdateManyStub = sinon.stub(User, "updateMany"); - Check.collection = { dropIndex: sinon.stub(), createIndex: sinon.stub() }; - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should return undefined", async function () { - const result = await updateChecksTTL("123", 10); - expect(result).to.be.undefined; - }); - - it("should log an error if dropIndex throws an error", async function () { - const err = new Error("Drop Index Error"); - Check.collection.dropIndex.throws(err); - await updateChecksTTL("123", 10); - expect(loggerStub.calledOnce).to.be.true; - expect(loggerStub.firstCall.args[0].message).to.equal(err.message); - }); - - it("should throw an error if createIndex throws an error", async function () { - const err = new Error("Create Index Error"); - Check.collection.createIndex.throws(err); - try { - await updateChecksTTL("123", 10); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - - it("should throw an error if User.updateMany throws an error", async function () { - const err = new Error("Update Many Error"); - userUpdateManyStub.throws(err); - try { - await updateChecksTTL("123", 10); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); -}); diff --git a/server/tests/db/hardwareCheckModule.test.js b/server/tests/db/hardwareCheckModule.test.js deleted file mode 100755 index 20ce197fb..000000000 --- a/server/tests/db/hardwareCheckModule.test.js +++ /dev/null @@ -1,136 +0,0 @@ -import sinon from "sinon"; -import HardwareCheck from "../../db/models/HardwareCheck.js"; -import { createHardwareCheck } from "../../db/mongo/modules/hardwareCheckModule.js"; -import Monitor from "../../db/models/Monitor.js"; -import logger from "../../utils/logger.js"; - -const mockHardwareCheck = { - data: { - cpu: { - physical_core: 4, - logical_core: 8, - frequency: 4800, - current_frequency: 1411, - temperature: [45, 50, 46, 47, 45, 50, 46, 47], - free_percent: 0.8552990910595134, - usage_percent: 0.14470090894048657, - }, - memory: { - total_bytes: 16467628032, - available_bytes: 7895044096, - used_bytes: 6599561216, - usage_percent: 0.4008, - }, - disk: [ - { - read_speed_bytes: null, - write_speed_bytes: null, - total_bytes: 931258499072, - free_bytes: 737097256960, - usage_percent: 0.1661, - }, - ], - host: { - os: "linux", - platform: "ubuntu", - kernel_version: "6.8.0-48-generic", - }, - }, - errors: [ - { - metric: ["cpu.temperature"], - err: "unable to read CPU temperature", - }, - ], -}; - -const mockMonitor = { - _id: "123", - uptimePercentage: 1, - status: true, - save: () => this, -}; - -describe("HardwareCheckModule", function () { - let hardwareCheckSaveStub, hardwareCheckCountDocumentsStub, monitorFindByIdStub, loggerStub; - - beforeEach(function () { - loggerStub = sinon.stub(logger, "error"); - hardwareCheckSaveStub = sinon.stub(HardwareCheck.prototype, "save"); - monitorFindByIdStub = sinon.stub(Monitor, "findById"); - hardwareCheckCountDocumentsStub = sinon.stub(HardwareCheck, "countDocuments"); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("createHardwareCheck", function () { - it("should return a hardware check", async function () { - hardwareCheckSaveStub.resolves(mockHardwareCheck); - monitorFindByIdStub.resolves(mockMonitor); - hardwareCheckCountDocumentsStub.resolves(1); - const hardwareCheck = await createHardwareCheck({ status: true }); - expect(hardwareCheck).to.exist; - expect(hardwareCheck).to.deep.equal(mockHardwareCheck); - }); - - it("should return a hardware check for a check with status false", async function () { - hardwareCheckSaveStub.resolves(mockHardwareCheck); - monitorFindByIdStub.resolves(mockMonitor); - hardwareCheckCountDocumentsStub.resolves(1); - const hardwareCheck = await createHardwareCheck({ status: false }); - expect(hardwareCheck).to.exist; - expect(hardwareCheck).to.deep.equal(mockHardwareCheck); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - monitorFindByIdStub.resolves(mockMonitor); - hardwareCheckSaveStub.rejects(err); - try { - await createHardwareCheck({}); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - - it("should log an error if a monitor is not found", async function () { - monitorFindByIdStub.resolves(null); - const res = await createHardwareCheck({}); - expect(loggerStub.calledOnce).to.be.true; - expect(res).to.be.null; - }); - - it("should handle a monitor with undefined uptimePercentage", async function () { - monitorFindByIdStub.resolves({ ...mockMonitor, uptimePercentage: undefined }); - hardwareCheckSaveStub.resolves(mockHardwareCheck); - hardwareCheckCountDocumentsStub.resolves(1); - const res = await createHardwareCheck({}); - expect(res).to.exist; - }); - - it("should handle a monitor with undefined uptimePercentage and true status", async function () { - monitorFindByIdStub.resolves({ - ...mockMonitor, - uptimePercentage: undefined, - }); - hardwareCheckSaveStub.resolves(mockHardwareCheck); - hardwareCheckCountDocumentsStub.resolves(1); - const res = await createHardwareCheck({ status: true }); - expect(res).to.exist; - }); - - it("should handle a monitor with undefined uptimePercentage and false status", async function () { - monitorFindByIdStub.resolves({ - ...mockMonitor, - uptimePercentage: undefined, - }); - hardwareCheckSaveStub.resolves(mockHardwareCheck); - hardwareCheckCountDocumentsStub.resolves(1); - const res = await createHardwareCheck({ status: false }); - expect(res).to.exist; - }); - }); -}); diff --git a/server/tests/db/inviteModule.test.js b/server/tests/db/inviteModule.test.js deleted file mode 100755 index e8913c983..000000000 --- a/server/tests/db/inviteModule.test.js +++ /dev/null @@ -1,103 +0,0 @@ -import sinon from "sinon"; -import InviteToken from "../../db/models/InviteToken.js"; -import { requestInviteToken, getInviteToken, getInviteTokenAndDelete } from "../../db/mongo/modules/inviteModule.js"; -import { errorMessages } from "../../utils/messages.js"; - -describe("Invite Module", function () { - const mockUserData = { - email: "test@test.com", - teamId: "123", - role: ["admin"], - token: "123", - }; - const mockInviteToken = { _id: 123, time: 123 }; - let inviteTokenDeleteManyStub, inviteTokenSaveStub, inviteTokenFindOneStub, inviteTokenFindOneAndDeleteStub; - - beforeEach(function () { - inviteTokenDeleteManyStub = sinon.stub(InviteToken, "deleteMany"); - inviteTokenSaveStub = sinon.stub(InviteToken.prototype, "save"); - inviteTokenFindOneStub = sinon.stub(InviteToken, "findOne"); - inviteTokenFindOneAndDeleteStub = sinon.stub(InviteToken, "findOneAndDelete"); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("requestInviteToken", function () { - it("should return a new invite token", async function () { - inviteTokenDeleteManyStub.resolves(); - inviteTokenSaveStub.resolves(); - const inviteToken = await requestInviteToken(mockUserData); - expect(inviteToken.email).to.equal(mockUserData.email); - expect(inviteToken.role).to.deep.equal(mockUserData.role); - expect(inviteToken.token).to.exist; - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - inviteTokenDeleteManyStub.rejects(err); - try { - await requestInviteToken(mockUserData); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("getInviteToken", function () { - it("should return an invite token", async function () { - inviteTokenFindOneStub.resolves(mockInviteToken); - const inviteToken = await getInviteToken(mockUserData.token); - expect(inviteToken).to.deep.equal(mockInviteToken); - }); - - it("should handle a token not found", async function () { - inviteTokenFindOneStub.resolves(null); - try { - await getInviteToken(mockUserData.token); - } catch (error) { - expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND); - } - }); - - it("should handle DB errors", async function () { - const err = new Error("test error"); - inviteTokenFindOneStub.rejects(err); - try { - await getInviteToken(mockUserData.token); - } catch (error) { - expect(error).to.deep.equal(err); - expect(error.method).to.equal("getInviteToken"); - } - }); - }); - - describe("getInviteTokenAndDelete", function () { - it("should return a deleted invite", async function () { - inviteTokenFindOneAndDeleteStub.resolves(mockInviteToken); - const deletedInvite = await getInviteTokenAndDelete(mockUserData.token); - expect(deletedInvite).to.deep.equal(mockInviteToken); - }); - - it("should handle a token not found", async function () { - inviteTokenFindOneAndDeleteStub.resolves(null); - try { - await getInviteTokenAndDelete(mockUserData.token); - } catch (error) { - expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND); - } - }); - - it("should handle DB errors", async function () { - const err = new Error("test error"); - inviteTokenFindOneAndDeleteStub.rejects(err); - try { - await getInviteTokenAndDelete(mockUserData.token); - } catch (error) { - expect(error).to.deep.equal(err); - expect(error.method).to.equal("getInviteTokenAndDelete"); - } - }); - }); -}); diff --git a/server/tests/db/maintenanceWindowModule.test.js b/server/tests/db/maintenanceWindowModule.test.js deleted file mode 100755 index 576c97f84..000000000 --- a/server/tests/db/maintenanceWindowModule.test.js +++ /dev/null @@ -1,255 +0,0 @@ -import sinon from "sinon"; -import MaintenanceWindow from "../../db/models/MaintenanceWindow.js"; -import { - createMaintenanceWindow, - getMaintenanceWindowById, - getMaintenanceWindowsByTeamId, - getMaintenanceWindowsByMonitorId, - deleteMaintenanceWindowById, - deleteMaintenanceWindowByMonitorId, - deleteMaintenanceWindowByUserId, - editMaintenanceWindowById, -} from "../../db/mongo/modules/maintenanceWindowModule.js"; - -describe("MaintenanceWindow Module", function () { - const mockMaintenanceWindow = { - monitorId: "123", - active: true, - oneTime: true, - start: 1, - end: 20000, - }; - - let mockMaintenanceWindows = [mockMaintenanceWindow]; - let maintenanceWindowSaveStub, - maintenanceWindowFindByIdStub, - maintenanceWindowCountDocumentsStub, - maintenanceWindowFindStub, - maintenanceWindowFindByIdAndDeleteStub, - maintenanceWindowDeleteManyStub, - maintenanceWindowFindByIdAndUpdateStub; - - beforeEach(function () { - maintenanceWindowSaveStub = sinon.stub(MaintenanceWindow.prototype, "save"); - maintenanceWindowFindByIdStub = sinon.stub(MaintenanceWindow, "findById"); - maintenanceWindowCountDocumentsStub = sinon.stub(MaintenanceWindow, "countDocuments"); - maintenanceWindowFindStub = sinon.stub(MaintenanceWindow, "find").returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().returns(mockMaintenanceWindows), - }), - }), - }); - maintenanceWindowFindByIdAndDeleteStub = sinon.stub(MaintenanceWindow, "findByIdAndDelete"); - maintenanceWindowDeleteManyStub = sinon.stub(MaintenanceWindow, "deleteMany"); - maintenanceWindowFindByIdAndUpdateStub = sinon.stub(MaintenanceWindow, "findByIdAndUpdate"); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("createMaintenanceWindow", function () { - it("should save a new maintenance window", async function () { - maintenanceWindowSaveStub.resolves(mockMaintenanceWindow); - const result = await createMaintenanceWindow(mockMaintenanceWindow); - expect(result).to.deep.equal(mockMaintenanceWindow); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - maintenanceWindowSaveStub.rejects(err); - try { - await createMaintenanceWindow(mockMaintenanceWindow); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("getMaintenanceWindowById", function () { - it("should return a maintenance window", async function () { - maintenanceWindowFindByIdStub.resolves(mockMaintenanceWindow); - const result = await getMaintenanceWindowById(mockMaintenanceWindow.id); - expect(result).to.deep.equal(mockMaintenanceWindow); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - maintenanceWindowFindByIdStub.rejects(err); - try { - await getMaintenanceWindowById(mockMaintenanceWindow.id); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("getMaintenanceWindowsByTeamId", function () { - let query; - - beforeEach(function () { - query = { - active: true, - page: 1, - rowsPerPage: 10, - field: "name", - order: "asc", - }; - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should return a list of maintenance windows and count", async function () { - maintenanceWindowCountDocumentsStub.resolves(1); - const result = await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query); - expect(result).to.deep.equal({ - maintenanceWindows: mockMaintenanceWindows, - maintenanceWindowCount: 1, - }); - }); - - it("should return a list of maintenance windows and count with empty query", async function () { - query = undefined; - maintenanceWindowCountDocumentsStub.resolves(1); - const result = await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query); - expect(result).to.deep.equal({ - maintenanceWindows: mockMaintenanceWindows, - maintenanceWindowCount: 1, - }); - }); - - it("should return a list of maintenance windows and count with no pagination provided", async function () { - query.page = undefined; - query.rowsPerPage = undefined; - maintenanceWindowCountDocumentsStub.resolves(1); - const result = await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query); - expect(result).to.deep.equal({ - maintenanceWindows: mockMaintenanceWindows, - maintenanceWindowCount: 1, - }); - }); - - it("should return a list of maintenance windows and count with field and desc order", async function () { - query.order = "desc"; - maintenanceWindowCountDocumentsStub.resolves(1); - const result = await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query); - expect(result).to.deep.equal({ - maintenanceWindows: mockMaintenanceWindows, - maintenanceWindowCount: 1, - }); - }); - - it("should return a list of maintenance windows and count no field", async function () { - query.field = undefined; - maintenanceWindowCountDocumentsStub.resolves(1); - const result = await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query); - expect(result).to.deep.equal({ - maintenanceWindows: mockMaintenanceWindows, - maintenanceWindowCount: 1, - }); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - maintenanceWindowCountDocumentsStub.rejects(err); - try { - await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("getMaintenanceWindowsByMonitorId", function () { - it("should return a list of maintenance windows", async function () { - maintenanceWindowFindStub.resolves(mockMaintenanceWindows); - const result = await getMaintenanceWindowsByMonitorId(mockMaintenanceWindow.monitorId); - expect(result).to.deep.equal(mockMaintenanceWindows); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - maintenanceWindowFindStub.rejects(err); - try { - await getMaintenanceWindowsByMonitorId(mockMaintenanceWindow.monitorId); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("deleteMaintenanceWindowById", function () { - it("should delete a maintenance window", async function () { - maintenanceWindowFindByIdAndDeleteStub.resolves(mockMaintenanceWindow); - const result = await deleteMaintenanceWindowById(mockMaintenanceWindow.id); - expect(result).to.deep.equal(mockMaintenanceWindow); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - maintenanceWindowFindByIdAndDeleteStub.rejects(err); - try { - await deleteMaintenanceWindowById(mockMaintenanceWindow.id); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("deleteMaintenanceWindowByMonitorId", function () { - it("should return the number of documents deleted", async function () { - maintenanceWindowDeleteManyStub.resolves({ deletedCount: 1 }); - const result = await deleteMaintenanceWindowByMonitorId(mockMaintenanceWindow.monitorId); - expect(result).to.deep.equal({ deletedCount: 1 }); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - maintenanceWindowDeleteManyStub.rejects(err); - try { - await deleteMaintenanceWindowByMonitorId(mockMaintenanceWindow.monitorId); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("deleteMaintenanceWindowByUserId", function () { - it("should return the number of documents deleted", async function () { - maintenanceWindowDeleteManyStub.resolves({ deletedCount: 1 }); - const result = await deleteMaintenanceWindowByUserId(mockMaintenanceWindow.userId); - expect(result).to.deep.equal({ deletedCount: 1 }); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - maintenanceWindowDeleteManyStub.rejects(err); - try { - await deleteMaintenanceWindowByUserId(mockMaintenanceWindow.userId); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("editMaintenanceWindowById", function () { - it("should return the updated maintenance window", async function () { - maintenanceWindowFindByIdAndUpdateStub.resolves(mockMaintenanceWindow); - const result = await editMaintenanceWindowById(mockMaintenanceWindow.id, mockMaintenanceWindow); - expect(result).to.deep.equal(mockMaintenanceWindow); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - maintenanceWindowFindByIdAndUpdateStub.rejects(err); - try { - await editMaintenanceWindowById(mockMaintenanceWindow.id, mockMaintenanceWindow); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); -}); diff --git a/server/tests/db/monitorModule.test.js b/server/tests/db/monitorModule.test.js deleted file mode 100755 index e59c352cf..000000000 --- a/server/tests/db/monitorModule.test.js +++ /dev/null @@ -1,1865 +0,0 @@ -import sinon from "sinon"; -import Monitor from "../../db/models/Monitor.js"; -import Check from "../../db/models/Check.js"; -import PageSpeedCheck from "../../db/models/PageSpeedCheck.js"; -import HardwareCheck from "../../db/models/HardwareCheck.js"; -import Notification from "../../db/models/Notification.js"; - -import { errorMessages } from "../../utils/messages.js"; -import { - getAllMonitors, - getAllMonitorsWithUptimeStats, - getMonitorStatsById, - getMonitorById, - getMonitorsAndSummaryByTeamId, - getMonitorsByTeamId, - createMonitor, - deleteMonitor, - deleteAllMonitors, - deleteMonitorsByUserId, - editMonitor, - addDemoMonitors, - calculateUptimeDuration, - getLastChecked, - getLatestResponseTime, - getAverageResponseTime, - getUptimePercentage, - getIncidents, - getMonitorChecks, - processChecksForDisplay, - groupChecksByTime, - calculateGroupStats, -} from "../../db/mongo/modules/monitorModule.js"; - -describe("monitorModule", function () { - let monitorFindStub, - monitorFindByIdStub, - monitorFindByIdAndUpdateStub, - monitorFindByIdAndDeleteStub, - monitorDeleteManyStub, - monitorCountStub, - monitorInsertManyStub, - checkFindStub, - pageSpeedCheckFindStub, - hardwareCheckFindStub; - - beforeEach(function () { - monitorFindStub = sinon.stub(Monitor, "find"); - monitorFindByIdStub = sinon.stub(Monitor, "findById"); - monitorFindByIdAndUpdateStub = sinon.stub(Monitor, "findByIdAndUpdate"); - monitorFindByIdAndDeleteStub = sinon.stub(Monitor, "findByIdAndDelete"); - monitorDeleteManyStub = sinon.stub(Monitor, "deleteMany"); - monitorCountStub = sinon.stub(Monitor, "countDocuments"); - - monitorInsertManyStub = sinon.stub(Monitor, "insertMany"); - checkFindStub = sinon.stub(Check, "find").returns({ - sort: sinon.stub(), - }); - pageSpeedCheckFindStub = sinon.stub(PageSpeedCheck, "find").returns({ - sort: sinon.stub(), - }); - hardwareCheckFindStub = sinon.stub(HardwareCheck, "find").returns({ - sort: sinon.stub(), - }); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("getAllMonitors", function () { - it("should return all monitors", async function () { - const mockMonitors = [ - { _id: "1", name: "Monitor 1", url: "test1.com" }, - { _id: "2", name: "Monitor 2", url: "test2.com" }, - ]; - monitorFindStub.returns(mockMonitors); - const result = await getAllMonitors(); - - expect(result).to.deep.equal(mockMonitors); - expect(monitorFindStub.calledOnce).to.be.true; - expect(monitorFindStub.firstCall.args).to.deep.equal([]); - }); - - it("should handle empty results", async function () { - monitorFindStub.returns([]); - const result = await getAllMonitors(); - expect(result).to.be.an("array").that.is.empty; - }); - - it("should throw error when database fails", async function () { - // Arrange - const error = new Error("Database error"); - error.service = "MonitorModule"; - error.method = "getAllMonitors"; - monitorFindStub.rejects(error); - // Act & Assert - try { - await getAllMonitors(); - expect.fail("Should have thrown an error"); - } catch (err) { - expect(err).to.equal(error); - expect(err.service).to.equal("monitorModule"); - expect(err.method).to.equal("getAllMonitors"); - } - }); - }); - - describe("getAllMonitorsWithUptimeStats", function () { - it("should return monitors with uptime stats for different time periods", async function () { - // Mock data - const mockMonitors = [ - { - _id: "monitor1", - type: "http", - toObject: () => ({ - _id: "monitor1", - type: "http", - name: "Test Monitor", - }), - }, - ]; - - const mockChecks = [{ status: true }, { status: true }, { status: false }, { status: true }]; - - monitorFindStub.resolves(mockMonitors); - checkFindStub.resolves(mockChecks); - - const result = await getAllMonitorsWithUptimeStats(); - - expect(result).to.be.an("array"); - expect(result).to.have.lengthOf(1); - - const monitor = result[0]; - expect(monitor).to.have.property("_id", "monitor1"); - expect(monitor).to.have.property("name", "Test Monitor"); - - // Check uptime percentages exist for all time periods - expect(monitor).to.have.property("1"); - expect(monitor).to.have.property("7"); - expect(monitor).to.have.property("30"); - expect(monitor).to.have.property("90"); - - // Verify uptime percentage calculation (3 successful out of 4 = 75%) - expect(monitor["1"]).to.equal(75); - expect(monitor["7"]).to.equal(75); - expect(monitor["30"]).to.equal(75); - expect(monitor["90"]).to.equal(75); - }); - - it("should return monitors with stats for pagespeed type", async function () { - // Mock data - const mockMonitors = [ - { - _id: "monitor1", - type: "pagespeed", - toObject: () => ({ - _id: "monitor1", - type: "pagespeed", - name: "Test Monitor", - }), - }, - ]; - - const mockChecks = [{ status: true }, { status: true }, { status: false }, { status: true }]; - - monitorFindStub.resolves(mockMonitors); - pageSpeedCheckFindStub.resolves(mockChecks); - - const result = await getAllMonitorsWithUptimeStats(); - - expect(result).to.be.an("array"); - expect(result).to.have.lengthOf(1); - - const monitor = result[0]; - expect(monitor).to.have.property("_id", "monitor1"); - expect(monitor).to.have.property("name", "Test Monitor"); - - // Check uptime percentages exist for all time periods - expect(monitor).to.have.property("1"); - expect(monitor).to.have.property("7"); - expect(monitor).to.have.property("30"); - expect(monitor).to.have.property("90"); - - // Verify uptime percentage calculation (3 successful out of 4 = 75%) - expect(monitor["1"]).to.equal(75); - expect(monitor["7"]).to.equal(75); - expect(monitor["30"]).to.equal(75); - expect(monitor["90"]).to.equal(75); - }); - - it("should return monitors with stats for hardware type", async function () { - // Mock data - const mockMonitors = [ - { - _id: "monitor1", - type: "hardware", - toObject: () => ({ - _id: "monitor1", - type: "hardware", - name: "Test Monitor", - }), - }, - ]; - - const mockChecks = [{ status: true }, { status: true }, { status: false }, { status: true }]; - - monitorFindStub.resolves(mockMonitors); - hardwareCheckFindStub.resolves(mockChecks); - - const result = await getAllMonitorsWithUptimeStats(); - - expect(result).to.be.an("array"); - expect(result).to.have.lengthOf(1); - - const monitor = result[0]; - expect(monitor).to.have.property("_id", "monitor1"); - expect(monitor).to.have.property("name", "Test Monitor"); - - // Check uptime percentages exist for all time periods - expect(monitor).to.have.property("1"); - expect(monitor).to.have.property("7"); - expect(monitor).to.have.property("30"); - expect(monitor).to.have.property("90"); - - // Verify uptime percentage calculation (3 successful out of 4 = 75%) - expect(monitor["1"]).to.equal(75); - expect(monitor["7"]).to.equal(75); - expect(monitor["30"]).to.equal(75); - expect(monitor["90"]).to.equal(75); - }); - - it("should handle errors appropriately", async function () { - // Setup stub to throw error - monitorFindStub.rejects(new Error("Database error")); - - try { - await getAllMonitorsWithUptimeStats(); - } catch (error) { - expect(error).to.be.an("error"); - expect(error.message).to.equal("Database error"); - expect(error.service).to.equal("monitorModule"); - expect(error.method).to.equal("getAllMonitorsWithUptimeStats"); - } - }); - - it("should handle empty monitor list", async function () { - monitorFindStub.resolves([]); - - const result = await getAllMonitorsWithUptimeStats(); - - expect(result).to.be.an("array"); - expect(result).to.have.lengthOf(0); - }); - - it("should handle monitor with no checks", async function () { - const mockMonitors = [ - { - _id: "monitor1", - type: "http", - toObject: () => ({ - _id: "monitor1", - type: "http", - name: "Test Monitor", - }), - }, - ]; - - monitorFindStub.resolves(mockMonitors); - checkFindStub.resolves([]); - - const result = await getAllMonitorsWithUptimeStats(); - - expect(result[0]).to.have.property("1", 0); - expect(result[0]).to.have.property("7", 0); - expect(result[0]).to.have.property("30", 0); - expect(result[0]).to.have.property("90", 0); - }); - }); - - describe("calculateUptimeDuration", function () { - let clock; - const NOW = new Date("2024-01-01T12:00:00Z").getTime(); - - beforeEach(function () { - // Fix the current time - clock = sinon.useFakeTimers(NOW); - }); - - afterEach(function () { - clock.restore(); - }); - - it("should return 0 when checks array is empty", function () { - expect(calculateUptimeDuration([])).to.equal(0); - }); - - it("should return 0 when checks array is null", function () { - expect(calculateUptimeDuration(null)).to.equal(0); - }); - - it("should calculate uptime from last down check to most recent check", function () { - const checks = [ - { status: true, createdAt: "2024-01-01T11:00:00Z" }, // Most recent - { status: true, createdAt: "2024-01-01T10:00:00Z" }, - { status: false, createdAt: "2024-01-01T09:00:00Z" }, // Last down - { status: true, createdAt: "2024-01-01T08:00:00Z" }, - ]; - - // Expected: 2 hours (from 09:00 to 11:00) = 7200000ms - expect(calculateUptimeDuration(checks)).to.equal(7200000); - }); - - it("should calculate uptime from first check when no down checks exist", function () { - const checks = [ - { status: true, createdAt: "2024-01-01T11:00:00Z" }, - { status: true, createdAt: "2024-01-01T10:00:00Z" }, - { status: true, createdAt: "2024-01-01T09:00:00Z" }, - ]; - - // Expected: Current time (12:00) - First check (09:00) = 3 hours = 10800000ms - expect(calculateUptimeDuration(checks)).to.equal(10800000); - }); - }); - - describe("getLastChecked", function () { - let clock; - const NOW = new Date("2024-01-01T12:00:00Z").getTime(); - - beforeEach(function () { - // Fix the current time - clock = sinon.useFakeTimers(NOW); - }); - - afterEach(function () { - clock.restore(); - }); - - it("should return 0 when checks array is empty", function () { - expect(getLastChecked([])).to.equal(0); - }); - - it("should return 0 when checks array is null", function () { - expect(getLastChecked(null)).to.equal(0); - }); - - it("should return time difference between now and most recent check", function () { - const checks = [ - { createdAt: "2024-01-01T11:30:00Z" }, // 30 minutes ago - { createdAt: "2024-01-01T11:00:00Z" }, - { createdAt: "2024-01-01T10:30:00Z" }, - ]; - - // Expected: 30 minutes = 1800000ms - expect(getLastChecked(checks)).to.equal(1800000); - }); - - it("should handle checks from different days", function () { - const checks = [ - { createdAt: "2023-12-31T12:00:00Z" }, // 24 hours ago - { createdAt: "2023-12-30T12:00:00Z" }, - ]; - - // Expected: 24 hours = 86400000ms - expect(getLastChecked(checks)).to.equal(86400000); - }); - }); - - describe("getLatestResponseTime", function () { - it("should return 0 when checks array is empty", function () { - expect(getLatestResponseTime([])).to.equal(0); - }); - - it("should return 0 when checks array is null", function () { - expect(getLatestResponseTime(null)).to.equal(0); - }); - - it("should return response time from most recent check", function () { - const checks = [ - { responseTime: 150, createdAt: "2024-01-01T11:30:00Z" }, // Most recent - { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" }, - { responseTime: 250, createdAt: "2024-01-01T10:30:00Z" }, - ]; - - expect(getLatestResponseTime(checks)).to.equal(150); - }); - - it("should handle missing responseTime in checks", function () { - const checks = [{ createdAt: "2024-01-01T11:30:00Z" }, { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" }]; - - expect(getLatestResponseTime(checks)).to.equal(0); - }); - }); - - describe("getAverageResponseTime", function () { - it("should return 0 when checks array is empty", function () { - expect(getAverageResponseTime([])).to.equal(0); - }); - - it("should return 0 when checks array is null", function () { - expect(getAverageResponseTime(null)).to.equal(0); - }); - - it("should calculate average response time from all checks", function () { - const checks = [ - { responseTime: 100, createdAt: "2024-01-01T11:30:00Z" }, - { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" }, - { responseTime: 300, createdAt: "2024-01-01T10:30:00Z" }, - ]; - - // Average: (100 + 200 + 300) / 3 = 200 - expect(getAverageResponseTime(checks)).to.equal(200); - }); - - it("should handle missing responseTime in some checks", function () { - const checks = [ - { responseTime: 100, createdAt: "2024-01-01T11:30:00Z" }, - { createdAt: "2024-01-01T11:00:00Z" }, - { responseTime: 300, createdAt: "2024-01-01T10:30:00Z" }, - ]; - - // Average: (100 + 300) / 2 = 200 - expect(getAverageResponseTime(checks)).to.equal(200); - }); - - it("should return 0 when no checks have responseTime", function () { - const checks = [{ createdAt: "2024-01-01T11:30:00Z" }, { createdAt: "2024-01-01T11:00:00Z" }]; - - expect(getAverageResponseTime(checks)).to.equal(0); - }); - }); - - describe("getUptimePercentage", function () { - it("should return 0 when checks array is empty", function () { - expect(getUptimePercentage([])).to.equal(0); - }); - - it("should return 0 when checks array is null", function () { - expect(getUptimePercentage(null)).to.equal(0); - }); - - it("should return 100 when all checks are up", function () { - const checks = [{ status: true }, { status: true }, { status: true }]; - expect(getUptimePercentage(checks)).to.equal(100); - }); - - it("should return 0 when all checks are down", function () { - const checks = [{ status: false }, { status: false }, { status: false }]; - expect(getUptimePercentage(checks)).to.equal(0); - }); - - it("should calculate correct percentage for mixed status checks", function () { - const checks = [{ status: true }, { status: false }, { status: true }, { status: true }]; - // 3 up out of 4 total = 75% - expect(getUptimePercentage(checks)).to.equal(75); - }); - - it("should handle undefined status values", function () { - const checks = [{ status: true }, { status: undefined }, { status: true }]; - // 2 up out of 3 total ≈ 66.67% - expect(getUptimePercentage(checks)).to.equal((2 / 3) * 100); - }); - }); - - describe("getIncidents", function () { - it("should return 0 when checks array is empty", function () { - expect(getIncidents([])).to.equal(0); - }); - - it("should return 0 when checks array is null", function () { - expect(getIncidents(null)).to.equal(0); - }); - - it("should return 0 when all checks are up", function () { - const checks = [{ status: true }, { status: true }, { status: true }]; - expect(getIncidents(checks)).to.equal(0); - }); - - it("should count all incidents when all checks are down", function () { - const checks = [{ status: false }, { status: false }, { status: false }]; - expect(getIncidents(checks)).to.equal(3); - }); - - it("should count correct number of incidents for mixed status checks", function () { - const checks = [{ status: true }, { status: false }, { status: true }, { status: false }, { status: true }]; - expect(getIncidents(checks)).to.equal(2); - }); - - it("should handle undefined status values", function () { - const checks = [{ status: true }, { status: undefined }, { status: false }, { status: false }]; - // Only counts explicit false values - expect(getIncidents(checks)).to.equal(2); - }); - }); - - describe("getMonitorChecks", function () { - let mockModel; - - beforeEach(function () { - // Create a mock model with chainable methods - const mockChecks = [ - { monitorId: "123", createdAt: new Date("2024-01-01") }, - { monitorId: "123", createdAt: new Date("2024-01-02") }, - ]; - - mockModel = { - find: sinon.stub().returns({ - sort: sinon.stub().returns(mockChecks), - }), - }; - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should return all checks and date-ranged checks", async function () { - // Arrange - const monitorId = "123"; - const dateRange = { - start: new Date("2024-01-01"), - end: new Date("2024-01-02"), - }; - const sortOrder = -1; - - // Act - const result = await getMonitorChecks(monitorId, mockModel, dateRange, sortOrder); - - // Assert - expect(result).to.have.keys(["checksAll", "checksForDateRange"]); - - // Verify find was called with correct parameters - expect(mockModel.find.firstCall.args[0]).to.deep.equal({ monitorId }); - expect(mockModel.find.secondCall.args[0]).to.deep.equal({ - monitorId, - createdAt: { $gte: dateRange.start, $lte: dateRange.end }, - }); - - // Verify sort was called with correct parameters - const sortCalls = mockModel.find().sort.getCalls(); - sortCalls.forEach((call) => { - expect(call.args[0]).to.deep.equal({ createdAt: sortOrder }); - }); - }); - - it("should handle empty results", async function () { - // Arrange - const emptyModel = { - find: sinon.stub().returns({ - sort: sinon.stub().returns([]), - }), - }; - - // Act - const result = await getMonitorChecks( - "123", - emptyModel, - { - start: new Date(), - end: new Date(), - }, - -1 - ); - - // Assert - expect(result.checksAll).to.be.an("array").that.is.empty; - expect(result.checksForDateRange).to.be.an("array").that.is.empty; - }); - - it("should maintain sort order", async function () { - // Arrange - const sortedChecks = [ - { monitorId: "123", createdAt: new Date("2024-01-02") }, - { monitorId: "123", createdAt: new Date("2024-01-01") }, - ]; - - const sortedModel = { - find: sinon.stub().returns({ - sort: sinon.stub().returns(sortedChecks), - }), - }; - - // Act - const result = await getMonitorChecks( - "123", - sortedModel, - { - start: new Date("2024-01-01"), - end: new Date("2024-01-02"), - }, - -1 - ); - - // Assert - expect(result.checksAll[0].createdAt).to.be.greaterThan(result.checksAll[1].createdAt); - expect(result.checksForDateRange[0].createdAt).to.be.greaterThan(result.checksForDateRange[1].createdAt); - }); - }); - - describe("processChecksForDisplay", function () { - let normalizeStub; - - beforeEach(function () { - normalizeStub = sinon.stub(); - }); - - it("should return original checks when numToDisplay is not provided", function () { - const checks = [1, 2, 3, 4, 5]; - const result = processChecksForDisplay(normalizeStub, checks); - expect(result).to.deep.equal(checks); - }); - - it("should return original checks when numToDisplay is greater than checks length", function () { - const checks = [1, 2, 3]; - const result = processChecksForDisplay(normalizeStub, checks, 5); - expect(result).to.deep.equal(checks); - }); - - it("should filter checks based on numToDisplay", function () { - const checks = [1, 2, 3, 4, 5, 6]; - const result = processChecksForDisplay(normalizeStub, checks, 3); - // Should return [1, 3, 5] as n = ceil(6/3) = 2 - expect(result).to.deep.equal([1, 3, 5]); - }); - - it("should handle empty checks array", function () { - const checks = []; - const result = processChecksForDisplay(normalizeStub, checks, 3); - expect(result).to.be.an("array").that.is.empty; - }); - - it("should call normalizeData when normalize is true", function () { - const checks = [1, 2, 3]; - normalizeStub.returns([10, 20, 30]); - - const result = processChecksForDisplay(normalizeStub, checks, null, true); - - expect(normalizeStub.args[0]).to.deep.equal([checks, 1, 100]); - expect(result).to.deep.equal([10, 20, 30]); - }); - - it("should handle both filtering and normalization", function () { - const checks = [1, 2, 3, 4, 5, 6]; - normalizeStub.returns([10, 30, 50]); - - const result = processChecksForDisplay(normalizeStub, checks, 3, true); - - expect(normalizeStub.args[0][0]).to.deep.equal([1, 3, 5]); - expect(result).to.deep.equal([10, 30, 50]); - }); - }); - - describe("groupChecksByTime", function () { - const mockChecks = [ - { createdAt: "2024-01-15T10:30:45Z" }, - { createdAt: "2024-01-15T10:45:15Z" }, - { createdAt: "2024-01-15T11:15:00Z" }, - { createdAt: "2024-01-16T10:30:00Z" }, - ]; - - it("should group checks by hour when dateRange is 'day'", function () { - const result = groupChecksByTime(mockChecks, "day"); - - // Get timestamps for 10:00 and 11:00 on Jan 15 - const time1 = new Date("2024-01-15T10:00:00Z").getTime(); - const time2 = new Date("2024-01-15T11:00:00Z").getTime(); - const time3 = new Date("2024-01-16T10:00:00Z").getTime(); - - expect(Object.keys(result)).to.have.lengthOf(3); - - expect(result[time1].checks).to.have.lengthOf(2); - expect(result[time2].checks).to.have.lengthOf(1); - expect(result[time3].checks).to.have.lengthOf(1); - }); - - it("should group checks by day when dateRange is not 'day'", function () { - const result = groupChecksByTime(mockChecks, "week"); - - expect(Object.keys(result)).to.have.lengthOf(2); - expect(result["2024-01-15"].checks).to.have.lengthOf(3); - expect(result["2024-01-16"].checks).to.have.lengthOf(1); - }); - - it("should handle empty checks array", function () { - const result = groupChecksByTime([], "day"); - expect(result).to.deep.equal({}); - }); - - it("should handle single check", function () { - const singleCheck = [{ createdAt: "2024-01-15T10:30:45Z" }]; - const result = groupChecksByTime(singleCheck, "day"); - - const expectedTime = new Date("2024-01-15T10:00:00Z").getTime(); - expect(Object.keys(result)).to.have.lengthOf(1); - expect(result[expectedTime].checks).to.have.lengthOf(1); - }); - - it("should skip invalid dates and process valid ones", function () { - const checksWithInvalidDate = [ - { createdAt: "invalid-date" }, - { createdAt: "2024-01-15T10:30:45Z" }, - { createdAt: null }, - { createdAt: undefined }, - { createdAt: "" }, - ]; - - const result = groupChecksByTime(checksWithInvalidDate, "day"); - - const expectedTime = new Date("2024-01-15T10:00:00Z").getTime(); - expect(Object.keys(result)).to.have.lengthOf(1); - expect(result[expectedTime].checks).to.have.lengthOf(1); - expect(result[expectedTime].checks[0].createdAt).to.equal("2024-01-15T10:30:45Z"); - }); - - it("should handle checks in same time group", function () { - const checksInSameHour = [{ createdAt: "2024-01-15T10:15:00Z" }, { createdAt: "2024-01-15T10:45:00Z" }]; - - const result = groupChecksByTime(checksInSameHour, "day"); - - const expectedTime = new Date("2024-01-15T10:00:00Z").getTime(); - expect(Object.keys(result)).to.have.lengthOf(1); - expect(result[expectedTime].checks).to.have.lengthOf(2); - }); - }); - - describe("calculateGroupStats", function () { - // Mock getUptimePercentage function - let uptimePercentageStub; - - beforeEach(function () { - uptimePercentageStub = sinon.stub(); - uptimePercentageStub.returns(95); // Default return value - }); - - it("should calculate stats correctly for a group of checks", function () { - const mockGroup = { - time: "2024-01-15", - checks: [ - { status: true, responseTime: 100 }, - { status: false, responseTime: 200 }, - { status: true, responseTime: 300 }, - ], - }; - - const result = calculateGroupStats(mockGroup, uptimePercentageStub); - - expect(result).to.deep.equal({ - time: "2024-01-15", - uptimePercentage: (2 / 3) * 100, - totalChecks: 3, - totalIncidents: 1, - avgResponseTime: 200, // (100 + 200 + 300) / 3 - }); - }); - - it("should handle empty checks array", function () { - const mockGroup = { - time: "2024-01-15", - checks: [], - }; - - const result = calculateGroupStats(mockGroup, uptimePercentageStub); - - expect(result).to.deep.equal({ - time: "2024-01-15", - uptimePercentage: 0, - totalChecks: 0, - totalIncidents: 0, - avgResponseTime: 0, - }); - }); - - it("should handle missing responseTime values", function () { - const mockGroup = { - time: "2024-01-15", - checks: [{ status: true }, { status: false, responseTime: 200 }, { status: true, responseTime: undefined }], - }; - - const result = calculateGroupStats(mockGroup, uptimePercentageStub); - - expect(result).to.deep.equal({ - time: "2024-01-15", - uptimePercentage: (2 / 3) * 100, - totalChecks: 3, - totalIncidents: 1, - avgResponseTime: 200, // 200 / 1 - }); - }); - - it("should handle all checks with status false", function () { - const mockGroup = { - time: "2024-01-15", - checks: [ - { status: false, responseTime: 100 }, - { status: false, responseTime: 200 }, - { status: false, responseTime: 300 }, - ], - }; - - const result = calculateGroupStats(mockGroup, uptimePercentageStub); - - expect(result).to.deep.equal({ - time: "2024-01-15", - uptimePercentage: 0, - totalChecks: 3, - totalIncidents: 3, - avgResponseTime: 200, - }); - }); - - it("should handle all checks with status true", function () { - const mockGroup = { - time: "2024-01-15", - checks: [ - { status: true, responseTime: 100 }, - { status: true, responseTime: 200 }, - { status: true, responseTime: 300 }, - ], - }; - - const result = calculateGroupStats(mockGroup, uptimePercentageStub); - - expect(result).to.deep.equal({ - time: "2024-01-15", - uptimePercentage: 100, - totalChecks: 3, - totalIncidents: 0, - avgResponseTime: 200, - }); - }); - }); - - describe("getMonitorStatsById", function () { - const now = new Date(); - const oneHourAgo = new Date(now - 3600000); - const twoHoursAgo = new Date(now - 7200000); - - const mockMonitor = { - _id: "monitor123", - type: "http", - name: "Test Monitor", - url: "https://test.com", - toObject: () => ({ - _id: "monitor123", - type: "http", - name: "Test Monitor", - url: "https://test.com", - }), - }; - - const mockMonitorPing = { - _id: "monitor123", - type: "ping", - name: "Test Monitor", - url: "https://test.com", - toObject: () => ({ - _id: "monitor123", - type: "http", - name: "Test Monitor", - url: "https://test.com", - }), - }; - const mockMonitorDocker = { - _id: "monitor123", - type: "docker", - name: "Test Monitor", - url: "https://test.com", - toObject: () => ({ - _id: "monitor123", - type: "http", - name: "Test Monitor", - url: "https://test.com", - }), - }; - - const checkDocs = [ - { - monitorId: "monitor123", - status: true, - responseTime: 100, - createdAt: new Date("2024-01-01T12:00:00Z"), - toObject: function () { - return { - monitorId: this.monitorId, - status: this.status, - responseTime: this.responseTime, - createdAt: this.createdAt, - }; - }, - }, - { - monitorId: "monitor123", - status: true, - responseTime: 150, - createdAt: new Date("2024-01-01T11:00:00Z"), - toObject: function () { - return { - monitorId: this.monitorId, - status: this.status, - responseTime: this.responseTime, - createdAt: this.createdAt, - }; - }, - }, - { - monitorId: "monitor123", - status: false, - responseTime: 200, - createdAt: new Date("2024-01-01T10:00:00Z"), - toObject: function () { - return { - monitorId: this.monitorId, - status: this.status, - responseTime: this.responseTime, - createdAt: this.createdAt, - }; - }, - }, - ]; - const req = { - params: { monitorId: "monitor123" }, - query: { - dateRange: "day", - sortOrder: "desc", - numToDisplay: 10, - normalize: true, - }, - }; - - beforeEach(function () { - checkFindStub.returns({ - sort: () => checkDocs, - }); - monitorFindByIdStub.returns(mockMonitor); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should return monitor stats with calculated values, sort order desc", async function () { - req.query.sortOrder = "desc"; - const result = await getMonitorStatsById(req); - expect(result).to.include.keys([ - "_id", - "type", - "name", - "url", - "uptimeDuration", - "lastChecked", - "latestResponseTime", - "periodIncidents", - "periodTotalChecks", - "periodAvgResponseTime", - "periodUptime", - "aggregateData", - ]); - expect(result.latestResponseTime).to.equal(100); - expect(result.periodTotalChecks).to.equal(3); - expect(result.periodIncidents).to.equal(1); - expect(result.periodUptime).to.be.a("number"); - expect(result.aggregateData).to.be.an("array"); - }); - - it("should return monitor stats with calculated values, ping type", async function () { - monitorFindByIdStub.returns(mockMonitorPing); - req.query.sortOrder = "desc"; - const result = await getMonitorStatsById(req); - expect(result).to.include.keys([ - "_id", - "type", - "name", - "url", - "uptimeDuration", - "lastChecked", - "latestResponseTime", - "periodIncidents", - "periodTotalChecks", - "periodAvgResponseTime", - "periodUptime", - "aggregateData", - ]); - expect(result.latestResponseTime).to.equal(100); - expect(result.periodTotalChecks).to.equal(3); - expect(result.periodIncidents).to.equal(1); - expect(result.periodUptime).to.be.a("number"); - expect(result.aggregateData).to.be.an("array"); - }); - - it("should return monitor stats with calculated values, docker type", async function () { - monitorFindByIdStub.returns(mockMonitorDocker); - req.query.sortOrder = "desc"; - const result = await getMonitorStatsById(req); - expect(result).to.include.keys([ - "_id", - "type", - "name", - "url", - "uptimeDuration", - "lastChecked", - "latestResponseTime", - "periodIncidents", - "periodTotalChecks", - "periodAvgResponseTime", - "periodUptime", - "aggregateData", - ]); - expect(result.latestResponseTime).to.equal(100); - expect(result.periodTotalChecks).to.equal(3); - expect(result.periodIncidents).to.equal(1); - expect(result.periodUptime).to.be.a("number"); - expect(result.aggregateData).to.be.an("array"); - }); - - it("should return monitor stats with calculated values", async function () { - req.query.sortOrder = "asc"; - const result = await getMonitorStatsById(req); - expect(result).to.include.keys([ - "_id", - "type", - "name", - "url", - "uptimeDuration", - "lastChecked", - "latestResponseTime", - "periodIncidents", - "periodTotalChecks", - "periodAvgResponseTime", - "periodUptime", - "aggregateData", - ]); - expect(result.latestResponseTime).to.equal(100); - expect(result.periodTotalChecks).to.equal(3); - expect(result.periodIncidents).to.equal(1); - expect(result.periodUptime).to.be.a("number"); - expect(result.aggregateData).to.be.an("array"); - }); - - it("should throw error when monitor is not found", async function () { - monitorFindByIdStub.returns(Promise.resolve(null)); - - const req = { - params: { monitorId: "nonexistent" }, - }; - - try { - await getMonitorStatsById(req); - expect.fail("Should have thrown an error"); - } catch (error) { - expect(error).to.be.an("Error"); - expect(error.service).to.equal("monitorModule"); - expect(error.method).to.equal("getMonitorStatsById"); - } - }); - }); - - describe("getMonitorById", function () { - let notificationFindStub; - let monitorSaveStub; - - beforeEach(function () { - // Create stubs - notificationFindStub = sinon.stub(Notification, "find"); - monitorSaveStub = sinon.stub().resolves(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should return monitor with notifications when found", async function () { - // Arrange - const monitorId = "123"; - const mockMonitor = { - _id: monitorId, - name: "Test Monitor", - save: monitorSaveStub, - }; - const mockNotifications = [ - { _id: "notif1", message: "Test notification 1" }, - { _id: "notif2", message: "Test notification 2" }, - ]; - - monitorFindByIdStub.resolves(mockMonitor); - notificationFindStub.resolves(mockNotifications); - - const result = await getMonitorById(monitorId); - expect(result._id).to.equal(monitorId); - expect(result.name).to.equal("Test Monitor"); - expect(monitorFindByIdStub.calledWith(monitorId)).to.be.true; - expect(notificationFindStub.calledWith({ monitorId })).to.be.true; - expect(monitorSaveStub.calledOnce).to.be.true; - }); - - it("should throw 404 error when monitor not found", async function () { - // Arrange - const monitorId = "nonexistent"; - monitorFindByIdStub.resolves(null); - - // Act & Assert - try { - await getMonitorById(monitorId); - expect.fail("Should have thrown an error"); - } catch (error) { - expect(error.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); - expect(error.status).to.equal(404); - expect(error.service).to.equal("monitorModule"); - expect(error.method).to.equal("getMonitorById"); - } - }); - - it("should handle database errors properly", async function () { - // Arrange - const monitorId = "123"; - const dbError = new Error("Database connection failed"); - monitorFindByIdStub.rejects(dbError); - - // Act & Assert - try { - await getMonitorById(monitorId); - expect.fail("Should have thrown an error"); - } catch (error) { - expect(error.service).to.equal("monitorModule"); - expect(error.method).to.equal("getMonitorById"); - expect(error.message).to.equal("Database connection failed"); - } - }); - - it("should handle notification fetch errors", async function () { - // Arrange - const monitorId = "123"; - const mockMonitor = { - _id: monitorId, - name: "Test Monitor", - save: monitorSaveStub, - }; - const notificationError = new Error("Notification fetch failed"); - - monitorFindByIdStub.resolves(mockMonitor); - notificationFindStub.rejects(notificationError); - - // Act & Assert - try { - await getMonitorById(monitorId); - expect.fail("Should have thrown an error"); - } catch (error) { - expect(error.service).to.equal("monitorModule"); - expect(error.method).to.equal("getMonitorById"); - expect(error.message).to.equal("Notification fetch failed"); - } - }); - - it("should handle monitor save errors", async function () { - // Arrange - const monitorId = "123"; - const mockMonitor = { - _id: monitorId, - name: "Test Monitor", - save: sinon.stub().rejects(new Error("Save failed")), - }; - const mockNotifications = []; - - monitorFindByIdStub.resolves(mockMonitor); - notificationFindStub.resolves(mockNotifications); - - // Act & Assert - try { - await getMonitorById(monitorId); - expect.fail("Should have thrown an error"); - } catch (error) { - expect(error.service).to.equal("monitorModule"); - expect(error.method).to.equal("getMonitorById"); - expect(error.message).to.equal("Save failed"); - } - }); - }); - - describe("getMonitorsAndSummaryByTeamId", function () { - it("should return monitors and correct summary counts", async function () { - // Arrange - const teamId = "team123"; - const type = "http"; - const mockMonitors = [ - { teamId, type, status: true, isActive: true }, // up - { teamId, type, status: false, isActive: true }, // down - { teamId, type, status: null, isActive: false }, // paused - { teamId, type, status: true, isActive: true }, // up - ]; - monitorFindStub.resolves(mockMonitors); - - // Act - const result = await getMonitorsAndSummaryByTeamId(teamId, type); - - // Assert - expect(result.monitors).to.have.lengthOf(4); - expect(result.monitorCounts).to.deep.equal({ - up: 2, - down: 1, - paused: 1, - total: 4, - }); - expect(monitorFindStub.calledOnceWith({ teamId, type })).to.be.true; - }); - - it("should return empty results for non-existent team", async function () { - // Arrange - monitorFindStub.resolves([]); - - // Act - const result = await getMonitorsAndSummaryByTeamId("nonexistent", "http"); - - // Assert - expect(result.monitors).to.have.lengthOf(0); - expect(result.monitorCounts).to.deep.equal({ - up: 0, - down: 0, - paused: 0, - total: 0, - }); - }); - - it("should handle database errors", async function () { - // Arrange - const error = new Error("Database error"); - error.service = "MonitorModule"; - error.method = "getMonitorsAndSummaryByTeamId"; - monitorFindStub.rejects(error); - - // Act & Assert - try { - await getMonitorsAndSummaryByTeamId("team123", "http"); - expect.fail("Should have thrown an error"); - } catch (err) { - expect(err).to.equal(error); - expect(err.service).to.equal("monitorModule"); - expect(err.method).to.equal("getMonitorsAndSummaryByTeamId"); - } - }); - }); - - describe("getMonitorsByTeamId", function () { - beforeEach(function () { - // Chain stubs for Monitor.find().skip().limit().sort() - - // Stub for CHECK_MODEL_LOOKUP model find - checkFindStub.returns({ - sort: sinon.stub().returns({ - limit: sinon.stub().returns([]), - }), - }); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should return monitors with basic query parameters", async function () { - const mockMonitors = [ - { _id: "1", type: "http", toObject: () => ({ _id: "1", type: "http" }) }, - { _id: "2", type: "ping", toObject: () => ({ _id: "2", type: "ping" }) }, - ]; - monitorFindStub.returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().returns(mockMonitors), - }), - }), - }); - - const req = { - params: { teamId: "team123" }, - query: { - type: "http", - page: 0, - rowsPerPage: 10, - field: "name", - status: false, - checkOrder: "desc", - }, - }; - - monitorCountStub.resolves(2); - - const result = await getMonitorsByTeamId(req); - - expect(result).to.have.property("monitors"); - expect(result).to.have.property("monitorCount", 2); - }); - - it("should return monitors with basic query parameters", async function () { - const mockMonitors = [ - { _id: "1", type: "http", toObject: () => ({ _id: "1", type: "http" }) }, - { _id: "2", type: "ping", toObject: () => ({ _id: "2", type: "ping" }) }, - ]; - monitorFindStub.returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().returns(mockMonitors), - }), - }), - }); - - const req = { - params: { teamId: "team123" }, - query: { - type: "http", - page: 0, - rowsPerPage: 10, - field: "name", - status: true, - checkOrder: "asc", - }, - }; - - monitorCountStub.resolves(2); - - const result = await getMonitorsByTeamId(req); - - expect(result).to.have.property("monitors"); - expect(result).to.have.property("monitorCount", 2); - }); - - it("should handle type filter with array input", async function () { - const req = { - params: { teamId: "team123" }, - query: { - type: ["http", "ping"], - }, - }; - - monitorFindStub.returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().returns([]), - }), - }), - }); - monitorCountStub.resolves(0); - - await getMonitorsByTeamId(req); - - expect(Monitor.find.firstCall.args[0]).to.deep.equal({ - teamId: "team123", - type: { $in: ["http", "ping"] }, - }); - }); - - it("should handle text search filter", async function () { - const req = { - params: { teamId: "team123" }, - query: { - filter: "search", - }, - }; - - monitorFindStub.returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().returns([]), - }), - }), - }); - monitorCountStub.resolves(0); - - await getMonitorsByTeamId(req); - - expect(Monitor.find.firstCall.args[0]).to.deep.equal({ - teamId: "team123", - $or: [{ name: { $regex: "search", $options: "i" } }, { url: { $regex: "search", $options: "i" } }], - }); - }); - - it("should handle pagination parameters", async function () { - const req = { - params: { teamId: "team123" }, - query: { - page: 2, - rowsPerPage: 5, - }, - }; - - monitorFindStub.returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().returns([]), - }), - }), - }); - monitorCountStub.resolves(0); - - const result = await getMonitorsByTeamId(req); - expect(result).to.deep.equal({ - monitors: [], - monitorCount: 0, - }); - }); - - it("should handle sorting parameters", async function () { - const req = { - params: { teamId: "team123" }, - query: { - field: "name", - order: "asc", - }, - }; - - monitorFindStub.returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().returns([]), - }), - }), - }); - monitorCountStub.resolves(0); - - await getMonitorsByTeamId(req); - - const result = await getMonitorsByTeamId(req); - expect(result).to.deep.equal({ - monitors: [], - monitorCount: 0, - }); - }); - - it("should return early when limit is -1", async function () { - // Arrange - const req = { - params: { teamId: "team123" }, - query: { - limit: "-1", - }, - }; - - const mockMonitors = [ - { _id: "1", type: "http" }, - { _id: "2", type: "ping" }, - ]; - - monitorFindStub.returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().returns(mockMonitors), - }), - }), - }); - - monitorCountStub.resolves(2); - - // Act - const result = await getMonitorsByTeamId(req); - - // Assert - expect(result).to.deep.equal({ - monitors: mockMonitors, - monitorCount: 2, - }); - }); - - it("should normalize checks when normalize parameter is provided", async function () { - const req = { - params: { teamId: "team123" }, - query: { normalize: "true" }, - }; - monitorCountStub.resolves(2); - - const mockMonitors = [ - { _id: "1", type: "http", toObject: () => ({ _id: "1", type: "http" }) }, - { _id: "2", type: "ping", toObject: () => ({ _id: "2", type: "ping" }) }, - ]; - - monitorFindStub.returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().returns(mockMonitors), - }), - }), - }); - - const result = await getMonitorsByTeamId(req); - expect(result.monitorCount).to.equal(2); - expect(result.monitors).to.have.lengthOf(2); - }); - - it("should handle database errors", async function () { - const req = { - params: { teamId: "team123" }, - query: {}, - }; - - const error = new Error("Database error"); - monitorFindStub.returns({ - skip: sinon.stub().returns({ - limit: sinon.stub().returns({ - sort: sinon.stub().throws(error), - }), - }), - }); - - try { - await getMonitorsByTeamId(req); - expect.fail("Should have thrown an error"); - } catch (err) { - expect(err.service).to.equal("monitorModule"); - expect(err.method).to.equal("getMonitorsByTeamId"); - expect(err.message).to.equal("Database error"); - } - }); - }); - - describe("createMonitor", function () { - it("should create a monitor without notifications", async function () { - let monitorSaveStub = sinon.stub(Monitor.prototype, "save").resolves(); - - const req = { - body: { - name: "Test Monitor", - url: "http://test.com", - type: "http", - notifications: ["someNotification"], - }, - }; - - const expectedMonitor = { - name: "Test Monitor", - url: "http://test.com", - type: "http", - notifications: undefined, - save: monitorSaveStub, - }; - - const result = await createMonitor(req); - expect(result.name).to.equal(expectedMonitor.name); - expect(result.url).to.equal(expectedMonitor.url); - }); - - it("should handle database errors", async function () { - const req = { - body: { - name: "Test Monitor", - }, - }; - - try { - await createMonitor(req); - expect.fail("Should have thrown an error"); - } catch (err) { - expect(err.service).to.equal("monitorModule"); - expect(err.method).to.equal("createMonitor"); - } - }); - }); - - describe("deleteMonitor", function () { - it("should delete a monitor successfully", async function () { - const monitorId = "123456789"; - const mockMonitor = { - _id: monitorId, - name: "Test Monitor", - url: "http://test.com", - }; - - const req = { - params: { monitorId }, - }; - - monitorFindByIdAndDeleteStub.resolves(mockMonitor); - - const result = await deleteMonitor(req); - - expect(result).to.deep.equal(mockMonitor); - sinon.assert.calledWith(monitorFindByIdAndDeleteStub, monitorId); - }); - - it("should throw error when monitor not found", async function () { - const monitorId = "nonexistent123"; - const req = { - params: { monitorId }, - }; - - monitorFindByIdAndDeleteStub.resolves(null); - - try { - await deleteMonitor(req); - expect.fail("Should have thrown an error"); - } catch (err) { - expect(err.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); - expect(err.service).to.equal("monitorModule"); - expect(err.method).to.equal("deleteMonitor"); - } - }); - - it("should handle database errors", async function () { - const monitorId = "123456789"; - const req = { - params: { monitorId }, - }; - - const dbError = new Error("Database connection error"); - monitorFindByIdAndDeleteStub.rejects(dbError); - - try { - await deleteMonitor(req); - expect.fail("Should have thrown an error"); - } catch (err) { - expect(err.message).to.equal("Database connection error"); - expect(err.service).to.equal("monitorModule"); - expect(err.method).to.equal("deleteMonitor"); - } - }); - }); - - describe("deleteAllMonitors", function () { - it("should delete all monitors for a team successfully", async function () { - const teamId = "team123"; - const mockMonitors = [ - { _id: "1", name: "Monitor 1", teamId }, - { _id: "2", name: "Monitor 2", teamId }, - ]; - - monitorFindStub.resolves(mockMonitors); - monitorDeleteManyStub.resolves({ deletedCount: 2 }); - - const result = await deleteAllMonitors(teamId); - - expect(result).to.deep.equal({ - monitors: mockMonitors, - deletedCount: 2, - }); - sinon.assert.calledWith(monitorFindStub, { teamId }); - sinon.assert.calledWith(monitorDeleteManyStub, { teamId }); - }); - - it("should return empty array when no monitors found", async function () { - const teamId = "emptyTeam"; - - monitorFindStub.resolves([]); - monitorDeleteManyStub.resolves({ deletedCount: 0 }); - - const result = await deleteAllMonitors(teamId); - - expect(result).to.deep.equal({ - monitors: [], - deletedCount: 0, - }); - sinon.assert.calledWith(monitorFindStub, { teamId }); - sinon.assert.calledWith(monitorDeleteManyStub, { teamId }); - }); - - it("should handle database errors", async function () { - const teamId = "team123"; - const dbError = new Error("Database connection error"); - monitorFindStub.rejects(dbError); - - try { - await deleteAllMonitors(teamId); - } catch (err) { - expect(err.message).to.equal("Database connection error"); - expect(err.service).to.equal("monitorModule"); - expect(err.method).to.equal("deleteAllMonitors"); - } - }); - - it("should handle deleteMany errors", async function () { - const teamId = "team123"; - monitorFindStub.resolves([{ _id: "1", name: "Monitor 1" }]); - monitorDeleteManyStub.rejects(new Error("Delete operation failed")); - - try { - await deleteAllMonitors(teamId); - } catch (err) { - expect(err.message).to.equal("Delete operation failed"); - expect(err.service).to.equal("monitorModule"); - expect(err.method).to.equal("deleteAllMonitors"); - } - }); - }); - - describe("deleteMonitorsByUserId", function () { - beforeEach(function () {}); - - afterEach(function () { - sinon.restore(); - }); - - it("should delete all monitors for a user successfully", async function () { - // Arrange - const userId = "user123"; - const mockResult = { - deletedCount: 3, - acknowledged: true, - }; - - monitorDeleteManyStub.resolves(mockResult); - - // Act - const result = await deleteMonitorsByUserId(userId); - - // Assert - expect(result).to.deep.equal(mockResult); - sinon.assert.calledWith(monitorDeleteManyStub, { userId: userId }); - }); - - it("should return zero deletedCount when no monitors found", async function () { - // Arrange - const userId = "nonexistentUser"; - const mockResult = { - deletedCount: 0, - acknowledged: true, - }; - - monitorDeleteManyStub.resolves(mockResult); - - // Act - const result = await deleteMonitorsByUserId(userId); - - // Assert - expect(result.deletedCount).to.equal(0); - sinon.assert.calledWith(monitorDeleteManyStub, { userId: userId }); - }); - - it("should handle database errors", async function () { - // Arrange - const userId = "user123"; - const dbError = new Error("Database connection error"); - monitorDeleteManyStub.rejects(dbError); - - // Act & Assert - try { - await deleteMonitorsByUserId(userId); - expect.fail("Should have thrown an error"); - } catch (err) { - expect(err.message).to.equal("Database connection error"); - expect(err.service).to.equal("monitorModule"); - expect(err.method).to.equal("deleteMonitorsByUserId"); - } - }); - }); - - describe("editMonitor", function () { - it("should edit a monitor successfully", async function () { - // Arrange - const candidateId = "monitor123"; - const candidateMonitor = { - name: "Updated Monitor", - url: "http://updated.com", - type: "http", - notifications: ["someNotification"], - }; - - const expectedUpdateData = { - name: "Updated Monitor", - url: "http://updated.com", - type: "http", - notifications: undefined, - }; - - const mockUpdatedMonitor = { - _id: candidateId, - ...expectedUpdateData, - }; - - monitorFindByIdAndUpdateStub.resolves(mockUpdatedMonitor); - - // Act - const result = await editMonitor(candidateId, candidateMonitor); - - // Assert - expect(result).to.deep.equal(mockUpdatedMonitor); - sinon.assert.calledWith(monitorFindByIdAndUpdateStub, candidateId, expectedUpdateData, { - new: true, - }); - }); - - it("should return null when monitor not found", async function () { - // Arrange - const candidateId = "nonexistent123"; - const candidateMonitor = { - name: "Updated Monitor", - }; - - monitorFindByIdAndUpdateStub.resolves(null); - - // Act - const result = await editMonitor(candidateId, candidateMonitor); - - // Assert - expect(result).to.be.null; - sinon.assert.calledWith(monitorFindByIdAndUpdateStub, candidateId, { name: "Updated Monitor", notifications: undefined }, { new: true }); - }); - - it("should remove notifications from update data", async function () { - // Arrange - const candidateId = "monitor123"; - const candidateMonitor = { - name: "Updated Monitor", - notifications: ["notification1", "notification2"], - }; - - const expectedUpdateData = { - name: "Updated Monitor", - notifications: undefined, - }; - - monitorFindByIdAndUpdateStub.resolves({ - _id: candidateId, - ...expectedUpdateData, - }); - - // Act - await editMonitor(candidateId, candidateMonitor); - - // Assert - sinon.assert.calledWith(monitorFindByIdAndUpdateStub, candidateId, expectedUpdateData, { - new: true, - }); - }); - - it("should handle database errors", async function () { - // Arrange - const candidateId = "monitor123"; - const candidateMonitor = { - name: "Updated Monitor", - }; - - const dbError = new Error("Database connection error"); - monitorFindByIdAndUpdateStub.rejects(dbError); - - // Act & Assert - try { - await editMonitor(candidateId, candidateMonitor); - expect.fail("Should have thrown an error"); - } catch (err) { - expect(err.message).to.equal("Database connection error"); - expect(err.service).to.equal("monitorModule"); - expect(err.method).to.equal("editMonitor"); - } - }); - }); - - describe("addDemoMonitors", function () { - it("should add demo monitors successfully", async function () { - // Arrange - const userId = "user123"; - const teamId = "team123"; - monitorInsertManyStub.resolves([{ _id: "123" }]); - const result = await addDemoMonitors(userId, teamId); - expect(result).to.deep.equal([{ _id: "123" }]); - }); - - it("should handle database errors", async function () { - const userId = "user123"; - const teamId = "team123"; - - const dbError = new Error("Database connection error"); - monitorInsertManyStub.rejects(dbError); - - try { - await addDemoMonitors(userId, teamId); - } catch (err) { - expect(err.message).to.equal("Database connection error"); - expect(err.service).to.equal("monitorModule"); - expect(err.method).to.equal("addDemoMonitors"); - } - }); - }); -}); diff --git a/server/tests/db/notificationModule.test.js b/server/tests/db/notificationModule.test.js deleted file mode 100755 index fbb97f33a..000000000 --- a/server/tests/db/notificationModule.test.js +++ /dev/null @@ -1,76 +0,0 @@ -import sinon from "sinon"; -import Notification from "../../db/models/Notification.js"; -import { createNotification, getNotificationsByMonitorId, deleteNotificationsByMonitorId } from "../../db/mongo/modules/notificationModule.js"; - -describe("notificationModule", function () { - const mockNotification = { - monitorId: "123", - }; - const mockNotifications = [mockNotification]; - let notificationSaveStub, notificationFindStub, notificationDeleteManyStub; - - beforeEach(function () { - notificationSaveStub = sinon.stub(Notification.prototype, "save").resolves(); - notificationFindStub = sinon.stub(Notification, "find").resolves(); - notificationDeleteManyStub = sinon.stub(Notification, "deleteMany").resolves(); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("createNotification", function () { - it("should create a new notification", async function () { - const notificationData = { _id: "123", name: "test" }; - notificationSaveStub.resolves(notificationData); - const res = await createNotification(notificationData); - expect(res).to.deep.equal(notificationData); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - notificationSaveStub.rejects(err); - try { - await createNotification(mockNotification); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("getNotificationsByMonitorId", function () { - it("should return notifications by monitor ID", async function () { - notificationFindStub.resolves(mockNotifications); - const res = await getNotificationsByMonitorId(mockNotification.monitorId); - expect(res).to.deep.equal(mockNotifications); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - notificationFindStub.rejects(err); - try { - await getNotificationsByMonitorId(mockNotification.monitorId); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("deleteNotificationsByMonitorId", function () { - it("should delete notifications by monitor ID", async function () { - notificationDeleteManyStub.resolves({ deletedCount: mockNotifications.length }); - const res = await deleteNotificationsByMonitorId(mockNotification.monitorId); - expect(res).to.deep.equal(mockNotifications.length); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - notificationDeleteManyStub.rejects(err); - try { - await deleteNotificationsByMonitorId(mockNotification.monitorId); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); -}); diff --git a/server/tests/db/pageSpeedCheckModule.test.js b/server/tests/db/pageSpeedCheckModule.test.js deleted file mode 100755 index 45135135d..000000000 --- a/server/tests/db/pageSpeedCheckModule.test.js +++ /dev/null @@ -1,63 +0,0 @@ -import sinon from "sinon"; -import PageSpeedCheck from "../../db/models/PageSpeedCheck.js"; -import { createPageSpeedCheck, deletePageSpeedChecksByMonitorId } from "../../db/mongo/modules/pageSpeedCheckModule.js"; - -const mockPageSpeedCheck = { - monitorId: "monitorId", - bestPractices: 1, - seo: 1, - performance: 1, -}; - -const mockDeletedResult = { deletedCount: 1 }; - -describe("pageSpeedCheckModule", function () { - let pageSpeedCheckSaveStub, pageSpeedCheckDeleteManyStub; - - beforeEach(function () { - pageSpeedCheckSaveStub = sinon.stub(PageSpeedCheck.prototype, "save"); - pageSpeedCheckDeleteManyStub = sinon.stub(PageSpeedCheck, "deleteMany"); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("createPageSpeedCheck", function () { - it("should return a page speed check", async function () { - pageSpeedCheckSaveStub.resolves(mockPageSpeedCheck); - const pageSpeedCheck = await createPageSpeedCheck(mockPageSpeedCheck); - expect(pageSpeedCheck).to.deep.equal(mockPageSpeedCheck); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - pageSpeedCheckSaveStub.rejects(err); - try { - await expect(createPageSpeedCheck(mockPageSpeedCheck)); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - }); - - describe("deletePageSpeedChecksByMonitorId", function () { - it("should return the number of deleted checks", async function () { - pageSpeedCheckDeleteManyStub.resolves(mockDeletedResult); - const result = await deletePageSpeedChecksByMonitorId("monitorId"); - expect(result).to.deep.equal(mockDeletedResult.deletedCount); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - pageSpeedCheckDeleteManyStub.rejects(err); - try { - await expect(deletePageSpeedChecksByMonitorId("monitorId")); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - }); -}); diff --git a/server/tests/db/recoveryModule.test.js b/server/tests/db/recoveryModule.test.js deleted file mode 100755 index 88afa257a..000000000 --- a/server/tests/db/recoveryModule.test.js +++ /dev/null @@ -1,168 +0,0 @@ -import sinon from "sinon"; -import RecoveryToken from "../../db/models/RecoveryToken.js"; -import User from "../../db/models/User.js"; -import { requestRecoveryToken, validateRecoveryToken, resetPassword } from "../../db/mongo/modules/recoveryModule.js"; -import { errorMessages } from "../../utils/messages.js"; - -const mockRecoveryToken = { - email: "test@test.com", - token: "1234567890", -}; - -const mockUser = { - email: "test@test.com", - password: "oldPassword", -}; - -const mockUserWithoutPassword = { - email: "test@test.com", -}; - -// Create a query builder that logs -const createQueryChain = (finalResult, comparePasswordResult = false) => ({ - select: () => ({ - select: async () => { - if (finalResult === mockUser) { - // Return a new object with all required methods - return { - email: "test@test.com", - password: "oldPassword", - comparePassword: sinon.stub().resolves(comparePasswordResult), - save: sinon.stub().resolves(), - }; - } - return finalResult; - }, - }), - // Add methods to the top level too - comparePassword: sinon.stub().resolves(comparePasswordResult), - save: sinon.stub().resolves(), -}); - -describe("recoveryModule", function () { - let deleteManyStub, saveStub, findOneStub, userCompareStub, userSaveStub, userFindOneStub; - let req, res; - - beforeEach(function () { - req = { - body: { email: "test@test.com" }, - }; - deleteManyStub = sinon.stub(RecoveryToken, "deleteMany"); - saveStub = sinon.stub(RecoveryToken.prototype, "save"); - findOneStub = sinon.stub(RecoveryToken, "findOne"); - userCompareStub = sinon.stub(User.prototype, "comparePassword"); - userSaveStub = sinon.stub(User.prototype, "save"); - userFindOneStub = sinon.stub().resolves(); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("requestRecoveryToken", function () { - it("should return a recovery token", async function () { - deleteManyStub.resolves(); - saveStub.resolves(mockRecoveryToken); - const result = await requestRecoveryToken(req, res); - expect(result.email).to.equal(mockRecoveryToken.email); - }); - - it("should handle an error", async function () { - const err = new Error("Test error"); - deleteManyStub.rejects(err); - try { - await requestRecoveryToken(req, res); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - }); - - describe("validateRecoveryToken", function () { - it("should return a recovery token if found", async function () { - findOneStub.resolves(mockRecoveryToken); - const result = await validateRecoveryToken(req, res); - expect(result).to.deep.equal(mockRecoveryToken); - }); - - it("should thrown an error if a token is not found", async function () { - findOneStub.resolves(null); - try { - await validateRecoveryToken(req, res); - } catch (error) { - expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND); - } - }); - - it("should handle DB errors", async function () { - const err = new Error("Test error"); - findOneStub.rejects(err); - try { - await validateRecoveryToken(req, res); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - }); - - describe("resetPassword", function () { - beforeEach(function () { - req.body = { - password: "test", - newPassword: "test1", - }; - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should thrown an error if a recovery token is not found", async function () { - findOneStub.resolves(null); - try { - await resetPassword(req, res); - } catch (error) { - expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND); - } - }); - - it("should throw an error if a user is not found", async function () { - findOneStub.resolves(mockRecoveryToken); - userFindOneStub = sinon.stub(User, "findOne").resolves(null); - try { - await resetPassword(req, res); - } catch (error) { - expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND); - } - }); - - it("should throw an error if the passwords match", async function () { - findOneStub.resolves(mockRecoveryToken); - saveStub.resolves(); - userFindOneStub = sinon.stub(User, "findOne").returns(createQueryChain(mockUser, true)); - try { - await resetPassword(req, res); - } catch (error) { - expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_RESET_PASSWORD_BAD_MATCH); - } - }); - - it("should return a user without password if successful", async function () { - findOneStub.resolves(mockRecoveryToken); - saveStub.resolves(); - userFindOneStub = sinon - .stub(User, "findOne") - .returns(createQueryChain(mockUser)) // First call will resolve to mockUser - .onSecondCall() - .returns(createQueryChain(mockUserWithoutPassword)); - const result = await resetPassword(req, res); - expect(result).to.deep.equal(mockUserWithoutPassword); - }); - }); -}); diff --git a/server/tests/db/settingsModule.test.js b/server/tests/db/settingsModule.test.js deleted file mode 100755 index 9d1a80c3e..000000000 --- a/server/tests/db/settingsModule.test.js +++ /dev/null @@ -1,56 +0,0 @@ -import sinon from "sinon"; -import { getAppSettings, updateAppSettings } from "../../db/mongo/modules/settingsModule.js"; -import AppSettings from "../../db/models/AppSettings.js"; - -const mockAppSettings = { - appName: "Test App", -}; - -describe("SettingsModule", function () { - let appSettingsFindOneStub, appSettingsFindOneAndUpdateStub; - - beforeEach(function () { - appSettingsFindOneStub = sinon.stub(AppSettings, "findOne"); - appSettingsFindOneAndUpdateStub = sinon.stub(AppSettings, "findOneAndUpdate"); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("getAppSettings", function () { - it("should return app settings", async function () { - appSettingsFindOneStub.resolves(mockAppSettings); - const result = await getAppSettings(); - expect(result).to.deep.equal(mockAppSettings); - }); - - it("should handle an error", async function () { - const err = new Error("Test error"); - appSettingsFindOneStub.throws(err); - try { - await getAppSettings(); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); - - describe("updateAppSettings", function () { - it("should update app settings", async function () { - appSettingsFindOneAndUpdateStub.resolves(mockAppSettings); - const result = await updateAppSettings(mockAppSettings); - expect(result).to.deep.equal(mockAppSettings); - }); - - it("should handle an error", async function () { - const err = new Error("Test error"); - appSettingsFindOneAndUpdateStub.throws(err); - try { - await updateAppSettings(mockAppSettings); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - }); -}); diff --git a/server/tests/db/statusPageModule.test.js b/server/tests/db/statusPageModule.test.js deleted file mode 100755 index 54e1a287f..000000000 --- a/server/tests/db/statusPageModule.test.js +++ /dev/null @@ -1,70 +0,0 @@ -import sinon from "sinon"; -import { createStatusPage, getStatusPageByUrl } from "../../db/mongo/modules/statusPageModule.js"; -import StatusPage from "../../db/models/StatusPage.js"; -import { errorMessages } from "../../utils/messages.js"; - -describe("statusPageModule", function () { - let statusPageFindOneStub, statusPageSaveStub, statusPageFindStub; - - beforeEach(function () { - statusPageSaveStub = sinon.stub(StatusPage.prototype, "save"); - statusPageFindOneStub = sinon.stub(StatusPage, "findOne"); - statusPageFindStub = sinon.stub(StatusPage, "find"); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("createStatusPage", function () { - it("should throw an error if a non-unique url is provided", async function () { - statusPageFindOneStub.resolves(true); - try { - await createStatusPage({ url: "test" }); - } catch (error) { - expect(error.status).to.equal(400); - expect(error.message).to.equal(errorMessages.STATUS_PAGE_URL_NOT_UNIQUE); - } - }); - - it("should handle duplicate URL errors", async function () { - const err = new Error("test"); - err.code = 11000; - statusPageSaveStub.rejects(err); - try { - await createStatusPage({ url: "test" }); - } catch (error) { - expect(error).to.deep.equal(err); - } - }); - - it("should return a status page if a unique url is provided", async function () { - statusPageFindOneStub.resolves(null); - statusPageFindStub.resolves([]); - const mockStatusPage = { url: "test" }; - const statusPage = await createStatusPage(mockStatusPage); - expect(statusPage).to.exist; - expect(statusPage.url).to.equal(mockStatusPage.url); - }); - }); - - describe("getStatusPageByUrl", function () { - it("should throw an error if a status page is not found", async function () { - statusPageFindOneStub.resolves(null); - try { - await getStatusPageByUrl("test"); - } catch (error) { - expect(error.status).to.equal(404); - expect(error.message).to.equal(errorMessages.STATUS_PAGE_NOT_FOUND); - } - }); - - it("should return a status page if a status page is found", async function () { - const mockStatusPage = { url: "test" }; - statusPageFindOneStub.resolves(mockStatusPage); - const statusPage = await getStatusPageByUrl(mockStatusPage.url); - expect(statusPage).to.exist; - expect(statusPage).to.deep.equal(mockStatusPage); - }); - }); -}); diff --git a/server/tests/db/userModule.test.js b/server/tests/db/userModule.test.js deleted file mode 100755 index d0f5ecb3e..000000000 --- a/server/tests/db/userModule.test.js +++ /dev/null @@ -1,294 +0,0 @@ -import sinon from "sinon"; -import UserModel from "../../db/models/User.js"; -import TeamModel from "../../db/models/Team.js"; -import { - insertUser, - getUserByEmail, - updateUser, - deleteUser, - deleteTeam, - deleteAllOtherUsers, - getAllUsers, - logoutUser, -} from "../../db/mongo/modules/userModule.js"; -import { errorMessages } from "../../utils/messages.js"; - -const mockUser = { - email: "test@test.com", - password: "password", - role: ["user"], -}; -const mockSuperUser = { - email: "test@test.com", - password: "password", - role: ["superadmin"], -}; -const imageFile = { - image: 1, -}; - -describe("userModule", function () { - let teamSaveStub, - teamFindByIdAndDeleteStub, - userSaveStub, - userFindStub, - userFindOneStub, - userFindByIdAndUpdateStub, - userFindByIdAndDeleteStub, - userDeleteManyStub, - userUpdateOneStub, - generateAvatarImageStub, - parseBooleanStub; - - beforeEach(function () { - teamSaveStub = sinon.stub(TeamModel.prototype, "save"); - teamFindByIdAndDeleteStub = sinon.stub(TeamModel, "findByIdAndDelete"); - userSaveStub = sinon.stub(UserModel.prototype, "save"); - userFindStub = sinon.stub(UserModel, "find"); - userFindOneStub = sinon.stub(UserModel, "findOne"); - userFindByIdAndUpdateStub = sinon.stub(UserModel, "findByIdAndUpdate"); - userFindByIdAndDeleteStub = sinon.stub(UserModel, "findByIdAndDelete"); - userDeleteManyStub = sinon.stub(UserModel, "deleteMany"); - userUpdateOneStub = sinon.stub(UserModel, "updateOne"); - generateAvatarImageStub = sinon.stub().resolves({ image: 2 }); - parseBooleanStub = sinon.stub().returns(true); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("insertUser", function () { - it("should insert a regular user", async function () { - userSaveStub.resolves(mockUser); - userFindOneStub.returns({ - select: sinon.stub().returns({ - select: sinon.stub().resolves(mockUser), - }), - }); - const result = await insertUser(mockUser, imageFile, generateAvatarImageStub); - expect(result).to.deep.equal(mockUser); - }); - - it("should insert a superadmin user", async function () { - userSaveStub.resolves(mockSuperUser); - userFindOneStub.returns({ - select: sinon.stub().returns({ - select: sinon.stub().resolves(mockSuperUser), - }), - }); - const result = await insertUser(mockSuperUser, imageFile, generateAvatarImageStub); - expect(result).to.deep.equal(mockSuperUser); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - userSaveStub.rejects(err); - try { - await insertUser(mockUser, imageFile, generateAvatarImageStub); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - - it("should handle a duplicate key error", async function () { - const err = new Error("test error"); - err.code = 11000; - userSaveStub.rejects(err); - try { - await insertUser(mockUser, imageFile, generateAvatarImageStub); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - }); - - describe("getUserByEmail", function () { - it("should return a user", async function () { - userFindOneStub.returns({ - select: sinon.stub().resolves(mockUser), - }); - const result = await getUserByEmail(mockUser.email); - expect(result).to.deep.equal(mockUser); - }); - }); - - describe("getUserByEmail", function () { - it("throw an error if a user is not found", async function () { - userFindOneStub.returns({ - select: sinon.stub().resolves(null), - }); - try { - await getUserByEmail(mockUser.email); - } catch (error) { - expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND); - } - }); - }); - - describe("updateUser", function () { - let req, res; - - beforeEach(function () { - req = { - params: { - userId: "testId", - }, - body: { - deleteProfileImage: "false", - email: "test@test.com", - }, - file: { - buffer: "test", - mimetype: "test", - }, - }; - res = {}; - }); - - afterEach(function () {}); - - it("should update a user", async function () { - parseBooleanStub.returns(false); - userFindByIdAndUpdateStub.returns({ - select: sinon.stub().returns({ - select: sinon.stub().resolves(mockUser), - }), - }); - const result = await updateUser(req, res, parseBooleanStub, generateAvatarImageStub); - expect(result).to.deep.equal(mockUser); - }); - - it("should delete a user profile image", async function () { - req.body.deleteProfileImage = "true"; - userFindByIdAndUpdateStub.returns({ - select: sinon.stub().returns({ - select: sinon.stub().resolves(mockUser), - }), - }); - const result = await updateUser(req, res, parseBooleanStub, generateAvatarImageStub); - expect(result).to.deep.equal(mockUser); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - userFindByIdAndUpdateStub.throws(err); - try { - await updateUser(req, res, parseBooleanStub, generateAvatarImageStub); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - }); - - describe("deleteUser", function () { - it("should return a deleted user", async function () { - userFindByIdAndDeleteStub.resolves(mockUser); - const result = await deleteUser("testId"); - expect(result).to.deep.equal(mockUser); - }); - - it("should throw an error if a user is not found", async function () { - try { - await deleteUser("testId"); - } catch (error) { - expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND); - } - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - userFindByIdAndDeleteStub.throws(err); - try { - await deleteUser("testId"); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - }); - - describe("deleteTeam", function () { - it("should return true if team deleted", async function () { - teamFindByIdAndDeleteStub.resolves(); - const result = await deleteTeam("testId"); - expect(result).to.equal(true); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - teamFindByIdAndDeleteStub.throws(err); - try { - await deleteTeam("testId"); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - }); - - describe("deleteAllOtherUsers", function () { - it("should return true if all other users deleted", async function () { - userDeleteManyStub.resolves(true); - const result = await deleteAllOtherUsers(); - expect(result).to.equal(true); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - userDeleteManyStub.throws(err); - try { - await deleteAllOtherUsers(); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - }); - - describe("getAllUsers", function () { - it("should return all users", async function () { - userFindStub.returns({ - select: sinon.stub().returns({ - select: sinon.stub().resolves([mockUser]), - }), - }); - const result = await getAllUsers(); - expect(result).to.deep.equal([mockUser]); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - userFindStub.throws(err); - try { - await getAllUsers(); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - }); - - describe("logoutUser", function () { - it("should return true if user logged out", async function () { - userUpdateOneStub.resolves(true); - const result = await logoutUser("testId"); - expect(result).to.equal(true); - }); - - it("should handle an error", async function () { - const err = new Error("test error"); - userUpdateOneStub.throws(err); - try { - await logoutUser("testId"); - } catch (error) { - expect(error).to.exist; - expect(error).to.deep.equal(err); - } - }); - }); -}); diff --git a/server/tests/services/emailService.test.js b/server/tests/services/emailService.test.js deleted file mode 100755 index cca86896a..000000000 --- a/server/tests/services/emailService.test.js +++ /dev/null @@ -1,168 +0,0 @@ -import sinon from "sinon"; -import EmailService from "../../service/emailService.js"; - -describe("EmailService - Constructor", function () { - let settingsServiceMock; - let fsMock; - let pathMock; - let compileMock; - let mjml2htmlMock; - let nodemailerMock; - let loggerMock; - - beforeEach(function () { - settingsServiceMock = { - getSettings: sinon.stub().returns({ - systemEmailHost: "smtp.example.com", - systemEmailPort: 465, - systemEmailAddress: "test@example.com", - systemEmailPassword: "password", - }), - }; - - fsMock = { - readFileSync: sinon.stub().returns(""), - }; - - pathMock = { - join: sinon.stub().callsFake((...args) => args.join("/")), - }; - - compileMock = sinon.stub().returns(() => ""); - - mjml2htmlMock = sinon.stub().returns({ html: "" }); - - nodemailerMock = { - createTransport: sinon.stub().returns({ - sendMail: sinon.stub().resolves({ messageId: "12345" }), - }), - }; - - loggerMock = { - error: sinon.stub(), - }; - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should initialize template loaders and email transporter", function () { - const emailService = new EmailService(settingsServiceMock, fsMock, pathMock, compileMock, mjml2htmlMock, nodemailerMock, loggerMock); - - // Verify that the settingsService is assigned correctly - expect(emailService.settingsService).to.equal(settingsServiceMock); - - // Verify that the template loaders are initialized - expect(emailService.templateLookup.welcomeEmailTemplate).to.be.a("function"); - expect(emailService.templateLookup.employeeActivationTemplate).to.be.a("function"); - - // Verify that the email transporter is initialized - expect(nodemailerMock.createTransport.calledOnce).to.be.true; - const emailConfig = nodemailerMock.createTransport.getCall(0).args[0]; - expect(emailConfig).to.deep.equal({ - host: "smtp.example.com", - port: 465, - secure: true, - auth: { - user: "test@example.com", - pass: "password", - }, - }); - }); - - it("should have undefined templates if FS fails", function () { - fsMock = { - readFileSync: sinon.stub().throws(new Error("File read error")), - }; - const emailService = new EmailService(settingsServiceMock, fsMock, pathMock, compileMock, mjml2htmlMock, nodemailerMock, loggerMock); - expect(loggerMock.error.called).to.be.true; - expect(loggerMock.error.firstCall.args[0].message).to.equal("File read error"); - }); -}); - -describe("EmailService - buildAndSendEmail", function () { - let settingsServiceMock; - let fsMock; - let pathMock; - let compileMock; - let mjml2htmlMock; - let nodemailerMock; - let loggerMock; - let emailService; - - beforeEach(function () { - settingsServiceMock = { - getSettings: sinon.stub().returns({ - systemEmailHost: "smtp.example.com", - systemEmailPort: 465, - systemEmailAddress: "test@example.com", - systemEmailPassword: "password", - }), - }; - - fsMock = { - readFileSync: sinon.stub().returns(""), - }; - - pathMock = { - join: sinon.stub().callsFake((...args) => args.join("/")), - }; - - compileMock = sinon.stub().returns(() => ""); - - mjml2htmlMock = sinon.stub().returns({ html: "" }); - - nodemailerMock = { - createTransport: sinon.stub().returns({ - sendMail: sinon.stub().resolves({ messageId: "12345" }), - }), - }; - - loggerMock = { - error: sinon.stub(), - }; - - emailService = new EmailService(settingsServiceMock, fsMock, pathMock, compileMock, mjml2htmlMock, nodemailerMock, loggerMock); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should build and send email successfully", async function () { - const messageId = await emailService.buildAndSendEmail("welcomeEmailTemplate", {}, "recipient@example.com", "Welcome"); - - expect(messageId).to.equal("12345"); - expect(nodemailerMock.createTransport().sendMail.calledOnce).to.be.true; - }); - - it("should log error if building HTML fails", async function () { - mjml2htmlMock.throws(new Error("MJML error")); - - const messageId = await emailService.buildAndSendEmail("welcomeEmailTemplate", {}, "recipient@example.com", "Welcome"); - expect(loggerMock.error.calledOnce).to.be.true; - expect(loggerMock.error.getCall(0).args[0].message).to.equal("MJML error"); - }); - - it("should log error if sending email fails", async function () { - nodemailerMock.createTransport().sendMail.rejects(new Error("SMTP error")); - await emailService.buildAndSendEmail("welcomeEmailTemplate", {}, "recipient@example.com", "Welcome"); - expect(loggerMock.error.calledOnce).to.be.true; - expect(loggerMock.error.getCall(0).args[0].message).to.equal("SMTP error"); - }); - - it("should log error if both building HTML and sending email fail", async function () { - mjml2htmlMock.throws(new Error("MJML error")); - nodemailerMock.createTransport().sendMail.rejects(new Error("SMTP error")); - - const messageId = await emailService.buildAndSendEmail("welcomeEmailTemplate", {}, "recipient@example.com", "Welcome"); - - expect(messageId).to.be.undefined; - expect(loggerMock.error.calledTwice).to.be.true; - expect(loggerMock.error.getCall(0).args[0].message).to.equal("MJML error"); - expect(loggerMock.error.getCall(1).args[0].message).to.equal("SMTP error"); - }); - - it("should log an error if buildHtml fails", async function () {}); -}); diff --git a/server/tests/services/jobQueue.test.js b/server/tests/services/jobQueue.test.js deleted file mode 100755 index 155f9119e..000000000 --- a/server/tests/services/jobQueue.test.js +++ /dev/null @@ -1,813 +0,0 @@ -import sinon from "sinon"; -import JobQueue from "../../service/jobQueue.js"; -import { log } from "console"; - -class QueueStub { - constructor(queueName, options) { - this.queueName = queueName; - this.options = options; - this.workers = []; - this.jobs = []; - } - - // Add any methods that are expected to be called on the Queue instance - add(job) { - this.jobs.push(job); - } - - removeRepeatable(id) { - const removedJob = this.jobs.find((job) => job.data._id === id); - this.jobs = this.jobs.filter((job) => job.data._id !== id); - if (removedJob) { - return true; - } - return false; - } - - getRepeatableJobs() { - return this.jobs; - } - async getJobs() { - return this.jobs; - } - - async pause() { - return true; - } - - async obliterate() { - return true; - } -} - -class WorkerStub { - constructor(QUEUE_NAME, workerTask) { - this.queueName = QUEUE_NAME; - this.workerTask = async () => workerTask({ data: { _id: 1 } }); - } - - async close() { - return true; - } -} - -describe("JobQueue", function () { - let settingsService, logger, db, networkService, statusService, notificationService, jobQueue; - - beforeEach(async function () { - settingsService = { getSettings: sinon.stub() }; - statusService = { updateStatus: sinon.stub() }; - notificationService = { handleNotifications: sinon.stub() }; - - logger = { error: sinon.stub(), info: sinon.stub() }; - db = { - getAllMonitors: sinon.stub().returns([]), - getMaintenanceWindowsByMonitorId: sinon.stub().returns([]), - }; - networkService = { getStatus: sinon.stub() }; - jobQueue = await JobQueue.createJobQueue(db, networkService, statusService, notificationService, settingsService, logger, QueueStub, WorkerStub); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("createJobQueue", function () { - it("should create a new JobQueue and add jobs for active monitors", async function () { - db.getAllMonitors.returns([ - { id: 1, isActive: true }, - { id: 2, isActive: true }, - ]); - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - // There should be double the jobs, as one is meant to be instantly executed - // And one is meant to be enqueued - expect(jobQueue.queue.jobs.length).to.equal(4); - }); - - it("should reject with an error if an error occurs", async function () { - db.getAllMonitors.throws("Error"); - try { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - } catch (error) { - expect(error.service).to.equal("JobQueue"); - expect(error.method).to.equal("createJobQueue"); - } - }); - - it("should reject with an error if an error occurs, should not overwrite error data", async function () { - const error = new Error("Error"); - error.service = "otherService"; - error.method = "otherMethod"; - db.getAllMonitors.throws(error); - - try { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - } catch (error) { - expect(error.service).to.equal("otherService"); - expect(error.method).to.equal("otherMethod"); - } - }); - }); - - describe("Constructor", function () { - it("should construct a new JobQueue with default port and host if not provided", async function () { - settingsService.getSettings.returns({}); - - expect(jobQueue.connection.host).to.equal("127.0.0.1"); - expect(jobQueue.connection.port).to.equal(6379); - }); - - it("should construct a new JobQueue with provided port and host", async function () { - settingsService.getSettings.returns({ redisHost: "localhost", redisPort: 1234 }); - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - expect(jobQueue.connection.host).to.equal("localhost"); - expect(jobQueue.connection.port).to.equal(1234); - }); - }); - - describe("isMaintenanceWindow", function () { - it("should throw an error if error occurs", async function () { - db.getMaintenanceWindowsByMonitorId.throws("Error"); - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - try { - jobQueue.isInMaintenanceWindow(1); - } catch (error) { - expect(error.service).to.equal("JobQueue"); - expect(error.method).to.equal("createWorker"); - } - }); - - it("should return true if in maintenance window with no repeat", async function () { - db.getMaintenanceWindowsByMonitorId.returns([ - { - active: true, - start: new Date(Date.now() - 1000).toISOString(), - end: new Date(Date.now() + 1000).toISOString(), - repeat: 0, - }, - ]); - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - const inWindow = await jobQueue.isInMaintenanceWindow(1); - expect(inWindow).to.be.true; - }); - - it("should return true if in maintenance window with repeat", async function () { - db.getMaintenanceWindowsByMonitorId.returns([ - { - active: true, - start: new Date(Date.now() - 10000).toISOString(), - end: new Date(Date.now() - 5000).toISOString(), - repeat: 1000, - }, - ]); - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - const inWindow = await jobQueue.isInMaintenanceWindow(1); - expect(inWindow).to.be.true; - }); - - it("should return false if in end < start", async function () { - db.getMaintenanceWindowsByMonitorId.returns([ - { - active: true, - start: new Date(Date.now() - 5000).toISOString(), - end: new Date(Date.now() - 10000).toISOString(), - repeat: 1000, - }, - ]); - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - const inWindow = await jobQueue.isInMaintenanceWindow(1); - expect(inWindow).to.be.false; - }); - - it("should return false if not in maintenance window", async function () { - db.getMaintenanceWindowsByMonitorId.returns([ - { - active: false, - start: new Date(Date.now() - 5000).toISOString(), - end: new Date(Date.now() - 10000).toISOString(), - repeat: 1000, - }, - ]); - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - const inWindow = await jobQueue.isInMaintenanceWindow(1); - expect(inWindow).to.be.false; - }); - }); - - describe("createJobHandler", function () { - it("resolve to an error if an error is thrown within", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - jobQueue.isInMaintenanceWindow = sinon.stub().throws("Error"); - try { - const handler = jobQueue.createJobHandler(); - await handler({ data: { _id: 1 } }); - } catch (error) { - expect(error.service).to.equal("JobQueue"); - expect(error.details).to.equal(`Error processing job 1: Error`); - } - }); - - it("should log info if job is in maintenance window", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - jobQueue.isInMaintenanceWindow = sinon.stub().returns(true); - const handler = jobQueue.createJobHandler(); - await handler({ data: { _id: 1 } }); - expect(logger.info.calledOnce).to.be.true; - expect(logger.info.firstCall.args[0].message).to.equal("Monitor 1 is in maintenance window"); - }); - - it("should return if status has not changed", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - jobQueue.isInMaintenanceWindow = sinon.stub().returns(false); - statusService.updateStatus = sinon.stub().returns({ statusChanged: false }); - const handler = jobQueue.createJobHandler(); - await handler({ data: { _id: 1 } }); - expect(jobQueue.notificationService.handleNotifications.notCalled).to.be.true; - }); - - it("should return if status has changed, but prevStatus was undefined (monitor paused)", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - jobQueue.isInMaintenanceWindow = sinon.stub().returns(false); - statusService.updateStatus = sinon.stub().returns({ statusChanged: true, prevStatus: undefined }); - const handler = jobQueue.createJobHandler(); - await handler({ data: { _id: 1 } }); - expect(jobQueue.notificationService.handleNotifications.notCalled).to.be.true; - }); - - it("should call notification service if status changed and monitor was not paused", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - jobQueue.isInMaintenanceWindow = sinon.stub().returns(false); - statusService.updateStatus = sinon.stub().returns({ statusChanged: true, prevStatus: false }); - const handler = jobQueue.createJobHandler(); - await handler({ data: { _id: 1 } }); - expect(jobQueue.notificationService.handleNotifications.calledOnce).to.be.true; - }); - }); - - describe("getWorkerStats", function () { - it("should throw an error if getRepeatable Jobs fails", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - jobQueue.queue.getRepeatableJobs = async () => { - throw new Error("Error"); - }; - try { - const stats = await jobQueue.getWorkerStats(); - } catch (error) { - expect(error.service).to.equal("JobQueue"); - expect(error.method).to.equal("getWorkerStats"); - } - }); - - it("should throw an error if getRepeatable Jobs fails but respect existing error data", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - jobQueue.queue.getRepeatableJobs = async () => { - const error = new Error("Existing Error"); - error.service = "otherService"; - error.method = "otherMethod"; - throw error; - }; - try { - await jobQueue.getWorkerStats(); - } catch (error) { - expect(error.service).to.equal("otherService"); - expect(error.method).to.equal("otherMethod"); - } - }); - }); - - describe("scaleWorkers", function () { - it("should scale workers to 5 if no workers", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - expect(jobQueue.workers.length).to.equal(5); - }); - - it("should scale workers up", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - jobQueue.scaleWorkers({ - load: 100, - jobs: Array.from({ length: 100 }, (_, i) => i + 1), - }); - expect(jobQueue.workers.length).to.equal(20); - }); - - it("should scale workers down, even with error of worker.close fails", async function () { - WorkerStub.prototype.close = async () => { - throw new Error("Error"); - }; - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - await jobQueue.scaleWorkers({ - load: 100, - jobs: Array.from({ length: 100 }, (_, i) => i + 1), - }); - - const res = await jobQueue.scaleWorkers({ - load: 0, - jobs: [], - }); - expect(jobQueue.workers.length).to.equal(5); - }); - - it("should scale workers down", async function () { - WorkerStub.prototype.close = async () => { - return true; - }; - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - await jobQueue.scaleWorkers({ - load: 40, - jobs: Array.from({ length: 40 }, (_, i) => i + 1), - }); - - const res = await jobQueue.scaleWorkers({ - load: 0, - jobs: [], - }); - expect(jobQueue.workers.length).to.equal(5); - }); - - it("should return false if scaling doesn't happen", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - const res = await jobQueue.scaleWorkers({ load: 5 }); - expect(jobQueue.workers.length).to.equal(5); - expect(res).to.be.false; - }); - }); - - describe("getJobs", function () { - it("should return jobs", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - const jobs = await jobQueue.getJobs(); - expect(jobs.length).to.equal(0); - }); - - it("should throw an error if getRepeatableJobs fails", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - try { - jobQueue.queue.getRepeatableJobs = async () => { - throw new Error("error"); - }; - - await jobQueue.getJobs(true); - } catch (error) { - expect(error.service).to.equal("JobQueue"); - expect(error.method).to.equal("getJobs"); - } - }); - - it("should throw an error if getRepeatableJobs fails but respect existing error data", async function () { - const jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); - try { - jobQueue.queue.getRepeatableJobs = async () => { - const error = new Error("Existing error"); - error.service = "otherService"; - error.method = "otherMethod"; - throw error; - }; - - await jobQueue.getJobs(true); - } catch (error) { - expect(error.service).to.equal("otherService"); - expect(error.method).to.equal("otherMethod"); - } - }); - }); - - describe("getJobStats", function () { - it("should return job stats for no jobs", async function () { - const jobStats = await jobQueue.getJobStats(); - expect(jobStats).to.deep.equal({ jobs: [], workers: 5 }); - }); - - it("should return job stats for jobs", async function () { - jobQueue.queue.getJobs = async () => { - return [{ data: { url: "test" }, getState: async () => "completed" }]; - }; - const jobStats = await jobQueue.getJobStats(); - expect(jobStats).to.deep.equal({ - jobs: [{ url: "test", state: "completed" }], - workers: 5, - }); - }); - - it("should reject with an error if mapping jobs fails", async function () { - jobQueue.queue.getJobs = async () => { - return [ - { - data: { url: "test" }, - getState: async () => { - throw new Error("Mapping Error"); - }, - }, - ]; - }; - try { - await jobQueue.getJobStats(); - } catch (error) { - expect(error.message).to.equal("Mapping Error"); - expect(error.service).to.equal("JobQueue"); - expect(error.method).to.equal("getJobStats"); - } - }); - - it("should reject with an error if mapping jobs fails but respect existing error data", async function () { - jobQueue.queue.getJobs = async () => { - return [ - { - data: { url: "test" }, - getState: async () => { - const error = new Error("Mapping Error"); - error.service = "otherService"; - error.method = "otherMethod"; - throw error; - }, - }, - ]; - }; - try { - await jobQueue.getJobStats(); - } catch (error) { - expect(error.message).to.equal("Mapping Error"); - expect(error.service).to.equal("otherService"); - expect(error.method).to.equal("otherMethod"); - } - }); - }); - - describe("addJob", function () { - it("should add a job to the queue", async function () { - jobQueue.addJob("test", { url: "test" }); - expect(jobQueue.queue.jobs.length).to.equal(1); - }); - - it("should reject with an error if adding fails", async function () { - jobQueue.queue.add = async () => { - throw new Error("Error adding job"); - }; - try { - await jobQueue.addJob("test", { url: "test" }); - } catch (error) { - expect(error.message).to.equal("Error adding job"); - expect(error.service).to.equal("JobQueue"); - expect(error.method).to.equal("addJob"); - } - }); - - it("should reject with an error if adding fails but respect existing error data", async function () { - jobQueue.queue.add = async () => { - const error = new Error("Error adding job"); - error.service = "otherService"; - error.method = "otherMethod"; - throw error; - }; - try { - await jobQueue.addJob("test", { url: "test" }); - } catch (error) { - expect(error.message).to.equal("Error adding job"); - expect(error.service).to.equal("otherService"); - expect(error.method).to.equal("otherMethod"); - } - }); - }); - - describe("deleteJob", function () { - it("should delete a job from the queue", async function () { - jobQueue.getWorkerStats = sinon.stub().returns({ load: 1, jobs: [{}] }); - jobQueue.scaleWorkers = sinon.stub(); - const monitor = { _id: 1 }; - const job = { data: monitor }; - jobQueue.queue.jobs = [job]; - await jobQueue.deleteJob(monitor); - // expect(jobQueue.queue.jobs.length).to.equal(0); - // expect(logger.info.calledOnce).to.be.true; - // expect(jobQueue.getWorkerStats.calledOnce).to.be.true; - // expect(jobQueue.scaleWorkers.calledOnce).to.be.true; - }); - - it("should log an error if job is not found", async function () { - jobQueue.getWorkerStats = sinon.stub().returns({ load: 1, jobs: [{}] }); - jobQueue.scaleWorkers = sinon.stub(); - const monitor = { _id: 1 }; - const job = { data: monitor }; - jobQueue.queue.jobs = [job]; - await jobQueue.deleteJob({ id_: 2 }); - expect(logger.error.calledOnce).to.be.true; - }); - - it("should reject with an error if removeRepeatable fails", async function () { - jobQueue.queue.removeRepeatable = async () => { - const error = new Error("removeRepeatable error"); - throw error; - }; - - try { - await jobQueue.deleteJob({ _id: 1 }); - } catch (error) { - expect(error.message).to.equal("removeRepeatable error"); - expect(error.service).to.equal("JobQueue"); - expect(error.method).to.equal("deleteJob"); - } - }); - - it("should reject with an error if removeRepeatable fails but respect existing error data", async function () { - jobQueue.queue.removeRepeatable = async () => { - const error = new Error("removeRepeatable error"); - error.service = "otherService"; - error.method = "otherMethod"; - throw error; - }; - - try { - await jobQueue.deleteJob({ _id: 1 }); - } catch (error) { - expect(error.message).to.equal("removeRepeatable error"); - expect(error.service).to.equal("otherService"); - expect(error.method).to.equal("otherMethod"); - } - }); - }); - - describe("getMetrics", function () { - it("should return metrics for the job queue", async function () { - jobQueue.queue.getWaitingCount = async () => 1; - jobQueue.queue.getActiveCount = async () => 2; - jobQueue.queue.getCompletedCount = async () => 3; - jobQueue.queue.getFailedCount = async () => 4; - jobQueue.queue.getDelayedCount = async () => 5; - jobQueue.queue.getRepeatableJobs = async () => [1, 2, 3]; - const metrics = await jobQueue.getMetrics(); - expect(metrics).to.deep.equal({ - waiting: 1, - active: 2, - completed: 3, - failed: 4, - delayed: 5, - repeatableJobs: 3, - }); - }); - - it("should log an error if metrics operations fail", async function () { - jobQueue.queue.getWaitingCount = async () => { - throw new Error("Error"); - }; - await jobQueue.getMetrics(); - expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal("Error"); - }); - }); - - describe("obliterate", function () { - it("should return true if obliteration is successful", async function () { - jobQueue.queue.pause = async () => true; - jobQueue.getJobs = async () => [{ key: 1, id: 1 }]; - jobQueue.queue.removeRepeatableByKey = async () => true; - jobQueue.queue.remove = async () => true; - jobQueue.queue.obliterate = async () => true; - const obliteration = await jobQueue.obliterate(); - expect(obliteration).to.be.true; - }); - - it("should throw an error if obliteration fails", async function () { - jobQueue.getMetrics = async () => { - throw new Error("Error"); - }; - - try { - await jobQueue.obliterate(); - } catch (error) { - expect(error.service).to.equal("JobQueue"); - expect(error.method).to.equal("obliterate"); - } - }); - - it("should throw an error if obliteration fails but respect existing error data", async function () { - jobQueue.getMetrics = async () => { - const error = new Error("Error"); - error.service = "otherService"; - error.method = "otherMethod"; - throw error; - }; - - try { - await jobQueue.obliterate(); - } catch (error) { - expect(error.service).to.equal("otherService"); - expect(error.method).to.equal("otherMethod"); - } - }); - }); -}); diff --git a/server/tests/services/networkService.test.js b/server/tests/services/networkService.test.js deleted file mode 100755 index cb48ed9b2..000000000 --- a/server/tests/services/networkService.test.js +++ /dev/null @@ -1,439 +0,0 @@ -import sinon from "sinon"; -import NetworkService from "../../service/networkService.js"; -import { expect } from "chai"; -import http from "http"; -import { errorMessages } from "../../utils/messages.js"; -describe("Network Service", function () { - let axios, ping, Docker, logger, networkService; - - beforeEach(function () { - axios = { - get: sinon.stub().resolves({ - data: { foo: "bar" }, - status: 200, - }), - }; - Docker = class { - listContainers = sinon.stub().resolves([ - { - Names: ["http://test.com"], - Id: "http://test.com", - }, - ]); - getContainer = sinon.stub().returns({ - inspect: sinon.stub().resolves({ State: { Status: "running" } }), - }); - }; - ping = { - promise: { - probe: sinon.stub().resolves({ response: { alive: true }, responseTime: 100, alive: true }), - }, - }; - logger = { error: sinon.stub() }; - networkService = new NetworkService(axios, ping, logger, http, Docker); - }); - - describe("constructor", function () { - it("should create a new NetworkService instance", function () { - const networkService = new NetworkService(); - expect(networkService).to.be.an.instanceOf(NetworkService); - }); - }); - - describe("timeRequest", function () { - it("should time an asynchronous operation", async function () { - const operation = sinon.stub().resolves("success"); - const { response, responseTime } = await networkService.timeRequest(operation); - expect(response).to.equal("success"); - expect(responseTime).to.be.a("number"); - }); - - it("should handle errors if operation throws error", async function () { - const error = new Error("Test error"); - const operation = sinon.stub().throws(error); - const { response, responseTime } = await networkService.timeRequest(operation); - expect(response).to.be.null; - expect(responseTime).to.be.a("number"); - expect(error.message).to.equal("Test error"); - }); - }); - - describe("requestPing", function () { - it("should return a response object if ping successful", async function () { - const pingResult = await networkService.requestPing({ - data: { url: "http://test.com", _id: "123" }, - }); - expect(pingResult.monitorId).to.equal("123"); - expect(pingResult.type).to.equal("ping"); - expect(pingResult.responseTime).to.be.a("number"); - expect(pingResult.status).to.be.true; - }); - - it("should return a response object if ping unsuccessful", async function () { - const error = new Error("Test error"); - networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); - const pingResult = await networkService.requestPing({ - data: { url: "http://test.com", _id: "123" }, - }); - expect(pingResult.monitorId).to.equal("123"); - expect(pingResult.type).to.equal("ping"); - expect(pingResult.responseTime).to.be.a("number"); - expect(pingResult.status).to.be.false; - expect(pingResult.code).to.equal(networkService.PING_ERROR); - }); - - it("should throw an error if ping cannot resolve", async function () { - const error = new Error("test error"); - networkService.timeRequest = sinon.stub().throws(error); - try { - await networkService.requestPing({ - data: { url: "http://test.com", _id: "123" }, - }); - } catch (error) { - expect(error).to.exist; - expect(error.method).to.equal("requestPing"); - } - }); - }); - - describe("requestHttp", function () { - it("should return a response object if http successful", async function () { - const job = { data: { url: "http://test.com", _id: "123", type: "http" } }; - const httpResult = await networkService.requestHttp(job); - expect(httpResult.monitorId).to.equal("123"); - expect(httpResult.type).to.equal("http"); - expect(httpResult.responseTime).to.be.a("number"); - expect(httpResult.status).to.be.true; - }); - - it("should return a response object if http unsuccessful", async function () { - const error = new Error("Test error"); - error.response = { status: 404 }; - networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); - const job = { data: { url: "http://test.com", _id: "123", type: "http" } }; - const httpResult = await networkService.requestHttp(job); - expect(httpResult.monitorId).to.equal("123"); - expect(httpResult.type).to.equal("http"); - expect(httpResult.responseTime).to.be.a("number"); - expect(httpResult.status).to.be.false; - expect(httpResult.code).to.equal(404); - }); - - it("should return a response object if http unsuccessful with unknown code", async function () { - const error = new Error("Test error"); - error.response = {}; - networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); - const job = { data: { url: "http://test.com", _id: "123", type: "http" } }; - const httpResult = await networkService.requestHttp(job); - expect(httpResult.monitorId).to.equal("123"); - expect(httpResult.type).to.equal("http"); - expect(httpResult.responseTime).to.be.a("number"); - expect(httpResult.status).to.be.false; - expect(httpResult.code).to.equal(networkService.NETWORK_ERROR); - }); - - it("should throw an error if an error occurs", async function () { - const error = new Error("test error"); - networkService.timeRequest = sinon.stub().throws(error); - try { - await networkService.requestHttp({ - data: { url: "http://test.com", _id: "123" }, - }); - } catch (error) { - expect(error).to.exist; - expect(error.method).to.equal("requestHttp"); - } - }); - }); - - describe("requestPagespeed", function () { - it("should return a response object if pagespeed successful", async function () { - const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } }; - const pagespeedResult = await networkService.requestPagespeed(job); - expect(pagespeedResult.monitorId).to.equal("123"); - expect(pagespeedResult.type).to.equal("pagespeed"); - expect(pagespeedResult.responseTime).to.be.a("number"); - expect(pagespeedResult.status).to.be.true; - }); - - it("should return a response object if pagespeed unsuccessful", async function () { - const error = new Error("Test error"); - error.response = { status: 404 }; - networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); - const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } }; - const pagespeedResult = await networkService.requestPagespeed(job); - expect(pagespeedResult.monitorId).to.equal("123"); - expect(pagespeedResult.type).to.equal("pagespeed"); - expect(pagespeedResult.responseTime).to.be.a("number"); - expect(pagespeedResult.status).to.be.false; - expect(pagespeedResult.code).to.equal(404); - }); - - it("should return a response object if pagespeed unsuccessful with an unknown code", async function () { - const error = new Error("Test error"); - error.response = {}; - networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); - const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } }; - const pagespeedResult = await networkService.requestPagespeed(job); - expect(pagespeedResult.monitorId).to.equal("123"); - expect(pagespeedResult.type).to.equal("pagespeed"); - expect(pagespeedResult.responseTime).to.be.a("number"); - expect(pagespeedResult.status).to.be.false; - expect(pagespeedResult.code).to.equal(networkService.NETWORK_ERROR); - }); - - it("should throw an error if pagespeed cannot resolve", async function () { - const error = new Error("test error"); - networkService.timeRequest = sinon.stub().throws(error); - try { - await networkService.requestPagespeed({ - data: { url: "http://test.com", _id: "123" }, - }); - } catch (error) { - expect(error).to.exist; - expect(error.method).to.equal("requestPagespeed"); - } - }); - }); - - describe("requestHardware", function () { - it("should return a response object if hardware successful", async function () { - const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } }; - const httpResult = await networkService.requestHardware(job); - expect(httpResult.monitorId).to.equal("123"); - expect(httpResult.type).to.equal("hardware"); - expect(httpResult.responseTime).to.be.a("number"); - expect(httpResult.status).to.be.true; - }); - - it("should return a response object if hardware successful and job has a secret", async function () { - const job = { - data: { - url: "http://test.com", - _id: "123", - type: "hardware", - secret: "my_secret", - }, - }; - const httpResult = await networkService.requestHardware(job); - expect(httpResult.monitorId).to.equal("123"); - expect(httpResult.type).to.equal("hardware"); - expect(httpResult.responseTime).to.be.a("number"); - expect(httpResult.status).to.be.true; - }); - - it("should return a response object if hardware unsuccessful", async function () { - const error = new Error("Test error"); - error.response = { status: 404 }; - networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); - const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } }; - const httpResult = await networkService.requestHardware(job); - expect(httpResult.monitorId).to.equal("123"); - expect(httpResult.type).to.equal("hardware"); - expect(httpResult.responseTime).to.be.a("number"); - expect(httpResult.status).to.be.false; - expect(httpResult.code).to.equal(404); - }); - - it("should return a response object if hardware unsuccessful with unknown code", async function () { - const error = new Error("Test error"); - error.response = {}; - networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); - const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } }; - const httpResult = await networkService.requestHardware(job); - expect(httpResult.monitorId).to.equal("123"); - expect(httpResult.type).to.equal("hardware"); - expect(httpResult.responseTime).to.be.a("number"); - expect(httpResult.status).to.be.false; - expect(httpResult.code).to.equal(networkService.NETWORK_ERROR); - }); - - it("should throw an error if hardware cannot resolve", async function () { - const error = new Error("test error"); - networkService.timeRequest = sinon.stub().throws(error); - try { - await networkService.requestHardware({ - data: { url: "http://test.com", _id: "123" }, - }); - } catch (error) { - expect(error).to.exist; - expect(error.method).to.equal("requestHardware"); - } - }); - }); - - describe("requestDocker", function () { - it("should return a response object if docker successful", async function () { - const job = { data: { url: "http://test.com", _id: "123", type: "docker" } }; - const dockerResult = await networkService.requestDocker(job); - expect(dockerResult.monitorId).to.equal("123"); - expect(dockerResult.type).to.equal("docker"); - expect(dockerResult.responseTime).to.be.a("number"); - expect(dockerResult.status).to.be.true; - }); - - it("should return a response object with status false if container not running", async function () { - Docker = class { - listContainers = sinon.stub().resolves([ - { - Names: ["/my_container"], - Id: "abc123", - }, - ]); - getContainer = sinon.stub().returns({ - inspect: sinon.stub().resolves({ State: { Status: "stopped" } }), - }); - }; - networkService = new NetworkService(axios, ping, logger, http, Docker); - const job = { data: { url: "abc123", _id: "123", type: "docker" } }; - const dockerResult = await networkService.requestDocker(job); - expect(dockerResult.status).to.be.false; - expect(dockerResult.code).to.equal(200); - }); - - it("should handle an error when fetching the container", async function () { - Docker = class { - listContainers = sinon.stub().resolves([ - { - Names: ["/my_container"], - Id: "abc123", - }, - ]); - getContainer = sinon.stub().returns({ - inspect: sinon.stub().throws(new Error("test error")), - }); - }; - networkService = new NetworkService(axios, ping, logger, http, Docker); - const job = { data: { url: "abc123", _id: "123", type: "docker" } }; - const dockerResult = await networkService.requestDocker(job); - expect(dockerResult.status).to.be.false; - expect(dockerResult.code).to.equal(networkService.NETWORK_ERROR); - }); - - it("should throw an error if operations fail", async function () { - Docker = class { - listContainers = sinon.stub().resolves([ - { - Names: ["/my_container"], - Id: "abc123", - }, - ]); - getContainer = sinon.stub().throws(new Error("test error")); - }; - networkService = new NetworkService(axios, ping, logger, http, Docker); - const job = { data: { url: "abc123", _id: "123", type: "docker" } }; - try { - await networkService.requestDocker(job); - } catch (error) { - expect(error.message).to.equal("test error"); - } - }); - - it("should throw an error if no matching images found", async function () { - Docker = class { - listContainers = sinon.stub().resolves([]); - getContainer = sinon.stub().throws(new Error("test error")); - }; - networkService = new NetworkService(axios, ping, logger, http, Docker); - const job = { data: { url: "abc123", _id: "123", type: "docker" } }; - try { - await networkService.requestDocker(job); - } catch (error) { - expect(error.message).to.equal(errorMessages.DOCKER_NOT_FOUND); - } - }); - }); - - describe("getStatus", function () { - beforeEach(function () { - networkService.requestPing = sinon.stub(); - networkService.requestHttp = sinon.stub(); - networkService.requestPagespeed = sinon.stub(); - networkService.requestHardware = sinon.stub(); - networkService.requestDocker = sinon.stub(); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should call requestPing if type is ping", async function () { - await networkService.getStatus({ data: { type: "ping" } }); - expect(networkService.requestPing.calledOnce).to.be.true; - expect(networkService.requestDocker.notCalled).to.be.true; - expect(networkService.requestHttp.notCalled).to.be.true; - expect(networkService.requestPagespeed.notCalled).to.be.true; - }); - - it("should call requestHttp if type is http", async function () { - await networkService.getStatus({ data: { type: "http" } }); - expect(networkService.requestPing.notCalled).to.be.true; - expect(networkService.requestDocker.notCalled).to.be.true; - expect(networkService.requestHttp.calledOnce).to.be.true; - expect(networkService.requestPagespeed.notCalled).to.be.true; - }); - - it("should call requestPagespeed if type is pagespeed", async function () { - await networkService.getStatus({ data: { type: "pagespeed" } }); - expect(networkService.requestPing.notCalled).to.be.true; - expect(networkService.requestDocker.notCalled).to.be.true; - expect(networkService.requestHttp.notCalled).to.be.true; - expect(networkService.requestPagespeed.calledOnce).to.be.true; - }); - - it("should call requestHardware if type is hardware", async function () { - await networkService.getStatus({ data: { type: "hardware" } }); - expect(networkService.requestHardware.calledOnce).to.be.true; - expect(networkService.requestDocker.notCalled).to.be.true; - expect(networkService.requestPing.notCalled).to.be.true; - expect(networkService.requestPagespeed.notCalled).to.be.true; - }); - - it("should call requestDocker if type is Docker", async function () { - await networkService.getStatus({ data: { type: "docker" } }); - expect(networkService.requestDocker.calledOnce).to.be.true; - expect(networkService.requestHardware.notCalled).to.be.true; - expect(networkService.requestPing.notCalled).to.be.true; - expect(networkService.requestPagespeed.notCalled).to.be.true; - }); - - it("should throw an error if an unknown type is provided", async function () { - try { - await networkService.getStatus({ data: { type: "unknown" } }); - } catch (error) { - expect(error.service).to.equal("NetworkService"); - expect(error.method).to.equal("getStatus"); - expect(error.message).to.equal("Unsupported type: unknown"); - } - }); - - it("should throw an error if job type is undefined", async function () { - try { - await networkService.getStatus({ data: { type: undefined } }); - } catch (error) { - expect(error.service).to.equal("NetworkService"); - expect(error.method).to.equal("getStatus"); - expect(error.message).to.equal("Unsupported type: unknown"); - } - }); - - it("should throw an error if job is empty", async function () { - try { - await networkService.getStatus({}); - } catch (error) { - expect(error.method).to.equal("getStatus"); - expect(error.message).to.equal("Unsupported type: unknown"); - } - }); - - it("should throw an error if job is null", async function () { - try { - await networkService.getStatus(null); - } catch (error) { - expect(error.service).to.equal("NetworkService"); - expect(error.method).to.equal("getStatus"); - expect(error.message).to.equal("Unsupported type: unknown"); - } - }); - }); -}); diff --git a/server/tests/services/notificationService.test.js b/server/tests/services/notificationService.test.js deleted file mode 100755 index 211cc8b46..000000000 --- a/server/tests/services/notificationService.test.js +++ /dev/null @@ -1,285 +0,0 @@ -import sinon from "sinon"; -import NotificationService from "../../service/notificationService.js"; -import { expect } from "chai"; - -describe("NotificationService", function () { - let emailService, db, logger, notificationService; - - beforeEach(function () { - db = { - getNotificationsByMonitorId: sinon.stub(), - }; - emailService = { - buildAndSendEmail: sinon.stub(), - }; - logger = { - warn: sinon.stub(), - }; - - notificationService = new NotificationService(emailService, db, logger); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("constructor", function () { - it("should create a new instance of NotificationService", function () { - expect(notificationService).to.be.an.instanceOf(NotificationService); - }); - }); - - describe("sendEmail", function () { - it("should send an email notification with Up Template", async function () { - const networkResponse = { - monitor: { - name: "Test Monitor", - url: "http://test.com", - }, - status: true, - prevStatus: false, - }; - const address = "test@test.com"; - await notificationService.sendEmail(networkResponse, address); - expect(notificationService.emailService.buildAndSendEmail.calledOnce).to.be.true; - expect( - notificationService.emailService.buildAndSendEmail.calledWith("serverIsUpTemplate", { monitor: "Test Monitor", url: "http://test.com" }) - ); - }); - - it("should send an email notification with Down Template", async function () { - const networkResponse = { - monitor: { - name: "Test Monitor", - url: "http://test.com", - }, - status: false, - prevStatus: true, - }; - const address = "test@test.com"; - await notificationService.sendEmail(networkResponse, address); - expect(notificationService.emailService.buildAndSendEmail.calledOnce).to.be.true; - }); - - it("should send an email notification with Up Template", async function () { - const networkResponse = { - monitor: { - name: "Test Monitor", - url: "http://test.com", - }, - status: true, - prevStatus: false, - }; - const address = "test@test.com"; - await notificationService.sendEmail(networkResponse, address); - expect(notificationService.emailService.buildAndSendEmail.calledOnce).to.be.true; - }); - }); - - describe("handleNotifications", function () { - it("should handle notifications based on the network response", async function () { - notificationService.sendEmail = sinon.stub(); - const res = await notificationService.handleNotifications({ - monitor: { - type: "email", - address: "www.google.com", - }, - }); - expect(res).to.be.true; - }); - - it("should handle hardware notifications", async function () { - notificationService.sendEmail = sinon.stub(); - const res = await notificationService.handleNotifications({ - monitor: { - type: "hardware", - address: "www.google.com", - }, - }); - expect(res).to.be.true; - }); - - it("should handle an error when getting notifications", async function () { - const testError = new Error("Test Error"); - notificationService.db.getNotificationsByMonitorId.rejects(testError); - await notificationService.handleNotifications({ monitorId: "123" }); - expect(notificationService.logger.warn.calledOnce).to.be.true; - }); - }); - - describe("sendHardwareEmail", function () { - let networkResponse, address, alerts; - - beforeEach(function () { - networkResponse = { - monitor: { - name: "Test Monitor", - url: "http://test.com", - }, - status: true, - prevStatus: false, - }; - address = "test@test.com"; - alerts = ["test"]; - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should send an email notification with Hardware Template", async function () { - emailService.buildAndSendEmail.resolves(true); - const res = await notificationService.sendHardwareEmail(networkResponse, address, alerts); - expect(res).to.be.true; - }); - - it("should return false if no alerts are provided", async function () { - alerts = []; - emailService.buildAndSendEmail.resolves(true); - const res = await notificationService.sendHardwareEmail(networkResponse, address, alerts); - expect(res).to.be.false; - }); - }); - - describe("handleStatusNotifications", function () { - let networkResponse; - - beforeEach(function () { - networkResponse = { - monitor: { - name: "Test Monitor", - url: "http://test.com", - }, - statusChanged: true, - status: true, - prevStatus: false, - }; - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should handle status notifications", async function () { - db.getNotificationsByMonitorId.resolves([{ type: "email", address: "test@test.com" }]); - const res = await notificationService.handleStatusNotifications(networkResponse); - expect(res).to.be.true; - }); - - it("should return false if status hasn't changed", async function () { - networkResponse.statusChanged = false; - const res = await notificationService.handleStatusNotifications(networkResponse); - expect(res).to.be.false; - }); - - it("should return false if prevStatus is undefined", async function () { - networkResponse.prevStatus = undefined; - const res = await notificationService.handleStatusNotifications(networkResponse); - expect(res).to.be.false; - }); - - it("should handle an error", async function () { - const testError = new Error("Test Error"); - db.getNotificationsByMonitorId.rejects(testError); - try { - await notificationService.handleStatusNotifications(networkResponse); - } catch (error) { - expect(error).to.be.an.instanceOf(Error); - expect(error.message).to.equal("Test Error"); - } - }); - }); - - describe("handleHardwareNotifications", function () { - let networkResponse; - - beforeEach(function () { - networkResponse = { - monitor: { - name: "Test Monitor", - url: "http://test.com", - thresholds: { - usage_cpu: 1, - usage_memory: 1, - usage_disk: 1, - }, - }, - payload: { - data: { - cpu: { - usage_percent: 0.655, - }, - memory: { - usage_percent: 0.783, - }, - disk: [ - { - name: "/dev/sda1", - usage_percent: 0.452, - }, - { - name: "/dev/sdb1", - usage_percent: 0.627, - }, - ], - }, - }, - }; - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("it should return false if no thresholds are set", function () { - it("should return false if no thresholds are set", async function () { - networkResponse.monitor.thresholds = undefined; - const res = await notificationService.handleHardwareNotifications(networkResponse); - expect(res).to.be.false; - }); - - it("should return false if metrics are null", async function () { - networkResponse.payload.data = null; - const res = await notificationService.handleHardwareNotifications(networkResponse); - expect(res).to.be.false; - }); - - it("should return true if request is well formed and thresholds > 0", async function () { - db.getNotificationsByMonitorId.resolves([ - { - type: "email", - address: "test@test.com", - alertThreshold: 1, - cpuAlertThreshold: 1, - memoryAlertThreshold: 1, - diskAlertThreshold: 1, - save: sinon.stub().resolves(), - }, - ]); - const res = await notificationService.handleHardwareNotifications(networkResponse); - expect(res).to.be.true; - }); - - it("should return true if thresholds are exceeded", async function () { - db.getNotificationsByMonitorId.resolves([ - { - type: "email", - address: "test@test.com", - alertThreshold: 1, - cpuAlertThreshold: 1, - memoryAlertThreshold: 1, - diskAlertThreshold: 1, - save: sinon.stub().resolves(), - }, - ]); - networkResponse.monitor.thresholds = { - usage_cpu: 0.01, - usage_memory: 0.01, - usage_disk: 0.01, - }; - const res = await notificationService.handleHardwareNotifications(networkResponse); - expect(res).to.be.true; - }); - }); - }); -}); diff --git a/server/tests/services/settingsService.test.js b/server/tests/services/settingsService.test.js deleted file mode 100755 index 6c9960e5e..000000000 --- a/server/tests/services/settingsService.test.js +++ /dev/null @@ -1,140 +0,0 @@ -import sinon from "sinon"; -import SettingsService from "../../service/settingsService.js"; -import { expect } from "chai"; -import NetworkService from "../../service/networkService.js"; -const SERVICE_NAME = "SettingsService"; - -describe("SettingsService", function () { - let sandbox, mockAppSettings; - - beforeEach(function () { - sandbox = sinon.createSandbox(); - sandbox.stub(process.env, "CLIENT_HOST").value("http://localhost"); - sandbox.stub(process.env, "JWT_SECRET").value("secret"); - sandbox.stub(process.env, "REFRESH_TOKEN_SECRET").value("refreshSecret"); - sandbox.stub(process.env, "DB_TYPE").value("postgres"); - sandbox.stub(process.env, "DB_CONNECTION_STRING").value("postgres://user:pass@localhost/db"); - sandbox.stub(process.env, "REDIS_HOST").value("localhost"); - sandbox.stub(process.env, "REDIS_PORT").value("6379"); - sandbox.stub(process.env, "TOKEN_TTL").value("3600"); - sandbox.stub(process.env, "REFRESH_TOKEN_TTL").value("86400"); - sandbox.stub(process.env, "PAGESPEED_API_KEY").value("apiKey"); - sandbox.stub(process.env, "SYSTEM_EMAIL_HOST").value("smtp.mailtrap.io"); - sandbox.stub(process.env, "SYSTEM_EMAIL_PORT").value("2525"); - sandbox.stub(process.env, "SYSTEM_EMAIL_ADDRESS").value("test@example.com"); - sandbox.stub(process.env, "SYSTEM_EMAIL_PASSWORD").value("password"); - }); - - mockAppSettings = { - settingOne: 123, - settingTwo: 456, - }; - - afterEach(function () { - sandbox.restore(); - sinon.restore(); - }); - - describe("constructor", function () { - it("should construct a new SettingsService", function () { - const settingsService = new SettingsService(mockAppSettings); - expect(settingsService.appSettings).to.equal(mockAppSettings); - }); - }); - - describe("loadSettings", function () { - it("should load settings from DB when environment variables are not set", async function () { - const dbSettings = { logLevel: "debug", apiBaseUrl: "http://localhost" }; - const appSettings = { findOne: sinon.stub().returns(dbSettings) }; - const settingsService = new SettingsService(appSettings); - settingsService.settings = {}; - const result = await settingsService.loadSettings(); - expect(result).to.deep.equal(dbSettings); - }); - - it("should throw an error if settings are not found", async function () { - const appSettings = { findOne: sinon.stub().returns(null) }; - const settingsService = new SettingsService(appSettings); - settingsService.settings = null; - - try { - await settingsService.loadSettings(); - } catch (error) { - expect(error.message).to.equal("Settings not found"); - expect(error.service).to.equal(SERVICE_NAME); - expect(error.method).to.equal("loadSettings"); - } - }); - - it("should add its method and service name to error if not present", async function () { - const appSettings = { findOne: sinon.stub().throws(new Error("Test error")) }; - const settingsService = new SettingsService(appSettings); - try { - await settingsService.loadSettings(); - } catch (error) { - expect(error.message).to.equal("Test error"); - expect(error.service).to.equal(SERVICE_NAME); - expect(error.method).to.equal("loadSettings"); - } - }); - - it("should not add its method and service name to error if present", async function () { - const error = new Error("Test error"); - error.method = "otherMethod"; - error.service = "OTHER_SERVICE"; - const appSettings = { findOne: sinon.stub().throws(error) }; - const settingsService = new SettingsService(appSettings); - try { - await settingsService.loadSettings(); - } catch (error) { - expect(error.message).to.equal("Test error"); - expect(error.service).to.equal("OTHER_SERVICE"); - expect(error.method).to.equal("otherMethod"); - } - }); - - it("should merge DB settings with environment variables", async function () { - const dbSettings = { logLevel: "debug", apiBaseUrl: "http://localhost" }; - const appSettings = { findOne: sinon.stub().returns(dbSettings) }; - const settingsService = new SettingsService(appSettings); - const result = await settingsService.loadSettings(); - expect(result).to.deep.equal(settingsService.settings); - expect(settingsService.settings.logLevel).to.equal("debug"); - expect(settingsService.settings.apiBaseUrl).to.equal("http://localhost"); - }); - }); - - describe("reloadSettings", function () { - it("should call loadSettings", async function () { - const dbSettings = { logLevel: "debug", apiBaseUrl: "http://localhost" }; - const appSettings = { findOne: sinon.stub().returns(dbSettings) }; - const settingsService = new SettingsService(appSettings); - settingsService.settings = {}; - const result = await settingsService.reloadSettings(); - expect(result).to.deep.equal(dbSettings); - }); - }); - - describe("getSettings", function () { - it("should return the current settings", function () { - const dbSettings = { logLevel: "debug", apiBaseUrl: "http://localhost" }; - const appSettings = { findOne: sinon.stub().returns(dbSettings) }; - const settingsService = new SettingsService(appSettings); - settingsService.settings = dbSettings; - const result = settingsService.getSettings(); - expect(result).to.deep.equal(dbSettings); - }); - - it("should throw an error if settings have not been loaded", function () { - const appSettings = { findOne: sinon.stub().returns(null) }; - const settingsService = new SettingsService(appSettings); - settingsService.settings = null; - - try { - settingsService.getSettings(); - } catch (error) { - expect(error.message).to.equal("Settings have not been loaded"); - } - }); - }); -}); diff --git a/server/tests/services/statusService.test.js b/server/tests/services/statusService.test.js deleted file mode 100755 index 289a36884..000000000 --- a/server/tests/services/statusService.test.js +++ /dev/null @@ -1,255 +0,0 @@ -import sinon from "sinon"; -import StatusService from "../../service/statusService.js"; -import { afterEach, describe } from "node:test"; - -describe("StatusService", () => { - let db, logger, statusService; - - beforeEach(function () { - db = { - getMonitorById: sinon.stub(), - createCheck: sinon.stub(), - createPagespeedCheck: sinon.stub(), - }; - logger = { - info: sinon.stub(), - error: sinon.stub(), - }; - statusService = new StatusService(db, logger); - }); - - afterEach(() => { - sinon.restore(); - }); - - describe("constructor", () => { - it("should create an instance of StatusService", function () { - expect(statusService).to.be.an.instanceOf(StatusService); - }); - }); - - describe("getStatusString", () => { - it("should return 'up' if status is true", function () { - expect(statusService.getStatusString(true)).to.equal("up"); - }); - - it("should return 'down' if status is false", function () { - expect(statusService.getStatusString(false)).to.equal("down"); - }); - - it("should return 'unknown' if status is undefined or null", function () { - expect(statusService.getStatusString(undefined)).to.equal("unknown"); - }); - }); - - describe("updateStatus", () => { - beforeEach(function () { - // statusService.insertCheck = sinon.stub().resolves; - }); - - afterEach(() => { - sinon.restore(); - }); - - it("should throw an error if an error occurs", async function () { - const error = new Error("Test error"); - statusService.db.getMonitorById = sinon.stub().throws(error); - try { - await statusService.updateStatus({ monitorId: "test", status: true }); - } catch (error) { - expect(error.message).to.equal("Test error"); - } - // expect(statusService.insertCheck.calledOnce).to.be.true; - }); - - it("should return {statusChanged: false} if status hasn't changed", async function () { - statusService.db.getMonitorById = sinon.stub().returns({ status: true }); - const result = await statusService.updateStatus({ - monitorId: "test", - status: true, - }); - expect(result).to.deep.equal({ statusChanged: false }); - // expect(statusService.insertCheck.calledOnce).to.be.true; - }); - - it("should return {statusChanged: true} if status has changed from down to up", async function () { - statusService.db.getMonitorById = sinon.stub().returns({ status: false, save: sinon.stub() }); - const result = await statusService.updateStatus({ - monitorId: "test", - status: true, - }); - expect(result.statusChanged).to.be.true; - expect(result.monitor.status).to.be.true; - expect(result.prevStatus).to.be.false; - // expect(statusService.insertCheck.calledOnce).to.be.true; - }); - - it("should return {statusChanged: true} if status has changed from up to down", async function () { - statusService.db.getMonitorById = sinon.stub().returns({ status: true, save: sinon.stub() }); - const result = await statusService.updateStatus({ - monitorId: "test", - status: false, - }); - expect(result.statusChanged).to.be.true; - expect(result.monitor.status).to.be.false; - expect(result.prevStatus).to.be.true; - // expect(statusService.insertCheck.calledOnce).to.be.true; - }); - }); - - describe("buildCheck", () => { - it("should build a check object", function () { - const check = statusService.buildCheck({ - monitorId: "test", - type: "test", - status: true, - responseTime: 100, - code: 200, - message: "Test message", - payload: { test: "test" }, - }); - expect(check.monitorId).to.equal("test"); - expect(check.status).to.be.true; - expect(check.statusCode).to.equal(200); - expect(check.responseTime).to.equal(100); - expect(check.message).to.equal("Test message"); - }); - - it("should build a check object for pagespeed type", function () { - const check = statusService.buildCheck({ - monitorId: "test", - type: "pagespeed", - status: true, - responseTime: 100, - code: 200, - message: "Test message", - payload: { - lighthouseResult: { - categories: { - accessibility: { score: 1 }, - "best-practices": { score: 1 }, - performance: { score: 1 }, - seo: { score: 1 }, - }, - audits: { - "cumulative-layout-shift": { score: 1 }, - "speed-index": { score: 1 }, - "first-contentful-paint": { score: 1 }, - "largest-contentful-paint": { score: 1 }, - "total-blocking-time": { score: 1 }, - }, - }, - }, - }); - expect(check.monitorId).to.equal("test"); - expect(check.status).to.be.true; - expect(check.statusCode).to.equal(200); - expect(check.responseTime).to.equal(100); - expect(check.message).to.equal("Test message"); - expect(check.accessibility).to.equal(100); - expect(check.bestPractices).to.equal(100); - expect(check.performance).to.equal(100); - expect(check.seo).to.equal(100); - expect(check.audits).to.deep.equal({ - cls: { score: 1 }, - si: { score: 1 }, - fcp: { score: 1 }, - lcp: { score: 1 }, - tbt: { score: 1 }, - }); - }); - - it("should build a check object for pagespeed type with missing data", function () { - const check = statusService.buildCheck({ - monitorId: "test", - type: "pagespeed", - status: true, - responseTime: 100, - code: 200, - message: "Test message", - payload: { - lighthouseResult: { - categories: {}, - audits: {}, - }, - }, - }); - expect(check.monitorId).to.equal("test"); - expect(check.status).to.be.true; - expect(check.statusCode).to.equal(200); - expect(check.responseTime).to.equal(100); - expect(check.message).to.equal("Test message"); - expect(check.accessibility).to.equal(0); - expect(check.bestPractices).to.equal(0); - expect(check.performance).to.equal(0); - expect(check.seo).to.equal(0); - expect(check.audits).to.deep.equal({ - cls: 0, - si: 0, - fcp: 0, - lcp: 0, - tbt: 0, - }); - }); - - it("should build a check for hardware type", function () { - const check = statusService.buildCheck({ - monitorId: "test", - type: "hardware", - status: true, - responseTime: 100, - code: 200, - message: "Test message", - payload: { data: { cpu: "cpu", memory: "memory", disk: "disk", host: "host" } }, - }); - expect(check.monitorId).to.equal("test"); - expect(check.status).to.be.true; - expect(check.statusCode).to.equal(200); - expect(check.responseTime).to.equal(100); - expect(check.message).to.equal("Test message"); - expect(check.cpu).to.equal("cpu"); - expect(check.memory).to.equal("memory"); - expect(check.disk).to.equal("disk"); - expect(check.host).to.equal("host"); - }); - - it("should build a check for hardware type with missing data", function () { - const check = statusService.buildCheck({ - monitorId: "test", - type: "hardware", - status: true, - responseTime: 100, - code: 200, - message: "Test message", - payload: {}, - }); - expect(check.monitorId).to.equal("test"); - expect(check.status).to.be.true; - expect(check.statusCode).to.equal(200); - expect(check.responseTime).to.equal(100); - expect(check.message).to.equal("Test message"); - expect(check.cpu).to.deep.equal({}); - expect(check.memory).to.deep.equal({}); - expect(check.disk).to.deep.equal({}); - expect(check.host).to.deep.equal({}); - }); - }); - - describe("insertCheck", () => { - it("should log an error if one is thrown", async function () { - const testError = new Error("Test error"); - statusService.db.createCheck = sinon.stub().throws(testError); - try { - await statusService.insertCheck({ monitorId: "test" }); - } catch (error) { - expect(error.message).to.equal(testError.message); - } - expect(statusService.logger.error.calledOnce).to.be.true; - }); - - it("should insert a check into the database", async function () { - await statusService.insertCheck({ monitorId: "test", type: "http" }); - expect(statusService.db.createCheck.calledOnce).to.be.true; - }); - }); -}); diff --git a/server/tests/utils/dataUtils.test.js b/server/tests/utils/dataUtils.test.js deleted file mode 100755 index ec9eabb40..000000000 --- a/server/tests/utils/dataUtils.test.js +++ /dev/null @@ -1,61 +0,0 @@ -import { NormalizeData, calculatePercentile } from "../../utils/dataUtils.js"; -import sinon from "sinon"; - -describe("NormalizeData", function () { - it("should normalize response times when checks length is greater than 1", function () { - const checks = [ - { responseTime: 20, _doc: { id: 1 } }, - { responseTime: 40, _doc: { id: 2 } }, - { responseTime: 60, _doc: { id: 3 } }, - ]; - const rangeMin = 1; - const rangeMax = 100; - - const result = NormalizeData(checks, rangeMin, rangeMax); - - expect(result).to.be.an("array"); - expect(result).to.have.lengthOf(3); - result.forEach((check) => { - expect(check).to.have.property("responseTime").that.is.a("number"); - expect(check).to.have.property("originalResponseTime").that.is.a("number"); - }); - }); - - it("should return checks with original response times when checks length is 1", function () { - const checks = [{ responseTime: 20, _doc: { id: 1 } }]; - const rangeMin = 1; - const rangeMax = 100; - - const result = NormalizeData(checks, rangeMin, rangeMax); - expect(result).to.be.an("array"); - expect(result).to.have.lengthOf(1); - expect(result[0]).to.have.property("originalResponseTime", 20); - }); - - it("should handle edge cases with extreme response times", function () { - const checks = [ - { responseTime: 5, _doc: { id: 1 } }, - { responseTime: 95, _doc: { id: 2 } }, - ]; - const rangeMin = 1; - const rangeMax = 100; - - const result = NormalizeData(checks, rangeMin, rangeMax); - - expect(result).to.be.an("array"); - expect(result).to.have.lengthOf(2); - expect(result[0]).to.have.property("responseTime").that.is.at.least(rangeMin); - expect(result[1]).to.have.property("responseTime").that.is.at.most(rangeMax); - }); -}); - -describe("calculatePercentile", function () { - it("should return the lower value when upper is greater than or equal to the length of the sorted array", function () { - const checks = [{ responseTime: 10 }, { responseTime: 20 }, { responseTime: 30 }, { responseTime: 40 }, { responseTime: 50 }]; - - const percentile = 100; - const result = calculatePercentile(checks, percentile); - const expected = 50; - expect(result).to.equal(expected); - }); -}); diff --git a/server/tests/utils/imageProcessing.test.js b/server/tests/utils/imageProcessing.test.js deleted file mode 100755 index 09330c317..000000000 --- a/server/tests/utils/imageProcessing.test.js +++ /dev/null @@ -1,53 +0,0 @@ -import { expect } from "chai"; -import sinon from "sinon"; -import sharp from "sharp"; -import { GenerateAvatarImage } from "../../utils/imageProcessing.js"; - -describe("imageProcessing - GenerateAvatarImage", function () { - it("should resize the image to 64x64 and return a base64 string", async function () { - const file = { - buffer: Buffer.from("test image buffer"), - }; - - // Stub the sharp function - const toBufferStub = sinon.stub().resolves(Buffer.from("resized image buffer")); - const resizeStub = sinon.stub().returns({ toBuffer: toBufferStub }); - const sharpStub = sinon.stub(sharp.prototype, "resize").returns({ toBuffer: toBufferStub }); - - const result = await GenerateAvatarImage(file); - - // Verify the result - const expected = Buffer.from("resized image buffer").toString("base64"); - expect(result).to.equal(expected); - - // Verify that the sharp function was called with the correct arguments - expect(sharpStub.calledOnceWith({ width: 64, height: 64, fit: "cover" })).to.be.true; - expect(toBufferStub.calledOnce).to.be.true; - - // Restore the stubbed functions - sharpStub.restore(); - }); - - it("should throw an error if resizing fails", async function () { - const file = { - buffer: Buffer.from("test image buffer"), - }; - - // Stub the sharp function to throw an error - const toBufferStub = sinon.stub().rejects(new Error("Resizing failed")); - const resizeStub = sinon.stub().returns({ toBuffer: toBufferStub }); - const sharpStub = sinon.stub(sharp.prototype, "resize").returns({ toBuffer: toBufferStub }); - - try { - await GenerateAvatarImage(file); - // If no error is thrown, fail the test - expect.fail("Expected error to be thrown"); - } catch (error) { - // Verify that the error message is correct - expect(error.message).to.equal("Resizing failed"); - } - - // Restore the stubbed functions - sharpStub.restore(); - }); -}); diff --git a/server/tests/utils/logger.test.js b/server/tests/utils/logger.test.js deleted file mode 100755 index 2d5bd82a9..000000000 --- a/server/tests/utils/logger.test.js +++ /dev/null @@ -1,139 +0,0 @@ -import sinon from "sinon"; -import logger from "../../utils/logger.js"; -import { Logger } from "../../utils/logger.js"; -import winston from "winston"; - -describe("Logger", function () { - let infoStub, warnStub, errorStub; - - beforeEach(function () { - infoStub = sinon.stub(logger.logger, "info"); - warnStub = sinon.stub(logger.logger, "warn"); - errorStub = sinon.stub(logger.logger, "error"); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe("constructor", function () { - let createLoggerStub; - - beforeEach(function () { - createLoggerStub = sinon.stub(winston, "createLogger"); - }); - - afterEach(function () { - sinon.restore(); - }); - - it("should convert message to JSON string if it is an object", function () { - const logMessage = { key: "value" }; - const expectedMessage = JSON.stringify(logMessage, null, 2); - - createLoggerStub.callsFake((config) => { - const consoleTransport = config.transports[0]; - const logEntry = { - level: "info", - message: logMessage, - timestamp: new Date().toISOString(), - }; - const formattedMessage = consoleTransport.format.transform(logEntry); - expect(formattedMessage).to.include(expectedMessage); - return { log: sinon.spy() }; - }); - - const logger = new Logger(); - logger.logger.info(logMessage); - }); - - it("should convert details to JSON string if it is an object", function () { - const logDetails = { key: "value" }; - const expectedDetails = JSON.stringify(logDetails, null, 2); // Removed .s - - createLoggerStub.callsFake((config) => { - const consoleTransport = config.transports[0]; - const logEntry = { - level: "info", - message: "", // Add empty message since it's required - details: logDetails, - timestamp: new Date().toISOString(), - }; - const formattedMessage = consoleTransport.format.transform(logEntry); - expect(formattedMessage).to.include(expectedDetails); - return { info: sinon.spy() }; // Changed to return info method - }); - - const logger = new Logger(); - logger.logger.info("", { details: logDetails }); // Updated to pass details properly - }); - }); - - describe("info", function () { - it("should log an informational message", function () { - const config = { - message: "Info message", - service: "TestService", - method: "TestMethod", - details: { key: "value" }, - }; - - logger.info(config); - - expect(infoStub.calledOnce).to.be.true; - expect( - infoStub.calledWith(config.message, { - service: config.service, - method: config.method, - details: config.details, - }) - ).to.be.true; - }); - }); - - describe("warn", function () { - it("should log a warning message", function () { - const config = { - message: "Warning message", - service: "TestService", - method: "TestMethod", - details: { key: "value" }, - }; - - logger.warn(config); - - expect(warnStub.calledOnce).to.be.true; - expect( - warnStub.calledWith(config.message, { - service: config.service, - method: config.method, - details: config.details, - }) - ).to.be.true; - }); - }); - - describe("error", function () { - it("should log an error message", function () { - const config = { - message: "Error message", - service: "TestService", - method: "TestMethod", - details: { key: "value" }, - stack: "Error stack trace", - }; - - logger.error(config); - - expect(errorStub.calledOnce).to.be.true; - expect( - errorStub.calledWith(config.message, { - service: config.service, - method: config.method, - details: config.details, - stack: config.stack, - }) - ).to.be.true; - }); - }); -}); diff --git a/server/tests/utils/messages.test.js b/server/tests/utils/messages.test.js deleted file mode 100755 index a7e98c477..000000000 --- a/server/tests/utils/messages.test.js +++ /dev/null @@ -1,23 +0,0 @@ -import { errorMessages, successMessages } from "../../utils/messages.js"; -describe("Messages", function () { - describe("messages - errorMessages", function () { - it("should have a DB_FIND_MONITOR_BY_ID function", function () { - const monitorId = "12345"; - expect(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)).to.equal(`Monitor with id ${monitorId} not found`); - }); - - it("should have a DB_DELETE_CHECKS function", function () { - const monitorId = "12345"; - expect(errorMessages.DB_DELETE_CHECKS(monitorId)).to.equal(`No checks found for monitor with id ${monitorId}`); - }); - }); - - describe("messages - successMessages", function () { - it("should have a MONITOR_GET_BY_USER_ID function", function () { - const userId = "12345"; - expect(successMessages.MONITOR_GET_BY_USER_ID(userId)).to.equal(`Got monitor for ${userId} successfully"`); - }); - - // Add more tests for other success messages as needed - }); -}); diff --git a/server/tests/utils/utils.test.js b/server/tests/utils/utils.test.js deleted file mode 100755 index 2630bb8e6..000000000 --- a/server/tests/utils/utils.test.js +++ /dev/null @@ -1,50 +0,0 @@ -import { ParseBoolean, getTokenFromHeaders } from "../../utils/utils.js"; - -describe("utils - ParseBoolean", function () { - it("should return true", function () { - const result = ParseBoolean("true"); - expect(result).to.be.true; - }); - - it("should return false", function () { - const result = ParseBoolean("false"); - expect(result).to.be.false; - }); - - it("should return false", function () { - const result = ParseBoolean(null); - expect(result).to.be.false; - }); - - it("should return false", function () { - const result = ParseBoolean(undefined); - expect(result).to.be.false; - }); -}); - -describe("utils - getTokenFromHeaders", function () { - it("should throw an error if authorization header is missing", function () { - const headers = {}; - expect(() => getTokenFromHeaders(headers)).to.throw("No auth headers"); - }); - - it("should throw an error if authorization header does not start with Bearer", function () { - const headers = { authorization: "Basic abcdef" }; - expect(() => getTokenFromHeaders(headers)).to.throw("Invalid auth headers"); - }); - - it("should return the token if authorization header is correctly formatted", function () { - const headers = { authorization: "Bearer abcdef" }; - expect(getTokenFromHeaders(headers)).to.equal("abcdef"); - }); - - it("should throw an error if authorization header has more than two parts", function () { - const headers = { authorization: "Bearer abc def" }; - expect(() => getTokenFromHeaders(headers)).to.throw("Invalid auth headers"); - }); - - it("should throw an error if authorization header has less than two parts", function () { - const headers = { authorization: "Bearer" }; - expect(() => getTokenFromHeaders(headers)).to.throw("Invalid auth headers"); - }); -}); diff --git a/server/tsconfig.json b/server/tsconfig.json new file mode 100644 index 000000000..3aa773ab1 --- /dev/null +++ b/server/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "module": "nodenext", + "target": "esnext", + "types": ["node"], + "sourceMap": true, + "declaration": false, + "declarationMap": false, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "strict": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "checkJs": false + }, + "exclude": ["node_modules", "dist", "**/*.config.js"] +}