How to use Chai-HTTP with Sinon? - node.js

Here is the route I want to test:
app.post('/api/user', (req, res) => {
dbService.replaceUserOnDuplicate(req.body, function returnResponse(insertedId) {
if (insertedId === 0 || insertedId === req.body.id) {
return res.sendStatus(200);
}
// TODO_MINH: Send an appropriate error to handle by the front-end
return res.send({});
});
});
I can use chai-http to do something like this (psuedo-code):
it('test', function (done) {
chai.request(server)
.post('/api/user')
.send({ user: SomeUserObject })
.end(function (err, res) {
res.should.have.status(200);
done();
});
});
However, the api/users route makes a Database call. How would I use sinon to stub this method (replaceUserOnDuplicate) so that it returns a dummy response (like 0, or anything)?
Is it possible? I'm looking at the Chai-HTTP syntax and I see no room to insert any Sinon stubbed methods.
For reference, here is the dbService (mySQL node.js):
replaceUserOnDuplicate: function(user, callback) {
this.tryConnect().getConnection(function(err, con) {
var sql = queries.ReplaceUserOnDuplicate;
// Insert parameters
con.query(sql, [user.id, user.googleID, user.gender, user.firstName, user.lastName, user.email, user.isProfileSetUp, user.location, user.phoneNumber,
// On Duplicate Key Update parameters
user.googleID, user.gender, user.firstName, user.lastName, user.email, user.isProfileSetUp, user.location, user.phoneNumber],
function (err, result) {
con.release();
if (err) throw err;
return callback(result.insertId);
});
});
},
Thanks for your help!

A potential solution: if I use middleware to set the property of req.db to our dbService object, then I can dependency inject the dbService's calls within chai-http...By sending them parameters with the .send(). I believe .send() can be chained.
Is this valid?
Example (Middleware):
var exposeDb = function(req, resp, next){
req.dbService= dbService;
next();
};
app.use('/api/user', exposeDb, next);

Related

How to pass Data Between multiple routes in express?

app.post("/login", passport.authenticate("local",), function (req, res) {
const user = new Model({
username: req.body.username,
password: req.body.password,
});
req.login(user, function (err) {
if (err) {
console.log("wrong password");
} else {
passport.authenticate("local")(req, res, function () {
res.redirect("/admin");
});
}
});
});
app.post("/admin", function (req, res) {
Model.findOne({username: "siddharth"}).exec(function(err, foundList){
if(foundList)
{
const list = new linkModel({
linkTitle: req.body.linkTitle,
linkUrl: req.body.linkUrl,
});
foundList.links.push(list);
foundList.save();
res.redirect("/admin");
}
else
{res.send("redirect to home page and then login");
res.redirect("/login");
}
});
});
How can i pass the username when authenticated from login routeto other route(admin) where mongoose query is defined findone?
As i have defined it explicitly.
Or i simple terms how can i pass the data among routes ?
You can't. Instead use a middleware to do the checks you want and pass on the result to another middleware or return the response in case of error.

How to wait for controller function to finish using sinon.js and Express in Node.js

I'm trying to write a unit test for one of my Express controller functions, however, the sinon calls don't wait for the controller function to finish. Do I need to write the controller function as async so I can await in the test?
it('should allow creating', function (done) {
const req = {
body: {
name: 'something here',
isPrivate: 'true',
},
flash: sinon.spy(),
user: { _id: mongoose.Types.ObjectId() }
};
const res = {
redirect: sinon.spy()
}
leagueController.create(req, res);
req.flash.calledWith('success', 'League created').should.be.true;
res.redirect.calledWith('/leagues').should.be.true;
League.findOne({ name: 'something here' }, (err, league) => {
should.exist(league);
done();
});
});
Here is a simplified controller function:
function create(req, res) {
const { name, isPrivate } = req.body;
let newLeague = new League({
name: name,
isPrivate: isPrivate,
});
newLeague.save(function (err) {
if (err) {
req.flash('error', err.message || err);
debug(err.message);
return res.redirect('/leagues/new');
}
req.flash('success', 'League created');
debug('league created');
return res.redirect('/leagues');
});
}
If your controller isn't actually sending a network request (which I hope not in a unit test) , easiest way here would be to use fake-timers and do a small clock.tick(50).
If it actually does something asynchronous, then best way would be to return a promise from newLeague.save

