useFakeTimers mocha chai sinon - not the right result on a test - mocha.js

I am trying to run a test where I want to verify that my helper file is running correctly, and if I have an expired token, I get an error kickback and cannot proceed.
I have a feeling that I can only fake the time directly in the test, and not outside of it. Thing is, I don't want to copy the jwt.verify function in my test because that defeats the purpose if I change the code in the actual helper file. Any help on this one to make this work?
I am faking the time with sinon. If I test to see what time I get now and after the clock tick, I do get the right results. But for some reason this is not applying to the function in another file.
my local.js file
const moment = require('moment');
const jwt = require('jsonwebtoken');
const secret = process.env.TOKEN_SECRET;
function encodeToken(user) {
const playload = {
exp: moment().add(1, 'hours').unix(), // expires the token in an hour
iat: moment().unix(),
sub: user.id
};
return jwt.sign(playload, secret);
}
function decodeToken(token, callback) {
const payload = jwt.verify(token, secret, function (err, decoded) {
const now = moment().unix();
console.log('tim: ' + decoded.exp); //just to see
console.log('now: ' + now); // just to see
if (now > decoded.exp) {
callback('Token has expired.');
}
callback(null, decoded);
});
}
module.exports = {
encodeToken,
decodeToken
};
and my test file:
process.env.NODE_ENV = 'test';
const chai = require('chai');
const should = chai.should();
const sinon = require('sinon');
const localAuth = require('../../src/server/auth/local');
describe('decodeToken()', function () {
var clock;
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
clock.restore();
});
it('should return a decoded payload', function (done) {
const token = localAuth.encodeToken({
id: 1
});
should.exist(token);
token.should.be.a('string');
clock.tick(36001000000);
localAuth.decodeToken(token, (err, res) => {
should.exist(err);
res.should.eql('Token has expired.');
done();
});
});
});

JWT checks the expiry and throws error by itself. So we just have to assert from the error message. I have made some changes to the code and made it working.
I tested this as below, (code snippets)
const moment = require('moment');
const jwt = require('jsonwebtoken');
const secret = 'abczzxczxczxc';
function encodeToken(user) {
const payload = {
exp: moment().add(1, 'hours').unix(), // expires the token in an hour
iat: moment().unix(),
sub: user.id
};
const token = jwt.sign(payload, secret);
return token;
}
function decodeToken(token, callback) {
jwt.verify(token, secret, function(err, decoded) {
callback(err, decoded);
});
}
module.exports = {
encodeToken,
decodeToken
};
Tested as below,
process.env.NODE_ENV = 'test';
const chai = require('chai');
const should = chai.should();
const sinon = require('sinon');
const localAuth = require('./');
describe('decodeToken()', function () {
var clock;
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
clock.restore();
});
it('should return a decoded payload', function (done) {
const token = localAuth.encodeToken({
id: 1
});
token.should.exist;
token.should.be.a('string');
clock.tick(36001000000);
localAuth.decodeToken(token, (err, res) => {
should.exist(err);
err.message.should.eql('jwt expired');
done();
});
});
});
Output
➜ faketimer ./node_modules/mocha/bin/mocha index_test.js
decodeToken()
✓ should return a decoded payload
1 passing (17ms)

Related

How can I use multiple await in cypress?

I am trying to fetch multiple data from UI using cypress.
First of all, I thought my selector is incorrect, but I have tried it with the same selector as the below codebase, but it still not working
below is the function
async getData(model?: any) {
const listSelector='[ng-repeat="workflow in vm.workflows track by $index"]';
const dataFromUi = {};
const data = await cy.get(listSelector); // this gives data
const data1 = await cy.get(listSelector); // this doesn't
dataFromUi['Test1'] = data;
dataFromUi['Test2'] = data1;
debugger;
return dataFromUi;
}
I was calling this method from a spec file, below is the calling spec file
describe('app test', () => {
beforeEach(() => {
cy.login();
cy.navigateUsingMenu('dasboard', '');
});
it('',async ()=>{
const result = await getData();
result.Test1 // contains data
result.Test2 // contains undefined
})
})
In data I am getting contents, but data1 returns undefined.
I have found a solution and that is using different methods and different it blocks. The below codebase is working, but I want them in a single block.
The service methods
async getData1(model?: any) {
const listSelector='[ng-repeat="workflow in vm.workflows track by $index"]';
const data = await cy.get(ListSelector);
return data;
}
async getData2(model?: any) {
const listSelector='[ng-repeat="workflow in vm.workflows track by $index"]';
const data1 = await cy.get(ListSelector);
return data1;
}
The spec
describe('app test', () => {
beforeEach(() => {
cy.login();
cy.navigateUsingMenu('dashboard', '');
});
it('',async ()=>{
const result = await getData1();
})
it('',async ()=>{
const result = await getData2();
})
})

Mock Lambda.invoke wrapped but not being called

