add gitignore

This commit is contained in:
Alex Holliday
2025-09-22 12:53:44 -07:00
parent e4e75632c5
commit d8ed9abebf
49 changed files with 590 additions and 9780 deletions

3
.gitignore vendored
View File

@@ -1 +1,2 @@
.vscode
.vscode
.VSCodeCounter

3
server/.gitignore vendored
View File

@@ -8,4 +8,5 @@ coverage
.clinic
node_modules
.vscode/*
public
public
dist

530
server/package-lock.json generated
View File

@@ -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",

View File

@@ -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"
}
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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();

View File

@@ -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
});

View File

@@ -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();

View File

@@ -1,5 +1,5 @@
import { Router } from "express";
import { isAllowed } from "../middleware/isAllowed.js";
import { isAllowed } from "../../middleware/isAllowed.js";
class SettingsRoutes {
constructor(settingsController) {

View File

@@ -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();

View File

@@ -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;
});
});

View File

@@ -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;
});
});

View File

@@ -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");
});
});
});

View File

@@ -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;
});
});

View File

@@ -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;
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -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,
});
});
});

View File

@@ -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,
});
});
});

View File

@@ -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);
});
});
});

View File

@@ -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);
}
});
});
});

View File

@@ -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;
});
});
});

View File

@@ -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");
}
});
});
});

View File

@@ -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);
}
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
});
});
});

View File

@@ -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);
}
});
});
});

View File

@@ -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);
});
});
});

View File

@@ -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);
}
});
});
});

View File

@@ -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);
});
});
});

View File

@@ -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);
}
});
});
});

View File

@@ -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("<mjml><body></body></mjml>"),
};
pathMock = {
join: sinon.stub().callsFake((...args) => args.join("/")),
};
compileMock = sinon.stub().returns(() => "<mjml><body></body></mjml>");
mjml2htmlMock = sinon.stub().returns({ html: "<html></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("<mjml><body></body></mjml>"),
};
pathMock = {
join: sinon.stub().callsFake((...args) => args.join("/")),
};
compileMock = sinon.stub().returns(() => "<mjml><body></body></mjml>");
mjml2htmlMock = sinon.stub().returns({ html: "<html></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 () {});
});

View File

@@ -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");
}
});
});
});

View File

@@ -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");
}
});
});
});

View File

@@ -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;
});
});
});
});

View File

@@ -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");
}
});
});
});

View File

@@ -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;
});
});
});

View File

@@ -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);
});
});

View File

@@ -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();
});
});

View File

@@ -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;
});
});
});

View File

@@ -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
});
});

View File

@@ -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");
});
});

26
server/tsconfig.json Normal file
View File

@@ -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"]
}