How to create mock function and have good coverage - node.js

I am finding it difficult to have code coverage and mocking request and few functions
cf.service.ts
import { omit } from "lodash";
var request = require('request');
const callForwardConfig = require('../../config/callForwardConfig').callForwardConfig;
import logger from "../../utils/logger";
import { SetCallforwardAs } from '../../interfaces/callforward.interface';
export async function appSerGetCallForwardingState(sid: string, token: string) {
try {
return await callForwardApiCall(sid, token).then((res) => {
return res;
})
} catch (e: any) {
throw new Error(e);
}
}
function callForwardApiCall(sid: string, token: string) {
let callforwardUrl = callForwardConfig.url.as;
return new Promise((resolve, reject) => {
request(`${callforwardUrl}?userId=${sid}`, {
method: 'get',
strictSSL: false,
mode: 'no-cors',
json: true,
headers: { 'Content-Type': 'application/json', Authorization: token},
}, (err: any, response: any, body: any) => {
if (err) {
reject(JSON.stringify(err))
} else {
resolve(body);
}
})
});
}
export async function putAppserCallForward(token: string, callForwardObj: SetCallforwardAs) {
return await updateCallForwardAs(token, callForwardObj).then((res) => {
return res;
})
}
async function updateCallForwardAs(token: string, callForwardObj: SetCallforwardAs) {
let callforwardUrl = callForwardConfig.url.as;
return await new Promise((resolve, reject) => {
let body = {
clusters: callForwardObj.clusters,
name: callForwardObj.name,
destination: callForwardObj.destination,
user: callForwardObj.user
}
logger.info(`App server update cfwrd Request object - ${JSON.stringify(body)}`)
request(`${callforwardUrl}`, {
method: 'put',
strictSSL: false,
mode: 'no-cors',
json: true,
body: body,
headers: { 'Content-Type': 'application/json', Authorization: token},
}, (err: any, response: any, body: any) => {
if (err) {
logger.error(`App server call forward update failure USER - ${callForwardObj.sid}`, JSON.stringify(err));
reject(JSON.stringify(err));
} else {
if (!body['success'])
logger.error(`App server call forward update failure USER - ${callForwardObj.sid} - Error - ${JSON.stringify(body)}`);
else
logger.info(`App server call forward update success USER - ${callForwardObj.sid}`);
resolve(body);
}
})
});
}
I have written test as below:
import createServer from "../../utils/server";
const appserService = require('../../service/provider/appser.service');
const request = require('request');
const app = createServer();
jest.mock('request');
const sid = 'A121';
describe("appserver service", () => {
it("appSerGetCallForwardingState", async () => {
const callForwardApiCallMock = jest.spyOn(appserService, 'callForwardApiCall');
callForwardApiCallMock.mockImplementation(() => {
return Promise.resolve('success');
});
appserService.appSerGetCallForwardingState(sid, 'token').then((res: any) => {
expect(res).toBe('success');
});
});
it("callForwardApiCall", async () => {
request.get.mockResolvedValue({ "success": "true" });
appserService.callForwardApiCall(sid, 'token').then((res: any) => {
expect(res).toBe({ "success": "true" });
});
});
it("callForwardApiCall error", async () => {
request.get.mockRejectedValue(new Error('error'));
appserService.callForwardApiCall(sid, 'token').then((res: any) => {
expect(res).toBe({ "success": "true" });
});
});
});
I am struggling to have good code coverage at - least 90%.
request object also needs to be mocked, and functions are not being exported like callForwardApiCall also not able to access from test file
Here is the report:

Related

One signal push notification node js API

Push notification node js API using One signal
Hello guys, I've watched a tutorial to implement push notifications on flutter app project.
the code I'll show is how to set up a push notification on node js API using one signal.
I need help to know how to view the notification using One Signal API.
here is the notification service folder
notification.services.js
const { ONE_SIGNAL_API_KEY } = require('../utils/config')
const { info } = require('../utils/logger')
const sendNotification = async (data, callback) => {
const headers = {
'Content-Type': 'application/json; charset=utf-8',
Authorization: 'Basic ' + ONE_SIGNAL_API_KEY,
}
const options = {
host: 'onesignal.com',
port: 443,
path: '/api/v1/notifications',
method: 'POST',
headers: headers,
}
const https = require('https')
const req = https.request(options, res => {
res.on('data', data => {
info(JSON.parse(data))
return callback(null, JSON.parse(data))
})
})
req.on('error', e => {
return callback({
message: e,
})
})
req.write(JSON.stringify(data))
req.end()
}
here is the notification controller folder
notification.controller.js
const { ONE_SIGNAL_APP_ID } = require('../utils/config')
const notificationsService = require('../services/notifications.services')
const sendNotification = (req, res, next) => {
const message = {
app_id: ONE_SIGNAL_APP_ID,
headings: { en: 'All Devices' },
contents: { en: 'Send push notifications to all devices' },
included_segments: ['All'],
content_available: true,
small_icon: 'ic_notification_icon',
data: {
// eslint-disable-next-line quotes
PushTitle: "Porc'Ivoire",
},
}
notificationsService.sendNotification(message, (error, results) => {
if (error) {
next(error)
}
return res.status(200).send({
message: 'Success',
data: results,
})
})
}
const sendNotificationToDevice = (req, res, next) => {
var message = {
app_id: ONE_SIGNAL_APP_ID,
headings: { en: '🤑 Paiement accepté' },
contents: {
en: 'Votre paiment a été effrctué avec succès',
},
included_segments: ['included_player_ids'],
include_player_ids: req.body.devices,
content_available: true,
small_icon: 'ic_notification_icon',
data: {
// eslint-disable-next-line quotes
PushTitle: "Porc'Ivoire",
},
}
notificationsService.sendNotification(message, (error, results) => {
if (error) {
next(error)
}
return res.status(200).send({
message: 'Success',
data: results,
})
})
}
module.exports = {
sendNotification,
sendNotificationToDevice,
}

