How to throw error in node js and catch it mocha - javascript

I cannot catch an error I throw in Node and catch it in mocha.
Please help!
Node:
Node code that throws error
app.put('/reviews/:id', (req, res) => {
var db = req.db;
Review.findById(req.params.id, 'title description', function (error, review) {
if (error) {
// console.error(error);
throw new Error('some error');
}
review.title = req.body.title;
review.description = req.body.description;
review.save(function (error) {
if (error) {
console.log(error)
}
res.send({
success: true
})
})
})
})
Mocha:
Mocha test
it('should throw exception for incorrect id', function (done) {
let updateReview = {
title: 'Updated Title',
description: 'Updated Description'
};
chai.request(app)
.get('/reviews')
.end(function (err, res) {
chai.request(app)
.put('/reviews/12')
.send(updateReview)
.end(function (err, res) {
expect(app).to.throw(Error);
done();
});
});
});
Error:
Error from the console.
Uncaught Error: some error

You can't expect an error to be thrown at the end of your http call.
You could test the return code for a specific request by adding a response instead of the throw line as the following :
app.put('/reviews/:id', (req, res) => {
var db = req.db;
Review.findById(req.params.id, 'title description', function (error, review{
if (error) {
// console.error(error);
return res.sendStatus(500);
}
review.title = req.body.title;
review.description = req.body.description;
review.save(function (error) {
if (error) {
console.log(error)
}
res.send({
success: true
})
})
})
And by updating your test :
it('should throw exception for incorrect id', function (done) {
let updateReview = {
title: 'Updated Title',
description: 'Updated Description'
};
chai.request(app)
.get('/reviews')
.end(function (err, res) {
chai.request(app)
.put('/reviews/12')
.send(updateReview)
.end(function (err, res) {
expect(res).to.have.status(500);
done();
});
});
});
If you want to test that a specific error is thrown for a given function, then you should call expect with the function as parameter. This could be done by isolating your database call in a function, for instance.

The .throw assertion only works on functions. More specifically: it should be passed a function reference, and when Chai calls that function, it expects that the function throws an error.
In your situation, app isn't a function, it's a reference to an Express application (it may be callable, but you're not testing the app function, you're testing how the app handles a particular request).
Throwing an error inside an Express app triggers Express's error handler. It will handle the error by sending a HTTP 500 response.
You can test for this:
expect(res).to.have.status(500);

Related

How to properly use nodejs soap

My code looks like this:
soap.createClient(url, function(err, client) {
if(err) {
res.status(500);
return res.send(err);
}
client.GetMemberPIN({pUserName: 'r'}, function(error, result) {
if(error) {
res.status(500);
return res.send(error)
}
return res.send(result);
});
});
I tried running it and it returns this error?
{
"code": "ECONNRESET"
}
I'd suggest testing a few things:
Make sure the url points to a valid WSDL document, e.g. https://www.crcind.com/csp/samples/SOAP.Demo.CLS?WSDL=1
Log which part of the process fails, e.g. the client creation, or the function call.
Here's a working example testing against a public server, this might help you to understand what could be going wrong with your code:
const soap = require('soap');
const url = 'https://www.crcind.com/csp/samples/SOAP.Demo.CLS?WSDL=1';
const args = { id: 1 };
soap.createClient(url, function(err, client) {
if (err) {
console.error("An error occurred creating client:", err);
return;
}
client.FindPerson(args, function(err, response) {
if (err) {
console.error("An error occurred calling client.FindPerson:", err);
return;
}
console.log("client.FindPerson: response:", response);
});
});

UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the clien

I get error: "UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client",
guessing that the problem is with promises, but I don't understand how to fix it.
How do I fix my code to avoid this error, but keep the logic and work with the database?
router.post("/addNote", (req, res) => {
let currentTime = new Date();
currentTime.setUTCHours(currentTime.getUTCHours() + 3);
const post = new PostModel({
title: req.body.inputHeader,
text: req.body.inputText,
author: req.body.author,
createdAt: currentTime
});
post.save().then(() => {
res.json({status: "saved"});
})});
router.get("/getNotes", (req, res) => {
PostModel.find().sort({createdAt: 'descending'}).then( (err, notes) => {
if (err)
res.json(err);
res.json(notes);
});
});
router.delete("/deleteNote/:id", (req, res) => {
PostModel.deleteOne(
{
_id: req.params.id
}
).then((notes) => {
if (notes)
res.json({status: "deleted"});
res.json({status: "error while deleting"});
});
});
router.put("/updateNote/:id", (req, res) => {
PostModel.findByIdAndUpdate(
req.params.id,
{
$set: req.body
},
err => {
if (err)
res.send(err);
res.send({status: "updated"})
}
).then((notes) => {
if (notes)
res.json({status: "update"});
res.json({status: "error while updating"});
});
});
router.get("/getNote", (req, res) => {
PostModel.findOne({ _id: req.params.id}).then(post => {
if (!post){
res.send({error: "not found"});
} else {
res.json(post)
}
});
});
router.post("/authorize", (req, res) => {
// bcrypt.hash ("", saltRounds, (err, hash) => {
// console.log(hash);
// });
let resultAuthorization = false;
if (req.body.login === authorization.login) {
resultAuthorization = bcrypt.compareSync(req.body.password, authorization.password);
}
if (resultAuthorization)
res.json({statusAuthorization: "correct"});
res.json({statusAuthorization: "incorrect"});
});
module.exports = router;
The problem is that you are calling res.json several times in one handler. When calling it a second time a response has already been sent so you can not send another response.
As tkausl already pointed out you are missing elses so that res.json is being called once.
You need to change your handlers similar to the /getNote handler.
The handler for the endpoint deleteNode/:id for example has to be changed to this:
router.delete("/deleteNote/:id", (req, res) => {
PostModel.deleteOne(
{
_id: req.params.id
}
).then((notes) => {
if (notes)
res.json({status: "deleted"});
else
res.json({status: "error while deleting"});
});
});
This else also needs to be added in /getNotes and /authorize.
The reason is you're trying to send a response more than once. Once the response is returned, if the program sends a response again, this error occurs.
The reason for the problem is that you do not return the current function after the if condition.
Let me explain with some codes
router.get("/getNotes", (req, res) => {
PostModel.find().sort({createdAt: 'descending'}).then( (err, notes) => {
if (err) {
res.json(err);
console.log('We encountered an error and sent the error as a response. But our function still continue...');
}
res.json(notes);
console.log('We tried to sent successfull response but function still continue');
});
});
So after the response, you should end the function or make sure that you do not call any other response function in the ongoing code stream/flow.
Lets fix your code.
router.get("/getNotes", (req, res) => {
PostModel.find().sort({createdAt: 'descending'}).then( (err, notes) => {
if (err) {
return res.json(err);
// It is not will be continued because the function returned with response.
}
return res.json(notes);
console.log('No console output')// It is will not be called because function returned.
});
});

MongoError: filter parameter must be an object

I am creating a rest api I have end point for Post/Movies: Request body should contain only movie title, and its presence should be validated Based on passed title, other movie details should be fetched from thememoviedb,and saved to application database.
app.post('/movies', (req, res) => {
request('https://api.themoviedb.org/3/discover/movie?callback=JSONP_CALLBACK&sort_by=popularity.desc&api_key=2931998c3a80d7806199320f76d65298', function (error, response, body) {
console.log('error:', error); // Print the error if one occurred and handle it
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
});
db.collection('movies').findOneAndUpdate(req.body.title,{
title: 'Avengers',
},(err, result) => {
if (err) {
res.send({
'error': 'An error has occured'
});
} else {
res.send(result.ops[0]);
}
});
});
when I run the app I get this error, what am I doing wrong here,? am new to nodejs and all this stuff just learning
Use $eq operator in the filter object $eq
{ <field>: { $eq: <value> } }
So the final snippet becomes like this:
app.post('/movies', (req, res) => {
/* code ... */
let { title } = req.body
db.collection('movies').findOneAndUpdate({ title: { $eq: title } }, { title: 'Avengers' }, (err, result) => {
if (err) {
res.send({ 'error': 'An error has occured' });
} else {
res.send(result.ops[0]);
}
});
});
Try the following,
db.collection('movies').findOneAndUpdate({title:req.body.title},{
$set:{
'Avengers'
}})

Can't set headers after they are sent when logging a user in

I'm scratching my head here, I'm using bcrypt to try and log in a user but I'm getting the following error:
Error: Can't set headers after they are sent.
Here's the code for that route:
router.post('/login', (req, res, next) => {
User.find({ email: req.body.email })
.exec()
.then(user => {
if (user.length < 1) {
res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
res.json(200).json({
message: 'Auth successful'
});
}
return res.status(401).json({
message: 'Auth failed 3'
});
});
})
.catch(err => {
res.status(500).json({
error: err
});
});
});
I thought the error might've come from the if else statements and trying to send a header twice, but I'm ending them before moving on the next conditional no? Am I miss-reading something here?
The error you see is caused when your code tries to send more than one response for the same request. The typical cause for this error is a coding mistake in how you handle asynchronous operations. In your code you show, I can see the following mistakes that can cause this error:
If user.length < 1, then you do res.status(401).json(...), but then you let the code continue to run where you then send other responses.
If you get an error from bcrypt.compare(), you send an error response and then let the code continue to run and send other responses.
If bcrypt.compare() succeeds, you send res.json(200).json(...) which is just wrong. You probably meant res.status(200).json(...).
If bcrypt.compare() succeeds and you have a result, you send two responses.
In looking at your code, it appears that you think that as soon as you do res.json() that the function returns and no other code executes. That is not the case. Until you hit a return, the rest of the code in that function continues to execute.
Here's one way to fix it (adding a few return statements and one else):
router.post('/login', (req, res, next) => {
User.find({ email: req.body.email }).exec().then(user => {
if (user.length < 1) {
res.status(401).json({message: 'Auth failed'});
return;
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
res.status(401).json({message: 'Auth failed'});
return;
}
if (result) {
res.json({message: 'Auth successful'});
} else {
res.status(401).json({message: 'Auth failed 3'});
}
});
}).catch(err => {
res.status(500).json({error: err});
});
});
Or, to do this in a little cleaner fashion where all responses with the same status are combined and all flow control is done with promises, you can do something like this:
const util = require('util');
bcyrpt.compareAsync = util.promisify(bcrypt.compare);
router.post('/login', (req, res, next) => {
User.find({ email: req.body.email }).exec().then(user => {
if (user.length < 1) {
throw new Error('No user match');
}
return bcrypt.compareAsync(req.body.password, user[0].password).then(result =>
if (!result) {
throw new Error('Auth failed 2');
}
res.json({message: 'Auth successful'});
}
}).catch(err => {
res.json(401).json({message: err.message});
});
}).catch(err => {
res.status(500).json({error: err});
});
});

Calling async function in node.js

I have an async function
async function getPostAsync() {
const post = await Post.findById('id');
// if promise was successful,
// but post with specific id doesn't exist
if (!post) {
throw new Error('Post was not found');
}
return post;
}
I am calling the function with
app.get('/', (req, res) => {
getPostAsync().then(post => {
res.json({
status: 'success',
});
}).catch(err => {
res.status(400).json({
status: 'error',
err
});
})
});
but I just receive
{
"status": "error",
"err": {}
}
I would expect to either get the error Post was not found or some error with the connection or something like that, but the variable err is simply an empty object in my catch statement.
Consider the following:
let e = Error('foobar');
console.log( JSON.stringify(e) )
This outputs {}, much like in your case. That's because errors don't serialize to JSON very well.
Instead, try this:
res.status(400).json({
status : 'error',
err : err.message // `String(err)` would also work
});

Categories

Resources