regarding unit testing in node js with sinon - node.js

class FetchTenant {
constructor (){
this.Config = this._getConfig();
this.Token = this._getToken();
this.TenantMap = new Map();
}
async getTenantId(Id){
if(!this.TenantMap[Id]){
const serviceid = await this._getInfo(Id, false);
this.TenantMap[Id] = serviceid;
}
return this.TenantMap[Id];
}
_getConfig() {
return get_env_from_local({ name: 'env_1' });
}
async _getToken() {
const options = {
method: 'POST',
uri: `${this.Config.url}`,
json: true,
resolveWithFullResponse: false,
transform2xxOnly: true,
transform: body => body.access_token,
auth: {
username: this.Config.clientid,
password: this.Config.clientsecret
},
form: {
grant_type: 'client_credentials'
},
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
};
return request(options)
.catch(err => {
logger.error('Could not get Token', err.statusCode, err.message);
return null;
});
}
async _getInfo(Id, newtoken) {
if(newtoken){
this.accessToken = await this._getToken();
if(this.accessToken == null){
logger.error(`fetching token failed`);
return null;
}
}
const options = {
method: 'GET',
uri: `${this.Config.url}/xyz/${Id}`,
json: true,
resolveWithFullResponse: false,
transform2xxOnly: true,
transform: body => body.tenantId,
auth: {
bearer: this.accessToken
},
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
};
return request(options)
.catch(err => {
if(err.statusCode != 401) {
logger.error(`Could not get tenant id`, err.statusCode, err.message);
return null;
}
else {
return this._getServiceInstanceInfo(Id, true);
}
});
}
}
module.exports = FetchTenant;
this is the class that i have created.
how to write a unit test for this class using sinon (stub and mock),i have to test only the public functions and the only public function here is getTenantId(Id) where in all the other private functions have a http request which can give either a valid response or an error.
is there any way to test the public function by mocking all the other private functions. i want to pre-define the data which will be returned by each private function and the primary data they fetch from env and use to send the request.