I'm having an issue while trying to mock lambda.invoke which I'm calling from within another lambda function.
The function is wrapped (I can't use sinon after, it will tell me it's already wrapped).
The test keeps calling the lambda function on AWS instead of calling the mock.
It does the same if I use sinon instead of aws-sdk-mock.
test.js
const { handler1 } = require('../handler');
const sinon = require('sinon');
const AWSMock = require('aws-sdk-mock');
describe('invoke', () => {
beforeEach(() => {
invokeMock = jest.fn();
AWSMock.mock('Lambda', 'invoke', invokeMock);
// const mLambda = { invoke: sinon.stub().returnsThis(), promise: sinon.stub() };
// sinon.stub(AWS, 'Lambda').callsFake(() => mLambda);
});
afterEach(() => {
AWSMock.restore();
sinon.restore();
});
test('test1', async () => {
const event = { test: 'ok'};
const handler = await handler1(event);
expect(handler.statusCode).toBe(204);
});
});
and my lambda function is:
handler.js
const AWS = require('aws-sdk');
module.exports.handler1 = (event) => {
// The initialisation bellow has to be in the handler not outside.
const lambda = new AWS.Lambda({
region: 'us-west-2' //change to your region
});
let params = {
InvocationType: 'Event',
LogType: 'Tail',
FunctionName: 'handler2', // the lambda function we are going to invoke
Payload: JSON.stringify(event)
};
return new Promise((resolve, reject) => {
lambda.invoke(params, function(error, data) {
if(error) return reject(error);
const payload = JSON.parse(data.Payload);
if(!payload.success){
return resolve({ statusCode: 400});
}
return resolve({ statusCode: 204});
});
});
};
EDIT: So the issue I had was because I had my lambda initialisation (const lambda = new AWS.Lambda({})) outside the handler instead on inside. Thanks to stijndepestel's answer.
It is not entirely clear from the code you have shared, but presumably, you have a reference to lambda in your handler.js before you have wrapped the function in your test. Could you add the const lambda = new AWS.Lamda({}) line inside your handler function instead of outside of it?

NextJS API Route Returns Before Data Received?

I'm not sure what's going on here. I have set up an API route in NextJS that returns before the data has been loaded. Can anyone point out any error here please?
I have this function that calls the data from makeRequest():
export async function getVendors() {
const vendors = await makeRequest(`Vendor.json`);
console.log({ vendors });
return vendors;
}
Then the route: /api/vendors.js
export default async (req, res) => {
const response = await getVendors();
return res.json(response);
};
And this is the makeRequest function:
const makeRequest = async (url) => {
// Get Auth Header
const axiosConfig = await getHeader();
// Intercept Rate Limited API Errors & Retry
api.interceptors.response.use(
function (response) {
return response;
},
async function (error) {
await new Promise(function (res) {
setTimeout(function () {
res();
}, 2000);
});
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
token[n] = null;
originalRequest._retry = true;
const refreshedHeader = await getHeader();
api.defaults.headers = refreshedHeader;
originalRequest.headers = refreshedHeader;
return Promise.resolve(api(originalRequest));
}
return Promise.reject(error);
}
);
// Call paginated API and return number of requests needed.
const getQueryCount = await api.get(url, axiosConfig).catch((error) => {
throw error;
});
const totalItems = parseInt(getQueryCount.data['#attributes'].count);
const queriesNeeded = Math.ceil(totalItems / 100);
// Loop through paginated API and push data to dataToReturn
const dataToReturn = [];
for (let i = 0; i < queriesNeeded; i++) {
setTimeout(async () => {
try {
const res = await api.get(`${url}?offset=${i * 100}`, axiosConfig);
console.log(`adding items ${i * 100} through ${(i + 1) * 100}`);
const { data } = res;
const arrayName = Object.keys(data)[1];
const selectedData = await data[arrayName];
selectedData.map((item) => {
dataToReturn.push(item);
});
if (i + 1 === queriesNeeded) {
console.log(dataToReturn);
return dataToReturn;
}
} catch (error) {
console.error(error);
}
}, 3000 * i);
}
};
The issue that I'm having is that getVendors() is returned before makeRequest() has finished getting the data.
Looks like your issue stems from your use of setTimeout. You're trying to return the data from inside the setTimeout call, and this won't work for a few reasons. So in this answer, I'll go over why I think it's not working as well as a potential solution for you.
setTimeout and the event loop
Take a look at this code snippet, what do you think will happen?
console.log('start')
setTimeout(() => console.log('timeout'), 1000)
console.log('end')
When you use setTimeout, the inner code is pulled out of the current event loop to run later. That's why end is logged before the timeout.
So when you use setTimeout to return the data, the function has already ended before the code inside the timeout even starts.
If you're new to the event loop, here's a really great talk: https://youtu.be/cCOL7MC4Pl0
returning inside setTimeout
However, there's another fundamental problem here. And it's that data returned inside of the setTimeout is the return value of the setTimeout function, not your parent function. Try running this, what do you think will happen?
const foo = () => {
setTimeout(() => {
return 'foo timeout'
}, 1000)
}
const bar = () => {
setTimeout(() => {
return 'bar timeout'
}, 1000)
return 'bar'
}
console.log(foo())
console.log(bar())
This is a result of a) the event loop mentioned above, and b) inside of the setTimeout, you're creating a new function with a new scope.
The solution
If you really need the setTimeout at the end, use a Promise. With a Promise, you can use the resolve parameter to resolve the outer promise from within the setTimeout.
const foo = () => {
return new Promise((resolve) => {
setTimeout(() => resolve('foo'), 1000)
})
}
const wrapper = async () => {
const returnedValue = await foo()
console.log(returnedValue)
}
wrapper()
Quick note
Since you're calling the setTimeout inside of an async function, you will likely want to move the setTimeout into it's own function. Otherwise, you are returning a nested promise.
// don't do this
const foo = async () => {
return new Promise((resolve) => resolve(true))
}
// because then the result is a promise
const result = await foo()
const trueResult = await result()