Problem with axios and activeCampaign api

i have created firebase cloud function to create contact in activeCampain. I have problem with catch errors from activeCampaign. If i sent request direct to activeCampain from insomnia everything works correctly, but if i use firebase cloud function with axios something goes wrong.
I will show the code.
I created a contact before and now it makes another query with the same data directly to activeCampaign api:
and i get expected result. But if made same request by firebase function i get properly status code but i don't see errors response from activeCampaign
FirebaseCloud function code:
const functions = require("firebase-functions");
const axios = require("axios");
const cors = require("cors")({ origin: true });
const addTagToContact = async (contactId: string, tagId: string) => {
try {
await axios({
method: "post",
url: "https://xyz.api-us1.com/api/3/contactTags",
headers: {
"Api-Token": "api-token",
},
data: {
contactTag: {
contact: contactId,
tag: tagId,
},
},
enter code here
});
} catch (e) {
console.error(e);
}
};
export const createNewContact = functions.https.onRequest((request: { body: any; }, response: { status: (arg0: number) => void; send: (arg0: { response?: unknown; status?: string; }) => void; }) => {
cors(request, response, async () => {
const newContactData = request.body;
if(!newContactData.email || !newContactData.fieldValues) {
return response.send({
response: 'No contact data provided'
})
}
try {
const responseActiveCampaign = await axios({
method: "post",
url: "https://xyz.api-us1.com/api/3/contacts",
headers: {
"Api-Token": "api-token",
"Content-Type": "application/json"
},
data: {
contact: newContactData,
},
});
console.log('response active campaign console log', responseActiveCampaign)
await addTagToContact(responseActiveCampaign.data.contact.id, "1")
return response.send({
response: responseActiveCampaign.data
})
} catch (error) {
console.error('catch error', error);
response.status(500);
response.send({
response: error,
});
}
});
});
response from this query:
How can i fix it? I would like to get error response from activeCampaign to use on my frontend
the catch should be like this:
catch (error) {
console.error('catch error', error);
response.status(500);
response.send({
response: error.response.data,
});
}

How to receive re-requested data when data requested by mounted() is re-requested by aixos interceptor

the code below is the first request for get list data
mounted() {
this.getList()
},
methods: {
handleClick(row) {
console.log(row.id)
console.log(row.url)
this.$router.push({ name: 'Main', query: { id: row.id } })
},
getList() {
axios
.get('/api/v1/requests/all', {
params: {
userId: this.$store.state.userInfo.id,
},
})
.then(response => {
let moment = require('moment')
for (var item of response.data.data) {
item.createdAt = moment(item.createdAt).format(
'YYYY-MM-DD HH:mm:ss',
)
}
this.items = response.data.data
})
.catch(error => {
console.log(error)
})
}
my interceptor
axios.interceptors.response.use(
function (response) {
return response
},
async function (error) {
const originalRequest = error.config
if (error.response.status === 401 && !originalRequest._retry) {
error.response.config._retry = true
sessionStorage.removeItem('access-token')
let headers = {
grant_type: 'refresh_token',
Authorization: sessionStorage.getItem('refresh-token'),
}
axios
.post('/api/v1/users/refresh_token', {}, { headers: headers })
.then(response => {
let token = response.data.data
sessionStorage.setItem('access-token', token)
originalRequest.headers['Authorization'] = token
originalRequest.headers['grant_type'] = 'grant_type'
return axios.request(originalRequest)
})
.catch(error => {
console.log(error)
alert('blablabla.')
})
}
return Promise.reject(error)
},
)
the flow is i understand
1.token expired
2.move to list page
3.mounted hook is request data
4.getList -> axios get('~~request/all')
5.interceptor->axios post('~~~refresh_token')
6.re request with new token(request/all)
7.re request is 200, but not update list page
i'd really appreciate your help :)
Seems like you need to return the second request (await for result and return). Right now the result of second request seems to be ignored
axios.interceptors.response.use(
function (response) {
return response;
},
async function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
error.response.config._retry = true;
sessionStorage.removeItem("access-token");
let headers = {
grant_type: "refresh_token",
Authorization: sessionStorage.getItem("refresh-token"),
};
const [secondError, res] = await axios // await here
.post("/api/v1/users/refresh_token", {}, { headers: headers })
.then(async (response) => {
let token = response.data.data;
sessionStorage.setItem("access-token", token);
originalRequest.headers["Authorization"] = token;
originalRequest.headers["grant_type"] = "grant_type";
return [null, await axios.request(originalRequest)];
})
.catch((err) => {
console.log(err);
alert("blablabla.");
return [err, null];
});
// Handle here
if(secondError) {
return Promise.reject(secondError);
}
return Promise.resolve(res)
}
return Promise.reject(error);
}
);
The above solution worked for me perfectly. here's the modified code that I used for my requirement.
export default function axiosInterceptor() {
//Add a response interceptor
axios.interceptors.response.use(
(res) => {
// Add configurations here
return res;
},
async function (error) {
const originalRequest = error.config;
let secondError, res
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
[secondError, res] = await axios({
method: "POST",
url: `${baseURL}/account/token/refreshtoken`,
withCredentials: true,
headers: { "Content-Type": "application/json" },
})
.then(async (response) => {
//handle success
return [null, await axios.request(originalRequest)];
}).catch(function (error) {
console.error(error)
});
}
if (secondError) {
return Promise.reject(secondError);
}
return Promise.resolve(res)
}
);
}

