Chai not reaching .end() - node.js

I am using Mocha and Chai to test my Node/Express API, and I can't figure out why the test is not reaching the .end()
Here is the test:
it('should authenticate successfully with user credentials', function (done) {
agent
.post('/login')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send({ 'username': 'username', 'password': 'password'})
.end(function (err, res) {
console.log(res);
console.log('***************************Authenticated*********************************************');
expect(res).to.have.status(200);
});
done();
});
And here is the route I am hitting:
app.post('/login', passport.authenticate('ldapauth', { successRedirect: '/' }));
I figure my problem may be with the fact that there is no formal response, but rather a redirect, but I am not sure how to handle it.

The solution ended up being to move the done() callback into my .end() method. Thanks #robertklep

If you are testing async methods int mocha, you should call call method in callback function as below.
it('should authenticate successfully with user credentials', function (done) {
agent
.post('/login')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send({ 'username': 'username', 'password': 'password'})
.end(function (err, res) {
console.log(res);
console.log('***************************Authenticated*********************************************');
expect(res).to.have.status(200);
done();
});
});

I have the same problem with chai request. I want to wait for .end callback before going to another function. but I can't use mocha because I'm using cucumber. how can I wait for chai .end callback?
in fact, I want to log (1) first but it does not work properly
When('I submit with method {string}:', function (string, docString) {
chai.request(app)
.post(endpoint)
.send(docString)
.end(function (err, res) {
console.log(1)
response = res
})
});
Then('I recieved ok', function () {
console.log(2)
// expect(response.status).to.deep.equal(200)
});

Related

What is the proper way to test routes secured with jwt token?

While the following test passes I feel I'm doing this wrong. Am I expected to log in every time i need to test a secure route? I've tried passing global vars around after i get the initial token but passing vars i'm finding extremely counter intuitive. Passing variables in a before() call presents me same issue as passing / accessing global vars inside nested promises.
describe('Users', function(done) {
var testToken = 'my-test-token'
it('logs in', function(done) { // <= Pass in done callback
var rT = 'tttttt'
chai.request(urlroot)
.post('/login')
.type('form')
.send({ email: 'test_user_1#this.com', password: '9999' })
.end(function(err, res) {
expect(res).to.have.status(200);
expect(res.body.token).to.be.a('string');
done()
});
});
it('gets all users', function(done) { // <= Pass in done callback
// console.log(urlroot + '/users');
chai.request(urlroot)
.post('/login')
.type('form')
.send({ email: 'test_user_1#this.com', password: '9999' })
.end(function(err, res) {
chai.request(urlapi)
.get('/users?secret_token='+res.body.token)
.end(function(err, res){
console.log('data', res.body);
// expect(res.body).to.be.json()
})
});
});
});
What I do is use before() method to call my authenticate service to get the token in the same way that the aplication would, and store into a variable.
Something like:
var token = "";
before(async () => {
//Get token
token = "Bearer " + await getToken();
});
Then, in every test you want to use the credentials use .set()
it('...', function (done) {
chai
.request(url)
.set("Authorization", token) //Call .set() before .get()
.get("/users")
//...
})

mocha, chai testing for post in nodejs

I am new to unit testing in node.js with sequelize.js. I followed this tutorial to implement the api. It is working fine and I need to test it for unit testing.
First I tried to test post operation for User class. Following code is to be tested using mocha.
// create a user
app.post('/api/users', (req, res) => {
User.create(req.body)
.then(user => res.json(user))
})
My attempt is as below.
var index = require('../index');
describe('POST testing on user', function () {
it('should add a row', function (done) {
index.post('/api/users').send({ 'name': 'he' })
.end(function (err, res) {
chai.expect(res.status).to.equal(200);
done();
})
});
});
Then it will give this error saying index.post is not a function. Where should I get wrong and how can I correct it to execute the test case.
Your logic is quite wrong. It should be:
describe('/POST Create User', () => {
it('Create User Testing', (done) => {
let user = {
'name': 'he'
}
chai.request('http://localhost:3000')
.post('/api/users')
.send(user)
.end((err, res) => {
});
});
});
});

