Mocha and Chai test expect not to throw error - mocha.js

I am doing some testing with Mocha and Chai
I have some functions that don't have a return value but if there is an error they throw an Error
I would like to test as the are passing the test if they don't throw an Error
for example
async function sum(a, b){
Promise.resolve()
.then()
if(typeof(a) !== 'number')
throw new Error('a should be a number')
if(typeof(b) !== 'number')
throw new Error('b should be a number')
console.log('the sum is ', a + b)
return
}
Then I want to test that it is working with something like this
expect(sum(1, 2)).not.to.throw()

The way to solve this was to use chai-as-promise library
const chai = require('chai');
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
const expect = chai.expect;
expect(sum(1, 2)).to.eventually.not.be.rejectedWith(Error)

Related

MailParser-mit returns undefined for attachments result

I am parsing email using mailparser-mit npm module. Below is the code.
const MailParser = require('mailparser-mit').MailParser;
const getAttachments = async(fullEmailBody) => {
try {
const mailparser = new MailParser();
mailparser.on('end', async function (mail_object) {
attachments = mail_object.attachments; // extract attachment from email
return attachments // attachment is present, I can console.log it as well but when i return it, it is not present at the caller
});
mailparser.write(fullEmailBody);
mailparser.end();
} catch (err) {
logger.error('Error:::::' + JSON.stringify(err));
}
}
let result = getAttachments(emailBody);
console.log("Result", result); // undefined
How can I get the results ?
I have tried using await at saveAttachmentToCos but still it is undefined

How to get error from backend with axios?

I'm trying to display an error I recieve in my backend to the user in my JSX frontend file.
This is the initial call from frontend
dispatch(createGoal({ values }))
Goalslice, directly called from JSX:
export const createGoal = createAsyncThunk(
'goals/create',
async (goalData, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token
return await goalService.createGoal(goalData, token)
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message)
}
}
)
Goalservice, directly called from goalslice:
const createGoal = async (goalData, token) => {
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
}
const response = await axios.post(API_URL, goalData, config)
return response.data
}
Goalcontroller, my backend:
const setGoal = asyncHandler(async (req, res) => {
const goals = await Goal.find({ user: req.user.id })
var count = Object.keys(goals).length
if(count >2){
res.status(400)
throw new Error('Maximum of 3 trackers per user')
}
if (!req.body.values) { //if this isnt there. check if the body is there.
res.status(400) //This is an error
throw new Error('Please add a date field') //this is express error handler
}
console.log(req.body.values.dates)
const goal = await Goal.create({
values: req.body.values.dates, //get from request body
permit: req.body.values.permits,
numpermit: req.body.values.num,
user: req.user.id,
})
res.status(200).json(goal)
})
I want to display this error:
throw new Error('Maximum of 3 trackers per user')
I tried a try/catch method, but I'm very new to this and I feel like i'm missing a very key point in how it all fits together.
This is my custom error handler if it helps:
const errorHandler = (err, req, res, next) => { //overwrite express error handler, next to handle any new req
const statusCode = res.statusCode ? res.statusCode : 500 //500 is server error. conditional
res.status(statusCode)
res.json({
message: err.message,
stack: process.env.NODE_ENV === 'production' ? null : err.stack, //gives additional info if in development mode : is else
})
}
module.exports = { //export for others to use
errorHandler,
}

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()

How can I create subsegments in AWSXRay?

Objective: show errors on AWS X-ray (all errors from lambda).
'use strict';
const AWSXRay = require('aws-xray-sdk-core'),
AWS = AWSXRay.captureAWS(require('aws-sdk')),
env = process.env;
AWS.config.update({
region: env.REGION
});
const dynamodbDocumentClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event, context, callback) => {
let seg=AWSXRay.getSegment();
try {
const params = {
/*PARAMS HERE*/
};
let res = await dynamodbDocumentClient.scan(params).promise();
throw('ERROR FOR TESTING');
return res;
//callback(null,res);
}
catch(err) {
let subseg=seg.addNewSubsegment('error');
subseg.addMetadata("error", "error", "my_error");
subseg.addAnnotation('errr', 'this is a test');
subseg.addError(err);
subseg.addErrorFlag();
subseg.close();
console.log('==ERROR==',err);
return err;
}
};
When I use AWSXRay.captureAWS the subsegment 'error' doesnt show on X-ray. If I dont use captureAWS the error appear in X-ray correctly.
The issue is likely with how you're structuring your Lambda. First, you shouldn't be using callback in an async function handler, async function handlers should return native promises or errors per the docs.
If you want to use a callback to return the result of your function, which it seems like you do, the handler should be synchronous. That being said, you're throwing your error after the callback, which will not be reflected in X-Ray because the X-Ray segment for Lambda functions closes when the callback is called. If an error is thrown before the callback (e.g. during the DynamoDB call) it should be captured in your error subsegment.
EDIT
After the original post was edited, I was unable to reproduce the error with this code:
const AWSXRay = require('aws-xray-sdk');
const AWS = AWSXRay.captureAWS(require('aws-sdk'));
const ddb = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event, context) => {
try {
const res = await ddb.scan({TableName: 'scorekeep-game'}).promise();
throw('Test Error');
return result;
} catch (e) {
console.log('caught!');
var sub2 = AWSXRay.getSegment().addNewSubsegment('err');
sub2.addError(e);
sub2.addErrorFlag();
sub2.close();
return e;
}
};

Using Promise.all with assertion

I have a small test (Mocha/Chai), which tests that the encryptPass function returns the correct hash:
const assert = require('chai').assert;
const bcrypt = require('bcrypt');
var encryptPass = require('../../shared/helpers/encryptPass.js');
var createUUID = require('../../shared/helpers/createUUID.js');
describe('encryptPass', function() {
it('returns correct password hash', () => {
var pass = createUUID();
var encryptedPass = encryptPass(pass);
Promise.all([pass, encryptedPass]).then(values => {
let [pass, encryptedPass] = values;
var compareResult = bcrypt.compareSync(pass, encryptedPass);
assert.equal(compareResult, true);
});
});
});
My concern is that there is that the assertion is assuming that the Promise.all function will work without throwing an error. How best to handle this?
Just return the promise that results from your test:
it('returns correct password hash', () => {
var pass = createUUID();
var encryptedPass = encryptPass(pass);
// Just return...
return Promise.all([pass, encryptedPass]).then(values => {
let [pass, encryptedPass] = values;
var compareResult = bcrypt.compareSync(pass, encryptedPass);
assert.equal(compareResult, true);
});
});
This will take care of telling Mocha that your test is asynchronous. In your original code, Mocha won't wait for the test to complete. Returning the promise forces it to wait. Secondly, by returning the promise you also control whether the test will be successful or not. If any of the promises passed to Promise.all is rejected, that's a test failure. If the code in your .then handler fails, that's also a test failure. For instance if assert.equal fails, an AssertionError is raised, which results in a promise rejection, which Mocha treats as a failure.

Resources