mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2025-12-31 06:39:35 -06:00
add gitignore
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
.vscode
|
||||
.vscode
|
||||
.VSCodeCounter
|
||||
3
server/.gitignore
vendored
3
server/.gitignore
vendored
@@ -8,4 +8,5 @@ coverage
|
||||
.clinic
|
||||
node_modules
|
||||
.vscode/*
|
||||
public
|
||||
public
|
||||
dist
|
||||
530
server/package-lock.json
generated
530
server/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
@@ -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) {
|
||||
@@ -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) {
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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
|
||||
});
|
||||
@@ -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();
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Router } from "express";
|
||||
import { isAllowed } from "../middleware/isAllowed.js";
|
||||
import { isAllowed } from "../../middleware/isAllowed.js";
|
||||
|
||||
class SettingsRoutes {
|
||||
constructor(settingsController) {
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
@@ -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
@@ -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,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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 () {});
|
||||
});
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
});
|
||||
@@ -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
26
server/tsconfig.json
Normal 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"]
|
||||
}
|
||||
Reference in New Issue
Block a user