How to use Chai-HTTP with Sinon?

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

request params when using Mocha and Chai to test file node.js

In the code below, I use req.body to get param from request.
Now, I want get param by using req.user (or req.someName, ...) but I don't know how to do this.
it.only('updateEmail',function(done){
chai.request(server)
.post('updateEmail')
.send({
new_email: faker.internet.email()
})
.end(function(err, res){
if (err){
console.log('err: ', err)
done()
}
else {
console.log('res: ', res.body)
done()
}
});
});

Why am I getting a 401 response in my tests?

I am trying to test a route with authentication in my Node / Express / Mongoose back-end.
Here's the test file
var should = require('should');
var _ = require('lodash');
var async = require('async');
var app = require('../../../../app');
var request = require('supertest');
var mongoose = require('mongoose');
var User = mongoose.model('User');
var Firm = mongoose.model('Firm');
var firm, user, userPassword, createdFirm, loggedInUser;
describe('GET /api/firms', function(){
beforeEach(function (done) {
firm = new Firm({
company: 'My test company',
corporateMail: 'test.com'
});
userPassword = 'password';
user = new User({
fistname: 'Fake User',
lastname: 'Fake User',
email: 'test#test.com',
job: 'Partner',
firmName:firm.company,
password:userPassword,
isActivated:true,
_firmId:firm._id
});
function createFirm(cb){
request(app)
.post('/api/firms')
.send(firm)
.expect(201)
.end(function(err, res){
if ( err ) throw err;
createdFirm = res.body;
cb();
});
}
function createUser(cb){
request(app)
.post('/api/common/users')
.send(user)
.expect(200)
.end(function(err, res){
createdUser = res.body;
if ( err ) throw err;
cb();
});
};
async.series([function(cb){
createFirm(cb);
}, function(cb){
createUser(cb);
}], done);
});
afterEach(function (done) {
firm.remove();
user.remove();
done();
});
it('should respond with 401 error', function(done) {
request(app)
.get('/api/firms')
.expect(401)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
it('should login', function(done) {
request(app)
.post('/auth/local')
.send({email:user.email, password:user.password})
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
it('should respond with 200 after login', function(done) {
request(app)
.get('/api/firms')
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
In the workflow the firm object is created first and then returns its Id so I can create the user with the firmId as a reference.
I would like to test the /api/firms route after the user is authenticated but in spite of my various attempts (using superagent, logging in the before section) I always get a 401 response in the last should section instead of an expected 200.
Actually the important thing to keep in mind is, as KJ3 said, how the authentication is set up. In my case I forgot to mention that I was using jwt. The way it works is the following, you supply a username and a password and the server returns a token created with jwt.
So it makes sense to send the token back for each request in the tests.
The way to achieve this is first by storing the token after authentication in the before section
function createUser(cb){
request(app)
.post('/api/users')
.send(user)
.expect(200)
.end(function(err, res){
if ( err ) throw err;
authToken = res.body.token;
cb();
});
};
Then by adding .set in the request with the token in the correct format ('Bearer ' + token , which is defined in the authentication service):
it('should respond with 200', function(done) {
var authToken = 'Bearer ' + createdUser.token;
request(app)
.get('/api/firms')
.set('Authorization', authToken)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
In the case the test sends a 200 back, which is expected and sends a 401 if the .set(...) is commented out.
Good news is that this is achieved with supertest, so no need to add anything, less good news is that you need to add the .set(...) to each test request.
If you were to go through the last 2 tests in a browser, depending on how you have it setup, yes it would work thanks to cookies and sessions, but here the /api/firms test is independent of the auth/local test. So a 401 is the correct response.
It really depends on how your auth is setup, but you need to authenticate on the /api/firms test too. Either by sending the credentials again (every single one of my mocha tests authenticates each time) or implement sessions into the tests, see this SO post for some direction.

Resources