node.js when different users make request at the same time returns the same response

I have node.js web app with such code
import request from 'request';
export const promisedRequest = (options: any): any => {
const deferred = new Promise((resolve: any, reject: any) => {
return request(options, (error: any, response: any, body: any) => {
if (error || !(response.statusCode >= 200 && response.statusCode <= 208)) {
reject({ error: error || response });
} else {
resolve({ response, body });
}
});
});
return deferred;
};
and it calls in this way
export const getProduct = (token: string, barcode: string): Promise<any> =>
promisedRequest({
url: `${process.env.API_URL}`,
method: 'POST',
json: true,
headers: {
'X-Token': token,
},
body: {
barcode: barcode,
},
})
.then(({ body }: any) => body.data[0])
.catch((error: any): void => {
const errorMessage = `Cannot get product: Product barcode: ${barcode}`;
console.error(errorMessage, error);
throw Error(errorMessage);
});
I checked the browser and requests send right but if I make requests almost at the same time (~2 milliseconds) I get the same response for two users.
Please help me with this. Thank you very much.

Set Mock Response for JWT Module NodeJS

I am writing Test case using JEST in NodeJs inside AzureFunction.
Im trying to mock JWT module outcome inside my index.test.js , however its not working and getting timedout. I wonder is it the return datatype mismatch? How to set response similar to cb of jwt verify method?
Here is my sample code. Please suggest!
Index.js
const JWT = require('./jwtDecoder')
module.exports = function(context, req) {
try {
JWT(req.body, process.env.jwtsecret, function(err, decoded) {
if (err) {
context.log("Invalid JWT::" + req.body);
context.res = {
headers: {
'Content-Type': 'application/json'
},
status: 400,
body: {
"error": err
}
};
context.done();
} else {
context.log("JWT Authentication Successful:");
context.res = {
headers: {
'Content-Type': 'application/json'
},
status: 200,
body: {
"message": "success"
}
};
context.done();
}
});
} catch (err) {
context.log("Exception in main function, PushHttpFunction:" + err);
context.res = {
headers: {
'Content-Type': 'application/json'
},
status: 500,
body: {
"error": err
}
};
context.done();
}
}
jwtDecoder.js
'use strict';
module.exports = (body, secret, cb) => {
console.log('inside jwtDecoder');
if (!body) {
return cb(new Error('invalid jwt data'));
}
require('jsonwebtoken').verify(body.toString('utf8'), secret, { algorithm: 'HS256' }, cb);
};
index.test.js
let indexTest = require('../index')
const { runStubFunctionFromBindings } = require('stub-azure-function-context')
let JWT = require('../jwtDecoder')
jest.mock("../jwtDecoder.js")
/* verify.mockImplementation(() => () => ({
err: new Error('invalid jwt data'),
decoded: 'ok'
})); */
JWT.mockImplementation(() => new Promise(function(resolve, reject) {
resolve('ok');
}));
beforeAll(() => {
process.env = Object.assign(process.env, {
NODE_ENV: "test",
});
});
describe('Simple Testing', () => {
test('return 200 by mocking simpleFunc response" ', async() => {
let request = {
body: "dummy.jwt.zT5p"
};
const context = await runStubFunctionFromBindings(indexTest, [
{ type: 'httpTrigger', name: 'req', direction: 'in', data: request },
{ type: 'http', name: 'res', direction: 'out' },
], new Date());
console.log('mockedResp::', context);
expect(context.res.status).toEqual(200);
}, 30000);
});
Basically you are mocking wrong, you can keep only this line:
jest.mock('./jwtDecoder.js', () => (res, req, cb) => cb(null, 'ok'))
as you need to mock callback
and remove all this part:
jest.mock("../jwtDecoder.js")
JWT.mockImplementation(() => new Promise(function(resolve, reject) {
resolve('ok');
}));

Resources