Merge pull request #983 from bluewave-labs/feat/be/network-service-tests

Feat/be/network service tests
This commit is contained in:
Alexander Holliday authored and GitHub committed 2024-10-19 16:36:36 +08:00
commit abfed1ca72
4 files changed
+809 -31

No files matched your search

+6 -1
View File
@@ -20,7 +20,12 @@ import { fileURLToPath } from "url";
import { connectDbAndRunServer } from "./configs/db.js";
import queueRouter from "./routes/queueRoute.js";
import JobQueue from "./service/jobQueue.js";
//Network service and dependencies
import NetworkService from "./service/networkService.js";
import axios from "axios";
import ping from "ping";
import http from "http";
// Email service and dependencies
import EmailService from "./service/emailService.js";
@@ -149,7 +154,7 @@ const startApp = async () => {
nodemailer,
logger
);
const networkService = new NetworkService(db, emailService);
const networkService = new NetworkService(db, emailService, axios, ping, logger, http);
const jobQueue = await JobQueue.createJobQueue(db, networkService, settingsService);
const cleanup = async () => {
+1 -1
View File
@@ -98,7 +98,7 @@ class JobQueue {
}, false);
if (!maintenanceWindowActive) {
const res = await this.networkService.getStatus(job);
await this.networkService.getStatus(job);
} else {
logger.info(`Monitor ${monitorId} is in maintenance window`, {
service: SERVICE_NAME,
+52 -29
View File
@@ -1,7 +1,3 @@
import axios from "axios";
import ping from "ping";
import logger from "../utils/logger.js";
import http from "http";
import { errorMessages, successMessages } from "../utils/messages.js";
/**
@@ -12,7 +8,17 @@ import { errorMessages, successMessages } from "../utils/messages.js";
*/
class NetworkService {
constructor(db, emailService) {
/**
* Creates an instance of NetworkService.
*
* @param {Object} db - The database service.
* @param {Object} emailService - The email service.
* @param {Object} axios - The axios HTTP client.
* @param {Object} ping - The ping service.
* @param {Object} logger - The logging service.
* @param {Object} http - The HTTP service.
*/
constructor(db, emailService, axios, ping, logger, http) {
this.db = db;
this.emailService = emailService;
this.TYPE_PING = "ping";
@@ -20,9 +26,19 @@ class NetworkService {
this.TYPE_PAGESPEED = "pagespeed";
this.SERVICE_NAME = "NetworkService";
this.NETWORK_ERROR = 5000;
this.axios = axios;
this.ping = ping;
this.logger = logger;
this.http = http;
}
async handleNotification(monitor, isAlive) {
/**
* Handles the notification process for a monitor.
*
* @param {Object} monitor - The monitor object containing monitor details.
* @param {boolean} isAlive - The status of the monitor (true if up, false if down).
* @returns {Promise<void>}
*/ async handleNotification(monitor, isAlive) {
try {
let template = isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate";
let status = isAlive === true ? "up" : "down";
@@ -39,7 +55,7 @@ class NetworkService {
}
}
} catch (error) {
logger.error(error.message, {
this.logger.error(error.message, {
method: "handleNotification",
service: this.SERVICE_NAME,
monitorId: monitor._id,
@@ -47,11 +63,19 @@ class NetworkService {
}
}
/**
* Handles the status update for a monitor job.
*
* @param {Object} job - The job object containing job details.
* @param {boolean} isAlive - The status of the monitor (true if up, false if down).
* @returns {Promise<void>}
*/
async handleStatusUpdate(job, isAlive) {
let monitor;
const { _id } = job.data;
// Look up the monitor, if it doesn't exist, it's probably been removed, return
try {
const { _id } = job.data;
monitor = await this.db.getMonitorById(_id);
} catch (error) {
return;
@@ -60,7 +84,7 @@ class NetworkService {
// Otherwise, try to update monitor status
try {
if (monitor === null || monitor === undefined) {
logger.error(`Null Monitor: ${_id}`, {
this.logger.error(`Null Monitor: ${_id}`, {
method: "handleStatusUpdate",
service: this.SERVICE_NAME,
jobId: job.id,
@@ -77,7 +101,7 @@ class NetworkService {
}
}
} catch (error) {
logger.error(error.message, {
this.logger.error(error.message, {
method: "handleStatusUpdate",
service: this.SERVICE_NAME,
jobId: job.id,
@@ -100,7 +124,7 @@ class NetworkService {
} catch (error) {
const endTime = Date.now();
error.responseTime = endTime - startTime;
error.service === undefined ? (error.service = SERVICE_NAME) : null;
error.service === undefined ? (error.service = this.SERVICE_NAME) : null;
error.method === undefined ? (error.method = "measureResponseTime") : null;
throw error;
}
@@ -112,13 +136,13 @@ class NetworkService {
* @returns {Promise<{boolean}} The result of logging and storing the check
*/
async handlePing(job) {
let isAlive;
const operation = async () => {
const response = await ping.promise.probe(job.data.url);
const response = await this.ping.promise.probe(job.data.url);
return response;
};
let isAlive;
try {
const { responseTime, response } = await this.measureResponseTime(operation);
isAlive = response.alive;
@@ -130,7 +154,7 @@ class NetworkService {
? successMessages.PING_SUCCESS
: errorMessages.PING_CANNOT_RESOLVE,
};
return await this.logAndStoreCheck(checkData, this.db.createCheck);
await this.logAndStoreCheck(checkData, this.db.createCheck);
} catch (error) {
isAlive = false;
const checkData = {
@@ -139,7 +163,7 @@ class NetworkService {
message: errorMessages.PING_CANNOT_RESOLVE,
responseTime: error.responseTime,
};
return await this.logAndStoreCheck(checkData, this.db.createCheck);
await this.logAndStoreCheck(checkData, this.db.createCheck);
} finally {
this.handleStatusUpdate(job, isAlive);
}
@@ -153,7 +177,7 @@ class NetworkService {
async handleHttp(job) {
// Define operation for timing
const operation = async () => {
const response = await axios.get(job.data.url);
const response = await this.axios.get(job.data.url);
return response;
};
@@ -171,12 +195,12 @@ class NetworkService {
status: isAlive,
responseTime,
statusCode: response.status,
message: http.STATUS_CODES[response.status],
message: this.http.STATUS_CODES[response.status],
};
return await this.logAndStoreCheck(checkData, this.db.createCheck);
await this.logAndStoreCheck(checkData, this.db.createCheck);
} catch (error) {
const statusCode = error.response?.status || this.NETWORK_ERROR;
let message = http.STATUS_CODES[statusCode] || "Network Error";
let message = this.http.STATUS_CODES[statusCode] || "Network Error";
isAlive = false;
const checkData = {
monitorId: job.data._id,
@@ -185,8 +209,7 @@ class NetworkService {
responseTime: error.responseTime,
message,
};
return await this.logAndStoreCheck(checkData, this.db.createCheck);
await this.logAndStoreCheck(checkData, this.db.createCheck);
} finally {
this.handleStatusUpdate(job, isAlive);
}
@@ -210,7 +233,8 @@ class NetworkService {
let isAlive;
try {
const url = job.data.url;
const response = await axios.get(
const response = await this.axios.get(
`https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&category=seo&category=accessibility&category=best-practices&category=performance`
);
const pageSpeedResults = response.data;
@@ -223,7 +247,6 @@ class NetworkService {
"largest-contentful-paint": lcp,
"total-blocking-time": tbt,
} = audits;
// Weights
// First Contentful Paint 10%
// Speed Index 10%
@@ -236,7 +259,7 @@ class NetworkService {
monitorId: job.data._id,
status: isAlive,
statusCode: response.status,
message: http.STATUS_CODES[response.status],
message: this.http.STATUS_CODES[response.status],
accessibility: (categories.accessibility?.score || 0) * 100,
bestPractices: (categories["best-practices"]?.score || 0) * 100,
seo: (categories.seo?.score || 0) * 100,
@@ -249,12 +272,11 @@ class NetworkService {
tbt,
},
};
this.logAndStoreCheck(checkData, this.db.createPageSpeedCheck);
} catch (error) {
isAlive = false;
const statusCode = error.response?.status || this.NETWORK_ERROR;
const message = http.STATUS_CODES[statusCode] || "Network Error";
const message = this.http.STATUS_CODES[statusCode] || "Network Error";
const checkData = {
monitorId: job.data._id,
status: isAlive,
@@ -287,7 +309,7 @@ class NetworkService {
case this.TYPE_PAGESPEED:
return await this.handlePagespeed(job);
default:
logger.error(`Unsupported type: ${job.data.type}`, {
this.logger.error(`Unsupported type: ${job.data.type}`, {
service: this.SERVICE_NAME,
method: "getStatus",
jobId: job.id,
@@ -311,8 +333,9 @@ class NetworkService {
if (insertedCheck !== null && insertedCheck !== undefined) {
return insertedCheck.status;
}
throw new Error();
} catch (error) {
logger.error(`Error wrtiting check for ${data.monitorId}`, {
this.logger.error(`Error writing check for ${data.monitorId}`, {
service: this.SERVICE_NAME,
method: "logAndStoreCheck",
monitorId: data.monitorId,
@@ -0,0 +1,750 @@
import sinon from "sinon";
import NetworkService from "../../service/networkService.js";
import { errorMessages, successMessages } from "../../utils/messages.js";
import exp from "constants";
describe("NetworkService - Constructor", function () {
let dbMock, emailServiceMock, axiosMock, pingMock, loggerMock, httpMock;
beforeEach(function () {
dbMock = sinon.stub();
emailServiceMock = sinon.stub();
axiosMock = sinon.stub();
pingMock = sinon.stub();
loggerMock = sinon.stub();
httpMock = sinon.stub();
});
it("should correctly initialize properties", function () {
const networkService = new NetworkService(
dbMock,
emailServiceMock,
axiosMock,
pingMock,
loggerMock,
httpMock
);
expect(networkService.db).to.equal(dbMock);
expect(networkService.emailService).to.equal(emailServiceMock);
expect(networkService.TYPE_PING).to.equal("ping");
expect(networkService.TYPE_HTTP).to.equal("http");
expect(networkService.TYPE_PAGESPEED).to.equal("pagespeed");
expect(networkService.SERVICE_NAME).to.equal("NetworkService");
expect(networkService.NETWORK_ERROR).to.equal(5000);
expect(networkService.axios).to.equal(axiosMock);
expect(networkService.ping).to.equal(pingMock);
expect(networkService.logger).to.equal(loggerMock);
expect(networkService.http).to.equal(httpMock);
});
});
describe("NetworkService - handleNotification", () => {
let dbMock, emailServiceMock, loggerMock, networkService;
beforeEach(function () {
dbMock = {
getNotificationsByMonitorId: sinon.stub(),
};
emailServiceMock = {
buildAndSendEmail: sinon.stub().resolves(),
};
loggerMock = {
error: sinon.stub(),
};
networkService = new NetworkService(
dbMock,
emailServiceMock,
null,
null,
loggerMock,
null
);
});
it("should send email notifications when monitor is down", async function () {
const monitor = { _id: "monitor1", name: "Test Monitor", url: "http://test.com" };
const notifications = [{ type: "email", address: "test@example.com" }];
dbMock.getNotificationsByMonitorId.resolves(notifications);
await networkService.handleNotification(monitor, false);
expect(emailServiceMock.buildAndSendEmail.calledOnce).to.be.true;
expect(
emailServiceMock.buildAndSendEmail.calledWith(
"serverIsDownTemplate",
{ monitorName: monitor.name, monitorUrl: monitor.url },
"test@example.com",
"Monitor Test Monitor is down"
)
).to.be.true;
});
it("should send email notifications when monitor is up", async function () {
const monitor = { _id: "monitor1", name: "Test Monitor", url: "http://test.com" };
const notifications = [{ type: "email", address: "test@example.com" }];
dbMock.getNotificationsByMonitorId.resolves(notifications);
await networkService.handleNotification(monitor, true);
expect(emailServiceMock.buildAndSendEmail.calledOnce).to.be.true;
expect(
emailServiceMock.buildAndSendEmail.calledWith(
"serverIsUpTemplate",
{ monitorName: monitor.name, monitorUrl: monitor.url },
"test@example.com",
"Monitor Test Monitor is up"
)
).to.be.true;
});
it("should log an error if an exception is thrown", async function () {
const monitor = { _id: "monitor1", name: "Test Monitor", url: "http://test.com" };
dbMock.getNotificationsByMonitorId.rejects(new Error("Database error"));
await networkService.handleNotification(monitor, true);
expect(loggerMock.error.calledOnce).to.be.true;
expect(
loggerMock.error.calledWith("Database error", {
method: "handleNotification",
service: networkService.SERVICE_NAME,
monitorId: monitor._id,
})
).to.be.true;
});
});
describe("NetworkService - handleStatusUpdate", function () {
let dbMock,
emailServiceMock,
axiosMock,
pingMock,
loggerMock,
httpMock,
networkService,
monitorMock;
beforeEach(function () {
dbMock = { getMonitorById: sinon.stub() };
emailServiceMock = sinon.stub();
axiosMock = sinon.stub();
pingMock = sinon.stub();
loggerMock = { error: sinon.stub() };
httpMock = sinon.stub();
networkService = new NetworkService(
dbMock,
emailServiceMock,
axiosMock,
pingMock,
loggerMock,
httpMock
);
monitorMock = {
status: sinon.stub(),
save: sinon.stub(),
};
});
afterEach(function () {
sinon.restore();
});
it("should return if getMonitorById throws an error", async function () {
dbMock.getMonitorById.throws(new Error("Database error"));
const res = await networkService.handleStatusUpdate(
{ data: { _id: "monitor1" } },
true
);
expect(res).to.be.undefined;
});
it("should log an error if monitor is null", async function () {
dbMock.getMonitorById.resolves(null);
await networkService.handleStatusUpdate({ data: { _id: "monitor1" } }, true);
expect(loggerMock.error.calledOnce).to.be.true;
expect(loggerMock.error.calledWith("Null Monitor: monitor1")).to.be.true;
});
it("should update monitor status if different", async function () {
monitorMock.status = false;
dbMock.getMonitorById.resolves(monitorMock);
await networkService.handleStatusUpdate({ data: { _id: "monitor1" } }, true);
expect(monitorMock.save.calledOnce).to.be.true;
expect(monitorMock.status).to.be.true;
});
it("should not update monitor status if same", async function () {
monitorMock.status = true;
dbMock.getMonitorById.resolves(monitorMock);
await networkService.handleStatusUpdate({ data: { _id: "monitor1" } }, true);
expect(monitorMock.save.called).to.be.false;
});
it("should log an error if exception is thrown during status update", async function () {
monitorMock.status = false;
dbMock.getMonitorById.resolves(monitorMock);
monitorMock.save.rejects(new Error("Save error"));
await networkService.handleStatusUpdate({ data: { _id: "monitor1" } }, true);
expect(loggerMock.error.calledOnce).to.be.true;
expect(loggerMock.error.calledWith("Save error")).to.be.true;
});
});
describe("NetworkService - measureResponseTime", () => {
let mockOperation,
dbMock,
emailServiceMock,
axiosMock,
pingMock,
loggerMock,
httpMock,
networkService;
beforeEach(() => {
mockOperation = sinon.stub();
dbMock = { getMonitorById: sinon.stub() };
emailServiceMock = sinon.stub();
axiosMock = sinon.stub();
pingMock = sinon.stub();
loggerMock = { error: sinon.stub() };
httpMock = sinon.stub();
networkService = new NetworkService(
dbMock,
emailServiceMock,
axiosMock,
pingMock,
loggerMock,
httpMock
);
});
afterEach(() => {
sinon.restore();
});
it("should return response time and response on successful operation", async () => {
const fakeResponse = { data: "test" };
mockOperation.resolves(fakeResponse);
const result = await networkService.measureResponseTime(mockOperation);
expect(result).to.have.property("responseTime").that.is.a("number");
expect(result).to.have.property("response").that.deep.equals(fakeResponse);
});
it("should throw an error with response time and additional properties on operation failure", async () => {
const fakeError = new Error("Operation failed");
mockOperation.rejects(fakeError);
try {
await networkService.measureResponseTime(mockOperation);
} catch (error) {
expect(error).to.have.property("responseTime").that.is.a("number");
expect(error).to.have.property("service", networkService.SERVICE_NAME);
expect(error).to.have.property("method", "measureResponseTime");
expect(error.message).to.equal("Operation failed");
}
});
it("should throw an error with response time set properties on operation failure", async () => {
const fakeError = new Error("Operation failed");
fakeError.service = "test";
fakeError.method = "testMethod";
mockOperation.rejects(fakeError);
try {
await networkService.measureResponseTime(mockOperation);
} catch (error) {
expect(error).to.have.property("responseTime").that.is.a("number");
expect(error).to.have.property("service", "test");
expect(error).to.have.property("method", "testMethod");
expect(error.message).to.equal("Operation failed");
}
});
});
describe("networkService - handlePing", () => {
let job,
dbMock,
pingMock,
loggerMock,
httpMock,
networkService,
logAndStoreCheckStub,
handleStatusUpdateStub;
beforeEach(function () {
job = {
data: {
url: "http://example.com",
_id: "12345",
},
};
dbMock = { getMonitorById: sinon.stub() };
pingMock = { promise: { probe: sinon.stub() } };
loggerMock = { error: sinon.stub() };
httpMock = sinon.stub();
networkService = new NetworkService(
dbMock,
null,
null,
pingMock,
loggerMock,
httpMock
);
logAndStoreCheckStub = sinon.stub(networkService, "logAndStoreCheck");
handleStatusUpdateStub = sinon.stub(networkService, "handleStatusUpdate");
});
afterEach(function () {
sinon.restore();
});
it("should handle a successful ping response", async function () {
const response = { alive: true };
const responseTime = 0;
pingMock.promise.probe.resolves(response);
logAndStoreCheckStub.resolves();
await networkService.handlePing(job);
expect(
logAndStoreCheckStub.calledOnceWith(
{
monitorId: job.data._id,
status: true,
responseTime,
message: successMessages.PING_SUCCESS,
},
networkService.db.createCheck
)
).to.be.true;
expect(handleStatusUpdateStub.calledOnceWith(job, true)).to.be.true;
});
it("should handle a successful ping response and isAlive === false", async function () {
const response = { alive: false };
const responseTime = 0;
pingMock.promise.probe.resolves(response);
logAndStoreCheckStub.resolves();
await networkService.handlePing(job);
console.log(logAndStoreCheckStub.getCall(0).args[0]);
expect(
logAndStoreCheckStub.calledOnceWith(
{
monitorId: job.data._id,
status: false,
responseTime,
message: errorMessages.PING_CANNOT_RESOLVE,
},
networkService.db.createCheck
)
).to.be.true;
expect(handleStatusUpdateStub.calledOnceWith(job, false)).to.be.true;
});
it("should handle a failed ping response", async function () {
const error = new Error("Ping failed");
error.responseTime = 0;
pingMock.promise.probe.rejects(error);
await networkService.handlePing(job);
expect(handleStatusUpdateStub.calledOnceWith(job, false)).to.be.true;
});
});
describe("NetworkService - handleHttp", () => {
let networkService,
axiosMock,
dbMock,
httpMock,
jobMock,
loggerMock,
logAndStoreCheckStub,
handleStatusUpdateStub;
beforeEach(() => {
axiosMock = {
get: sinon.stub(),
};
dbMock = {
createCheck: sinon.stub().resolves(),
};
httpMock = {
STATUS_CODES: {
200: "OK",
500: "Internal Server Error",
},
};
loggerMock = {
error: sinon.stub(),
};
networkService = new NetworkService(
dbMock,
null,
axiosMock,
null,
loggerMock,
httpMock
);
jobMock = {
data: {
url: "http://example.com",
_id: "monitorId123",
},
};
sinon.stub(networkService, "measureResponseTime").callsFake(async (operation) => {
const response = await operation();
return { responseTime: 100, response };
});
logAndStoreCheckStub = sinon.stub(networkService, "logAndStoreCheck").resolves();
handleStatusUpdateStub = sinon.stub(networkService, "handleStatusUpdate").resolves();
});
afterEach(() => {
sinon.restore();
});
it("should handle a successful HTTP response", async () => {
const responseMock = { status: 200 };
axiosMock.get.resolves(responseMock);
await networkService.handleHttp(jobMock);
expect(networkService.logAndStoreCheck.calledOnce).to.be.true;
const checkData = networkService.logAndStoreCheck.getCall(0).args[0];
expect(checkData).to.include({
monitorId: jobMock.data._id,
status: true,
statusCode: 200,
message: "OK",
});
expect(networkService.handleStatusUpdate.calledOnceWith(jobMock, true)).to.be.true;
});
it("should handle an HTTP error response", async () => {
const errorMock = { response: { status: 500 }, responseTime: 200 };
axiosMock.get.rejects(errorMock);
await networkService.handleHttp(jobMock);
expect(networkService.logAndStoreCheck.calledOnce).to.be.true;
const checkData = networkService.logAndStoreCheck.getCall(0).args[0];
expect(checkData).to.include({
monitorId: jobMock.data._id,
status: false,
statusCode: 500,
message: "Internal Server Error",
});
expect(networkService.handleStatusUpdate.calledOnceWith(jobMock, false)).to.be.true;
});
it("should handle an HTTP error response with undefined status", async () => {
const errorMock = { response: { status: undefined }, responseTime: 200 };
axiosMock.get.rejects(errorMock);
await networkService.handleHttp(jobMock);
expect(networkService.logAndStoreCheck.calledOnce).to.be.true;
const checkData = networkService.logAndStoreCheck.getCall(0).args[0];
expect(checkData).to.include({
monitorId: jobMock.data._id,
status: false,
statusCode: 5000,
message: "Network Error",
});
expect(networkService.handleStatusUpdate.calledOnceWith(jobMock, false)).to.be.true;
});
});
describe("networkService - handlePagespeed", () => {
let dbMock,
axiosMock,
jobMock,
emailServiceMock,
pingMock,
loggerMock,
httpMock,
networkService,
logAndStoreCheckStub,
handleStatusUpdateStub;
beforeEach(() => {
jobMock = {
data: {
_id: "12345",
url: "http://example.com",
},
};
dbMock = { getMonitorById: sinon.stub() };
axiosMock = { get: sinon.stub() };
emailServiceMock = sinon.stub();
pingMock = { promise: { probe: sinon.stub() } };
loggerMock = { error: sinon.stub() };
httpMock = {
STATUS_CODES: {
200: "OK",
500: "Internal Server Error",
},
};
networkService = new NetworkService(
dbMock,
emailServiceMock,
axiosMock,
pingMock,
loggerMock,
httpMock
);
logAndStoreCheckStub = sinon.stub(networkService, "logAndStoreCheck").resolves();
handleStatusUpdateStub = sinon.stub(networkService, "handleStatusUpdate").resolves();
});
afterEach(() => {
sinon.restore();
});
it("should handle a successful PageSpeed response", async () => {
const responseMock = {
status: 200,
data: {
lighthouseResult: {
categories: {
accessibility: { score: 0.9 },
"best-practices": { score: 0.8 },
seo: { score: 0.7 },
performance: { score: 0.6 },
},
audits: {
"cumulative-layout-shift": { score: 0.1 },
"speed-index": { score: 0.2 },
"first-contentful-paint": { score: 0.3 },
"largest-contentful-paint": { score: 0.4 },
"total-blocking-time": { score: 0.5 },
},
},
},
};
axiosMock.get.resolves(responseMock);
await networkService.handlePagespeed(jobMock);
expect(networkService.logAndStoreCheck.calledOnce).to.be.true;
const checkData = networkService.logAndStoreCheck.getCall(0).args[0];
expect(checkData).to.include({
monitorId: jobMock.data._id,
status: true,
statusCode: 200,
message: "OK",
accessibility: 90,
bestPractices: 80,
seo: 70,
performance: 60,
});
expect(networkService.handleStatusUpdate.calledOnceWith(jobMock, true)).to.be.true;
});
it("should handle a successful PageSpeed response with missing data", async () => {
const responseMock = {
status: 200,
data: {
lighthouseResult: {
categories: {},
audits: {
"cumulative-layout-shift": { score: 0.1 },
"speed-index": { score: 0.2 },
"first-contentful-paint": { score: 0.3 },
"largest-contentful-paint": { score: 0.4 },
"total-blocking-time": { score: 0.5 },
},
},
},
};
axiosMock.get.resolves(responseMock);
await networkService.handlePagespeed(jobMock);
expect(networkService.logAndStoreCheck.calledOnce).to.be.true;
const checkData = networkService.logAndStoreCheck.getCall(0).args[0];
expect(checkData).to.include({
monitorId: jobMock.data._id,
status: true,
statusCode: 200,
message: "OK",
accessibility: 0,
bestPractices: 0,
seo: 0,
performance: 0,
});
expect(networkService.handleStatusUpdate.calledOnceWith(jobMock, true)).to.be.true;
});
it("should handle an error PageSpeed response", async () => {
const errorMock = { response: { status: 500 } };
axiosMock.get.rejects(errorMock);
await networkService.handlePagespeed(jobMock);
expect(networkService.logAndStoreCheck.calledOnce).to.be.true;
const checkData = networkService.logAndStoreCheck.getCall(0).args[0];
expect(checkData).to.include({
monitorId: jobMock.data._id,
status: false,
statusCode: 500,
message: "Internal Server Error",
accessibility: 0,
bestPractices: 0,
seo: 0,
performance: 0,
});
expect(networkService.handleStatusUpdate.calledOnceWith(jobMock, false)).to.be.true;
});
it("should handle an error PageSpeed response with unknown status", async () => {
const errorMock = { response: { status: undefined } };
axiosMock.get.rejects(errorMock);
await networkService.handlePagespeed(jobMock);
expect(networkService.logAndStoreCheck.calledOnce).to.be.true;
const checkData = networkService.logAndStoreCheck.getCall(0).args[0];
expect(checkData).to.include({
monitorId: jobMock.data._id,
status: false,
statusCode: 5000,
message: "Network Error",
accessibility: 0,
bestPractices: 0,
seo: 0,
performance: 0,
});
expect(networkService.handleStatusUpdate.calledOnceWith(jobMock, false)).to.be.true;
});
});
describe("NetworkService - getStatus", () => {
let dbMock, emailServiceMock, axiosMock, pingMock, loggerMock, httpMock, networkService;
beforeEach(() => {
dbMock = { getMonitorById: sinon.stub() };
emailServiceMock = sinon.stub();
axiosMock = sinon.stub();
pingMock = sinon.stub();
loggerMock = { error: sinon.stub() };
httpMock = sinon.stub();
networkService = new NetworkService(
dbMock,
emailServiceMock,
axiosMock,
pingMock,
loggerMock,
httpMock
);
});
afterEach(() => {
sinon.restore();
});
it("should return true if the job type is ping and handlePing is successful", async () => {
const job = { data: { type: networkService.TYPE_PING } };
sinon.stub(networkService, "handlePing").resolves(true);
const result = await networkService.getStatus(job);
expect(result).to.be.true;
});
it("should return false if the job type is ping and handlePing is not successful", async () => {
const job = { data: { type: networkService.TYPE_PING } };
sinon.stub(networkService, "handlePing").resolves(false);
const result = await networkService.getStatus(job);
expect(result).to.be.false;
});
it("should return true if the job type is http and handleHttp is successful", async () => {
const job = { data: { type: networkService.TYPE_HTTP } };
sinon.stub(networkService, "handleHttp").resolves(true);
const result = await networkService.getStatus(job);
expect(result).to.be.true;
});
it("should return false if the job type is http and handleHttp is not successful", async () => {
const job = { data: { type: networkService.TYPE_HTTP } };
sinon.stub(networkService, "handleHttp").resolves(false);
const result = await networkService.getStatus(job);
expect(result).to.be.false;
});
it("should return true if the job type is pagespeed and handlePagespeed is successful", async () => {
const job = { data: { type: networkService.TYPE_PAGESPEED } };
sinon.stub(networkService, "handlePagespeed").resolves(true);
const result = await networkService.getStatus(job);
expect(result).to.be.true;
});
it("should return false if the job type is pagespeed and handlePagespeed is not successful", async () => {
const job = { data: { type: networkService.TYPE_PAGESPEED } };
sinon.stub(networkService, "handlePagespeed").resolves(false);
const result = await networkService.getStatus(job);
expect(result).to.be.false;
});
it("should log an error and return false if the job type is unknown", async () => {
const job = { data: { type: "unknown" } };
const result = await networkService.getStatus(job);
expect(result).to.be.false;
expect(loggerMock.error.calledOnce).to.be.true;
expect(loggerMock.error.calledWith(`Unsupported type: unknown`)).to.be.true;
});
});
describe("NetworkService - logAndStoreCheck", async () => {
let dbMock, emailServiceMock, axiosMock, pingMock, loggerMock, httpMock, networkService;
beforeEach(() => {
dbMock = { getMonitorById: sinon.stub() };
emailServiceMock = sinon.stub();
axiosMock = sinon.stub();
pingMock = sinon.stub();
loggerMock = { error: sinon.stub() };
httpMock = sinon.stub();
networkService = new NetworkService(
dbMock,
emailServiceMock,
axiosMock,
pingMock,
loggerMock,
httpMock
);
});
const data = { monitorId: "12345" };
const writeToDB = sinon.stub();
afterEach(() => {
sinon.restore();
});
it("should log an error if `writeToDb` throws an error", async () => {
writeToDB.rejects(new Error("Database error"));
await networkService.logAndStoreCheck(data, writeToDB);
expect(loggerMock.error.calledOnce).to.be.true;
expect(loggerMock.error.calledWith(`Error writing check for ${data.monitorId}`)).to.be
.true;
});
it("should return the status of the inserted check if successful", async () => {
writeToDB.resolves({ status: true });
const result = await networkService.logAndStoreCheck(data, writeToDB);
expect(result).to.be.true;
});
it("should thrown an error if the check is not inserted (null)", async () => {
writeToDB.resolves(null);
await networkService.logAndStoreCheck(data, writeToDB);
expect(loggerMock.error.calledOnce).to.be.true;
expect(loggerMock.error.calledWith(`Error writing check for ${data.monitorId}`)).to.be
.true;
});
it("should thrown an error if the check is not inserted (undefined)", async () => {
writeToDB.resolves(undefined);
await networkService.logAndStoreCheck(data, writeToDB);
expect(loggerMock.error.calledOnce).to.be.true;
expect(loggerMock.error.calledWith(`Error writing check for ${data.monitorId}`)).to.be
.true;
});
});