passport.authenticate() using a Promise instead of a Custom Callback

passport.authenticate(), how can I define a Promise instead of using a Custom Ballback?
How to used passport.authenticate() is referenced within here:
http://www.passportjs.org/docs/authenticate/
Within this page, there is a section Custom Ballback:
If the built-in options are not sufficient for handling an authentication request, a custom callback can be provided to allow the application to handle success or failure.
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
The Custom Callback is defined as:
function(err, user, info){...}
What I wish to do is replace this Custom Callback with a Promise.
[Promise](resolve, reject)
.then(res => {
})
.catch(err => {
})
How can I do this? Thank you.
You can use the es6-promisify package. It is very easy to use, here is an example:
const {promisify} = require("es6-promisify");
// Convert the stat function
const fs = require("fs");
const stat = promisify(fs.stat);
// Now usable as a promise!
stat("example.txt").then(function (stats) {
console.log("Got stats", stats);
}).catch(function (err) {
console.error("Yikes!", err);
});
Thanks all for your helpful responses #sterling-archer and #el-finito
I had found a related issue within Passport.js Github repository helpful for using Passport to handle passport.authenticate() callback:
"Using node's promisify with passport"
export const authenticate = (req, res) =>
new Promise((resolve, reject) => {
passport.authenticate(
[passport strategy],
{ session: false },
(err, user) => {
if (err) reject(new Error(err))
else if (!user) reject(new Error('Not authenticated'))
resolve(user)
})(req, res)
})

Returning an Object from middleware function in Node.JS

I am new to Node.JS coming from a Java Background I am using express to build this Rest API . What I am trying to do is build the concept of a manager. I am looking for a elegant way of returning a user object in the following:
users route: user.js
router.get('/find/:email', function(req, res, next){
userWare.findUserByEmail(req, res, next)
});
middleware/manager: usermiddleware.js
module.exports = {
findUserByEmail: function(req, res, next) {
models.core_user.find({
where:{
email: req.params.email
}
}).then(function(user){
res.json(user)
}, function(err){
res.status(404).json(err);
});
},
}
So In this above function I would like to return the user object to the route instead of the json. so that I can create the json from the object in the route. The whole point of this manager class will be to fectch and return objects.
What you need to do is call the callback function with the data you need or return the promise.
Callback
user.js
router.get('/find/:email', function (req, res, next) {
userWare.findUserByEmail(req.params.email, function (err, data) {
// error as first parameter or null if no error occurred
if (err) {
return res.status(404).json(err);
}
res.json(user);
});
});
usermiddleware.js
module.exports = {
findUserByEmail: function (email, next) {
models.core_user.find({
where: {
email: email
}
}).then(
function (user) {
// async call of callback with user object
next(null, user);
},
function (err) {
// async call of callback with error
next(err);
}
);
}
};
Promise
You could also just return the promise returned by your model, then it would look like this:
user.js
router.get('/find/:email', function (req, res, next) {
userWare.findUserByEmail(req.params.email).then(
function (user) {
res.json(user);
},
function (err) {
res.status(404).json(err)
}
);
});
usermiddleware.js
module.exports = {
findUserByEmail: function (email) {
// return the promise to the caller
return models.core_user.find({
where: {
email: email
}
});
}
};

How to send response in Node Express with file and data

module.exports.validateUser = function (req, res) {
User.find({ 'username': req.body.username, 'password': req.body.password }, function (err, result) {
if (result.length) {
var gridfs = req.app.get('gridfs');
var readstream = gridfs.createReadStream({
_id: result[0]._doc.picture
});
req.on('error', function (err) {
res.send(500, err);
});
readstream.on('error', function (err) {
res.send(500, err);
});
// Just sends the file
//readstream.pipe(res);
res.send('This is incedible');
} else {
res.send(false);
}
});
};
Just after the user is validated I am getting the file associated with it. Further I want to send some data along with the fine in response. I've used multer and gridfs-stream. Is this achievable. If not, then does something looks fishy in this approach?

Resources