You can use nock (https://www.npmjs.com/package/nock) to mock all your http calls and test the public function you would normally do.

Related

Implementation of rxjs BehaviourSubject in Next.js for state management not working

Trying to store jwt token on login using rxjs behavioursubject
Then creating a http request with Authorization: Bearer ${user.jwtToken} in the
I believe I need to have
a) initial value,
b) a source that can be turned into an observable
c) a public variable that can be subscribed
On log in the user is correctly added to the user subject here "userSubject.next(user);"
But whenever I try to create the bearer token its always null
// The Accounts Service
// initialise and set initial value
const userSubject = new BehaviorSubject(null);
const authApiUrl = "https:testApi";
export const accountService = {
` user: userSubject.asObservable(), get userValue() { return userSubject.value },
login,
getAllUsers
};
function login(email, password) {
return fetchWrapper.post(process.env.AuthApiUrl + '/accounts/authenticate', { email, password })
.then(user => {
userSubject.next(user);
localStorage.setItem('user', JSON.stringify(user));
return user;
});
}
function getAllUsers() {
return await fetchWrapper.get(process.env.AuthApiUrl + '/accounts/get-all-users');
}
}
// The fetchwrapper
export const fetchWrapper = {
get,
post
};
function post(url, body) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeader(url) },
credentials: 'include',
body: JSON.stringify(body)
};
return fetch(url, requestOptions).then(handleResponse);
}
function get(url) {
const requestOptions = {
method: 'GET',
headers: authHeader(url)
};
return fetch(url, requestOptions).then(handleResponse);
}
function authHeader(url) {
// return auth header with basic auth credentials if user is logged in and request is to the api url
// THE accountService.userValue IS ALWAYS NULL???
const user = accountService.userValue;
const isLoggedIn = user && user.jwtToken;
const isApiUrl = url.startsWith(process.env.AuthApiUrl);
if (isLoggedIn && isApiUrl) {
return { Authorization: `Bearer ${user.jwtToken}` };
} else {
return {};
}
}
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
if ([401, 403].includes(response.status) && accountService.userValue) {
// auto logout if 401 Unauthorized or 403 Forbidden response returned from api
accountService.logout();
}
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
return data;
});
}

How to create mock function and have good coverage

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:

How can I send different request in intercepter?

I'm trying to send different request in interceptor
I want to send accessToken in request header authorization for every request except one case
so I write this code in interceptor request.use
config.headers = {authorization: `Bearer ${accessToken}`};
but if this error occurred
error.response.data.code === 'expired'
I want to send refreshtoken in header authorization not accesstoken
so I write this code in interceptor.response.use.error
const { data } = await axios.post(
`${Config.API_URL}/user/refreshToken`,
{},
{ headers: { authorization: `Bearer ${refreshToken}` } }
);
this is my code
useEffect(() => {
axios.interceptors.request.use(async (config: any) => {
const accessToken = await EncryptedStorage.getItem("accessToken");
config.headers = { authorization: `Bearer ${accessToken}` };
return config;
});
axios.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
const {
config,
response: { status },
} = error;
if (status === 419) {
if (error.response.data.code === "expired") {
const originalRequest = config;
const refreshToken = await EncryptedStorage.getItem("refreshToken");
const { data } = await axios.post(
`${Config.API_URL}/user/refreshToken`,
{},
{ headers: { authorization: `Bearer ${refreshToken}` } }
);
return axios(originalRequest);
}
}
return Promise.reject(error);
}
);
}, [dispatch]);
how can i fix my code?
if i use my code if error.response.data.code === 'expired'
the headers.authorization accesstoken is still being requested.
Make it so your request interceptor only sets a default authorization header without overriding anything already present
axios.interceptors.request.use(async (config) => {
const accessToken = await EncryptedStorage.getItem("accessToken");
return {
...config,
headers: {
authorization: `Bearer ${accessToken}`,
...config.headers
}
}
});
You could also avoid making the getItem() request entirely which might save a little time
axios.interceptors.request.use(async (config) => {
if (!config.headers.authorization) {
config.headers.authorization = `Bearer ${await EncryptedStorage.getItem("accessToken")}`
}
return config;
});

Why does funtion in Node return undefined?

I need to configure the base url of my endpoints after I make successful http requests (status code: 200).
I've written the below function but it's not working as I was expecting... It always returns undefined. I know it's something about promises but I don't have much experience.
const get = require('./functions/functionGET');
function geturl() {
var baseip;
get(
"username",
"password",
"http://**.***.**.35:8048/TESTDEV02/ODataV4/Company"
)
.then((response) => {
if (response.statusCode === 200) {
baseip = "http://**.***.**.35:8048/TESTDEV02/ODataV4/Company('*******')";
} else {
get(
"username",
"password",
"http://**.***.**.36:8048/TESTDEV02/ODataV4/Company"
).then((response) => {
if (response.statusCode === 200) {
baseip = "http://**.***.**.36:8048/TESTDEV02/ODataV4/Company('*******')";
} else {
get(
"username",
"password",
"http://**.***.**.37:8048/TESTDEV02/ODataV4/Company"
).then((response) => {
if (response.statusCode === 200) {
baseip = "http://**.***.**.37:8048/TESTDEV02/ODataV4/Company('*******')";
}
});
}
});
}
})
console.log(baseip)
return baseip;
}
module.exports = geturl();
EDIT
This is the get() function:
const { promisify } = require('util');
var httpntlm = require('httpntlm');
const Logger = require('../../services/error_logger')
const logger = new Logger('app')
module.exports = function (username, password, url) {
var httpntlmGetAsync = promisify(httpntlm.get);
return httpntlmGetAsync({
url: url,
username: username,
password: password,
workstation: '',
domain: '',
headers: {
'Access-Control-Allow-Origin': '*',
'Accept': '*/*',
'Content-Type': 'application/json; charset=utf-8'
}
})
.catch(err => logger.error(`URL: ${url}, Error: ${err}`))
then(response => {
return response.body
});
};
Looks like you need to put await before each get() method. At the same time you have to declare your function as async:
async function geturl() { //...
Without an await the next line of you code after get() gets executed regardless if get() returned anything yet. Therefore baseip is left as undefined.
EDIT: this is assuming your get() method from ./functions/functionGET is asynchronous.

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