diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index 638c1fc02..3aeb21fa0 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -86,7 +86,7 @@ const getMonitorCertificate = async (req, res, next) => { const monitorUrl = new URL(monitor.url); const certificate = await sslChecker(monitorUrl.hostname); if (certificate && certificate.validTo) { - return res.json({ + return res.status(200).json({ success: true, msg: successMessages.MONITOR_CERTIFICATE, data: { @@ -94,7 +94,7 @@ const getMonitorCertificate = async (req, res, next) => { }, }); } else { - return res.json({ + return res.status(200).json({ success: true, msg: successMessages.MONITOR_CERTIFICATE, data: { certificateDate: "N/A" }, diff --git a/Server/package-lock.json b/Server/package-lock.json index bdd7db873..09793bb98 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -36,6 +36,7 @@ "devDependencies": { "nodemon": "3.1.0", "nyc": "17.1.0", + "proxyquire": "2.1.3", "sinon": "19.0.2" } }, @@ -2802,6 +2803,20 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, + "node_modules/fill-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", + "integrity": "sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-object": "~1.0.1", + "merge-descriptors": "~1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3535,6 +3550,22 @@ "node": ">=8" } }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3575,6 +3606,16 @@ "node": ">=0.12.0" } }, + "node_modules/is-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", + "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -4968,6 +5009,13 @@ "node": ">=10" } }, + "node_modules/module-not-found-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", + "integrity": "sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g==", + "dev": true, + "license": "MIT" + }, "node_modules/mongodb": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz", @@ -5833,6 +5881,13 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -6545,6 +6600,18 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/proxyquire": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.3.tgz", + "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-keys": "^1.0.2", + "module-not-found-error": "^1.0.1", + "resolve": "^1.11.1" + } + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -6683,6 +6750,24 @@ "dev": true, "license": "ISC" }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -7242,6 +7327,19 @@ "node": ">=4" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/svgo": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", diff --git a/Server/package.json b/Server/package.json index 0ada3d893..cb6997132 100644 --- a/Server/package.json +++ b/Server/package.json @@ -38,6 +38,7 @@ "devDependencies": { "nodemon": "3.1.0", "nyc": "17.1.0", + "proxyquire": "2.1.3", "sinon": "19.0.2" } } diff --git a/Server/tests/controllers/monitorController.test.js b/Server/tests/controllers/monitorController.test.js index 7fc950176..242a52523 100644 --- a/Server/tests/controllers/monitorController.test.js +++ b/Server/tests/controllers/monitorController.test.js @@ -13,8 +13,14 @@ const { addDemoMonitors, } = require("../../controllers/monitorController"); const jwt = require("jsonwebtoken"); -const { errorMessages, successMessages } = require("../../utils/messages"); +const proxyquire = require("proxyquire"); const sinon = require("sinon"); +const { errorMessages, successMessages } = require("../../utils/messages"); +const { valid } = require("joi"); +const sslCheckerStub = sinon.stub(); +const monitorController = proxyquire("../../controllers/monitorController", { + "ssl-checker": sslCheckerStub, +}); describe("Monitor Controller - getAllMonitors", () => { beforeEach(() => { @@ -111,3 +117,93 @@ describe("Monitor Controller - getMonitorStatsById", () => { ).to.be.true; }); }); + +describe("Monitor Controller - getMonitorCertificate", () => { + beforeEach(() => { + req = { + params: { + monitorId: "123", + }, + query: {}, + body: {}, + db: { + getMonitorById: sinon.stub(), + }, + }; + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub(), + }; + next = sinon.stub(); + }); + afterEach(() => { + sinon.restore(); + }); + + it("should reject with an error if param validation fails", async () => { + req.params = {}; + await getMonitorCertificate(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].status).to.equal(422); + }); + it("should reject with an error if creating a URL fails", async () => { + req.db.getMonitorById.returns(null); + await getMonitorCertificate(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0]).to.be.instanceOf(TypeError); + }); + it("should reject with an error if SSL checker fails", async () => { + req.db.getMonitorById.returns({ url: "https://example.com" }); + sslCheckerStub.throws(new Error("SSL checker error")); + await monitorController.getMonitorCertificate(req, res, next); + expect(next.firstCall.args[0]).to.be.an("error"); + expect(next.firstCall.args[0].message).to.equal("SSL checker error"); + }); + it("should return success message and data if all operations succeed", async () => { + req.db.getMonitorById.returns({ url: "https://example.com" }); + const certificate = { validTo: 1 }; + sslCheckerStub.returns(certificate); + await monitorController.getMonitorCertificate(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_CERTIFICATE, + data: { + certificateDate: new Date(certificate.validTo).toLocaleDateString(), + }, + }) + ).to.be.true; + }); + it("should return success message and N/A if certificate is not found", async () => { + req.db.getMonitorById.returns({ url: "https://example.com" }); + sslCheckerStub.returns(null); + await monitorController.getMonitorCertificate(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_CERTIFICATE, + data: { + certificateDate: "N/A", + }, + }) + ).to.be.true; + }); + + it("should return success message and N/A if certificate doesn't have validTo", async () => { + req.db.getMonitorById.returns({ url: "https://example.com" }); + sslCheckerStub.returns({ validFrom: 1 }); + await monitorController.getMonitorCertificate(req, res, next); + expect(res.status.firstCall.args[0]).to.equal(200); + expect( + res.json.calledOnceWith({ + success: true, + msg: successMessages.MONITOR_CERTIFICATE, + data: { + certificateDate: "N/A", + }, + }) + ).to.be.true; + }); +});