errors creating test for passport using mocha

I've been working a while on getting a test to work with Mocha and Passport. I tried a couple articles on here, but I can't get them to work.
Currently, I have installed supertest and I'm trying that.
process.env.NODE_ENV = 'test';
var chai = require('chai');
var chaiHttp = require('chai-http');
var app = require('../app');
//var request = require('supertest')//(app);
//var server = request.agent(app);
//var Strategy = require('passport-strategy');
var Strategy = require('passport-local').Strategy;
var m_ticket_data = require('../model/ticket');
var m_Kbase_data = require('../model/Kbase');
var m_KbaseScript_schema = require('../model/KbaseScript');
var should = chai.should();
var expect = chai.expect;
chai.use(chaiHttp);
chai.use(require('chai-passport-strategy'));
describe('Ticket', function() {
var user, info;
before(function(done) {
console.log("hello from strategy");
chai.passport.use( new Strategy(function(username, password, done){
console.log("hello from strategy2");
done(null, { id: '1234' }, { scope: 'read' });
}
))
.success(function(u, i) {
user = u;
info = i;
done();
})
.req(function(req) {
//req.headers.authorization = 'Bearer vF9dft4qmT';
})
.authenticate();
done();
});
it ('blankout the database', function(done){
m_ticket_data.remove({}, function(){
done();
});
});
it('looks for a blank from /ticket/all', function (done) {
chai.request('http://127.0.0.1:5000')
.get('/ticket/all')
.end(function (err, res) {
res.status.should.equal(200);
console.log(res.body);
//expect(res.body).to.deep.equal({});
done();
});
});
});
I can't create a temp user for testing, so I was thinking I was going to have to overwrite the authentication. However, I'm having a problem doing that. I found this npm (https://github.com/jaredhanson/chai-passport-strategy), and I'm trying this as the latest.
This is a test I created that works fine. I don't need to authenticate for it.
process.env.NODE_ENV = 'test';
var chai = require('chai');
var chaiHttp = require('chai-http');
var server = require('../app');
var m_Kbase_data = require('../model/Kbase');
var m_KbaseScript_schema = require('../model/KbaseScript');
var should = chai.should();
var expect = chai.expect;
chai.use(chaiHttp);
describe('KBasefull', function() {
m_Kbase_data.collection.drop();
it('need to add a kbase article for testing /KBase', function (done) {
chai.request('http://127.0.0.1:5000')
.post('/kbase')
.send({Problem: "Problem", description: "Description", resolution: "Something to fix"})
.end(function(err, res){
res.should.have.status(200);
done();
});
});
}

How to use superagent when testing in Jest-CLI?

I need to fetch some real data in my tests from a remote url. I Superagent is not being mocked. I have done that by including node_modules/superagent/ in unmockedModulePathPatterns.
This is the file I am trying to test, the .end() function is never called.
This is my test, which fails with a timeout error.
jest.dontMock("../Stocks.js");
jest.dontMock("superagent");
describe("Stock Actions", () => {
var makeRequest = require('../Stocks')
pit("doesn't crash", function () {
var promise = makeRequest("Hello World")
promise.then(function (str) {
expect(str).toBe("yay");
});
return promise;
});
});
And this is the module it's trying to test:
import Reflux from 'reflux';
import request from 'superagent';
console.log("request-superagent", request)
const makeRequest = Reflux.createAction({ asyncResult: true });
const Store = Reflux.createStore({
init() {
this.listenTo(makeRequest, 'onMakeRequest');
},
onMakeRequest(url) {
request('GET', 'http://api.example.com/list/')
.end(function (err, res) {
console.log("res.text", res.text);
if (!res.ok) {
makeRequest.failed("aw");
}
makeRequest.completed("yay");
});
}
});
module.exports = makeRequest;
How do I use superagent in jest-cli?

Resources